diff --git a/.idea/dictionaries/irmen.xml b/.idea/dictionaries/irmen.xml
new file mode 100644
index 000000000..097618853
--- /dev/null
+++ b/.idea/dictionaries/irmen.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..9c754da3c
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt
index e9bd3a32c..f25963683 100644
--- a/compiler/src/prog8/CompilerMain.kt
+++ b/compiler/src/prog8/CompilerMain.kt
@@ -1,6 +1,12 @@
package prog8
import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.base.checkIdentifiers
+import prog8.ast.base.checkRecursion
+import prog8.ast.base.checkValid
+import prog8.ast.base.reorderStatements
+import prog8.ast.statements.Directive
import prog8.astvm.AstVm
import prog8.compiler.*
import prog8.compiler.target.c64.AsmGen
diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt
deleted file mode 100644
index ff4d7be27..000000000
--- a/compiler/src/prog8/ast/AST.kt
+++ /dev/null
@@ -1,2633 +0,0 @@
-package prog8.ast
-
-import org.antlr.v4.runtime.IntStream
-import org.antlr.v4.runtime.ParserRuleContext
-import org.antlr.v4.runtime.tree.TerminalNode
-import prog8.compiler.RuntimeValue
-import prog8.compiler.HeapValues
-import prog8.compiler.IntegerOrAddressOf
-import prog8.compiler.target.c64.Petscii
-import prog8.functions.BuiltinFunctions
-import prog8.functions.NotConstArgumentException
-import prog8.functions.builtinFunctionReturnType
-import prog8.parser.CustomLexer
-import prog8.parser.prog8Parser
-import java.io.CharConversionException
-import java.io.File
-import java.nio.file.Path
-import kotlin.math.abs
-import kotlin.math.floor
-
-
-/**************************** AST Data classes ****************************/
-
-enum class DataType {
- UBYTE,
- BYTE,
- UWORD,
- WORD,
- FLOAT,
- STR,
- STR_S,
- ARRAY_UB,
- ARRAY_B,
- ARRAY_UW,
- ARRAY_W,
- ARRAY_F;
-
- /**
- * is the type assignable to the given other type?
- */
- infix fun isAssignableTo(targetType: DataType) =
- // what types are assignable to others without loss of precision?
- when(this) {
- UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
- BYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
- UWORD -> targetType == UWORD || targetType == FLOAT
- WORD -> targetType == WORD || targetType==UWORD || targetType == FLOAT
- FLOAT -> targetType == FLOAT
- STR -> targetType == STR || targetType==STR_S
- STR_S -> targetType == STR || targetType==STR_S
- in ArrayDatatypes -> targetType === this
- else -> false
- }
-
-
- infix fun isAssignableTo(targetTypes: Set) = targetTypes.any { this isAssignableTo it }
-
- infix fun biggerThan(other: DataType) =
- when(this) {
- in ByteDatatypes -> false
- in WordDatatypes -> other in ByteDatatypes
- else -> true
- }
-}
-
-enum class Register {
- A,
- X,
- Y
-}
-
-enum class RegisterOrPair {
- A,
- X,
- Y,
- AX,
- AY,
- XY
-} // only used in parameter and return value specs in asm subroutines
-
-enum class Statusflag {
- Pc,
- Pz,
- Pv,
- Pn
-}
-
-enum class BranchCondition {
- CS,
- CC,
- EQ,
- Z,
- NE,
- NZ,
- VS,
- VC,
- MI,
- NEG,
- PL,
- POS
-}
-
-val IterableDatatypes = setOf(
- DataType.STR, DataType.STR_S,
- DataType.ARRAY_UB, DataType.ARRAY_B,
- DataType.ARRAY_UW, DataType.ARRAY_W,
- DataType.ARRAY_F)
-
-val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
-val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
-val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
-val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
-val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
-val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
-val ArrayElementTypes = mapOf(
- DataType.ARRAY_B to DataType.BYTE,
- DataType.ARRAY_UB to DataType.UBYTE,
- DataType.ARRAY_W to DataType.WORD,
- DataType.ARRAY_UW to DataType.UWORD,
- DataType.ARRAY_F to DataType.FLOAT)
-
-
-class FatalAstException (override var message: String) : Exception(message)
-
-open class AstException (override var message: String) : Exception(message)
-
-class SyntaxError(override var message: String, val position: Position) : AstException(message) {
- override fun toString() = "$position Syntax error: $message"
-}
-
-class NameError(override var message: String, val position: Position) : AstException(message) {
- override fun toString() = "$position Name error: $message"
-}
-
-open class ExpressionError(message: String, val position: Position) : AstException(message) {
- override fun toString() = "$position Error: $message"
-}
-
-class UndefinedSymbolError(symbol: IdentifierReference)
- : ExpressionError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
-
-
-data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
- override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
-}
-
-
-interface IAstProcessor {
- fun process(program: Program) {
- program.modules.forEach { process(it) }
- }
-
- fun process(module: Module) {
- module.statements = module.statements.asSequence().map { it.process(this) }.toMutableList()
- }
-
- fun process(expr: PrefixExpression): IExpression {
- expr.expression = expr.expression.process(this)
- return expr
- }
-
- fun process(expr: BinaryExpression): IExpression {
- expr.left = expr.left.process(this)
- expr.right = expr.right.process(this)
- return expr
- }
-
- fun process(directive: Directive): IStatement {
- return directive
- }
-
- fun process(block: Block): IStatement {
- block.statements = block.statements.asSequence().map { it.process(this) }.toMutableList()
- return block
- }
-
- fun process(decl: VarDecl): IStatement {
- decl.value = decl.value?.process(this)
- decl.arraysize?.process(this)
- return decl
- }
-
- fun process(subroutine: Subroutine): IStatement {
- subroutine.statements = subroutine.statements.asSequence().map { it.process(this) }.toMutableList()
- return subroutine
- }
-
- fun process(functionCall: FunctionCall): IExpression {
- val newtarget = functionCall.target.process(this)
- if(newtarget is IdentifierReference)
- functionCall.target = newtarget
- functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList()
- return functionCall
- }
-
- fun process(functionCallStatement: FunctionCallStatement): IStatement {
- val newtarget = functionCallStatement.target.process(this)
- if(newtarget is IdentifierReference)
- functionCallStatement.target = newtarget
- functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList()
- return functionCallStatement
- }
-
- fun process(identifier: IdentifierReference): IExpression {
- // 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 process(jump: Jump): IStatement {
- if(jump.identifier!=null) {
- val ident = jump.identifier.process(this)
- if(ident is IdentifierReference && ident!==jump.identifier) {
- return Jump(null, ident, null, jump.position)
- }
- }
- return jump
- }
-
- fun process(ifStatement: IfStatement): IStatement {
- ifStatement.condition = ifStatement.condition.process(this)
- ifStatement.truepart = ifStatement.truepart.process(this) as AnonymousScope
- ifStatement.elsepart = ifStatement.elsepart.process(this) as AnonymousScope
- return ifStatement
- }
-
- fun process(branchStatement: BranchStatement): IStatement {
- branchStatement.truepart = branchStatement.truepart.process(this) as AnonymousScope
- branchStatement.elsepart = branchStatement.elsepart.process(this) as AnonymousScope
- return branchStatement
- }
-
- fun process(range: RangeExpr): IExpression {
- range.from = range.from.process(this)
- range.to = range.to.process(this)
- range.step = range.step.process(this)
- return range
- }
-
- fun process(label: Label): IStatement {
- return label
- }
-
- fun process(literalValue: LiteralValue): LiteralValue {
- if(literalValue.arrayvalue!=null) {
- for(av in literalValue.arrayvalue.withIndex()) {
- val newvalue = av.value.process(this)
- literalValue.arrayvalue[av.index] = newvalue
- }
- }
- return literalValue
- }
-
- fun process(assignment: Assignment): IStatement {
- assignment.targets = assignment.targets.map { it.process(this) }
- assignment.value = assignment.value.process(this)
- return assignment
- }
-
- fun process(postIncrDecr: PostIncrDecr): IStatement {
- postIncrDecr.target = postIncrDecr.target.process(this)
- return postIncrDecr
- }
-
- fun process(contStmt: Continue): IStatement {
- return contStmt
- }
-
- fun process(breakStmt: Break): IStatement {
- return breakStmt
- }
-
- fun process(forLoop: ForLoop): IStatement {
- forLoop.loopVar?.process(this)
- forLoop.iterable = forLoop.iterable.process(this)
- forLoop.body = forLoop.body.process(this) as AnonymousScope
- return forLoop
- }
-
- fun process(whileLoop: WhileLoop): IStatement {
- whileLoop.condition = whileLoop.condition.process(this)
- whileLoop.body = whileLoop.body.process(this) as AnonymousScope
- return whileLoop
- }
-
- fun process(repeatLoop: RepeatLoop): IStatement {
- repeatLoop.untilCondition = repeatLoop.untilCondition.process(this)
- repeatLoop.body = repeatLoop.body.process(this) as AnonymousScope
- return repeatLoop
- }
-
- fun process(returnStmt: Return): IStatement {
- returnStmt.values = returnStmt.values.map { it.process(this) }
- return returnStmt
- }
-
- fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
- arrayIndexedExpression.identifier.process(this)
- arrayIndexedExpression.arrayspec.process(this)
- return arrayIndexedExpression
- }
-
- fun process(assignTarget: AssignTarget): AssignTarget {
- assignTarget.arrayindexed?.process(this)
- assignTarget.identifier?.process(this)
- assignTarget.memoryAddress?.process(this)
- return assignTarget
- }
-
- fun process(scope: AnonymousScope): IStatement {
- scope.statements = scope.statements.asSequence().map { it.process(this) }.toMutableList()
- return scope
- }
-
- fun process(typecast: TypecastExpression): IExpression {
- typecast.expression = typecast.expression.process(this)
- return typecast
- }
-
- fun process(memread: DirectMemoryRead): IExpression {
- memread.addressExpression = memread.addressExpression.process(this)
- return memread
- }
-
- fun process(memwrite: DirectMemoryWrite): IExpression {
- memwrite.addressExpression = memwrite.addressExpression.process(this)
- return memwrite
- }
-
- fun process(addressOf: AddressOf): IExpression {
- addressOf.identifier.process(this)
- return addressOf
- }
-
- fun process(inlineAssembly: InlineAssembly): IStatement {
- return inlineAssembly
- }
-}
-
-
-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(this)!!
- }
-
- fun definingSubroutine(): Subroutine? = findParentNode(this)
-
- fun definingScope(): INameScope {
- val scope = findParentNode(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")
- }
-}
-
-
-// find the parent node of a specific type or interface
-// (useful to figure out in what namespace/block something is defined, etc)
-inline fun findParentNode(node: Node): T? {
- var candidate = node.parent
- while(candidate !is T && candidate !is ParentSentinel)
- candidate = candidate.parent
- return if(candidate is ParentSentinel)
- null
- else
- candidate as T
-}
-
-
-interface IStatement : Node {
- fun process(processor: IAstProcessor) : IStatement
- 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()
- 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(this)!!
- }
-}
-
-
-interface IFunctionCall {
- var target: IdentifierReference
- var arglist: MutableList
-}
-
-
-interface INameScope {
- val name: String
- val position: Position
- val statements: MutableList
- val parent: Node
-
- fun linkParents(parent: Node)
-
- fun subScopes(): Map {
- val subscopes = mutableMapOf()
- for(stmt in statements) {
- when(stmt) {
- is INameScope -> subscopes[stmt.name] = stmt
- 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
- }
- }
- }
- return subscopes
- }
-
- fun getLabelOrVariable(name: String): IStatement? {
- // TODO this is called A LOT and could perhaps be optimized a bit more, but adding a 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
- }
- return null
- }
-
- fun allDefinedSymbols(): List> {
- 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, localContext: Node) : IStatement? {
- if(scopedName.size>1) {
- // 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")
- }
-}
-
-object ParentSentinel : Node {
- override val position = Position("<>", 0, 0, 0)
- override var parent: Node = this
- override fun linkParents(parent: Node) {}
-}
-
-object BuiltinFunctionScopePlaceholder : INameScope {
- override val name = "<>"
- override val position = Position("<>", 0, 0, 0)
- override var statements = mutableListOf()
- override var parent: Node = ParentSentinel
- override fun linkParents(parent: Node) {}
-}
-
-class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement {
- override var parent: Node = ParentSentinel
- override fun linkParents(parent: Node) {}
- override fun process(processor: IAstProcessor): IStatement = this
- override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
- override val expensiveToInline = false
-}
-
-
-
-/*********** Everything starts from here, the Program; zero or more modules *************/
-
-class Program(val name: String, val modules: MutableList) {
- val namespace = GlobalNamespace(modules)
- val heap = HeapValues()
-
- val loadAddress: Int
- get() = modules.first().loadAddress
-
- fun entrypoint(): Subroutine? {
- val mainBlocks = modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
- if(mainBlocks.size > 1)
- throw FatalAstException("more than one 'main' block")
- return if(mainBlocks.isEmpty()) {
- null
- } else {
- mainBlocks[0].subScopes()["start"] as Subroutine?
- }
- }
-}
-
-
-class Module(override val name: String,
- override var statements: MutableList,
- override val position: Position,
- val isLibraryModule: Boolean,
- val source: Path) : Node, INameScope {
- override lateinit var parent: Node
- lateinit var program: Program
- val importedBy = mutableListOf()
- val imports = mutableSetOf()
-
- var loadAddress: Int = 0 // can be set with the %address directive
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- statements.forEach {it.linkParents(this)}
- }
-
- override fun definingScope(): INameScope = program.namespace
-
- override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
-}
-
-
-class GlobalNamespace(val modules: List): Node, INameScope {
- override val name = "<<>>"
- override val position = Position("<<>>", 0, 0, 0)
- override val statements = mutableListOf()
- override var parent: Node = ParentSentinel
-
- override fun linkParents(parent: Node) {
- modules.forEach { it.linkParents(this) }
- }
-
- override fun lookup(scopedName: List, localContext: Node): IStatement? {
- 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)
- builtinPlaceholder.parent = ParentSentinel
- return builtinPlaceholder
- }
-
- val stmt = localContext.definingModule().lookup(scopedName, localContext)
- return when (stmt) {
- is Label, is VarDecl, is Block, is Subroutine -> stmt
- null -> null
- else -> throw NameError("wrong identifier target: $stmt", stmt.position)
- }
- }
-}
-
-
-class Block(override val name: String,
- val address: Int?,
- override var statements: MutableList,
- val isInLibrary: Boolean,
- override val position: Position) : IStatement, INameScope {
- override lateinit var parent: Node
- override val expensiveToInline
- get() = statements.any { it.expensiveToInline }
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- statements.forEach {it.linkParents(this)}
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "Block(name=$name, address=$address, ${statements.size} statements)"
- }
-
- 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, override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- args.forEach{it.linkParents(this)}
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-}
-
-
-data class DirectiveArg(val str: String?, val name: String?, val int: Int?, override val position: Position) : Node {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-}
-
-
-data class Label(val name: String, override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "Label(name=$name, pos=$position)"
- }
-
- val scopedname: String by lazy { makeScopedName(name) }
-}
-
-
-open class Return(var values: List, override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = values.any { it !is LiteralValue }
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- values.forEach {it.linkParents(this)}
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "Return(values: $values, pos=$position)"
- }
-}
-
-
-class ReturnFromIrq(override val position: Position) : Return(emptyList(), position) {
- override fun process(processor: IAstProcessor) = this
-
- override fun toString(): String {
- return "ReturnFromIrq(pos=$position)"
- }
-}
-
-
-class Continue(override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent=parent
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-}
-
-class Break(override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent=parent
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-}
-
-
-class ArrayIndex(var index: IExpression, override val position: Position) : Node {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- index.linkParents(this)
- }
-
- companion object {
- fun forArray(v: LiteralValue, heap: HeapValues): ArrayIndex {
- val arraySize = v.arrayvalue?.size ?: heap.get(v.heapId!!).arraysize
- return ArrayIndex(LiteralValue.optimalNumeric(arraySize, v.position), v.position)
- }
- }
-
- fun process(processor: IAstProcessor) {
- index = index.process(processor)
- }
-
- override fun toString(): String {
- return("ArrayIndex($index, pos=$position)")
- }
-
- fun size() = (index as? LiteralValue)?.asIntegerValue
-}
-
-
-enum class VarDeclType {
- VAR,
- CONST,
- MEMORY
-}
-
-class VarDecl(val type: VarDeclType,
- private val declaredDatatype: DataType,
- val zeropage: Boolean,
- var arraysize: ArrayIndex?,
- val name: String,
- var value: IExpression?,
- val isArray: Boolean,
- val autoGenerated: Boolean,
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline
- get() = value!=null && value !is LiteralValue
-
- val datatypeErrors = mutableListOf() // don't crash at init time, report them in the AstChecker
- val datatype =
- if (!isArray) declaredDatatype
- else when (declaredDatatype) {
- DataType.UBYTE -> DataType.ARRAY_UB
- DataType.BYTE -> DataType.ARRAY_B
- DataType.UWORD -> DataType.ARRAY_UW
- DataType.WORD -> DataType.ARRAY_W
- DataType.FLOAT -> DataType.ARRAY_F
- else -> {
- datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
- DataType.UBYTE
- }
- }
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- arraysize?.linkParents(this)
- value?.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- val scopedname: String by lazy { makeScopedName(name) }
-
- override fun toString(): String {
- return "VarDecl(name=$name, vartype=$type, datatype=$datatype, array=$isArray, value=$value, pos=$position)"
- }
-
- fun asDefaultValueDecl(parent: Node?): VarDecl {
- val constValue = when(declaredDatatype) {
- DataType.UBYTE -> LiteralValue(DataType.UBYTE, 0, position=position)
- DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position=position)
- DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue=0, position=position)
- DataType.WORD -> LiteralValue(DataType.WORD, wordvalue=0, position=position)
- DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue=0.0, position=position)
- else -> throw FatalAstException("can only set a default value for a numeric type")
- }
- val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, constValue, isArray, true, position)
- if(parent!=null)
- decl.linkParents(parent)
- return decl
- }
-}
-
-
-open class Assignment(var targets: List, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline
- get() = value !is LiteralValue
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- targets.forEach { it.linkParents(this) }
- value.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return("Assignment(augop: $aug_op, targets: $targets, value: $value, pos=$position)")
- }
-
- val singleTarget: AssignTarget?
- get() {
- return targets.singleOrNull() // common case
- }
-}
-
-// 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)
- : Assignment(listOf(target), aug_op, value, position)
-
-
-data class AssignTarget(val register: Register?,
- val identifier: IdentifierReference?,
- val arrayindexed: ArrayIndexedExpression?,
- var memoryAddress: DirectMemoryWrite?,
- override val position: Position) : Node {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- identifier?.linkParents(this)
- arrayindexed?.linkParents(this)
- memoryAddress?.linkParents(this)
- }
-
- fun process(processor: IAstProcessor) = processor.process(this)
-
- companion object {
- fun fromExpr(expr: IExpression): AssignTarget {
- return when (expr) {
- is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
- is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
- is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
- is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
- is DirectMemoryWrite -> AssignTarget(null, null, null, expr, expr.position)
- else -> throw FatalAstException("invalid expression object $expr")
- }
- }
- }
-
- fun inferType(program: Program, stmt: IStatement): DataType? {
- if(register!=null)
- return DataType.UBYTE
-
- if(identifier!=null) {
- val symbol = program.namespace.lookup(identifier.nameInSource, stmt) ?: return null
- if (symbol is VarDecl) return symbol.datatype
- }
-
- if(arrayindexed!=null) {
- val dt = arrayindexed.inferType(program)
- if(dt!=null)
- return dt
- }
-
- if(memoryAddress!=null)
- return DataType.UBYTE
-
- return null
- }
-
- fun shortString(withTypePrefix: Boolean=false): String {
- if(register!=null)
- return (if(withTypePrefix) "0register::" else "") + register.name
- if(identifier!=null)
- return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last()
- if(arrayindexed!=null)
- return (if(withTypePrefix) "2arrayidx::" else "") + arrayindexed.identifier.nameInSource.last()
- val address = memoryAddress?.addressExpression
- if(address is LiteralValue)
- return (if(withTypePrefix) "1address::" else "") +address.asIntegerValue.toString()
- return if(withTypePrefix) "???::???" else "???"
- }
-
- fun isMemoryMapped(namespace: INameScope): Boolean =
- memoryAddress!=null || (identifier?.targetVarDecl(namespace)?.type==VarDeclType.MEMORY)
-
- infix fun isSameAs(value: IExpression): Boolean {
- return when {
- this.memoryAddress!=null -> false
- this.register!=null -> value is RegisterExpr && value.register==register
- this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier.nameInSource
- this.arrayindexed!=null -> value is ArrayIndexedExpression &&
- value.identifier.nameInSource==arrayindexed.identifier.nameInSource &&
- value.arrayspec.size()!=null &&
- arrayindexed.arrayspec.size()!=null &&
- value.arrayspec.size()==arrayindexed.arrayspec.size()
- else -> false
- }
- }
-
- fun isSameAs(other: AssignTarget, program: Program): Boolean {
- if(this===other)
- return true
- if(this.register!=null && other.register!=null)
- return this.register==other.register
- if(this.identifier!=null && other.identifier!=null)
- return this.identifier.nameInSource==other.identifier.nameInSource
- if(this.memoryAddress!=null && other.memoryAddress!=null) {
- val addr1 = this.memoryAddress!!.addressExpression.constValue(program)
- val addr2 = other.memoryAddress!!.addressExpression.constValue(program)
- return addr1!=null && addr2!=null && addr1==addr2
- }
- if(this.arrayindexed!=null && other.arrayindexed!=null) {
- if(this.arrayindexed.identifier.nameInSource == other.arrayindexed.identifier.nameInSource) {
- val x1 = this.arrayindexed.arrayspec.index.constValue(program)
- val x2 = other.arrayindexed.arrayspec.index.constValue(program)
- return x1!=null && x2!=null && x1==x2
- }
- }
- return false
- }
-
- fun isNotMemory(namespace: INameScope): Boolean {
- if(this.register!=null)
- return true
- if(this.memoryAddress!=null)
- return false
- if(this.arrayindexed!=null) {
- val targetStmt = this.arrayindexed.identifier.targetVarDecl(namespace)
- if(targetStmt!=null)
- return targetStmt.type!=VarDeclType.MEMORY
- }
- if(this.identifier!=null) {
- val targetStmt = this.identifier.targetVarDecl(namespace)
- if(targetStmt!=null)
- return targetStmt.type!=VarDeclType.MEMORY
- }
- return false
- }
-}
-
-
-interface IExpression: Node {
- fun constValue(program: Program): LiteralValue?
- fun process(processor: IAstProcessor): IExpression
- fun referencesIdentifier(name: String): Boolean
- 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 LiteralValue -> return (other is LiteralValue && other==this)
- }
- return false
- }
-}
-
-
-// note: some expression elements are mutable, to be able to rewrite/process the expression tree
-
-class PrefixExpression(val operator: String, var expression: IExpression, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- expression.linkParents(this)
- }
-
- override fun constValue(program: Program): LiteralValue? = null
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
- override fun inferType(program: Program): DataType? = expression.inferType(program)
-
- override fun toString(): String {
- return "Prefix($operator $expression)"
- }
-}
-
-
-class BinaryExpression(var left: IExpression, var operator: String, var right: IExpression, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- left.linkParents(this)
- right.linkParents(this)
- }
-
- override fun toString(): String {
- return "[$left $operator $right]"
- }
-
- // binary expression should actually have been optimized away into a single value, before const value was requested...
- override fun constValue(program: Program): LiteralValue? = null
-
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
- override fun inferType(program: Program): DataType? {
- val leftDt = left.inferType(program)
- val rightDt = right.inferType(program)
- return when (operator) {
- "+", "-", "*", "**", "%" -> if (leftDt == null || rightDt == null) null else {
- try {
- arithmeticOpDt(leftDt, rightDt)
- } catch (x: FatalAstException) {
- null
- }
- }
- "/" -> if (leftDt == null || rightDt == null) null else divisionOpDt(leftDt, rightDt)
- "&" -> leftDt
- "|" -> leftDt
- "^" -> leftDt
- "and", "or", "xor",
- "<", ">",
- "<=", ">=",
- "==", "!=" -> DataType.UBYTE
- "<<", ">>" -> leftDt
- else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
- }
- }
-
- companion object {
- fun divisionOpDt(leftDt: DataType, rightDt: DataType): DataType {
- return when (leftDt) {
- DataType.UBYTE -> when (rightDt) {
- DataType.UBYTE, DataType.UWORD -> DataType.UBYTE
- DataType.BYTE, DataType.WORD -> DataType.WORD
- DataType.FLOAT -> DataType.BYTE
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.BYTE -> when (rightDt) {
- in NumericDatatypes -> DataType.BYTE
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.UWORD -> when (rightDt) {
- DataType.UBYTE, DataType.UWORD -> DataType.UWORD
- DataType.BYTE, DataType.WORD -> DataType.WORD
- DataType.FLOAT -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.WORD -> when (rightDt) {
- in NumericDatatypes -> DataType.WORD
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.FLOAT -> when (rightDt) {
- in NumericDatatypes -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- }
-
- fun arithmeticOpDt(leftDt: DataType, rightDt: DataType): DataType {
- return when (leftDt) {
- DataType.UBYTE -> when (rightDt) {
- DataType.UBYTE -> DataType.UBYTE
- DataType.BYTE -> DataType.BYTE
- DataType.UWORD -> DataType.UWORD
- DataType.WORD -> DataType.WORD
- DataType.FLOAT -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.BYTE -> when (rightDt) {
- in ByteDatatypes -> DataType.BYTE
- in WordDatatypes -> DataType.WORD
- DataType.FLOAT -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.UWORD -> when (rightDt) {
- DataType.UBYTE, DataType.UWORD -> DataType.UWORD
- DataType.BYTE, DataType.WORD -> DataType.WORD
- DataType.FLOAT -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.WORD -> when (rightDt) {
- in IntegerDatatypes -> DataType.WORD
- DataType.FLOAT -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- DataType.FLOAT -> when (rightDt) {
- in NumericDatatypes -> DataType.FLOAT
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
- }
- }
- }
-
- fun commonDatatype(leftDt: DataType, rightDt: DataType,
- left: IExpression, right: IExpression): Pair {
- // byte + byte -> byte
- // byte + word -> word
- // word + byte -> word
- // word + word -> word
- // a combination with a float will be float (but give a warning about this!)
-
- if(this.operator=="/") {
- // division is a bit weird, don't cast the operands
- val commondt = divisionOpDt(leftDt, rightDt)
- return Pair(commondt, null)
- }
-
- return when (leftDt) {
- DataType.UBYTE -> {
- when (rightDt) {
- DataType.UBYTE -> Pair(DataType.UBYTE, null)
- DataType.BYTE -> Pair(DataType.BYTE, left)
- DataType.UWORD -> Pair(DataType.UWORD, left)
- DataType.WORD -> Pair(DataType.WORD, left)
- DataType.FLOAT -> Pair(DataType.FLOAT, left)
- else -> throw FatalAstException("non-numeric datatype $rightDt")
- }
- }
- DataType.BYTE -> {
- when (rightDt) {
- DataType.UBYTE -> Pair(DataType.BYTE, right)
- DataType.BYTE -> Pair(DataType.BYTE, null)
- DataType.UWORD -> Pair(DataType.WORD, left)
- DataType.WORD -> Pair(DataType.WORD, left)
- DataType.FLOAT -> Pair(DataType.FLOAT, left)
- else -> throw FatalAstException("non-numeric datatype $rightDt")
- }
- }
- DataType.UWORD -> {
- when (rightDt) {
- DataType.UBYTE -> Pair(DataType.UWORD, right)
- DataType.BYTE -> Pair(DataType.UWORD, right)
- DataType.UWORD -> Pair(DataType.UWORD, null)
- DataType.WORD -> Pair(DataType.WORD, left)
- DataType.FLOAT -> Pair(DataType.FLOAT, left)
- else -> throw FatalAstException("non-numeric datatype $rightDt")
- }
- }
- DataType.WORD -> {
- when (rightDt) {
- DataType.UBYTE -> Pair(DataType.WORD, right)
- DataType.BYTE -> Pair(DataType.WORD, right)
- DataType.UWORD -> Pair(DataType.WORD, right)
- DataType.WORD -> Pair(DataType.WORD, null)
- DataType.FLOAT -> Pair(DataType.FLOAT, left)
- else -> throw FatalAstException("non-numeric datatype $rightDt")
- }
- }
- DataType.FLOAT -> {
- Pair(DataType.FLOAT, right)
- }
- else -> throw FatalAstException("non-numeric datatype $leftDt")
- }
- }
-}
-
-class ArrayIndexedExpression(val identifier: IdentifierReference,
- var arrayspec: ArrayIndex,
- override val position: Position) : IExpression {
- override lateinit var parent: Node
- override fun linkParents(parent: Node) {
- this.parent = parent
- identifier.linkParents(this)
- arrayspec.linkParents(this)
- }
-
- override fun constValue(program: Program): LiteralValue? = null
- override fun process(processor: IAstProcessor): IExpression = processor.process(this)
- override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
-
- override fun inferType(program: Program): DataType? {
- val target = identifier.targetStatement(program.namespace)
- if (target is VarDecl) {
- return when (target.datatype) {
- in NumericDatatypes -> null
- in StringDatatypes -> DataType.UBYTE
- DataType.ARRAY_UB -> DataType.UBYTE
- DataType.ARRAY_B -> DataType.BYTE
- DataType.ARRAY_UW -> DataType.UWORD
- DataType.ARRAY_W -> DataType.WORD
- DataType.ARRAY_F -> DataType.FLOAT
- else -> throw FatalAstException("invalid dt")
- }
- }
- return null
- }
-
- override fun toString(): String {
- return "ArrayIndexed(ident=$identifier, arraysize=$arrayspec; pos=$position)"
- }
-}
-
-
-class TypecastExpression(var expression: IExpression, var type: DataType, val implicit: Boolean, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- expression.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
- override fun inferType(program: Program): DataType? = type
- override fun constValue(program: Program): LiteralValue? {
- val cv = expression.constValue(program) ?: return null
- val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
- return LiteralValue.fromNumber(value.numericValue(), value.type, position)
- }
-
- override fun toString(): String {
- return "Typecast($expression as $type)"
- }
-}
-
-
-data class AddressOf(val identifier: IdentifierReference, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- identifier.parent=this
- }
-
- var scopedname: String? = null // will be set in a later state by the compiler
- override fun constValue(program: Program): LiteralValue? = null
- override fun referencesIdentifier(name: String) = false
- override fun inferType(program: Program) = DataType.UWORD
- override fun process(processor: IAstProcessor) = processor.process(this)
-}
-
-
-class DirectMemoryRead(var addressExpression: IExpression, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- this.addressExpression.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String) = false
- override fun inferType(program: Program): DataType? = DataType.UBYTE
- override fun constValue(program: Program): LiteralValue? = null
-
- override fun toString(): String {
- return "DirectMemoryRead($addressExpression)"
- }
-}
-
-
-class DirectMemoryWrite(var addressExpression: IExpression, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- this.addressExpression.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String) = false
- override fun inferType(program: Program): DataType? = DataType.UBYTE
- override fun constValue(program: Program): LiteralValue? = null
-
- override fun toString(): String {
- return "DirectMemoryWrite($addressExpression)"
- }
-}
-
-
-private data class NumericLiteral(val number: Number, val datatype: DataType)
-
-
-open class LiteralValue(val type: DataType,
- val bytevalue: Short? = null,
- val wordvalue: Int? = null,
- val floatvalue: Double? = null,
- val strvalue: String? = null,
- val arrayvalue: Array? = null,
- initHeapId: Int? =null,
- override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
-
- val isString = type in StringDatatypes
- val isNumeric = type in NumericDatatypes
- val isArray = type in ArrayDatatypes
- var heapId = initHeapId
- private set
-
- companion object {
- fun fromBoolean(bool: Boolean, position: Position) =
- LiteralValue(DataType.UBYTE, bytevalue = if(bool) 1 else 0, position=position)
-
- fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
- return when(type) {
- in ByteDatatypes -> LiteralValue(type, bytevalue = value.toShort(), position = position)
- in WordDatatypes -> LiteralValue(type, wordvalue = value.toInt(), position = position)
- DataType.FLOAT -> LiteralValue(type, floatvalue = value.toDouble(), position = position)
- else -> throw FatalAstException("non numeric datatype")
- }
- }
-
- fun optimalNumeric(value: Number, position: Position): LiteralValue {
- return if(value is Double) {
- LiteralValue(DataType.FLOAT, floatvalue = value, position = position)
- } else {
- val intval = value.toInt()
- when (intval) {
- in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue=intval.toShort(), position = position)
- in -128..127 -> LiteralValue(DataType.BYTE, bytevalue=intval.toShort(), position = position)
- in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = intval, position = position)
- in -32768..32767 -> LiteralValue(DataType.WORD, wordvalue = intval, position = position)
- else -> LiteralValue(DataType.FLOAT, floatvalue = intval.toDouble(), position = position)
- }
- }
- }
-
- fun optimalInteger(value: Number, position: Position): LiteralValue {
- val intval = value.toInt()
- if(intval.toDouble() != value.toDouble())
- throw FatalAstException("value is not an integer: $value")
- return when (intval) {
- in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue=value.toShort(), position = position)
- in -128..127 -> LiteralValue(DataType.BYTE, bytevalue=value.toShort(), position = position)
- in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = value.toInt(), position = position)
- else -> throw FatalAstException("integer overflow: $value")
- }
- }
- }
-
- init {
- when(type){
- in ByteDatatypes -> if(bytevalue==null) throw FatalAstException("literal value missing bytevalue")
- in WordDatatypes -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
- DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
- in StringDatatypes ->
- if(strvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
- in ArrayDatatypes ->
- if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
- else -> throw FatalAstException("invalid type $type")
- }
- if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && strvalue==null && heapId==null)
- throw FatalAstException("literal value without actual value")
- }
-
- val asNumericValue: Number? = when {
- bytevalue!=null -> bytevalue
- wordvalue!=null -> wordvalue
- floatvalue!=null -> floatvalue
- else -> null
- }
-
- val asIntegerValue: Int? = when {
- bytevalue!=null -> bytevalue.toInt()
- wordvalue!=null -> wordvalue
- // don't round a float value, otherwise code will not detect that it's not an integer
- else -> null
- }
-
- val asBooleanValue: Boolean =
- (floatvalue!=null && floatvalue != 0.0) ||
- (bytevalue!=null && bytevalue != 0.toShort()) ||
- (wordvalue!=null && wordvalue != 0) ||
- (strvalue!=null && strvalue.isNotEmpty()) ||
- (arrayvalue != null && arrayvalue.isNotEmpty())
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- arrayvalue?.forEach {it.linkParents(this)}
- }
-
- override fun constValue(program: Program): LiteralValue? {
- if(arrayvalue!=null) {
- for(v in arrayvalue) {
- if(v.constValue(program)==null) return null
- }
- }
- return this
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- val vstr = when(type) {
- DataType.UBYTE -> "ubyte:$bytevalue"
- DataType.BYTE -> "byte:$bytevalue"
- DataType.UWORD -> "uword:$wordvalue"
- DataType.WORD -> "word:$wordvalue"
- DataType.FLOAT -> "float:$floatvalue"
- in StringDatatypes -> "str:$strvalue"
- in ArrayDatatypes -> "array:$arrayvalue"
- else -> throw FatalAstException("weird datatype")
- }
- return "LiteralValue($vstr)"
- }
-
- override fun inferType(program: Program) = type
-
- override fun hashCode(): Int {
- val bh = bytevalue?.hashCode() ?: 0x10001234
- val wh = wordvalue?.hashCode() ?: 0x01002345
- val fh = floatvalue?.hashCode() ?: 0x00103456
- val sh = strvalue?.hashCode() ?: 0x00014567
- val ah = arrayvalue?.hashCode() ?: 0x11119876
- var hash = bh * 31 xor wh
- hash = hash*31 xor fh
- hash = hash*31 xor sh
- hash = hash*31 xor ah
- hash = hash*31 xor type.hashCode()
- return hash
- }
-
- override fun equals(other: Any?): Boolean {
- if(other==null || other !is LiteralValue)
- return false
- if(isNumeric && other.isNumeric)
- return asNumericValue?.toDouble()==other.asNumericValue?.toDouble()
- if(isArray && other.isArray)
- return arrayvalue!!.contentEquals(other.arrayvalue!!) && heapId==other.heapId
- if(isString && other.isString)
- return strvalue==other.strvalue && heapId==other.heapId
-
- if(type!=other.type)
- return false
-
- return compareTo(other) == 0
- }
-
- operator fun compareTo(other: LiteralValue): Int {
- val numLeft = asNumericValue?.toDouble()
- val numRight = other.asNumericValue?.toDouble()
- if(numLeft!=null && numRight!=null)
- return numLeft.compareTo(numRight)
-
- if(strvalue!=null && other.strvalue!=null)
- return strvalue.compareTo(other.strvalue)
-
- throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
- }
-
- fun intoDatatype(targettype: DataType): LiteralValue? {
- if(type==targettype)
- return this
- when(type) {
- DataType.UBYTE -> {
- if(targettype==DataType.BYTE && bytevalue!! <= 127)
- return LiteralValue(targettype, bytevalue=bytevalue, position=position)
- if(targettype==DataType.WORD || targettype==DataType.UWORD)
- return LiteralValue(targettype, wordvalue=bytevalue!!.toInt(), position=position)
- if(targettype==DataType.FLOAT)
- return LiteralValue(targettype, floatvalue=bytevalue!!.toDouble(), position=position)
- }
- DataType.BYTE -> {
- if(targettype==DataType.UBYTE && bytevalue!! >= 0)
- return LiteralValue(targettype, bytevalue=bytevalue, position=position)
- if(targettype==DataType.UWORD && bytevalue!! >= 0)
- return LiteralValue(targettype, wordvalue=bytevalue.toInt(), position=position)
- if(targettype==DataType.WORD)
- return LiteralValue(targettype, wordvalue=bytevalue!!.toInt(), position=position)
- if(targettype==DataType.FLOAT)
- return LiteralValue(targettype, floatvalue=bytevalue!!.toDouble(), position=position)
- }
- DataType.UWORD -> {
- if(targettype==DataType.BYTE && wordvalue!! <= 127)
- return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
- if(targettype==DataType.UBYTE && wordvalue!! <= 255)
- return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
- if(targettype==DataType.WORD && wordvalue!! <= 32767)
- return LiteralValue(targettype, wordvalue=wordvalue, position=position)
- if(targettype==DataType.FLOAT)
- return LiteralValue(targettype, floatvalue=wordvalue!!.toDouble(), position=position)
- }
- DataType.WORD -> {
- if(targettype==DataType.BYTE && wordvalue!! in -128..127)
- return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
- if(targettype==DataType.UBYTE && wordvalue!! in 0..255)
- return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position)
- if(targettype==DataType.UWORD && wordvalue!! >=0)
- return LiteralValue(targettype, wordvalue=wordvalue, position=position)
- if(targettype==DataType.FLOAT)
- return LiteralValue(targettype, floatvalue=wordvalue!!.toDouble(), position=position)
- }
- DataType.FLOAT -> {
- if(floor(floatvalue!!)==floatvalue) {
- val value = floatvalue.toInt()
- if (targettype == DataType.BYTE && value in -128..127)
- return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
- if (targettype == DataType.UBYTE && value in 0..255)
- return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
- if (targettype == DataType.WORD && value in -32768..32767)
- return LiteralValue(targettype, wordvalue = value, position = position)
- if (targettype == DataType.UWORD && value in 0..65535)
- return LiteralValue(targettype, wordvalue = value, position = position)
- }
- }
- in StringDatatypes -> {
- if(targettype in StringDatatypes)
- return this
- }
- else -> {}
- }
- return null // invalid type conversion from $this to $targettype
- }
-
- fun addToHeap(heap: HeapValues) {
- if(heapId==null) {
- if (strvalue != null) {
- heapId = heap.addString(type, strvalue)
- }
- else if (arrayvalue!=null) {
- if(arrayvalue.any {it is AddressOf}) {
- val intArrayWithAddressOfs = arrayvalue.map {
- when (it) {
- is AddressOf -> IntegerOrAddressOf(null, it)
- is LiteralValue -> IntegerOrAddressOf(it.asIntegerValue, null)
- else -> throw FatalAstException("invalid datatype in array")
- }
- }
- heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
- } else {
- val valuesInArray = arrayvalue.map { (it as LiteralValue).asNumericValue!! }
- if(type==DataType.ARRAY_F) {
- val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
- heapId = heap.addDoublesArray(doubleArray)
- } else {
- val integerArray = valuesInArray.map { it.toInt() }
- heapId = heap.addIntegerArray(type, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
- }
- }
- }
- }
- }
-}
-
-
-class RangeExpr(var from: IExpression,
- var to: IExpression,
- var step: IExpression,
- override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- from.linkParents(this)
- to.linkParents(this)
- step.linkParents(this)
- }
-
- override fun constValue(program: Program): LiteralValue? = null
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
- override fun inferType(program: Program): DataType? {
- val fromDt=from.inferType(program)
- val toDt=to.inferType(program)
- return when {
- fromDt==null || toDt==null -> null
- fromDt==DataType.UBYTE && toDt==DataType.UBYTE -> DataType.UBYTE
- fromDt==DataType.UWORD && toDt==DataType.UWORD -> DataType.UWORD
- fromDt==DataType.STR && toDt==DataType.STR -> DataType.STR
- fromDt==DataType.STR_S && toDt==DataType.STR_S -> DataType.STR_S
- fromDt==DataType.WORD || toDt==DataType.WORD -> DataType.WORD
- fromDt==DataType.BYTE || toDt==DataType.BYTE -> DataType.BYTE
- else -> DataType.UBYTE
- }
- }
- override fun toString(): String {
- return "RangeExpr(from $from, to $to, step $step, pos=$position)"
- }
-
- fun size(): Int? {
- val fromLv = (from as? LiteralValue)
- val toLv = (to as? LiteralValue)
- if(fromLv==null || toLv==null)
- return null
- return toConstantIntegerRange()?.count()
- }
-
- fun toConstantIntegerRange(): IntProgression? {
- val fromLv = from as? LiteralValue
- val toLv = to as? LiteralValue
- if(fromLv==null || toLv==null)
- return null // non-constant range
- val fromVal: Int
- val toVal: Int
- if(fromLv.isString && toLv.isString) {
- // string range -> int range over petscii values
- fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt()
- toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt()
- } else {
- // integer range
- fromVal = (from as LiteralValue).asIntegerValue!!
- toVal = (to as LiteralValue).asIntegerValue!!
- }
- val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1
- return when {
- fromVal <= toVal -> when {
- stepVal <= 0 -> IntRange.EMPTY
- stepVal == 1 -> fromVal..toVal
- else -> fromVal..toVal step stepVal
- }
- else -> when {
- stepVal >= 0 -> IntRange.EMPTY
- stepVal == -1 -> fromVal downTo toVal
- else -> fromVal downTo toVal step abs(stepVal)
- }
- }
- }
-}
-
-
-class RegisterExpr(val register: Register, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-
- override fun constValue(program: Program): LiteralValue? = null
- override fun process(processor: IAstProcessor) = this
- override fun referencesIdentifier(name: String): Boolean = false
- override fun toString(): String {
- return "RegisterExpr(register=$register, pos=$position)"
- }
-
- override fun inferType(program: Program) = DataType.UBYTE
-}
-
-
-data class IdentifierReference(val nameInSource: List, override val position: Position) : IExpression {
- override lateinit var parent: Node
-
- fun targetStatement(namespace: INameScope) =
- if(nameInSource.size==1 && nameInSource[0] in BuiltinFunctions)
- BuiltinFunctionStatementPlaceholder(nameInSource[0], position)
- else
- namespace.lookup(nameInSource, this)
-
- fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
- fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-
- override fun constValue(program: Program): LiteralValue? {
- val node = program.namespace.lookup(nameInSource, this)
- ?: throw UndefinedSymbolError(this)
- val vardecl = node as? VarDecl
- if(vardecl==null) {
- return null
- } else if(vardecl.type!=VarDeclType.CONST) {
- return null
- }
- return vardecl.value?.constValue(program)
- }
-
- override fun toString(): String {
- return "IdentifierRef($nameInSource)"
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
-
- override fun inferType(program: Program): DataType? {
- val targetStmt = targetStatement(program.namespace)
- if(targetStmt is VarDecl) {
- return targetStmt.datatype
- } else {
- throw FatalAstException("cannot get datatype from identifier reference ${this}, pos=$position")
- }
- }
-
- fun heapId(namespace: INameScope): Int {
- val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
- return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
- }
-}
-
-
-class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- target.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "PostIncrDecr(op: $operator, target: $target, pos=$position)"
- }
-}
-
-
-class Jump(val address: Int?,
- val identifier: IdentifierReference?,
- val generatedLabel: String?, // used in code generation scenarios
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- identifier?.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)"
- }
-}
-
-
-class FunctionCall(override var target: IdentifierReference,
- override var arglist: MutableList,
- override val position: Position) : IExpression, IFunctionCall {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- target.linkParents(this)
- arglist.forEach { it.linkParents(this) }
- }
-
- override fun constValue(program: Program) = constValue(program, true)
-
- private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
- // if the function is a built-in function and the args are consts, should try to const-evaluate!
- // lenghts of arrays and strings are constants that are determined at compile time!
- if(target.nameInSource.size>1) return null
- try {
- var resultValue: LiteralValue? = null
- val func = BuiltinFunctions[target.nameInSource[0]]
- if(func!=null) {
- val exprfunc = func.constExpressionFunc
- if(exprfunc!=null)
- resultValue = exprfunc(arglist, position, program)
- else if(func.returntype==null)
- throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
- }
-
- if(withDatatypeCheck) {
- val resultDt = this.inferType(program)
- if(resultValue==null || resultDt == resultValue.type)
- return resultValue
- throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
- } else {
- return resultValue
- }
- }
- catch(x: NotConstArgumentException) {
- // const-evaluating the builtin function call failed.
- return null
- }
- }
-
- override fun toString(): String {
- return "FunctionCall(target=$target, pos=$position)"
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
- override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)}
-
- override fun inferType(program: Program): DataType? {
- val constVal = constValue(program ,false)
- if(constVal!=null)
- return constVal.type
- val stmt = target.targetStatement(program.namespace) ?: return null
- when (stmt) {
- is BuiltinFunctionStatementPlaceholder -> {
- if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
- target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
- return null // these have no return value
- }
- return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
- }
- is Subroutine -> {
- if(stmt.returntypes.isEmpty())
- return null // no return value
- if(stmt.returntypes.size==1)
- return stmt.returntypes[0]
- return null // has multiple return types... so not a single resulting datatype possible
- }
- is Label -> return null
- }
- return null // calling something we don't recognise...
- }
-}
-
-
-class FunctionCallStatement(override var target: IdentifierReference,
- override var arglist: MutableList,
- override val position: Position) : IStatement, IFunctionCall {
- override lateinit var parent: Node
- override val expensiveToInline
- get() = arglist.any { it !is LiteralValue }
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- target.linkParents(this)
- arglist.forEach { it.linkParents(this) }
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "FunctionCallStatement(target=$target, pos=$position)"
- }
-}
-
-
-class InlineAssembly(val assembly: String, override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = true
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-}
-
-
-data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean?)
-
-class AnonymousScope(override var statements: MutableList,
- override val position: Position) : INameScope, IStatement {
- override val name: String
- override lateinit var parent: Node
- override val expensiveToInline
- get() = statements.any { it.expensiveToInline }
-
- init {
- name = "" // make sure it's an invalid soruce code identifier so user source code can never produce it
- sequenceNumber++
- }
-
- companion object {
- private var sequenceNumber = 1
- }
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- statements.forEach { it.linkParents(this) }
- }
- override fun process(processor: IAstProcessor) = processor.process(this)
-}
-
-
-class NopStatement(override val position:Position): IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = false
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-
- override fun process(processor: IAstProcessor) = this
-}
-
-
-// the subroutine class covers both the normal user-defined subroutines,
-// and also the predefined/ROM/register-based subroutines.
-// (multiple return types can only occur for the latter type)
-class Subroutine(override val name: String,
- val parameters: List,
- val returntypes: List,
- val asmParameterRegisters: List,
- val asmReturnvaluesRegisters: List,
- val asmClobbers: Set,
- val asmAddress: Int?,
- val isAsmSubroutine: Boolean,
- override var statements: MutableList,
- override val position: Position) : IStatement, INameScope {
-
- var keepAlways: Boolean = false
- override val expensiveToInline
- get() = statements.any { it.expensiveToInline }
-
- override lateinit var parent: Node
- val calledBy = mutableListOf()
- val calls = mutableSetOf()
-
- val scopedname: String by lazy { makeScopedName(name) }
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- parameters.forEach { it.linkParents(this) }
- statements.forEach { it.linkParents(this) }
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
- }
-
- fun amountOfRtsInAsm(): Int = statements
- .asSequence()
- .filter { it is InlineAssembly }
- .map { (it as InlineAssembly).assembly }
- .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
-
- val canBeAsmSubroutine =false // TODO disabled for now, see below about problem with converting to asm subroutine
-// !isAsmSubroutine
-// && ((parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD))
-// || (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE }))
-
- fun intoAsmSubroutine(): Subroutine {
- // TODO turn subroutine into asm calling convention. Requires rethinking of how parameters are handled (conflicts with local vardefs now, see AstIdentifierChecker...)
- return this // TODO
-
-// println("TO ASM $this") // TODO
-// val paramregs = if (parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE))
-// listOf(RegisterOrStatusflag(RegisterOrPair.Y, null, null))
-// else if (parameters.size == 1 && parameters[0].type in setOf(DataType.WORD, DataType.UWORD))
-// listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
-// else if (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE })
-// listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
-// else throw FatalAstException("cannot convert subroutine to asm parameters")
-//
-// val asmsub=Subroutine(
-// name,
-// parameters,
-// returntypes,
-// paramregs,
-// emptyList(),
-// emptySet(),
-// null,
-// true,
-// statements,
-// position
-// )
-// asmsub.linkParents(parent)
-// return asmsub
- }
-}
-
-
-open class SubroutineParameter(val name: String,
- val type: DataType,
- override val position: Position) : Node {
- override lateinit var parent: Node
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- }
-}
-
-class IfStatement(var condition: IExpression,
- var truepart: AnonymousScope,
- var elsepart: AnonymousScope,
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline: Boolean
- get() = truepart.expensiveToInline || elsepart.expensiveToInline
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- condition.linkParents(this)
- truepart.linkParents(this)
- elsepart.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor): IStatement = processor.process(this)
-}
-
-
-class BranchStatement(var condition: BranchCondition,
- var truepart: AnonymousScope,
- var elsepart: AnonymousScope,
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline: Boolean
- get() = truepart.expensiveToInline || elsepart.expensiveToInline
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- truepart.linkParents(this)
- elsepart.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor): IStatement = processor.process(this)
-}
-
-
-class ForLoop(val loopRegister: Register?,
- val decltype: DataType?,
- val zeropage: Boolean,
- val loopVar: IdentifierReference?,
- var iterable: IExpression,
- var body: AnonymousScope,
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = true
-
- override fun linkParents(parent: Node) {
- this.parent=parent
- loopVar?.linkParents(if(decltype==null) this else body)
- iterable.linkParents(this)
- body.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor) = processor.process(this)
-
- override fun toString(): String {
- return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
- }
-
- companion object {
- const val iteratorLoopcounterVarname = "prog8forloopcounter"
- }
-}
-
-
-class WhileLoop(var condition: IExpression,
- var body: AnonymousScope,
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = true
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- condition.linkParents(this)
- body.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor): IStatement = processor.process(this)
-}
-
-
-class RepeatLoop(var body: AnonymousScope,
- var untilCondition: IExpression,
- override val position: Position) : IStatement {
- override lateinit var parent: Node
- override val expensiveToInline = true
-
- override fun linkParents(parent: Node) {
- this.parent = parent
- untilCondition.linkParents(this)
- body.linkParents(this)
- }
-
- override fun process(processor: IAstProcessor): IStatement = processor.process(this)
-}
-
-
-/***************** Antlr Extension methods to create AST ****************/
-
-fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path) : Module {
- val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
- return Module(nameWithoutSuffix, modulestatement().asSequence().map { it.toAst(isLibrary) }.toMutableList(), toPosition(), isLibrary, source)
-}
-
-
-private fun ParserRuleContext.toPosition() : Position {
- val customTokensource = this.start.tokenSource as? CustomLexer
- val filename =
- when {
- customTokensource!=null -> customTokensource.modulePath.fileName.toString()
- start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
- else -> File(start.inputStream.sourceName).name
- }
- // note: be ware of TAB characters in the source text, they count as 1 column...
- return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine+stop.text.length)
-}
-
-
-private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : IStatement {
- val directive = directive()?.toAst()
- if(directive!=null) return directive
-
- val block = block()?.toAst(isInLibrary)
- if(block!=null) return block
-
- throw FatalAstException(text)
-}
-
-
-private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : IStatement =
- Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
-
-
-private fun prog8Parser.Statement_blockContext.toAst(): MutableList =
- statement().asSequence().map { it.toAst() }.toMutableList()
-
-
-private fun prog8Parser.StatementContext.toAst() : IStatement {
- vardecl()?.let {
- return VarDecl(VarDeclType.VAR,
- it.datatype().toAst(),
- it.ZEROPAGE()!=null,
- it.arrayindex()?.toAst(),
- it.identifier().text,
- null,
- it.ARRAYSIG()!=null || it.arrayindex()!=null,
- false,
- it.toPosition())
- }
-
- varinitializer()?.let {
- val vd = it.vardecl()
- return VarDecl(VarDeclType.VAR,
- vd.datatype().toAst(),
- vd.ZEROPAGE()!=null,
- vd.arrayindex()?.toAst(),
- vd.identifier().text,
- it.expression().toAst(),
- vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
- false,
- it.toPosition())
- }
-
- constdecl()?.let {
- val cvarinit = it.varinitializer()
- val vd = cvarinit.vardecl()
- return VarDecl(VarDeclType.CONST,
- vd.datatype().toAst(),
- vd.ZEROPAGE()!=null,
- vd.arrayindex()?.toAst(),
- vd.identifier().text,
- cvarinit.expression().toAst(),
- vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
- false,
- cvarinit.toPosition())
- }
-
- memoryvardecl()?.let {
- val mvarinit = it.varinitializer()
- val vd = mvarinit.vardecl()
- return VarDecl(VarDeclType.MEMORY,
- vd.datatype().toAst(),
- vd.ZEROPAGE()!=null,
- vd.arrayindex()?.toAst(),
- vd.identifier().text,
- mvarinit.expression().toAst(),
- vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
- false,
- mvarinit.toPosition())
- }
-
- assignment()?.let {
- return Assignment(it.assign_targets().toAst(), null, it.expression().toAst(), it.toPosition())
- }
-
- augassignment()?.let {
- return Assignment(listOf(it.assign_target().toAst()),
- it.operator.text,
- it.expression().toAst(),
- it.toPosition())
- }
-
- postincrdecr()?.let {
- return PostIncrDecr(it.assign_target().toAst(), it.operator.text, it.toPosition())
- }
-
- val directive = directive()?.toAst()
- if(directive!=null) return directive
-
- val label = labeldef()?.toAst()
- if(label!=null) return label
-
- val jump = unconditionaljump()?.toAst()
- if(jump!=null) return jump
-
- val fcall = functioncall_stmt()?.toAst()
- if(fcall!=null) return fcall
-
- val ifstmt = if_stmt()?.toAst()
- if(ifstmt!=null) return ifstmt
-
- val returnstmt = returnstmt()?.toAst()
- if(returnstmt!=null) return returnstmt
-
- val sub = subroutine()?.toAst()
- if(sub!=null) return sub
-
- val asm = inlineasm()?.toAst()
- if(asm!=null) return asm
-
- val branchstmt = branch_stmt()?.toAst()
- if(branchstmt!=null) return branchstmt
-
- val forloop = forloop()?.toAst()
- if(forloop!=null) return forloop
-
- val repeatloop = repeatloop()?.toAst()
- if(repeatloop!=null) return repeatloop
-
- val whileloop = whileloop()?.toAst()
- if(whileloop!=null) return whileloop
-
- val breakstmt = breakstmt()?.toAst()
- if(breakstmt!=null) return breakstmt
-
- val continuestmt = continuestmt()?.toAst()
- if(continuestmt!=null) return continuestmt
-
- val asmsubstmt = asmsubroutine()?.toAst()
- if(asmsubstmt!=null) return asmsubstmt
-
- throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
-}
-
-private fun prog8Parser.Assign_targetsContext.toAst(): List = assign_target().map { it.toAst() }
-
-
-private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement {
- val name = identifier().text
- val address = asmsub_address()?.address?.toAst()?.number?.toInt()
- val params = asmsub_params()?.toAst() ?: emptyList()
- val returns = asmsub_returns()?.toAst() ?: emptyList()
- val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
- val normalReturnvalues = returns.map { it.type }
- val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
- val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
- val clobbers = clobber()?.toAst() ?: emptySet()
- val statements = statement_block()?.toAst() ?: mutableListOf()
- return Subroutine(name, normalParameters, normalReturnvalues,
- paramRegisters, returnRegisters, clobbers, address, true, statements, toPosition())
-}
-
-private class AsmSubroutineParameter(name: String,
- type: DataType,
- val registerOrPair: RegisterOrPair?,
- val statusflag: Statusflag?,
- val stack: Boolean,
- position: Position) : SubroutineParameter(name, type, position)
-
-private class AsmSubroutineReturn(val type: DataType,
- val registerOrPair: RegisterOrPair?,
- val statusflag: Statusflag?,
- val stack: Boolean,
- val position: Position)
-
-private fun prog8Parser.ClobberContext.toAst(): Set
- = this.register().asSequence().map { it.toAst() }.toSet()
-
-
-private fun prog8Parser.Asmsub_returnsContext.toAst(): List
- = asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
-
-private fun prog8Parser.Asmsub_paramsContext.toAst(): List
- = asmsub_param().map { AsmSubroutineParameter(it.vardecl().identifier().text, it.vardecl().datatype().toAst(),
- it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
-
-
-private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
-
-
-private fun prog8Parser.Functioncall_stmtContext.toAst(): IStatement {
- val location = scoped_identifier().toAst()
- return if(expression_list() == null)
- FunctionCallStatement(location, mutableListOf(), toPosition())
- else
- FunctionCallStatement(location, expression_list().toAst().toMutableList(), toPosition())
-}
-
-
-private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall {
- val location = scoped_identifier().toAst()
- return if(expression_list() == null)
- FunctionCall(location, mutableListOf(), toPosition())
- else
- FunctionCall(location, expression_list().toAst().toMutableList(), toPosition())
-}
-
-
-private fun prog8Parser.InlineasmContext.toAst() =
- InlineAssembly(INLINEASMBLOCK().text, toPosition())
-
-
-private fun prog8Parser.ReturnstmtContext.toAst() : Return {
- val values = expression_list()
- return Return(values?.toAst() ?: emptyList(), toPosition())
-}
-
-private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
- val address = integerliteral()?.toAst()?.number?.toInt()
- val identifier = scoped_identifier()?.toAst()
- return Jump(address, identifier, null, toPosition())
-}
-
-
-private fun prog8Parser.LabeldefContext.toAst(): IStatement =
- Label(children[0].text, toPosition())
-
-
-private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
- return Subroutine(identifier().text,
- sub_params()?.toAst() ?: emptyList(),
- sub_return_part()?.toAst() ?: emptyList(),
- emptyList(),
- emptyList(),
- emptySet(),
- null,
- false,
- statement_block()?.toAst() ?: mutableListOf(),
- toPosition())
-}
-
-private fun prog8Parser.Sub_return_partContext.toAst(): List {
- val returns = sub_returns() ?: return emptyList()
- return returns.datatype().map { it.toAst() }
-}
-
-
-private fun prog8Parser.Sub_paramsContext.toAst(): List =
- vardecl().map {
- SubroutineParameter(it.identifier().text, it.datatype().toAst(), it.toPosition())
- }
-
-
-private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
- val register = register()?.toAst()
- val identifier = scoped_identifier()
- return when {
- register!=null -> AssignTarget(register, null, null, null, toPosition())
- identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
- arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
- directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
- else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
- }
-}
-
-private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
-
-private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
-
-private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
-
-
-private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
- ArrayIndex(expression().toAst(), toPosition())
-
-
-private fun prog8Parser.DirectiveContext.toAst() : Directive =
- Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
-
-
-private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg =
- DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
-
-
-private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
- fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
- val integer: Int
- var datatype = DataType.UBYTE
- when (radix) {
- 10 -> {
- integer = try {
- text.toInt()
- } catch(x: NumberFormatException) {
- throw AstException("${toPosition()} invalid decimal literal ${x.message}")
- }
- datatype = when(integer) {
- in 0..255 -> DataType.UBYTE
- in -128..127 -> DataType.BYTE
- in 0..65535 -> DataType.UWORD
- in -32768..32767 -> DataType.WORD
- else -> DataType.FLOAT
- }
- }
- 2 -> {
- if(text.length>8)
- datatype = DataType.UWORD
- try {
- integer = text.toInt(2)
- } catch(x: NumberFormatException) {
- throw AstException("${toPosition()} invalid binary literal ${x.message}")
- }
- }
- 16 -> {
- if(text.length>2)
- datatype = DataType.UWORD
- try {
- integer = text.toInt(16)
- } catch(x: NumberFormatException) {
- throw AstException("${toPosition()} invalid hexadecimal literal ${x.message}")
- }
- }
- else -> throw FatalAstException("invalid radix")
- }
- return NumericLiteral(integer, if(forceWord) DataType.UWORD else datatype)
- }
- val terminal: TerminalNode = children[0] as TerminalNode
- val integerPart = this.intpart.text
- return when (terminal.symbol.type) {
- prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10, wordsuffix()!=null)
- prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16, wordsuffix()!=null)
- prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2, wordsuffix()!=null)
- else -> throw FatalAstException(terminal.text)
- }
-}
-
-
-private fun prog8Parser.ExpressionContext.toAst() : IExpression {
-
- val litval = literalvalue()
- if(litval!=null) {
- val booleanlit = litval.booleanliteral()?.toAst()
- return if(booleanlit!=null) {
- LiteralValue.fromBoolean(booleanlit, litval.toPosition())
- }
- else {
- val intLit = litval.integerliteral()?.toAst()
- when {
- intLit!=null -> when(intLit.datatype) {
- DataType.UBYTE -> LiteralValue(DataType.UBYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
- DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
- DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
- DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
- DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue= intLit.number.toDouble(), position = litval.toPosition())
- else -> throw FatalAstException("invalid datatype for numeric literal")
- }
- litval.floatliteral()!=null -> LiteralValue(DataType.FLOAT, floatvalue = litval.floatliteral().toAst(), position = litval.toPosition())
- litval.stringliteral()!=null -> LiteralValue(DataType.STR, strvalue = unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
- litval.charliteral()!=null -> {
- try {
- LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], position = litval.toPosition())
- } catch (ce: CharConversionException) {
- throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
- }
- }
- litval.arrayliteral()!=null -> {
- val array = litval.arrayliteral()?.toAst()
- // the actual type of the arraysize can not yet be determined here (missing namespace & heap)
- // the ConstantFolder takes care of that and converts the type if needed.
- LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
- }
- else -> throw FatalAstException("invalid parsed literal")
- }
- }
- }
-
- if(register()!=null)
- return RegisterExpr(register().toAst(), register().toPosition())
-
- if(scoped_identifier()!=null)
- return scoped_identifier().toAst()
-
- if(bop!=null)
- return BinaryExpression(left.toAst(), bop.text, right.toAst(), toPosition())
-
- if(prefix!=null)
- return PrefixExpression(prefix.text, expression(0).toAst(), toPosition())
-
- val funcall = functioncall()?.toAst()
- if(funcall!=null) return funcall
-
- if (rangefrom!=null && rangeto!=null) {
- val step = rangestep?.toAst() ?: LiteralValue(DataType.UBYTE, 1, position = toPosition())
- return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
- }
-
- if(childCount==3 && children[0].text=="(" && children[2].text==")")
- return expression(0).toAst() // expression within ( )
-
- if(arrayindexed()!=null)
- return arrayindexed().toAst()
-
- if(typecast()!=null)
- return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), false, toPosition())
-
- if(directmemory()!=null)
- return DirectMemoryRead(directmemory().expression().toAst(), toPosition())
-
- if(addressof()!=null)
- return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
-
- throw FatalAstException(text)
-}
-
-
-private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
- return ArrayIndexedExpression(scoped_identifier().toAst(),
- arrayindex().toAst(),
- toPosition())
-}
-
-
-private fun prog8Parser.Expression_listContext.toAst() = expression().map{ it.toAst() }
-
-
-private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference =
- IdentifierReference(listOf(text), toPosition())
-
-
-private fun prog8Parser.Scoped_identifierContext.toAst() : IdentifierReference =
- IdentifierReference(NAME().map { it.text }, toPosition())
-
-
-private fun prog8Parser.FloatliteralContext.toAst() = text.toDouble()
-
-
-private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
- "true" -> true
- "false" -> false
- else -> throw FatalAstException(text)
-}
-
-
-private fun prog8Parser.ArrayliteralContext.toAst() : Array =
- expression().map { it.toAst() }.toTypedArray()
-
-
-private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
- val condition = expression().toAst()
- val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
- val elseStatements = else_part()?.toAst() ?: mutableListOf()
- val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition() ?: statement().toPosition())
- val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
- return IfStatement(condition, trueScope, elseScope, toPosition())
-}
-
-private fun prog8Parser.Else_partContext.toAst(): MutableList {
- return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
-}
-
-
-private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
- val branchcondition = branchcondition().toAst()
- val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
- val elseStatements = else_part()?.toAst() ?: mutableListOf()
- val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition() ?: statement().toPosition())
- val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
- return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
-}
-
-private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
-
-
-private fun prog8Parser.ForloopContext.toAst(): ForLoop {
- val loopregister = register()?.toAst()
- val datatype = datatype()?.toAst()
- val zeropage = ZEROPAGE()!=null
- val loopvar = identifier()?.toAst()
- val iterable = expression()!!.toAst()
- val scope =
- if(statement()!=null)
- AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
- else
- AnonymousScope(statement_block().toAst(), statement_block().toPosition())
- return ForLoop(loopregister, datatype, zeropage, loopvar, iterable, scope, toPosition())
-}
-
-
-private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
-
-private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
-
-
-private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
- val condition = expression().toAst()
- val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
- val scope = AnonymousScope(statements, statement_block()?.toPosition() ?: statement().toPosition())
- return WhileLoop(condition, scope, toPosition())
-}
-
-
-private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
- val untilCondition = expression().toAst()
- val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
- val scope = AnonymousScope(statements, statement_block()?.toPosition() ?: statement().toPosition())
- return RepeatLoop(scope, untilCondition, toPosition())
-}
-
-
-internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
-
-internal fun unescape(str: String, position: Position): String {
- val result = mutableListOf()
- val iter = str.iterator()
- while(iter.hasNext()) {
- val c = iter.nextChar()
- if(c=='\\') {
- val ec = iter.nextChar()
- result.add(when(ec) {
- '\\' -> '\\'
- 'n' -> '\n'
- 'r' -> '\r'
- '"' -> '"'
- 'u' -> {
- "${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
- }
- else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
- })
- } else {
- result.add(c)
- }
- }
- return result.joinToString("")
-}
diff --git a/compiler/src/prog8/ast/AstRecursionChecker.kt b/compiler/src/prog8/ast/AstRecursionChecker.kt
deleted file mode 100644
index a27616568..000000000
--- a/compiler/src/prog8/ast/AstRecursionChecker.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-package prog8.ast
-
-/**
- * Checks for the occurrence of recursive subroutine calls
- */
-
-internal fun Program.checkRecursion() {
- val checker = AstRecursionChecker(namespace)
- checker.process(this)
- printErrors(checker.result(), name)
-}
-
-
-private class DirectedGraph {
- private val graph = mutableMapOf>()
- private var uniqueVertices = mutableSetOf()
- val numVertices : Int
- get() = uniqueVertices.size
-
- fun add(from: VT, to: VT) {
- var targets = graph[from]
- if(targets==null) {
- targets = mutableSetOf()
- graph[from] = targets
- }
- targets.add(to)
- uniqueVertices.add(from)
- uniqueVertices.add(to)
- }
-
- fun print() {
- println("#vertices: $numVertices")
- graph.forEach { (from, to) ->
- println("$from CALLS:")
- to.forEach { println(" $it") }
- }
- val cycle = checkForCycle()
- if(cycle.isNotEmpty()) {
- println("CYCLIC! $cycle")
- }
- }
-
- fun checkForCycle(): MutableList {
- val visited = uniqueVertices.associateWith { false }.toMutableMap()
- val recStack = uniqueVertices.associateWith { false }.toMutableMap()
- val cycle = mutableListOf()
- for(node in uniqueVertices) {
- if(isCyclicUntil(node, visited, recStack, cycle))
- return cycle
- }
- return mutableListOf()
- }
-
- private fun isCyclicUntil(node: VT,
- visited: MutableMap,
- recStack: MutableMap,
- cycleNodes: MutableList): Boolean {
-
- if(recStack[node]==true) return true
- if(visited[node]==true) return false
-
- // mark current node as visited and add to recursion stack
- visited[node] = true
- recStack[node] = true
-
- // recurse for all neighbours
- val neighbors = graph[node]
- if(neighbors!=null) {
- for (neighbour in neighbors) {
- if (isCyclicUntil(neighbour, visited, recStack, cycleNodes)) {
- cycleNodes.add(node)
- return true
- }
- }
- }
-
- // pop node from recursion stack
- recStack[node] = false
- return false
- }
-}
-
-
-private class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
- private val callGraph = DirectedGraph()
-
- internal fun result(): List {
- val cycle = callGraph.checkForCycle()
- if(cycle.isEmpty())
- return emptyList()
- val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
- return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain"))
- }
-
- override fun process(functionCallStatement: FunctionCallStatement): IStatement {
- val scope = functionCallStatement.definingScope()
- val targetStatement = functionCallStatement.target.targetStatement(namespace)
- if(targetStatement!=null) {
- val targetScope = when (targetStatement) {
- is Subroutine -> targetStatement
- else -> targetStatement.definingScope()
- }
- callGraph.add(scope, targetScope)
- }
- return super.process(functionCallStatement)
- }
-
- override fun process(functionCall: FunctionCall): IExpression {
- val scope = functionCall.definingScope()
- val targetStatement = functionCall.target.targetStatement(namespace)
- if(targetStatement!=null) {
- val targetScope = when (targetStatement) {
- is Subroutine -> targetStatement
- else -> targetStatement.definingScope()
- }
- callGraph.add(scope, targetScope)
- }
- return super.process(functionCall)
- }
-}
diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt
new file mode 100644
index 000000000..94e761af3
--- /dev/null
+++ b/compiler/src/prog8/ast/AstToplevel.kt
@@ -0,0 +1,92 @@
+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.compiler.HeapValues
+import prog8.functions.BuiltinFunctions
+import java.nio.file.Path
+
+
+/*********** Everything starts from here, the Program; zero or more modules *************/
+
+class Program(val name: String, val modules: MutableList) {
+ val namespace = GlobalNamespace(modules)
+ val heap = HeapValues()
+
+ val loadAddress: Int
+ get() = modules.first().loadAddress
+
+ fun entrypoint(): Subroutine? {
+ val mainBlocks = modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
+ if(mainBlocks.size > 1)
+ throw FatalAstException("more than one 'main' block")
+ return if(mainBlocks.isEmpty()) {
+ null
+ } else {
+ mainBlocks[0].subScopes()["start"] as Subroutine?
+ }
+ }
+}
+
+class Module(override val name: String,
+ override var statements: MutableList,
+ override val position: Position,
+ val isLibraryModule: Boolean,
+ val source: Path) : Node, INameScope {
+ override lateinit var parent: Node
+ lateinit var program: Program
+ val importedBy = mutableListOf()
+ val imports = mutableSetOf()
+
+ var loadAddress: Int = 0 // can be set with the %address directive
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ statements.forEach {it.linkParents(this)}
+ }
+
+ override fun definingScope(): INameScope = program.namespace
+
+ override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
+}
+
+class GlobalNamespace(val modules: List): Node, INameScope {
+ override val name = "<<>>"
+ override val position = Position("<<>>", 0, 0, 0)
+ override val statements = mutableListOf()
+ override var parent: Node = ParentSentinel
+
+ override fun linkParents(parent: Node) {
+ modules.forEach { it.linkParents(this) }
+ }
+
+ override fun lookup(scopedName: List, localContext: Node): IStatement? {
+ 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)
+ builtinPlaceholder.parent = ParentSentinel
+ return builtinPlaceholder
+ }
+
+ val stmt = localContext.definingModule().lookup(scopedName, localContext)
+ return when (stmt) {
+ is Label, is VarDecl, is Block, is Subroutine -> stmt
+ null -> null
+ else -> throw NameError("wrong identifier target: $stmt", stmt.position)
+ }
+ }
+}
+
+object BuiltinFunctionScopePlaceholder : INameScope {
+ override val name = "<>"
+ override val position = Position("<>", 0, 0, 0)
+ override var statements = mutableListOf()
+ override var parent: Node = ParentSentinel
+ override fun linkParents(parent: Node) {}
+}
diff --git a/compiler/src/prog8/ast/Interfaces.kt b/compiler/src/prog8/ast/Interfaces.kt
new file mode 100644
index 000000000..10e1fff14
--- /dev/null
+++ b/compiler/src/prog8/ast/Interfaces.kt
@@ -0,0 +1,194 @@
+package prog8.ast
+
+import prog8.ast.base.*
+import prog8.ast.expressions.*
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.*
+
+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(this)!!
+ }
+
+ fun definingSubroutine(): Subroutine? = findParentNode(this)
+
+ fun definingScope(): INameScope {
+ val scope = findParentNode(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 process(processor: IAstProcessor) : IStatement
+ 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()
+ 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(this)!!
+ }
+}
+
+interface IFunctionCall {
+ var target: IdentifierReference
+ var arglist: MutableList
+}
+
+interface INameScope {
+ val name: String
+ val position: Position
+ val statements: MutableList
+ val parent: Node
+
+ fun linkParents(parent: Node)
+
+ fun subScopes(): Map {
+ val subscopes = mutableMapOf()
+ for(stmt in statements) {
+ when(stmt) {
+ is INameScope -> subscopes[stmt.name] = stmt
+ 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
+ }
+ }
+ }
+ return subscopes
+ }
+
+ fun getLabelOrVariable(name: String): IStatement? {
+ // TODO this is called A LOT and could perhaps be optimized a bit more, but adding a 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
+ }
+ return null
+ }
+
+ fun allDefinedSymbols(): List> {
+ 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, localContext: Node) : IStatement? {
+ if(scopedName.size>1) {
+ // 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): LiteralValue?
+ fun process(processor: IAstProcessor): IExpression
+ fun referencesIdentifier(name: String): Boolean
+ 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 LiteralValue -> return (other is LiteralValue && other==this)
+ }
+ return false
+ }
+}
diff --git a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt
new file mode 100644
index 000000000..20affd00b
--- /dev/null
+++ b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt
@@ -0,0 +1,572 @@
+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.*
+import prog8.ast.base.*
+import prog8.ast.expressions.*
+import prog8.ast.statements.*
+import java.io.CharConversionException
+import java.io.File
+import java.nio.file.Path
+import prog8.compiler.target.c64.Petscii
+import prog8.parser.CustomLexer
+import prog8.parser.prog8Parser
+
+
+/***************** Antlr Extension methods to create AST ****************/
+
+private data class NumericLiteral(val number: Number, val datatype: DataType)
+
+
+fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path) : Module {
+ val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
+ return Module(nameWithoutSuffix, modulestatement().asSequence().map { it.toAst(isLibrary) }.toMutableList(), toPosition(), isLibrary, source)
+}
+
+
+private fun ParserRuleContext.toPosition() : Position {
+ val customTokensource = this.start.tokenSource as? CustomLexer
+ val filename =
+ when {
+ customTokensource!=null -> customTokensource.modulePath.fileName.toString()
+ start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
+ else -> File(start.inputStream.sourceName).name
+ }
+ // note: be ware of TAB characters in the source text, they count as 1 column...
+ return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
+}
+
+
+private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : IStatement {
+ val directive = directive()?.toAst()
+ if(directive!=null) return directive
+
+ val block = block()?.toAst(isInLibrary)
+ if(block!=null) return block
+
+ throw FatalAstException(text)
+}
+
+
+private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : IStatement =
+ Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
+
+
+private fun prog8Parser.Statement_blockContext.toAst(): MutableList =
+ statement().asSequence().map { it.toAst() }.toMutableList()
+
+
+private fun prog8Parser.StatementContext.toAst() : IStatement {
+ vardecl()?.let {
+ return VarDecl(VarDeclType.VAR,
+ it.datatype().toAst(),
+ it.ZEROPAGE() != null,
+ it.arrayindex()?.toAst(),
+ it.identifier().text,
+ null,
+ it.ARRAYSIG() != null || it.arrayindex() != null,
+ false,
+ it.toPosition())
+ }
+
+ varinitializer()?.let {
+ val vd = it.vardecl()
+ return VarDecl(VarDeclType.VAR,
+ vd.datatype().toAst(),
+ vd.ZEROPAGE() != null,
+ vd.arrayindex()?.toAst(),
+ vd.identifier().text,
+ it.expression().toAst(),
+ vd.ARRAYSIG() != null || vd.arrayindex() != null,
+ false,
+ it.toPosition())
+ }
+
+ constdecl()?.let {
+ val cvarinit = it.varinitializer()
+ val vd = cvarinit.vardecl()
+ return VarDecl(VarDeclType.CONST,
+ vd.datatype().toAst(),
+ vd.ZEROPAGE() != null,
+ vd.arrayindex()?.toAst(),
+ vd.identifier().text,
+ cvarinit.expression().toAst(),
+ vd.ARRAYSIG() != null || vd.arrayindex() != null,
+ false,
+ cvarinit.toPosition())
+ }
+
+ memoryvardecl()?.let {
+ val mvarinit = it.varinitializer()
+ val vd = mvarinit.vardecl()
+ return VarDecl(VarDeclType.MEMORY,
+ vd.datatype().toAst(),
+ vd.ZEROPAGE() != null,
+ vd.arrayindex()?.toAst(),
+ vd.identifier().text,
+ mvarinit.expression().toAst(),
+ vd.ARRAYSIG() != null || vd.arrayindex() != null,
+ false,
+ mvarinit.toPosition())
+ }
+
+ assignment()?.let {
+ return Assignment(it.assign_targets().toAst(), null, it.expression().toAst(), it.toPosition())
+ }
+
+ augassignment()?.let {
+ return Assignment(listOf(it.assign_target().toAst()),
+ it.operator.text,
+ it.expression().toAst(),
+ it.toPosition())
+ }
+
+ postincrdecr()?.let {
+ return PostIncrDecr(it.assign_target().toAst(), it.operator.text, it.toPosition())
+ }
+
+ val directive = directive()?.toAst()
+ if(directive!=null) return directive
+
+ val label = labeldef()?.toAst()
+ if(label!=null) return label
+
+ val jump = unconditionaljump()?.toAst()
+ if(jump!=null) return jump
+
+ val fcall = functioncall_stmt()?.toAst()
+ if(fcall!=null) return fcall
+
+ val ifstmt = if_stmt()?.toAst()
+ if(ifstmt!=null) return ifstmt
+
+ val returnstmt = returnstmt()?.toAst()
+ if(returnstmt!=null) return returnstmt
+
+ val sub = subroutine()?.toAst()
+ if(sub!=null) return sub
+
+ val asm = inlineasm()?.toAst()
+ if(asm!=null) return asm
+
+ val branchstmt = branch_stmt()?.toAst()
+ if(branchstmt!=null) return branchstmt
+
+ val forloop = forloop()?.toAst()
+ if(forloop!=null) return forloop
+
+ val repeatloop = repeatloop()?.toAst()
+ if(repeatloop!=null) return repeatloop
+
+ val whileloop = whileloop()?.toAst()
+ if(whileloop!=null) return whileloop
+
+ val breakstmt = breakstmt()?.toAst()
+ if(breakstmt!=null) return breakstmt
+
+ val continuestmt = continuestmt()?.toAst()
+ if(continuestmt!=null) return continuestmt
+
+ val asmsubstmt = asmsubroutine()?.toAst()
+ if(asmsubstmt!=null) return asmsubstmt
+
+ throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
+}
+
+private fun prog8Parser.Assign_targetsContext.toAst(): List = assign_target().map { it.toAst() }
+
+
+private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement {
+ val name = identifier().text
+ val address = asmsub_address()?.address?.toAst()?.number?.toInt()
+ val params = asmsub_params()?.toAst() ?: emptyList()
+ val returns = asmsub_returns()?.toAst() ?: emptyList()
+ val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
+ val normalReturnvalues = returns.map { it.type }
+ val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
+ val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
+ val clobbers = clobber()?.toAst() ?: emptySet()
+ val statements = statement_block()?.toAst() ?: mutableListOf()
+ return Subroutine(name, normalParameters, normalReturnvalues,
+ paramRegisters, returnRegisters, clobbers, address, true, statements, toPosition())
+}
+
+private class AsmSubroutineParameter(name: String,
+ type: DataType,
+ val registerOrPair: RegisterOrPair?,
+ val statusflag: Statusflag?,
+ val stack: Boolean,
+ position: Position) : SubroutineParameter(name, type, position)
+
+private class AsmSubroutineReturn(val type: DataType,
+ val registerOrPair: RegisterOrPair?,
+ val statusflag: Statusflag?,
+ val stack: Boolean,
+ val position: Position)
+
+private fun prog8Parser.ClobberContext.toAst(): Set
+ = this.register().asSequence().map { it.toAst() }.toSet()
+
+
+private fun prog8Parser.Asmsub_returnsContext.toAst(): List
+ = asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
+
+private fun prog8Parser.Asmsub_paramsContext.toAst(): List
+ = asmsub_param().map {
+ AsmSubroutineParameter(it.vardecl().identifier().text, it.vardecl().datatype().toAst(),
+ it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition())
+}
+
+
+private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
+
+
+private fun prog8Parser.Functioncall_stmtContext.toAst(): IStatement {
+ val location = scoped_identifier().toAst()
+ return if(expression_list() == null)
+ FunctionCallStatement(location, mutableListOf(), toPosition())
+ else
+ FunctionCallStatement(location, expression_list().toAst().toMutableList(), toPosition())
+}
+
+
+private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall {
+ val location = scoped_identifier().toAst()
+ return if(expression_list() == null)
+ FunctionCall(location, mutableListOf(), toPosition())
+ else
+ FunctionCall(location, expression_list().toAst().toMutableList(), toPosition())
+}
+
+
+private fun prog8Parser.InlineasmContext.toAst() =
+ InlineAssembly(INLINEASMBLOCK().text, toPosition())
+
+
+private fun prog8Parser.ReturnstmtContext.toAst() : Return {
+ val values = expression_list()
+ return Return(values?.toAst() ?: emptyList(), toPosition())
+}
+
+private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
+ val address = integerliteral()?.toAst()?.number?.toInt()
+ val identifier = scoped_identifier()?.toAst()
+ return Jump(address, identifier, null, toPosition())
+}
+
+
+private fun prog8Parser.LabeldefContext.toAst(): IStatement =
+ Label(children[0].text, toPosition())
+
+
+private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
+ return Subroutine(identifier().text,
+ sub_params()?.toAst() ?: emptyList(),
+ sub_return_part()?.toAst() ?: emptyList(),
+ emptyList(),
+ emptyList(),
+ emptySet(),
+ null,
+ false,
+ statement_block()?.toAst() ?: mutableListOf(),
+ toPosition())
+}
+
+private fun prog8Parser.Sub_return_partContext.toAst(): List {
+ val returns = sub_returns() ?: return emptyList()
+ return returns.datatype().map { it.toAst() }
+}
+
+
+private fun prog8Parser.Sub_paramsContext.toAst(): List =
+ vardecl().map {
+ SubroutineParameter(it.identifier().text, it.datatype().toAst(), it.toPosition())
+ }
+
+
+private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
+ val register = register()?.toAst()
+ val identifier = scoped_identifier()
+ return when {
+ register!=null -> AssignTarget(register, null, null, null, toPosition())
+ identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
+ arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
+ directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
+ else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
+ }
+}
+
+private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
+
+private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
+
+private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
+
+
+private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
+ ArrayIndex(expression().toAst(), toPosition())
+
+
+private fun prog8Parser.DirectiveContext.toAst() : Directive =
+ Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
+
+
+private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg =
+ DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
+
+
+private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
+ fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
+ val integer: Int
+ var datatype = DataType.UBYTE
+ when (radix) {
+ 10 -> {
+ integer = try {
+ text.toInt()
+ } catch(x: NumberFormatException) {
+ throw AstException("${toPosition()} invalid decimal literal ${x.message}")
+ }
+ datatype = when(integer) {
+ in 0..255 -> DataType.UBYTE
+ in -128..127 -> DataType.BYTE
+ in 0..65535 -> DataType.UWORD
+ in -32768..32767 -> DataType.WORD
+ else -> DataType.FLOAT
+ }
+ }
+ 2 -> {
+ if(text.length>8)
+ datatype = DataType.UWORD
+ try {
+ integer = text.toInt(2)
+ } catch(x: NumberFormatException) {
+ throw AstException("${toPosition()} invalid binary literal ${x.message}")
+ }
+ }
+ 16 -> {
+ if(text.length>2)
+ datatype = DataType.UWORD
+ try {
+ integer = text.toInt(16)
+ } catch(x: NumberFormatException) {
+ throw AstException("${toPosition()} invalid hexadecimal literal ${x.message}")
+ }
+ }
+ else -> throw FatalAstException("invalid radix")
+ }
+ return NumericLiteral(integer, if (forceWord) DataType.UWORD else datatype)
+ }
+ val terminal: TerminalNode = children[0] as TerminalNode
+ val integerPart = this.intpart.text
+ return when (terminal.symbol.type) {
+ prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10, wordsuffix()!=null)
+ prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16, wordsuffix()!=null)
+ prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2, wordsuffix()!=null)
+ else -> throw FatalAstException(terminal.text)
+ }
+}
+
+
+private fun prog8Parser.ExpressionContext.toAst() : IExpression {
+
+ val litval = literalvalue()
+ if(litval!=null) {
+ val booleanlit = litval.booleanliteral()?.toAst()
+ return if(booleanlit!=null) {
+ LiteralValue.fromBoolean(booleanlit, litval.toPosition())
+ }
+ else {
+ val intLit = litval.integerliteral()?.toAst()
+ when {
+ intLit!=null -> when(intLit.datatype) {
+ DataType.UBYTE -> LiteralValue(DataType.UBYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
+ DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
+ DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
+ DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
+ DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = intLit.number.toDouble(), position = litval.toPosition())
+ else -> throw FatalAstException("invalid datatype for numeric literal")
+ }
+ litval.floatliteral()!=null -> LiteralValue(DataType.FLOAT, floatvalue = litval.floatliteral().toAst(), position = litval.toPosition())
+ litval.stringliteral()!=null -> LiteralValue(DataType.STR, strvalue = unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
+ litval.charliteral()!=null -> {
+ try {
+ LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], position = litval.toPosition())
+ } catch (ce: CharConversionException) {
+ throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
+ }
+ }
+ litval.arrayliteral()!=null -> {
+ val array = litval.arrayliteral()?.toAst()
+ // the actual type of the arraysize can not yet be determined here (missing namespace & heap)
+ // the ConstantFolder takes care of that and converts the type if needed.
+ LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
+ }
+ else -> throw FatalAstException("invalid parsed literal")
+ }
+ }
+ }
+
+ if(register()!=null)
+ return RegisterExpr(register().toAst(), register().toPosition())
+
+ if(scoped_identifier()!=null)
+ return scoped_identifier().toAst()
+
+ if(bop!=null)
+ return BinaryExpression(left.toAst(), bop.text, right.toAst(), toPosition())
+
+ if(prefix!=null)
+ return PrefixExpression(prefix.text, expression(0).toAst(), toPosition())
+
+ val funcall = functioncall()?.toAst()
+ if(funcall!=null) return funcall
+
+ if (rangefrom!=null && rangeto!=null) {
+ val step = rangestep?.toAst() ?: LiteralValue(DataType.UBYTE, 1, position = toPosition())
+ return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
+ }
+
+ if(childCount==3 && children[0].text=="(" && children[2].text==")")
+ return expression(0).toAst() // expression within ( )
+
+ if(arrayindexed()!=null)
+ return arrayindexed().toAst()
+
+ if(typecast()!=null)
+ return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), false, toPosition())
+
+ if(directmemory()!=null)
+ return DirectMemoryRead(directmemory().expression().toAst(), toPosition())
+
+ if(addressof()!=null)
+ return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
+
+ throw FatalAstException(text)
+}
+
+
+private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
+ return ArrayIndexedExpression(scoped_identifier().toAst(),
+ arrayindex().toAst(),
+ toPosition())
+}
+
+
+private fun prog8Parser.Expression_listContext.toAst() = expression().map{ it.toAst() }
+
+
+private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference =
+ IdentifierReference(listOf(text), toPosition())
+
+
+private fun prog8Parser.Scoped_identifierContext.toAst() : IdentifierReference =
+ IdentifierReference(NAME().map { it.text }, toPosition())
+
+
+private fun prog8Parser.FloatliteralContext.toAst() = text.toDouble()
+
+
+private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
+ "true" -> true
+ "false" -> false
+ else -> throw FatalAstException(text)
+}
+
+
+private fun prog8Parser.ArrayliteralContext.toAst() : Array =
+ expression().map { it.toAst() }.toTypedArray()
+
+
+private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
+ val condition = expression().toAst()
+ val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
+ val elseStatements = else_part()?.toAst() ?: mutableListOf()
+ val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
+ ?: statement().toPosition())
+ val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
+ return IfStatement(condition, trueScope, elseScope, toPosition())
+}
+
+private fun prog8Parser.Else_partContext.toAst(): MutableList {
+ return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
+}
+
+
+private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
+ val branchcondition = branchcondition().toAst()
+ val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
+ val elseStatements = else_part()?.toAst() ?: mutableListOf()
+ val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
+ ?: statement().toPosition())
+ val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
+ return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
+}
+
+private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
+
+
+private fun prog8Parser.ForloopContext.toAst(): ForLoop {
+ val loopregister = register()?.toAst()
+ val datatype = datatype()?.toAst()
+ val zeropage = ZEROPAGE()!=null
+ val loopvar = identifier()?.toAst()
+ val iterable = expression()!!.toAst()
+ val scope =
+ if(statement()!=null)
+ AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
+ else
+ AnonymousScope(statement_block().toAst(), statement_block().toPosition())
+ return ForLoop(loopregister, datatype, zeropage, loopvar, iterable, scope, toPosition())
+}
+
+
+private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
+
+private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
+
+
+private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
+ val condition = expression().toAst()
+ val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
+ val scope = AnonymousScope(statements, statement_block()?.toPosition()
+ ?: statement().toPosition())
+ return WhileLoop(condition, scope, toPosition())
+}
+
+
+private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
+ val untilCondition = expression().toAst()
+ val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
+ val scope = AnonymousScope(statements, statement_block()?.toPosition()
+ ?: statement().toPosition())
+ return RepeatLoop(scope, untilCondition, toPosition())
+}
+
+
+internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
+
+internal fun unescape(str: String, position: Position): String {
+ val result = mutableListOf()
+ val iter = str.iterator()
+ while(iter.hasNext()) {
+ val c = iter.nextChar()
+ if(c=='\\') {
+ val ec = iter.nextChar()
+ result.add(when(ec) {
+ '\\' -> '\\'
+ 'n' -> '\n'
+ 'r' -> '\r'
+ '"' -> '"'
+ 'u' -> {
+ "${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
+ }
+ else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
+ })
+ } else {
+ result.add(c)
+ }
+ }
+ return result.joinToString("")
+}
diff --git a/compiler/src/prog8/ast/base/Base.kt b/compiler/src/prog8/ast/base/Base.kt
new file mode 100644
index 000000000..150927433
--- /dev/null
+++ b/compiler/src/prog8/ast/base/Base.kt
@@ -0,0 +1,130 @@
+package prog8.ast.base
+
+import prog8.ast.Node
+
+/**************************** AST Data classes ****************************/
+
+enum class DataType {
+ UBYTE,
+ BYTE,
+ UWORD,
+ WORD,
+ FLOAT,
+ STR,
+ STR_S,
+ ARRAY_UB,
+ ARRAY_B,
+ ARRAY_UW,
+ ARRAY_W,
+ ARRAY_F;
+
+ /**
+ * is the type assignable to the given other type?
+ */
+ infix fun isAssignableTo(targetType: DataType) =
+ // what types are assignable to others without loss of precision?
+ when(this) {
+ UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
+ BYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
+ UWORD -> targetType == UWORD || targetType == FLOAT
+ WORD -> targetType == WORD || targetType==UWORD || targetType == FLOAT
+ FLOAT -> targetType == FLOAT
+ STR -> targetType == STR || targetType==STR_S
+ STR_S -> targetType == STR || targetType==STR_S
+ in ArrayDatatypes -> targetType === this
+ else -> false
+ }
+
+
+ infix fun isAssignableTo(targetTypes: Set) = targetTypes.any { this isAssignableTo it }
+
+ infix fun biggerThan(other: DataType) =
+ when(this) {
+ in ByteDatatypes -> false
+ in WordDatatypes -> other in ByteDatatypes
+ else -> true
+ }
+}
+
+enum class Register {
+ A,
+ X,
+ Y
+}
+
+enum class RegisterOrPair {
+ A,
+ X,
+ Y,
+ AX,
+ AY,
+ XY
+} // only used in parameter and return value specs in asm subroutines
+
+enum class Statusflag {
+ Pc,
+ Pz,
+ Pv,
+ Pn
+}
+
+enum class BranchCondition {
+ CS,
+ CC,
+ EQ,
+ Z,
+ NE,
+ NZ,
+ VS,
+ VC,
+ MI,
+ NEG,
+ PL,
+ POS
+}
+
+enum class VarDeclType {
+ VAR,
+ CONST,
+ MEMORY
+}
+
+val IterableDatatypes = setOf(
+ DataType.STR, DataType.STR_S,
+ DataType.ARRAY_UB, DataType.ARRAY_B,
+ DataType.ARRAY_UW, DataType.ARRAY_W,
+ DataType.ARRAY_F)
+val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
+val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
+val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
+val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
+val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
+val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
+val ArrayElementTypes = mapOf(
+ DataType.ARRAY_B to DataType.BYTE,
+ DataType.ARRAY_UB to DataType.UBYTE,
+ DataType.ARRAY_W to DataType.WORD,
+ DataType.ARRAY_UW to DataType.UWORD,
+ DataType.ARRAY_F to DataType.FLOAT)
+
+// find the parent node of a specific type or interface
+// (useful to figure out in what namespace/block something is defined, etc)
+inline fun findParentNode(node: Node): T? {
+ var candidate = node.parent
+ while(candidate !is T && candidate !is ParentSentinel)
+ candidate = candidate.parent
+ return if(candidate is ParentSentinel)
+ null
+ else
+ candidate as T
+}
+
+object ParentSentinel : Node {
+ override val position = Position("<>", 0, 0, 0)
+ override var parent: Node = this
+ override fun linkParents(parent: Node) {}
+}
+
+data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
+ override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
+}
diff --git a/compiler/src/prog8/ast/base/ErrorReporting.kt b/compiler/src/prog8/ast/base/ErrorReporting.kt
new file mode 100644
index 000000000..19b4365ec
--- /dev/null
+++ b/compiler/src/prog8/ast/base/ErrorReporting.kt
@@ -0,0 +1,37 @@
+package prog8.ast.base
+
+import prog8.parser.ParsingFailedError
+
+
+fun printErrors(errors: List, moduleName: String) {
+ val reportedMessages = mutableSetOf()
+ print("\u001b[91m") // bright red
+ errors.forEach {
+ val msg = it.toString()
+ if(msg !in reportedMessages) {
+ System.err.println(msg)
+ reportedMessages.add(msg)
+ }
+ }
+ print("\u001b[0m") // reset color
+ if(reportedMessages.isNotEmpty())
+ throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
+}
+
+
+fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
+ print("\u001b[93m") // bright yellow
+ print("$position Warning: $msg")
+ if(detailInfo==null)
+ print("\n")
+ else
+ println(": $detailInfo\n")
+ print("\u001b[0m") // normal
+}
+
+
+fun printWarning(msg: String) {
+ print("\u001b[93m") // bright yellow
+ print("Warning: $msg")
+ print("\u001b[0m\n") // normal
+}
diff --git a/compiler/src/prog8/ast/base/Errors.kt b/compiler/src/prog8/ast/base/Errors.kt
new file mode 100644
index 000000000..14ffe0e21
--- /dev/null
+++ b/compiler/src/prog8/ast/base/Errors.kt
@@ -0,0 +1,22 @@
+package prog8.ast.base
+
+import prog8.ast.expressions.IdentifierReference
+
+class FatalAstException (override var message: String) : Exception(message)
+
+open class AstException (override var message: String) : Exception(message)
+
+class SyntaxError(override var message: String, val position: Position) : AstException(message) {
+ override fun toString() = "$position Syntax error: $message"
+}
+
+class NameError(override var message: String, val position: Position) : AstException(message) {
+ override fun toString() = "$position Name error: $message"
+}
+
+open class ExpressionError(message: String, val position: Position) : AstException(message) {
+ override fun toString() = "$position Error: $message"
+}
+
+class UndefinedSymbolError(symbol: IdentifierReference)
+ : ExpressionError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt
new file mode 100644
index 000000000..5eb1af007
--- /dev/null
+++ b/compiler/src/prog8/ast/base/Extensions.kt
@@ -0,0 +1,84 @@
+package prog8.ast.base
+
+import prog8.ast.*
+import prog8.ast.expressions.IdentifierReference
+import prog8.ast.processing.*
+import prog8.ast.statements.Assignment
+import prog8.ast.statements.ForLoop
+import prog8.compiler.CompilationOptions
+
+
+// the name of the subroutine that should be called for every block to initialize its variables
+internal const val initvarsSubName="prog8_init_vars"
+
+
+// prefix for literal values that are turned into a variable on the heap
+internal const val autoHeapValuePrefix = "auto_heap_value_"
+
+
+internal fun Program.checkValid(compilerOptions: CompilationOptions) {
+ val checker = AstChecker(this, compilerOptions)
+ checker.process(this)
+ printErrors(checker.result(), name)
+}
+
+
+internal fun Program.reorderStatements() {
+ val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
+ initvalueCreator.process(this)
+
+ val checker = StatementReorderer(this)
+ checker.process(this)
+}
+
+internal fun Module.checkImportedValid() {
+ val checker = ImportedAstChecker()
+ checker.process(this)
+ printErrors(checker.result(), name)
+}
+
+internal fun Program.checkRecursion() {
+ val checker = AstRecursionChecker(namespace)
+ checker.process(this)
+ printErrors(checker.result(), name)
+}
+
+
+internal fun Program.checkIdentifiers() {
+ val checker = AstIdentifiersChecker(namespace)
+ checker.process(this)
+
+ if(modules.map {it.name}.toSet().size != modules.size) {
+ throw FatalAstException("modules should all be unique")
+ }
+
+ // add any anonymous variables for heap values that are used,
+ // and replace an iterable literalvalue by identifierref to new local variable
+ for (variable in checker.anonymousVariablesFromHeap.values) {
+ val scope = variable.first.definingScope()
+ scope.statements.add(variable.second)
+ val parent = variable.first.parent
+ when {
+ parent is Assignment && parent.value === variable.first -> {
+ val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
+ idref.linkParents(parent)
+ parent.value = idref
+ }
+ parent is IFunctionCall -> {
+ val parameterPos = parent.arglist.indexOf(variable.first)
+ val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
+ idref.linkParents(parent)
+ parent.arglist[parameterPos] = idref
+ }
+ parent is ForLoop -> {
+ val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
+ idref.linkParents(parent)
+ parent.iterable = idref
+ }
+ else -> TODO("replace literalvalue by identifierref: $variable (in $parent)")
+ }
+ variable.second.linkParents(scope as Node)
+ }
+
+ printErrors(checker.result(), name)
+}
diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt
new file mode 100644
index 000000000..a4553b367
--- /dev/null
+++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt
@@ -0,0 +1,793 @@
+package prog8.ast.expressions
+
+import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.*
+import prog8.compiler.HeapValues
+import prog8.compiler.IntegerOrAddressOf
+import prog8.compiler.RuntimeValue
+import prog8.compiler.target.c64.Petscii
+import prog8.functions.BuiltinFunctions
+import prog8.functions.NotConstArgumentException
+import prog8.functions.builtinFunctionReturnType
+import kotlin.math.abs
+import kotlin.math.floor
+
+class PrefixExpression(val operator: String, var expression: IExpression, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ expression.linkParents(this)
+ }
+
+ override fun constValue(program: Program): LiteralValue? = null
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
+ override fun inferType(program: Program): DataType? = expression.inferType(program)
+
+ override fun toString(): String {
+ return "Prefix($operator $expression)"
+ }
+}
+
+class BinaryExpression(var left: IExpression, var operator: String, var right: IExpression, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ left.linkParents(this)
+ right.linkParents(this)
+ }
+
+ override fun toString(): String {
+ return "[$left $operator $right]"
+ }
+
+ // binary expression should actually have been optimized away into a single value, before const value was requested...
+ override fun constValue(program: Program): LiteralValue? = null
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
+ override fun inferType(program: Program): DataType? {
+ val leftDt = left.inferType(program)
+ val rightDt = right.inferType(program)
+ return when (operator) {
+ "+", "-", "*", "**", "%" -> if (leftDt == null || rightDt == null) null else {
+ try {
+ arithmeticOpDt(leftDt, rightDt)
+ } catch (x: FatalAstException) {
+ null
+ }
+ }
+ "/" -> if (leftDt == null || rightDt == null) null else divisionOpDt(leftDt, rightDt)
+ "&" -> leftDt
+ "|" -> leftDt
+ "^" -> leftDt
+ "and", "or", "xor",
+ "<", ">",
+ "<=", ">=",
+ "==", "!=" -> DataType.UBYTE
+ "<<", ">>" -> leftDt
+ else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
+ }
+ }
+
+ companion object {
+ fun divisionOpDt(leftDt: DataType, rightDt: DataType): DataType {
+ return when (leftDt) {
+ DataType.UBYTE -> when (rightDt) {
+ DataType.UBYTE, DataType.UWORD -> DataType.UBYTE
+ DataType.BYTE, DataType.WORD -> DataType.WORD
+ DataType.FLOAT -> DataType.BYTE
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.BYTE -> when (rightDt) {
+ in NumericDatatypes -> DataType.BYTE
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.UWORD -> when (rightDt) {
+ DataType.UBYTE, DataType.UWORD -> DataType.UWORD
+ DataType.BYTE, DataType.WORD -> DataType.WORD
+ DataType.FLOAT -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.WORD -> when (rightDt) {
+ in NumericDatatypes -> DataType.WORD
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.FLOAT -> when (rightDt) {
+ in NumericDatatypes -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ }
+
+ fun arithmeticOpDt(leftDt: DataType, rightDt: DataType): DataType {
+ return when (leftDt) {
+ DataType.UBYTE -> when (rightDt) {
+ DataType.UBYTE -> DataType.UBYTE
+ DataType.BYTE -> DataType.BYTE
+ DataType.UWORD -> DataType.UWORD
+ DataType.WORD -> DataType.WORD
+ DataType.FLOAT -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.BYTE -> when (rightDt) {
+ in ByteDatatypes -> DataType.BYTE
+ in WordDatatypes -> DataType.WORD
+ DataType.FLOAT -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.UWORD -> when (rightDt) {
+ DataType.UBYTE, DataType.UWORD -> DataType.UWORD
+ DataType.BYTE, DataType.WORD -> DataType.WORD
+ DataType.FLOAT -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.WORD -> when (rightDt) {
+ in IntegerDatatypes -> DataType.WORD
+ DataType.FLOAT -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ DataType.FLOAT -> when (rightDt) {
+ in NumericDatatypes -> DataType.FLOAT
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
+ }
+ }
+ }
+
+ fun commonDatatype(leftDt: DataType, rightDt: DataType,
+ left: IExpression, right: IExpression): Pair {
+ // byte + byte -> byte
+ // byte + word -> word
+ // word + byte -> word
+ // word + word -> word
+ // a combination with a float will be float (but give a warning about this!)
+
+ if(this.operator=="/") {
+ // division is a bit weird, don't cast the operands
+ val commondt = divisionOpDt(leftDt, rightDt)
+ return Pair(commondt, null)
+ }
+
+ return when (leftDt) {
+ DataType.UBYTE -> {
+ when (rightDt) {
+ DataType.UBYTE -> Pair(DataType.UBYTE, null)
+ DataType.BYTE -> Pair(DataType.BYTE, left)
+ DataType.UWORD -> Pair(DataType.UWORD, left)
+ DataType.WORD -> Pair(DataType.WORD, left)
+ DataType.FLOAT -> Pair(DataType.FLOAT, left)
+ else -> throw FatalAstException("non-numeric datatype $rightDt")
+ }
+ }
+ DataType.BYTE -> {
+ when (rightDt) {
+ DataType.UBYTE -> Pair(DataType.BYTE, right)
+ DataType.BYTE -> Pair(DataType.BYTE, null)
+ DataType.UWORD -> Pair(DataType.WORD, left)
+ DataType.WORD -> Pair(DataType.WORD, left)
+ DataType.FLOAT -> Pair(DataType.FLOAT, left)
+ else -> throw FatalAstException("non-numeric datatype $rightDt")
+ }
+ }
+ DataType.UWORD -> {
+ when (rightDt) {
+ DataType.UBYTE -> Pair(DataType.UWORD, right)
+ DataType.BYTE -> Pair(DataType.UWORD, right)
+ DataType.UWORD -> Pair(DataType.UWORD, null)
+ DataType.WORD -> Pair(DataType.WORD, left)
+ DataType.FLOAT -> Pair(DataType.FLOAT, left)
+ else -> throw FatalAstException("non-numeric datatype $rightDt")
+ }
+ }
+ DataType.WORD -> {
+ when (rightDt) {
+ DataType.UBYTE -> Pair(DataType.WORD, right)
+ DataType.BYTE -> Pair(DataType.WORD, right)
+ DataType.UWORD -> Pair(DataType.WORD, right)
+ DataType.WORD -> Pair(DataType.WORD, null)
+ DataType.FLOAT -> Pair(DataType.FLOAT, left)
+ else -> throw FatalAstException("non-numeric datatype $rightDt")
+ }
+ }
+ DataType.FLOAT -> {
+ Pair(DataType.FLOAT, right)
+ }
+ else -> throw FatalAstException("non-numeric datatype $leftDt")
+ }
+ }
+}
+
+class ArrayIndexedExpression(val identifier: IdentifierReference,
+ var arrayspec: ArrayIndex,
+ override val position: Position) : IExpression {
+ override lateinit var parent: Node
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ identifier.linkParents(this)
+ arrayspec.linkParents(this)
+ }
+
+ override fun constValue(program: Program): LiteralValue? = null
+ override fun process(processor: IAstProcessor): IExpression = processor.process(this)
+ override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
+
+ override fun inferType(program: Program): DataType? {
+ val target = identifier.targetStatement(program.namespace)
+ if (target is VarDecl) {
+ return when (target.datatype) {
+ in NumericDatatypes -> null
+ in StringDatatypes -> DataType.UBYTE
+ DataType.ARRAY_UB -> DataType.UBYTE
+ DataType.ARRAY_B -> DataType.BYTE
+ DataType.ARRAY_UW -> DataType.UWORD
+ DataType.ARRAY_W -> DataType.WORD
+ DataType.ARRAY_F -> DataType.FLOAT
+ else -> throw FatalAstException("invalid dt")
+ }
+ }
+ return null
+ }
+
+ override fun toString(): String {
+ return "ArrayIndexed(ident=$identifier, arraysize=$arrayspec; pos=$position)"
+ }
+}
+
+class TypecastExpression(var expression: IExpression, var type: DataType, val implicit: Boolean, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ expression.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
+ override fun inferType(program: Program): DataType? = type
+ override fun constValue(program: Program): LiteralValue? {
+ val cv = expression.constValue(program) ?: return null
+ val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
+ return LiteralValue.fromNumber(value.numericValue(), value.type, position)
+ }
+
+ override fun toString(): String {
+ return "Typecast($expression as $type)"
+ }
+}
+
+data class AddressOf(val identifier: IdentifierReference, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ identifier.parent=this
+ }
+
+ var scopedname: String? = null // will be set in a later state by the compiler
+ override fun constValue(program: Program): LiteralValue? = null
+ override fun referencesIdentifier(name: String) = false
+ override fun inferType(program: Program) = DataType.UWORD
+ override fun process(processor: IAstProcessor) = processor.process(this)
+}
+
+class DirectMemoryRead(var addressExpression: IExpression, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ this.addressExpression.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String) = false
+ override fun inferType(program: Program): DataType? = DataType.UBYTE
+ override fun constValue(program: Program): LiteralValue? = null
+
+ override fun toString(): String {
+ return "DirectMemoryRead($addressExpression)"
+ }
+}
+
+class DirectMemoryWrite(var addressExpression: IExpression, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ this.addressExpression.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String) = false
+ override fun inferType(program: Program): DataType? = DataType.UBYTE
+ override fun constValue(program: Program): LiteralValue? = null
+
+ override fun toString(): String {
+ return "DirectMemoryWrite($addressExpression)"
+ }
+}
+
+open class LiteralValue(val type: DataType,
+ val bytevalue: Short? = null,
+ val wordvalue: Int? = null,
+ val floatvalue: Double? = null,
+ val strvalue: String? = null,
+ val arrayvalue: Array? = null,
+ initHeapId: Int? =null,
+ override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
+
+ val isString = type in StringDatatypes
+ val isNumeric = type in NumericDatatypes
+ val isArray = type in ArrayDatatypes
+ var heapId = initHeapId
+ private set
+
+ companion object {
+ fun fromBoolean(bool: Boolean, position: Position) =
+ LiteralValue(DataType.UBYTE, bytevalue = if (bool) 1 else 0, position = position)
+
+ fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
+ return when(type) {
+ in ByteDatatypes -> LiteralValue(type, bytevalue = value.toShort(), position = position)
+ in WordDatatypes -> LiteralValue(type, wordvalue = value.toInt(), position = position)
+ DataType.FLOAT -> LiteralValue(type, floatvalue = value.toDouble(), position = position)
+ else -> throw FatalAstException("non numeric datatype")
+ }
+ }
+
+ fun optimalNumeric(value: Number, position: Position): LiteralValue {
+ return if(value is Double) {
+ LiteralValue(DataType.FLOAT, floatvalue = value, position = position)
+ } else {
+ when (val intval = value.toInt()) {
+ in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = intval.toShort(), position = position)
+ in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = intval.toShort(), position = position)
+ in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = intval, position = position)
+ in -32768..32767 -> LiteralValue(DataType.WORD, wordvalue = intval, position = position)
+ else -> LiteralValue(DataType.FLOAT, floatvalue = intval.toDouble(), position = position)
+ }
+ }
+ }
+
+ fun optimalInteger(value: Number, position: Position): LiteralValue {
+ val intval = value.toInt()
+ if(intval.toDouble() != value.toDouble())
+ throw FatalAstException("value is not an integer: $value")
+ return when (intval) {
+ in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = value.toShort(), position = position)
+ in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
+ in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = value.toInt(), position = position)
+ else -> throw FatalAstException("integer overflow: $value")
+ }
+ }
+ }
+
+ init {
+ when(type){
+ in ByteDatatypes -> if(bytevalue==null) throw FatalAstException("literal value missing bytevalue")
+ in WordDatatypes -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
+ DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
+ in StringDatatypes ->
+ if(strvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
+ in ArrayDatatypes ->
+ if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
+ else -> throw FatalAstException("invalid type $type")
+ }
+ if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && strvalue==null && heapId==null)
+ throw FatalAstException("literal value without actual value")
+ }
+
+ val asNumericValue: Number? = when {
+ bytevalue!=null -> bytevalue
+ wordvalue!=null -> wordvalue
+ floatvalue!=null -> floatvalue
+ else -> null
+ }
+
+ val asIntegerValue: Int? = when {
+ bytevalue!=null -> bytevalue.toInt()
+ wordvalue!=null -> wordvalue
+ // don't round a float value, otherwise code will not detect that it's not an integer
+ else -> null
+ }
+
+ val asBooleanValue: Boolean =
+ (floatvalue!=null && floatvalue != 0.0) ||
+ (bytevalue!=null && bytevalue != 0.toShort()) ||
+ (wordvalue!=null && wordvalue != 0) ||
+ (strvalue!=null && strvalue.isNotEmpty()) ||
+ (arrayvalue != null && arrayvalue.isNotEmpty())
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ arrayvalue?.forEach {it.linkParents(this)}
+ }
+
+ override fun constValue(program: Program): LiteralValue? {
+ if(arrayvalue!=null) {
+ for(v in arrayvalue) {
+ if(v.constValue(program)==null) return null
+ }
+ }
+ return this
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ val vstr = when(type) {
+ DataType.UBYTE -> "ubyte:$bytevalue"
+ DataType.BYTE -> "byte:$bytevalue"
+ DataType.UWORD -> "uword:$wordvalue"
+ DataType.WORD -> "word:$wordvalue"
+ DataType.FLOAT -> "float:$floatvalue"
+ in StringDatatypes -> "str:$strvalue"
+ in ArrayDatatypes -> "array:$arrayvalue"
+ else -> throw FatalAstException("weird datatype")
+ }
+ return "LiteralValue($vstr)"
+ }
+
+ override fun inferType(program: Program) = type
+
+ override fun hashCode(): Int {
+ val bh = bytevalue?.hashCode() ?: 0x10001234
+ val wh = wordvalue?.hashCode() ?: 0x01002345
+ val fh = floatvalue?.hashCode() ?: 0x00103456
+ val sh = strvalue?.hashCode() ?: 0x00014567
+ val ah = arrayvalue?.hashCode() ?: 0x11119876
+ var hash = bh * 31 xor wh
+ hash = hash*31 xor fh
+ hash = hash*31 xor sh
+ hash = hash*31 xor ah
+ hash = hash*31 xor type.hashCode()
+ return hash
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if(other==null || other !is LiteralValue)
+ return false
+ if(isNumeric && other.isNumeric)
+ return asNumericValue?.toDouble()==other.asNumericValue?.toDouble()
+ if(isArray && other.isArray)
+ return arrayvalue!!.contentEquals(other.arrayvalue!!) && heapId==other.heapId
+ if(isString && other.isString)
+ return strvalue==other.strvalue && heapId==other.heapId
+
+ if(type!=other.type)
+ return false
+
+ return compareTo(other) == 0
+ }
+
+ operator fun compareTo(other: LiteralValue): Int {
+ val numLeft = asNumericValue?.toDouble()
+ val numRight = other.asNumericValue?.toDouble()
+ if(numLeft!=null && numRight!=null)
+ return numLeft.compareTo(numRight)
+
+ if(strvalue!=null && other.strvalue!=null)
+ return strvalue.compareTo(other.strvalue)
+
+ throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
+ }
+
+ fun intoDatatype(targettype: DataType): LiteralValue? {
+ if(type==targettype)
+ return this
+ when(type) {
+ DataType.UBYTE -> {
+ if(targettype== DataType.BYTE && bytevalue!! <= 127)
+ return LiteralValue(targettype, bytevalue = bytevalue, position = position)
+ if(targettype== DataType.WORD || targettype== DataType.UWORD)
+ return LiteralValue(targettype, wordvalue = bytevalue!!.toInt(), position = position)
+ if(targettype== DataType.FLOAT)
+ return LiteralValue(targettype, floatvalue = bytevalue!!.toDouble(), position = position)
+ }
+ DataType.BYTE -> {
+ if(targettype== DataType.UBYTE && bytevalue!! >= 0)
+ return LiteralValue(targettype, bytevalue = bytevalue, position = position)
+ if(targettype== DataType.UWORD && bytevalue!! >= 0)
+ return LiteralValue(targettype, wordvalue = bytevalue.toInt(), position = position)
+ if(targettype== DataType.WORD)
+ return LiteralValue(targettype, wordvalue = bytevalue!!.toInt(), position = position)
+ if(targettype== DataType.FLOAT)
+ return LiteralValue(targettype, floatvalue = bytevalue!!.toDouble(), position = position)
+ }
+ DataType.UWORD -> {
+ if(targettype== DataType.BYTE && wordvalue!! <= 127)
+ return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
+ if(targettype== DataType.UBYTE && wordvalue!! <= 255)
+ return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
+ if(targettype== DataType.WORD && wordvalue!! <= 32767)
+ return LiteralValue(targettype, wordvalue = wordvalue, position = position)
+ if(targettype== DataType.FLOAT)
+ return LiteralValue(targettype, floatvalue = wordvalue!!.toDouble(), position = position)
+ }
+ DataType.WORD -> {
+ if(targettype== DataType.BYTE && wordvalue!! in -128..127)
+ return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
+ if(targettype== DataType.UBYTE && wordvalue!! in 0..255)
+ return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
+ if(targettype== DataType.UWORD && wordvalue!! >=0)
+ return LiteralValue(targettype, wordvalue = wordvalue, position = position)
+ if(targettype== DataType.FLOAT)
+ return LiteralValue(targettype, floatvalue = wordvalue!!.toDouble(), position = position)
+ }
+ DataType.FLOAT -> {
+ if(floor(floatvalue!!) ==floatvalue) {
+ val value = floatvalue.toInt()
+ if (targettype == DataType.BYTE && value in -128..127)
+ return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
+ if (targettype == DataType.UBYTE && value in 0..255)
+ return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
+ if (targettype == DataType.WORD && value in -32768..32767)
+ return LiteralValue(targettype, wordvalue = value, position = position)
+ if (targettype == DataType.UWORD && value in 0..65535)
+ return LiteralValue(targettype, wordvalue = value, position = position)
+ }
+ }
+ in StringDatatypes -> {
+ if(targettype in StringDatatypes)
+ return this
+ }
+ else -> {}
+ }
+ return null // invalid type conversion from $this to $targettype
+ }
+
+ fun addToHeap(heap: HeapValues) {
+ if(heapId==null) {
+ if (strvalue != null) {
+ heapId = heap.addString(type, strvalue)
+ }
+ else if (arrayvalue!=null) {
+ if(arrayvalue.any {it is AddressOf }) {
+ val intArrayWithAddressOfs = arrayvalue.map {
+ when (it) {
+ is AddressOf -> IntegerOrAddressOf(null, it)
+ is LiteralValue -> IntegerOrAddressOf(it.asIntegerValue, null)
+ else -> throw FatalAstException("invalid datatype in array")
+ }
+ }
+ heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
+ } else {
+ val valuesInArray = arrayvalue.map { (it as LiteralValue).asNumericValue!! }
+ heapId = if(type== DataType.ARRAY_F) {
+ val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
+ heap.addDoublesArray(doubleArray)
+ } else {
+ val integerArray = valuesInArray.map { it.toInt() }
+ heap.addIntegerArray(type, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
+ }
+ }
+ }
+ }
+ }
+}
+
+class RangeExpr(var from: IExpression,
+ var to: IExpression,
+ var step: IExpression,
+ override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ from.linkParents(this)
+ to.linkParents(this)
+ step.linkParents(this)
+ }
+
+ override fun constValue(program: Program): LiteralValue? = null
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
+ override fun inferType(program: Program): DataType? {
+ val fromDt=from.inferType(program)
+ val toDt=to.inferType(program)
+ return when {
+ fromDt==null || toDt==null -> null
+ fromDt== DataType.UBYTE && toDt== DataType.UBYTE -> DataType.UBYTE
+ fromDt== DataType.UWORD && toDt== DataType.UWORD -> DataType.UWORD
+ fromDt== DataType.STR && toDt== DataType.STR -> DataType.STR
+ fromDt== DataType.STR_S && toDt== DataType.STR_S -> DataType.STR_S
+ fromDt== DataType.WORD || toDt== DataType.WORD -> DataType.WORD
+ fromDt== DataType.BYTE || toDt== DataType.BYTE -> DataType.BYTE
+ else -> DataType.UBYTE
+ }
+ }
+ override fun toString(): String {
+ return "RangeExpr(from $from, to $to, step $step, pos=$position)"
+ }
+
+ fun size(): Int? {
+ val fromLv = (from as? LiteralValue)
+ val toLv = (to as? LiteralValue)
+ if(fromLv==null || toLv==null)
+ return null
+ return toConstantIntegerRange()?.count()
+ }
+
+ fun toConstantIntegerRange(): IntProgression? {
+ val fromLv = from as? LiteralValue
+ val toLv = to as? LiteralValue
+ if(fromLv==null || toLv==null)
+ return null // non-constant range
+ val fromVal: Int
+ val toVal: Int
+ if(fromLv.isString && toLv.isString) {
+ // string range -> int range over petscii values
+ fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt()
+ toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt()
+ } else {
+ // integer range
+ fromVal = (from as LiteralValue).asIntegerValue!!
+ toVal = (to as LiteralValue).asIntegerValue!!
+ }
+ val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1
+ return when {
+ fromVal <= toVal -> when {
+ stepVal <= 0 -> IntRange.EMPTY
+ stepVal == 1 -> fromVal..toVal
+ else -> fromVal..toVal step stepVal
+ }
+ else -> when {
+ stepVal >= 0 -> IntRange.EMPTY
+ stepVal == -1 -> fromVal downTo toVal
+ else -> fromVal downTo toVal step abs(stepVal)
+ }
+ }
+ }
+}
+
+class RegisterExpr(val register: Register, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+
+ override fun constValue(program: Program): LiteralValue? = null
+ override fun process(processor: IAstProcessor) = this
+ override fun referencesIdentifier(name: String): Boolean = false
+ override fun toString(): String {
+ return "RegisterExpr(register=$register, pos=$position)"
+ }
+
+ override fun inferType(program: Program) = DataType.UBYTE
+}
+
+data class IdentifierReference(val nameInSource: List, override val position: Position) : IExpression {
+ override lateinit var parent: Node
+
+ fun targetStatement(namespace: INameScope) =
+ if(nameInSource.size==1 && nameInSource[0] in BuiltinFunctions)
+ BuiltinFunctionStatementPlaceholder(nameInSource[0], position)
+ else
+ namespace.lookup(nameInSource, this)
+
+ fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
+ fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+
+ override fun constValue(program: Program): LiteralValue? {
+ val node = program.namespace.lookup(nameInSource, this)
+ ?: throw UndefinedSymbolError(this)
+ val vardecl = node as? VarDecl
+ if(vardecl==null) {
+ return null
+ } else if(vardecl.type!= VarDeclType.CONST) {
+ return null
+ }
+ return vardecl.value?.constValue(program)
+ }
+
+ override fun toString(): String {
+ return "IdentifierRef($nameInSource)"
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
+
+ override fun inferType(program: Program): DataType? {
+ val targetStmt = targetStatement(program.namespace)
+ if(targetStmt is VarDecl) {
+ return targetStmt.datatype
+ } else {
+ throw FatalAstException("cannot get datatype from identifier reference ${this}, pos=$position")
+ }
+ }
+
+ fun heapId(namespace: INameScope): Int {
+ val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
+ return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
+ }
+}
+
+class FunctionCall(override var target: IdentifierReference,
+ override var arglist: MutableList,
+ override val position: Position) : IExpression, IFunctionCall {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ target.linkParents(this)
+ arglist.forEach { it.linkParents(this) }
+ }
+
+ override fun constValue(program: Program) = constValue(program, true)
+
+ private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
+ // if the function is a built-in function and the args are consts, should try to const-evaluate!
+ // lenghts of arrays and strings are constants that are determined at compile time!
+ if(target.nameInSource.size>1) return null
+ try {
+ var resultValue: LiteralValue? = null
+ val func = BuiltinFunctions[target.nameInSource[0]]
+ if(func!=null) {
+ val exprfunc = func.constExpressionFunc
+ if(exprfunc!=null)
+ resultValue = exprfunc(arglist, position, program)
+ else if(func.returntype==null)
+ throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
+ }
+
+ if(withDatatypeCheck) {
+ val resultDt = this.inferType(program)
+ if(resultValue==null || resultDt == resultValue.type)
+ return resultValue
+ throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
+ } else {
+ return resultValue
+ }
+ }
+ catch(x: NotConstArgumentException) {
+ // const-evaluating the builtin function call failed.
+ return null
+ }
+ }
+
+ override fun toString(): String {
+ return "FunctionCall(target=$target, pos=$position)"
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+ override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)}
+
+ override fun inferType(program: Program): DataType? {
+ val constVal = constValue(program ,false)
+ if(constVal!=null)
+ return constVal.type
+ val stmt = target.targetStatement(program.namespace) ?: return null
+ when (stmt) {
+ is BuiltinFunctionStatementPlaceholder -> {
+ if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
+ target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
+ return null // these have no return value
+ }
+ return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
+ }
+ is Subroutine -> {
+ if(stmt.returntypes.isEmpty())
+ return null // no return value
+ if(stmt.returntypes.size==1)
+ return stmt.returntypes[0]
+ return null // has multiple return types... so not a single resulting datatype possible
+ }
+ is Label -> return null
+ }
+ return null // calling something we don't recognise...
+ }
+}
diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt
similarity index 93%
rename from compiler/src/prog8/ast/AstChecker.kt
rename to compiler/src/prog8/ast/processing/AstChecker.kt
index 293739e10..a354b48fa 100644
--- a/compiler/src/prog8/ast/AstChecker.kt
+++ b/compiler/src/prog8/ast/processing/AstChecker.kt
@@ -1,58 +1,19 @@
-package prog8.ast
+package prog8.ast.processing
+import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.base.printWarning
+import prog8.ast.expressions.*
+import prog8.ast.statements.*
import prog8.compiler.CompilationOptions
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
import prog8.functions.BuiltinFunctions
-import prog8.parser.ParsingFailedError
import java.io.File
-/**
- * General checks on the Ast
- */
-
-internal fun Program.checkValid(compilerOptions: CompilationOptions) {
- val checker = AstChecker(this, compilerOptions)
- checker.process(this)
- printErrors(checker.result(), name)
-}
-
-
-fun printErrors(errors: List, moduleName: String) {
- val reportedMessages = mutableSetOf()
- print("\u001b[91m") // bright red
- errors.forEach {
- val msg = it.toString()
- if(msg !in reportedMessages) {
- System.err.println(msg)
- reportedMessages.add(msg)
- }
- }
- print("\u001b[0m") // reset color
- if(reportedMessages.isNotEmpty())
- throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
-}
-
-
-fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
- print("\u001b[93m") // bright yellow
- print("$position Warning: $msg")
- if(detailInfo==null)
- print("\n")
- else
- println(": $detailInfo\n")
- print("\u001b[0m") // normal
-}
-
-fun printWarning(msg: String) {
- print("\u001b[93m") // bright yellow
- print("Warning: $msg")
- print("\u001b[0m\n") // normal
-}
-
-private class AstChecker(private val program: Program,
- private val compilerOptions: CompilationOptions) : IAstProcessor {
+internal class AstChecker(private val program: Program,
+ private val compilerOptions: CompilationOptions) : IAstProcessor {
private val checkResult: MutableList = mutableListOf()
private val heapStringSentinel: Int
init {
@@ -142,7 +103,7 @@ private class AstChecker(private val program: Program,
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) {
val valueDt=rv.second.inferType(program)
if(rv.first.value!=valueDt)
- checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index+1} doesn't match subroutine return type ${rv.first.value}", rv.second.position))
+ checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index + 1} doesn't match subroutine return type ${rv.first.value}", rv.second.position))
}
return super.process(returnStmt)
}
@@ -158,36 +119,36 @@ private class AstChecker(private val program: Program,
if (forLoop.loopRegister != null) {
printWarning("using a register as loop variable is risky (it could get clobbered in the body)", forLoop.position)
// loop register
- if (iterableDt != DataType.UBYTE && iterableDt!=DataType.ARRAY_UB && iterableDt !in StringDatatypes)
+ if (iterableDt != DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes)
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
} else {
// loop variable
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
- if(loopvar==null || loopvar.type==VarDeclType.CONST) {
+ if(loopvar==null || loopvar.type== VarDeclType.CONST) {
checkResult.add(SyntaxError("for loop requires a variable to loop with", forLoop.position))
} else {
when (loopvar.datatype) {
DataType.UBYTE -> {
- if(iterableDt!=DataType.UBYTE && iterableDt!=DataType.ARRAY_UB && iterableDt !in StringDatatypes)
+ if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes)
checkResult.add(ExpressionError("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position))
}
DataType.UWORD -> {
- if(iterableDt!=DataType.UBYTE && iterableDt!=DataType.UWORD && iterableDt !in StringDatatypes &&
- iterableDt !=DataType.ARRAY_UB && iterableDt!=DataType.ARRAY_UW)
+ if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt !in StringDatatypes &&
+ iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
checkResult.add(ExpressionError("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position))
}
DataType.BYTE -> {
- if(iterableDt!=DataType.BYTE && iterableDt!=DataType.ARRAY_B)
+ if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position))
}
DataType.WORD -> {
- if(iterableDt!=DataType.BYTE && iterableDt!=DataType.WORD &&
- iterableDt !=DataType.ARRAY_B && iterableDt!=DataType.ARRAY_W)
+ if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
+ iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
checkResult.add(ExpressionError("word loop variable can only loop over bytes or words", forLoop.position))
}
DataType.FLOAT -> {
- if(iterableDt!=DataType.FLOAT && iterableDt != DataType.ARRAY_F)
+ if(iterableDt!= DataType.FLOAT && iterableDt != DataType.ARRAY_F)
checkResult.add(ExpressionError("float loop variable can only loop over floats", forLoop.position))
}
else -> checkResult.add(ExpressionError("loop variable must be numeric type", forLoop.position))
@@ -351,7 +312,7 @@ private class AstChecker(private val program: Program,
// This is not easy to fix because strings and arrays are treated a bit simplistic (a "virtual" pointer to the value on the heap)
// while passing them as subroutine parameters would require a "real" pointer OR copying the VALUE to the subroutine's parameter variable (which is very inefficient).
// For now, don't pass strings and arrays as parameters and instead create the workaround as suggested in the error message below.
- if(!subroutine.parameters.all{it.type in NumericDatatypes}) {
+ if(!subroutine.parameters.all{it.type in NumericDatatypes }) {
err("Non-asm subroutine can only take numerical parameters (no str/array types) for now. Workaround (for nested subroutine): access the variable from the outer scope directly.")
}
}
@@ -494,19 +455,19 @@ private class AstChecker(private val program: Program,
}
// CONST can only occur on simple types (byte, word, float)
- if(decl.type==VarDeclType.CONST) {
+ if(decl.type== VarDeclType.CONST) {
if (decl.datatype !in NumericDatatypes)
err("const modifier can only be used on numeric types (byte, word, float)")
}
// FLOATS
- if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!=VarDeclType.MEMORY) {
+ if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY) {
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", decl.position))
}
// ARRAY without size specifier MUST have an iterable initializer value
if(decl.isArray && decl.arraysize==null) {
- if(decl.type==VarDeclType.MEMORY)
+ if(decl.type== VarDeclType.MEMORY)
checkResult.add(SyntaxError("memory mapped array must have a size specification", decl.position))
if(decl.value==null) {
checkResult.add(SyntaxError("array variable is missing a size specification or an initialization value", decl.position))
@@ -528,15 +489,15 @@ private class AstChecker(private val program: Program,
// initialize numeric var with value zero by default.
val litVal =
when {
- decl.datatype in ByteDatatypes -> LiteralValue(decl.datatype, bytevalue=0, position = decl.position)
- decl.datatype in WordDatatypes -> LiteralValue(decl.datatype, wordvalue=0, position = decl.position)
- else -> LiteralValue(decl.datatype, floatvalue=0.0, position = decl.position)
+ decl.datatype in ByteDatatypes -> LiteralValue(decl.datatype, bytevalue = 0, position = decl.position)
+ decl.datatype in WordDatatypes -> LiteralValue(decl.datatype, wordvalue = 0, position = decl.position)
+ else -> LiteralValue(decl.datatype, floatvalue = 0.0, position = decl.position)
}
litVal.parent = decl
decl.value = litVal
}
- decl.type==VarDeclType.VAR -> {
- val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position=decl.position) // point to the sentinel heap value instead
+ decl.type== VarDeclType.VAR -> {
+ val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position = decl.position) // point to the sentinel heap value instead
litVal.parent=decl
decl.value = litVal
}
@@ -728,7 +689,7 @@ private class AstChecker(private val program: Program,
if(divisor==0.0)
checkResult.add(ExpressionError("division by zero", expr.right.position))
if(expr.operator=="%") {
- if ((rightDt != DataType.UBYTE && rightDt != DataType.UWORD) || (leftDt!=DataType.UBYTE && leftDt!=DataType.UWORD))
+ if ((rightDt != DataType.UBYTE && rightDt != DataType.UWORD) || (leftDt!= DataType.UBYTE && leftDt!= DataType.UWORD))
checkResult.add(ExpressionError("remainder can only be used on unsigned integer operands", expr.right.position))
}
}
@@ -864,7 +825,7 @@ private class AstChecker(private val program: Program,
val argDt = arg.first.value.inferType(program)
if(argDt!=null && !(argDt isAssignableTo arg.second.type)) {
// for asm subroutines having STR param it's okay to provide a UWORD too (pointer value)
- if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt==DataType.UWORD))
+ if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt== DataType.UWORD))
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
}
@@ -878,13 +839,13 @@ private class AstChecker(private val program: Program,
val asmParamReg = target.asmParameterRegisters[arg.first.index]
if(asmParamReg.statusflag!=null) {
if(argDt !in ByteDatatypes)
- checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be byte type for statusflag", position))
+ checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for statusflag", position))
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if(argDt !in ByteDatatypes)
- checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be byte type for single register", position))
+ checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for single register", position))
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
- if(argDt !in WordDatatypes+ IterableDatatypes)
- checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be word type for register pair", position))
+ if(argDt !in WordDatatypes + IterableDatatypes)
+ checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} must be word type for register pair", position))
}
}
}
@@ -899,7 +860,7 @@ private class AstChecker(private val program: Program,
if(target==null) {
checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position))
} else {
- if(target !is VarDecl || target.type==VarDeclType.CONST) {
+ if(target !is VarDecl || target.type== VarDeclType.CONST) {
checkResult.add(SyntaxError("can only increment or decrement a variable", postIncrDecr.position))
} else if(target.datatype !in NumericDatatypes) {
checkResult.add(SyntaxError("can only increment or decrement a byte/float/word variable", postIncrDecr.position))
@@ -945,7 +906,7 @@ private class AstChecker(private val program: Program,
// check index value 0..255
val dtx = arrayIndexedExpression.arrayspec.index.inferType(program)
- if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE)
+ if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE)
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
return super.process(arrayIndexedExpression)
@@ -1121,7 +1082,7 @@ private class AstChecker(private val program: Program,
value.arrayvalue.map {it.constValue(program)?.asNumericValue!!.toDouble()}.toDoubleArray()
else
heap.get(value.heapId!!).doubleArray!!
- if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE})
+ if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
return err("floating point value overflow")
return true
}
@@ -1190,26 +1151,26 @@ private class AstChecker(private val program: Program,
checkResult.add(SyntaxError("can't assign a range value", position))
val result = when(targetDatatype) {
- DataType.BYTE -> sourceDatatype==DataType.BYTE
- DataType.UBYTE -> sourceDatatype==DataType.UBYTE
- DataType.WORD -> sourceDatatype==DataType.BYTE || sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.WORD
- DataType.UWORD -> sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.UWORD
+ DataType.BYTE -> sourceDatatype== DataType.BYTE
+ DataType.UBYTE -> sourceDatatype== DataType.UBYTE
+ DataType.WORD -> sourceDatatype== DataType.BYTE || sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.WORD
+ DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
DataType.FLOAT -> sourceDatatype in NumericDatatypes
- DataType.STR -> sourceDatatype==DataType.STR
- DataType.STR_S -> sourceDatatype==DataType.STR_S
+ DataType.STR -> sourceDatatype== DataType.STR
+ DataType.STR_S -> sourceDatatype== DataType.STR_S
else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position))
}
if(result)
return true
- if((sourceDatatype==DataType.UWORD || sourceDatatype==DataType.WORD) && (targetDatatype==DataType.UBYTE || targetDatatype==DataType.BYTE)) {
+ if((sourceDatatype== DataType.UWORD || sourceDatatype== DataType.WORD) && (targetDatatype== DataType.UBYTE || targetDatatype== DataType.BYTE)) {
if(assignTargets.size==2 && assignTargets[0].register!=null && assignTargets[1].register!=null)
return true // for asm subroutine calls that return a (U)WORD that's going to be stored into two BYTES (registers), we make an exception.
else
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
}
- else if(sourceDatatype==DataType.FLOAT && targetDatatype in IntegerDatatypes)
+ else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
else
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position))
diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt
similarity index 82%
rename from compiler/src/prog8/ast/AstIdentifiersChecker.kt
rename to compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt
index 1c6e6a228..183cb8ae2 100644
--- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt
+++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt
@@ -1,54 +1,14 @@
-package prog8.ast
+package prog8.ast.processing
+import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.base.autoHeapValuePrefix
+import prog8.ast.expressions.*
+import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions
-/**
- * Checks the validity of all identifiers (no conflicts)
- * Also makes sure that subroutine's parameters also become local variable decls in the subroutine's scope.
- * Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
- */
-internal fun Program.checkIdentifiers() {
- val checker = AstIdentifiersChecker(namespace)
- checker.process(this)
-
- if(modules.map {it.name}.toSet().size != modules.size) {
- throw FatalAstException("modules should all be unique")
- }
-
- // add any anonymous variables for heap values that are used,
- // and replace an iterable literalvalue by identifierref to new local variable
- for (variable in checker.anonymousVariablesFromHeap.values) {
- val scope = variable.first.definingScope()
- scope.statements.add(variable.second)
- val parent = variable.first.parent
- when {
- parent is Assignment && parent.value === variable.first -> {
- val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
- idref.linkParents(parent)
- parent.value = idref
- }
- parent is IFunctionCall -> {
- val parameterPos = parent.arglist.indexOf(variable.first)
- val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
- idref.linkParents(parent)
- parent.arglist[parameterPos] = idref
- }
- parent is ForLoop -> {
- val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
- idref.linkParents(parent)
- parent.iterable = idref
- }
- else -> TODO("replace literalvalue by identifierref: $variable (in $parent)")
- }
- variable.second.linkParents(scope as Node)
- }
-
- printErrors(checker.result(), name)
-}
-
-
-private class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor {
+internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor {
private val checkResult: MutableList = mutableListOf()
private var blocks: MutableMap = mutableMapOf()
@@ -202,7 +162,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
}
override fun process(assignTarget: AssignTarget): AssignTarget {
- if(assignTarget.register==Register.X)
+ if(assignTarget.register== Register.X)
printWarning("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
return super.process(assignTarget)
}
@@ -254,5 +214,3 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
}
}
-
-internal const val autoHeapValuePrefix = "auto_heap_value_"
diff --git a/compiler/src/prog8/ast/processing/AstRecursionChecker.kt b/compiler/src/prog8/ast/processing/AstRecursionChecker.kt
new file mode 100644
index 000000000..e2cb75ebf
--- /dev/null
+++ b/compiler/src/prog8/ast/processing/AstRecursionChecker.kt
@@ -0,0 +1,117 @@
+package prog8.ast.processing
+
+import prog8.ast.*
+import prog8.ast.base.AstException
+import prog8.ast.expressions.FunctionCall
+import prog8.ast.statements.FunctionCallStatement
+import prog8.ast.statements.Subroutine
+
+
+internal class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
+ private val callGraph = DirectedGraph()
+
+ internal fun result(): List {
+ val cycle = callGraph.checkForCycle()
+ if(cycle.isEmpty())
+ return emptyList()
+ val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
+ return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain"))
+ }
+
+ override fun process(functionCallStatement: FunctionCallStatement): IStatement {
+ val scope = functionCallStatement.definingScope()
+ val targetStatement = functionCallStatement.target.targetStatement(namespace)
+ if(targetStatement!=null) {
+ val targetScope = when (targetStatement) {
+ is Subroutine -> targetStatement
+ else -> targetStatement.definingScope()
+ }
+ callGraph.add(scope, targetScope)
+ }
+ return super.process(functionCallStatement)
+ }
+
+ override fun process(functionCall: FunctionCall): IExpression {
+ val scope = functionCall.definingScope()
+ val targetStatement = functionCall.target.targetStatement(namespace)
+ if(targetStatement!=null) {
+ val targetScope = when (targetStatement) {
+ is Subroutine -> targetStatement
+ else -> targetStatement.definingScope()
+ }
+ callGraph.add(scope, targetScope)
+ }
+ return super.process(functionCall)
+ }
+
+
+ private class DirectedGraph {
+ private val graph = mutableMapOf>()
+ private var uniqueVertices = mutableSetOf()
+ val numVertices : Int
+ get() = uniqueVertices.size
+
+ fun add(from: VT, to: VT) {
+ var targets = graph[from]
+ if(targets==null) {
+ targets = mutableSetOf()
+ graph[from] = targets
+ }
+ targets.add(to)
+ uniqueVertices.add(from)
+ uniqueVertices.add(to)
+ }
+
+ fun print() {
+ println("#vertices: $numVertices")
+ graph.forEach { (from, to) ->
+ println("$from CALLS:")
+ to.forEach { println(" $it") }
+ }
+ val cycle = checkForCycle()
+ if(cycle.isNotEmpty()) {
+ println("CYCLIC! $cycle")
+ }
+ }
+
+ fun checkForCycle(): MutableList {
+ val visited = uniqueVertices.associateWith { false }.toMutableMap()
+ val recStack = uniqueVertices.associateWith { false }.toMutableMap()
+ val cycle = mutableListOf()
+ for(node in uniqueVertices) {
+ if(isCyclicUntil(node, visited, recStack, cycle))
+ return cycle
+ }
+ return mutableListOf()
+ }
+
+ private fun isCyclicUntil(node: VT,
+ visited: MutableMap,
+ recStack: MutableMap,
+ cycleNodes: MutableList): Boolean {
+
+ if(recStack[node]==true) return true
+ if(visited[node]==true) return false
+
+ // mark current node as visited and add to recursion stack
+ visited[node] = true
+ recStack[node] = true
+
+ // recurse for all neighbours
+ val neighbors = graph[node]
+ if(neighbors!=null) {
+ for (neighbour in neighbors) {
+ if (isCyclicUntil(neighbour, visited, recStack, cycleNodes)) {
+ cycleNodes.add(node)
+ return true
+ }
+ }
+ }
+
+ // pop node from recursion stack
+ recStack[node] = false
+ return false
+ }
+ }
+
+}
diff --git a/compiler/src/prog8/ast/processing/IAstProcessor.kt b/compiler/src/prog8/ast/processing/IAstProcessor.kt
new file mode 100644
index 000000000..566ed40db
--- /dev/null
+++ b/compiler/src/prog8/ast/processing/IAstProcessor.kt
@@ -0,0 +1,197 @@
+package prog8.ast.processing
+
+import prog8.ast.*
+import prog8.ast.expressions.*
+import prog8.ast.statements.*
+
+interface IAstProcessor {
+ fun process(program: Program) {
+ program.modules.forEach { process(it) }
+ }
+
+ fun process(module: Module) {
+ module.statements = module.statements.asSequence().map { it.process(this) }.toMutableList()
+ }
+
+ fun process(expr: PrefixExpression): IExpression {
+ expr.expression = expr.expression.process(this)
+ return expr
+ }
+
+ fun process(expr: BinaryExpression): IExpression {
+ expr.left = expr.left.process(this)
+ expr.right = expr.right.process(this)
+ return expr
+ }
+
+ fun process(directive: Directive): IStatement {
+ return directive
+ }
+
+ fun process(block: Block): IStatement {
+ block.statements = block.statements.asSequence().map { it.process(this) }.toMutableList()
+ return block
+ }
+
+ fun process(decl: VarDecl): IStatement {
+ decl.value = decl.value?.process(this)
+ decl.arraysize?.process(this)
+ return decl
+ }
+
+ fun process(subroutine: Subroutine): IStatement {
+ subroutine.statements = subroutine.statements.asSequence().map { it.process(this) }.toMutableList()
+ return subroutine
+ }
+
+ fun process(functionCall: FunctionCall): IExpression {
+ val newtarget = functionCall.target.process(this)
+ if(newtarget is IdentifierReference)
+ functionCall.target = newtarget
+ functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList()
+ return functionCall
+ }
+
+ fun process(functionCallStatement: FunctionCallStatement): IStatement {
+ val newtarget = functionCallStatement.target.process(this)
+ if(newtarget is IdentifierReference)
+ functionCallStatement.target = newtarget
+ functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList()
+ return functionCallStatement
+ }
+
+ fun process(identifier: IdentifierReference): IExpression {
+ // 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 process(jump: Jump): IStatement {
+ if(jump.identifier!=null) {
+ val ident = jump.identifier.process(this)
+ if(ident is IdentifierReference && ident!==jump.identifier) {
+ return Jump(null, ident, null, jump.position)
+ }
+ }
+ return jump
+ }
+
+ fun process(ifStatement: IfStatement): IStatement {
+ ifStatement.condition = ifStatement.condition.process(this)
+ ifStatement.truepart = ifStatement.truepart.process(this) as AnonymousScope
+ ifStatement.elsepart = ifStatement.elsepart.process(this) as AnonymousScope
+ return ifStatement
+ }
+
+ fun process(branchStatement: BranchStatement): IStatement {
+ branchStatement.truepart = branchStatement.truepart.process(this) as AnonymousScope
+ branchStatement.elsepart = branchStatement.elsepart.process(this) as AnonymousScope
+ return branchStatement
+ }
+
+ fun process(range: RangeExpr): IExpression {
+ range.from = range.from.process(this)
+ range.to = range.to.process(this)
+ range.step = range.step.process(this)
+ return range
+ }
+
+ fun process(label: Label): IStatement {
+ return label
+ }
+
+ fun process(literalValue: LiteralValue): LiteralValue {
+ if(literalValue.arrayvalue!=null) {
+ for(av in literalValue.arrayvalue.withIndex()) {
+ val newvalue = av.value.process(this)
+ literalValue.arrayvalue[av.index] = newvalue
+ }
+ }
+ return literalValue
+ }
+
+ fun process(assignment: Assignment): IStatement {
+ assignment.targets = assignment.targets.map { it.process(this) }
+ assignment.value = assignment.value.process(this)
+ return assignment
+ }
+
+ fun process(postIncrDecr: PostIncrDecr): IStatement {
+ postIncrDecr.target = postIncrDecr.target.process(this)
+ return postIncrDecr
+ }
+
+ fun process(contStmt: Continue): IStatement {
+ return contStmt
+ }
+
+ fun process(breakStmt: Break): IStatement {
+ return breakStmt
+ }
+
+ fun process(forLoop: ForLoop): IStatement {
+ forLoop.loopVar?.process(this)
+ forLoop.iterable = forLoop.iterable.process(this)
+ forLoop.body = forLoop.body.process(this) as AnonymousScope
+ return forLoop
+ }
+
+ fun process(whileLoop: WhileLoop): IStatement {
+ whileLoop.condition = whileLoop.condition.process(this)
+ whileLoop.body = whileLoop.body.process(this) as AnonymousScope
+ return whileLoop
+ }
+
+ fun process(repeatLoop: RepeatLoop): IStatement {
+ repeatLoop.untilCondition = repeatLoop.untilCondition.process(this)
+ repeatLoop.body = repeatLoop.body.process(this) as AnonymousScope
+ return repeatLoop
+ }
+
+ fun process(returnStmt: Return): IStatement {
+ returnStmt.values = returnStmt.values.map { it.process(this) }
+ return returnStmt
+ }
+
+ fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
+ arrayIndexedExpression.identifier.process(this)
+ arrayIndexedExpression.arrayspec.process(this)
+ return arrayIndexedExpression
+ }
+
+ fun process(assignTarget: AssignTarget): AssignTarget {
+ assignTarget.arrayindexed?.process(this)
+ assignTarget.identifier?.process(this)
+ assignTarget.memoryAddress?.process(this)
+ return assignTarget
+ }
+
+ fun process(scope: AnonymousScope): IStatement {
+ scope.statements = scope.statements.asSequence().map { it.process(this) }.toMutableList()
+ return scope
+ }
+
+ fun process(typecast: TypecastExpression): IExpression {
+ typecast.expression = typecast.expression.process(this)
+ return typecast
+ }
+
+ fun process(memread: DirectMemoryRead): IExpression {
+ memread.addressExpression = memread.addressExpression.process(this)
+ return memread
+ }
+
+ fun process(memwrite: DirectMemoryWrite): IExpression {
+ memwrite.addressExpression = memwrite.addressExpression.process(this)
+ return memwrite
+ }
+
+ fun process(addressOf: AddressOf): IExpression {
+ addressOf.identifier.process(this)
+ return addressOf
+ }
+
+ fun process(inlineAssembly: InlineAssembly): IStatement {
+ return inlineAssembly
+ }
+}
diff --git a/compiler/src/prog8/ast/ImportedAstChecker.kt b/compiler/src/prog8/ast/processing/ImportedAstChecker.kt
similarity index 78%
rename from compiler/src/prog8/ast/ImportedAstChecker.kt
rename to compiler/src/prog8/ast/processing/ImportedAstChecker.kt
index 839c67f37..67938ea4e 100644
--- a/compiler/src/prog8/ast/ImportedAstChecker.kt
+++ b/compiler/src/prog8/ast/processing/ImportedAstChecker.kt
@@ -1,18 +1,11 @@
-package prog8.ast
+package prog8.ast.processing
+import prog8.ast.*
+import prog8.ast.base.SyntaxError
+import prog8.ast.base.printWarning
+import prog8.ast.statements.Directive
-/**
- * Checks that are specific for imported modules.
- */
-
-internal fun Module.checkImportedValid() {
- val checker = ImportedAstChecker()
- checker.process(this)
- printErrors(checker.result(), name)
-}
-
-
-private class ImportedAstChecker : IAstProcessor {
+internal class ImportedAstChecker : IAstProcessor {
private val checkResult: MutableList = mutableListOf()
internal fun result(): List {
diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt
similarity index 69%
rename from compiler/src/prog8/ast/StmtReorderer.kt
rename to compiler/src/prog8/ast/processing/StatementReorderer.kt
index e5a18d310..1807c363d 100644
--- a/compiler/src/prog8/ast/StmtReorderer.kt
+++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt
@@ -1,19 +1,17 @@
-package prog8.ast
+package prog8.ast.processing
+import prog8.ast.*
+import prog8.ast.base.DataType
+import prog8.ast.base.FatalAstException
+import prog8.ast.base.initvarsSubName
+import prog8.ast.base.printWarning
+import prog8.ast.expressions.BinaryExpression
+import prog8.ast.expressions.FunctionCall
+import prog8.ast.expressions.TypecastExpression
+import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions
-internal fun Program.reorderStatements() {
- val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
- initvalueCreator.process(this)
-
- val checker = StatementReorderer(this)
- checker.process(this)
-}
-
-internal const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
-
-
-private class StatementReorderer(private val program: Program): IAstProcessor {
+internal class StatementReorderer(private val program: Program): IAstProcessor {
// Reorders the statements in a way the compiler needs.
// - 'main' block must be the very first statement UNLESS it has an address set.
// - blocks are ordered by address, where blocks without address are put at the end.
@@ -144,7 +142,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
// and if an assembly block doesn't contain a rts/rti
if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) {
- if (subroutine.statements.lastOrNull {it !is VarDecl} !is Return) {
+ if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) {
val returnStmt = Return(emptyList(), subroutine.position)
returnStmt.linkParents(subroutine)
subroutine.statements.add(returnStmt)
@@ -237,8 +235,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) {
// see if a typecast is needed to convert the arguments into the required parameter's type
- val sub = call.target.targetStatement(scope)
- when(sub) {
+ when(val sub = call.target.targetStatement(scope)) {
is Subroutine -> {
for(arg in sub.parameters.zip(call.arglist.withIndex())) {
val argtype = arg.second.value.inferType(program)
@@ -312,119 +309,3 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
return super.process(typecast)
}
}
-
-
-private class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstProcessor {
- // For VarDecls that declare an initialization value:
- // Replace the vardecl with an assignment (to set the initial value),
- // and add a new vardecl with the default constant value of that type (usually zero) to the scope.
- // This makes sure the variables get reset to the intended value on a next run of the program.
- // Variable decls without a value don't get this treatment, which means they retain the last
- // value they had when restarting the program.
- // This is done in a separate step because it interferes with the namespace lookup of symbols
- // in other ast processors.
-
- // Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
-
- private val vardeclsToAdd = mutableMapOf>()
-
- override fun process(module: Module) {
- vardeclsToAdd.clear()
- super.process(module)
-
- // add any new vardecls to the various scopes
- for(decl in vardeclsToAdd)
- for(d in decl.value) {
- d.value.linkParents(decl.key as Node)
- decl.key.statements.add(0, d.value)
- }
- }
-
- override fun process(decl: VarDecl): IStatement {
- super.process(decl)
- if(decl.type!=VarDeclType.VAR || decl.value==null)
- return decl
-
- if(decl.datatype in NumericDatatypes) {
- val scope = decl.definingScope()
- addVarDecl(scope, decl.asDefaultValueDecl(null))
- val declvalue = decl.value!!
- val value =
- if(declvalue is LiteralValue) {
- val converted = declvalue.intoDatatype(decl.datatype)
- converted ?: declvalue
- }
- else
- declvalue
- val identifierName = listOf(decl.name) // // TODO this was: (scoped name) decl.scopedname.split(".")
- return VariableInitializationAssignment(
- AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
- null,
- value,
- decl.position
- )
- }
- return decl
- }
-
- override fun process(functionCall: FunctionCall): IExpression {
- val targetStatement = functionCall.target.targetSubroutine(namespace)
- if(targetStatement!=null) {
- var node: Node = functionCall
- while(node !is IStatement)
- node=node.parent
- addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
- }
- return functionCall
- }
-
- override fun process(functionCallStatement: FunctionCallStatement): IStatement {
- val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
- if(targetStatement!=null)
- addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
- return functionCallStatement
- }
-
- private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList, parent: IStatement) {
- // 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) {
- if(argparam.second is AddressOf)
- continue
- val idref = argparam.second as? IdentifierReference
- val strvalue = argparam.second as? LiteralValue
- if(idref!=null) {
- val variable = idref.targetVarDecl(namespace)
- if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
- val pointerExpr = AddressOf(idref, idref.position)
- pointerExpr.scopedname = parent.makeScopedName(idref.nameInSource.single())
- pointerExpr.linkParents(arglist[argparam.first.index].parent)
- arglist[argparam.first.index] = pointerExpr
- }
- }
- else if(strvalue!=null) {
- if(strvalue.isString) {
- // replace the argument with &autovar
- val autoVarName = "$autoHeapValuePrefix${strvalue.heapId}"
- val autoHeapvarRef = IdentifierReference(listOf(autoVarName), strvalue.position)
- val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
- pointerExpr.scopedname = parent.makeScopedName(autoVarName)
- pointerExpr.linkParents(arglist[argparam.first.index].parent)
- arglist[argparam.first.index] = pointerExpr
- // add a vardecl so that the autovar can be resolved in later lookups
- val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue,
- isArray = false, autoGenerated = false, position=strvalue.position)
- addVarDecl(strvalue.definingScope(), variable)
- }
- }
- }
- }
- }
-
- private fun addVarDecl(scope: INameScope, variable: VarDecl) {
- if(scope !in vardeclsToAdd)
- vardeclsToAdd[scope] = mutableMapOf()
- vardeclsToAdd.getValue(scope)[variable.name]=variable
- }
-
-}
diff --git a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt
new file mode 100644
index 000000000..12d6c88a8
--- /dev/null
+++ b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt
@@ -0,0 +1,125 @@
+package prog8.ast.processing
+
+import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.base.autoHeapValuePrefix
+import prog8.ast.expressions.AddressOf
+import prog8.ast.expressions.FunctionCall
+import prog8.ast.expressions.IdentifierReference
+import prog8.ast.expressions.LiteralValue
+import prog8.ast.statements.*
+
+internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstProcessor {
+ // For VarDecls that declare an initialization value:
+ // Replace the vardecl with an assignment (to set the initial value),
+ // and add a new vardecl with the default constant value of that type (usually zero) to the scope.
+ // This makes sure the variables get reset to the intended value on a next run of the program.
+ // Variable decls without a value don't get this treatment, which means they retain the last
+ // value they had when restarting the program.
+ // This is done in a separate step because it interferes with the namespace lookup of symbols
+ // in other ast processors.
+
+ // Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
+
+ private val vardeclsToAdd = mutableMapOf>()
+
+ override fun process(module: Module) {
+ vardeclsToAdd.clear()
+ super.process(module)
+
+ // add any new vardecls to the various scopes
+ for(decl in vardeclsToAdd)
+ for(d in decl.value) {
+ d.value.linkParents(decl.key as Node)
+ decl.key.statements.add(0, d.value)
+ }
+ }
+
+ override fun process(decl: VarDecl): IStatement {
+ super.process(decl)
+ if(decl.type!= VarDeclType.VAR || decl.value==null)
+ return decl
+
+ if(decl.datatype in NumericDatatypes) {
+ val scope = decl.definingScope()
+ addVarDecl(scope, decl.asDefaultValueDecl(null))
+ val declvalue = decl.value!!
+ val value =
+ if(declvalue is LiteralValue) {
+ val converted = declvalue.intoDatatype(decl.datatype)
+ converted ?: declvalue
+ }
+ else
+ declvalue
+ val identifierName = listOf(decl.name) // // TODO this was: (scoped name) decl.scopedname.split(".")
+ return VariableInitializationAssignment(
+ AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
+ null,
+ value,
+ decl.position
+ )
+ }
+ return decl
+ }
+
+ override fun process(functionCall: FunctionCall): IExpression {
+ val targetStatement = functionCall.target.targetSubroutine(namespace)
+ if(targetStatement!=null) {
+ var node: Node = functionCall
+ while(node !is IStatement)
+ node=node.parent
+ addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
+ }
+ return functionCall
+ }
+
+ override fun process(functionCallStatement: FunctionCallStatement): IStatement {
+ val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
+ if(targetStatement!=null)
+ addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
+ return functionCallStatement
+ }
+
+ private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList, parent: IStatement) {
+ // 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) {
+ if(argparam.second is AddressOf)
+ continue
+ val idref = argparam.second as? IdentifierReference
+ val strvalue = argparam.second as? LiteralValue
+ if(idref!=null) {
+ val variable = idref.targetVarDecl(namespace)
+ if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
+ val pointerExpr = AddressOf(idref, idref.position)
+ pointerExpr.scopedname = parent.makeScopedName(idref.nameInSource.single())
+ pointerExpr.linkParents(arglist[argparam.first.index].parent)
+ arglist[argparam.first.index] = pointerExpr
+ }
+ }
+ else if(strvalue!=null) {
+ if(strvalue.isString) {
+ // replace the argument with &autovar
+ val autoVarName = "$autoHeapValuePrefix${strvalue.heapId}"
+ val autoHeapvarRef = IdentifierReference(listOf(autoVarName), strvalue.position)
+ val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
+ pointerExpr.scopedname = parent.makeScopedName(autoVarName)
+ pointerExpr.linkParents(arglist[argparam.first.index].parent)
+ arglist[argparam.first.index] = pointerExpr
+ // add a vardecl so that the autovar can be resolved in later lookups
+ val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue,
+ isArray = false, autoGenerated = false, position = strvalue.position)
+ addVarDecl(strvalue.definingScope(), variable)
+ }
+ }
+ }
+ }
+ }
+
+ private fun addVarDecl(scope: INameScope, variable: VarDecl) {
+ if(scope !in vardeclsToAdd)
+ vardeclsToAdd[scope] = mutableMapOf()
+ vardeclsToAdd.getValue(scope)[variable.name]=variable
+ }
+
+}
diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt
new file mode 100644
index 000000000..ce7b70ae2
--- /dev/null
+++ b/compiler/src/prog8/ast/statements/AstStatements.kt
@@ -0,0 +1,639 @@
+package prog8.ast.statements
+
+import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.expressions.*
+import prog8.ast.processing.IAstProcessor
+import prog8.compiler.HeapValues
+
+
+class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement {
+ override var parent: Node = ParentSentinel
+ override fun linkParents(parent: Node) {}
+ override fun process(processor: IAstProcessor): IStatement = this
+ override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
+ override val expensiveToInline = false
+}
+
+
+data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean?)
+
+
+class Block(override val name: String,
+ val address: Int?,
+ override var statements: MutableList,
+ val isInLibrary: Boolean,
+ override val position: Position) : IStatement, INameScope {
+ override lateinit var parent: Node
+ override val expensiveToInline
+ get() = statements.any { it.expensiveToInline }
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ statements.forEach {it.linkParents(this)}
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "Block(name=$name, address=$address, ${statements.size} statements)"
+ }
+
+ 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, override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ args.forEach{it.linkParents(this)}
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+}
+
+data class DirectiveArg(val str: String?, val name: String?, val int: Int?, override val position: Position) : Node {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+}
+
+data class Label(val name: String, override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "Label(name=$name, pos=$position)"
+ }
+
+ val scopedname: String by lazy { makeScopedName(name) }
+}
+
+open class Return(var values: List, override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = values.any { it !is LiteralValue }
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ values.forEach {it.linkParents(this)}
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "Return(values: $values, pos=$position)"
+ }
+}
+
+class ReturnFromIrq(override val position: Position) : Return(emptyList(), position) {
+ override fun process(processor: IAstProcessor) = this
+
+ override fun toString(): String {
+ return "ReturnFromIrq(pos=$position)"
+ }
+}
+
+class Continue(override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent=parent
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+}
+
+class Break(override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent=parent
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+}
+
+class VarDecl(val type: VarDeclType,
+ private val declaredDatatype: DataType,
+ val zeropage: Boolean,
+ var arraysize: ArrayIndex?,
+ val name: String,
+ var value: IExpression?,
+ val isArray: Boolean,
+ val autoGenerated: Boolean,
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline
+ get() = value!=null && value !is LiteralValue
+
+ val datatypeErrors = mutableListOf() // don't crash at init time, report them in the AstChecker
+ val datatype =
+ if (!isArray) declaredDatatype
+ else when (declaredDatatype) {
+ DataType.UBYTE -> DataType.ARRAY_UB
+ DataType.BYTE -> DataType.ARRAY_B
+ DataType.UWORD -> DataType.ARRAY_UW
+ DataType.WORD -> DataType.ARRAY_W
+ DataType.FLOAT -> DataType.ARRAY_F
+ else -> {
+ datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
+ DataType.UBYTE
+ }
+ }
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ arraysize?.linkParents(this)
+ value?.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ val scopedname: String by lazy { makeScopedName(name) }
+
+ override fun toString(): String {
+ return "VarDecl(name=$name, vartype=$type, datatype=$datatype, array=$isArray, value=$value, pos=$position)"
+ }
+
+ fun asDefaultValueDecl(parent: Node?): VarDecl {
+ val constValue = when(declaredDatatype) {
+ DataType.UBYTE -> LiteralValue(DataType.UBYTE, 0, position = position)
+ DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position = position)
+ DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = 0, position = position)
+ DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = 0, position = position)
+ DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = 0.0, position = position)
+ else -> throw FatalAstException("can only set a default value for a numeric type")
+ }
+ val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, constValue, isArray, true, position)
+ if(parent!=null)
+ decl.linkParents(parent)
+ return decl
+ }
+}
+
+class ArrayIndex(var index: IExpression, override val position: Position) : Node {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ index.linkParents(this)
+ }
+
+ companion object {
+ fun forArray(v: LiteralValue, heap: HeapValues): ArrayIndex {
+ val arraySize = v.arrayvalue?.size ?: heap.get(v.heapId!!).arraysize
+ return ArrayIndex(LiteralValue.optimalNumeric(arraySize, v.position), v.position)
+ }
+ }
+
+ fun process(processor: IAstProcessor) {
+ index = index.process(processor)
+ }
+
+ override fun toString(): String {
+ return("ArrayIndex($index, pos=$position)")
+ }
+
+ fun size() = (index as? LiteralValue)?.asIntegerValue
+}
+
+open class Assignment(var targets: List, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline
+ get() = value !is LiteralValue
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ targets.forEach { it.linkParents(this) }
+ value.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return("Assignment(augop: $aug_op, targets: $targets, value: $value, pos=$position)")
+ }
+
+ val singleTarget: AssignTarget?
+ get() {
+ return targets.singleOrNull() // common case
+ }
+}
+
+// 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)
+ : Assignment(listOf(target), aug_op, value, position)
+
+data class AssignTarget(val register: Register?,
+ val identifier: IdentifierReference?,
+ val arrayindexed: ArrayIndexedExpression?,
+ var memoryAddress: DirectMemoryWrite?,
+ override val position: Position) : Node {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ identifier?.linkParents(this)
+ arrayindexed?.linkParents(this)
+ memoryAddress?.linkParents(this)
+ }
+
+ fun process(processor: IAstProcessor) = processor.process(this)
+
+ companion object {
+ fun fromExpr(expr: IExpression): AssignTarget {
+ return when (expr) {
+ is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
+ is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
+ is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
+ is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
+ is DirectMemoryWrite -> AssignTarget(null, null, null, expr, expr.position)
+ else -> throw FatalAstException("invalid expression object $expr")
+ }
+ }
+ }
+
+ fun inferType(program: Program, stmt: IStatement): DataType? {
+ if(register!=null)
+ return DataType.UBYTE
+
+ if(identifier!=null) {
+ val symbol = program.namespace.lookup(identifier.nameInSource, stmt) ?: return null
+ if (symbol is VarDecl) return symbol.datatype
+ }
+
+ if(arrayindexed!=null) {
+ val dt = arrayindexed.inferType(program)
+ if(dt!=null)
+ return dt
+ }
+
+ if(memoryAddress!=null)
+ return DataType.UBYTE
+
+ return null
+ }
+
+ fun shortString(withTypePrefix: Boolean=false): String {
+ if(register!=null)
+ return (if(withTypePrefix) "0register::" else "") + register.name
+ if(identifier!=null)
+ return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last()
+ if(arrayindexed!=null)
+ return (if(withTypePrefix) "2arrayidx::" else "") + arrayindexed.identifier.nameInSource.last()
+ val address = memoryAddress?.addressExpression
+ if(address is LiteralValue)
+ return (if(withTypePrefix) "1address::" else "") +address.asIntegerValue.toString()
+ return if(withTypePrefix) "???::???" else "???"
+ }
+
+ fun isMemoryMapped(namespace: INameScope): Boolean =
+ memoryAddress!=null || (identifier?.targetVarDecl(namespace)?.type== VarDeclType.MEMORY)
+
+ infix fun isSameAs(value: IExpression): Boolean {
+ return when {
+ this.memoryAddress!=null -> false
+ this.register!=null -> value is RegisterExpr && value.register==register
+ this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier.nameInSource
+ this.arrayindexed!=null -> value is ArrayIndexedExpression &&
+ value.identifier.nameInSource==arrayindexed.identifier.nameInSource &&
+ value.arrayspec.size()!=null &&
+ arrayindexed.arrayspec.size()!=null &&
+ value.arrayspec.size()==arrayindexed.arrayspec.size()
+ else -> false
+ }
+ }
+
+ fun isSameAs(other: AssignTarget, program: Program): Boolean {
+ if(this===other)
+ return true
+ if(this.register!=null && other.register!=null)
+ return this.register==other.register
+ if(this.identifier!=null && other.identifier!=null)
+ return this.identifier.nameInSource==other.identifier.nameInSource
+ if(this.memoryAddress!=null && other.memoryAddress!=null) {
+ val addr1 = this.memoryAddress!!.addressExpression.constValue(program)
+ val addr2 = other.memoryAddress!!.addressExpression.constValue(program)
+ return addr1!=null && addr2!=null && addr1==addr2
+ }
+ if(this.arrayindexed!=null && other.arrayindexed!=null) {
+ if(this.arrayindexed.identifier.nameInSource == other.arrayindexed.identifier.nameInSource) {
+ val x1 = this.arrayindexed.arrayspec.index.constValue(program)
+ val x2 = other.arrayindexed.arrayspec.index.constValue(program)
+ return x1!=null && x2!=null && x1==x2
+ }
+ }
+ return false
+ }
+
+ fun isNotMemory(namespace: INameScope): Boolean {
+ if(this.register!=null)
+ return true
+ if(this.memoryAddress!=null)
+ return false
+ if(this.arrayindexed!=null) {
+ val targetStmt = this.arrayindexed.identifier.targetVarDecl(namespace)
+ if(targetStmt!=null)
+ return targetStmt.type!= VarDeclType.MEMORY
+ }
+ if(this.identifier!=null) {
+ val targetStmt = this.identifier.targetVarDecl(namespace)
+ if(targetStmt!=null)
+ return targetStmt.type!= VarDeclType.MEMORY
+ }
+ return false
+ }
+}
+
+class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ target.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "PostIncrDecr(op: $operator, target: $target, pos=$position)"
+ }
+}
+
+class Jump(val address: Int?,
+ val identifier: IdentifierReference?,
+ val generatedLabel: String?, // used in code generation scenarios
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ identifier?.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)"
+ }
+}
+
+class FunctionCallStatement(override var target: IdentifierReference,
+ override var arglist: MutableList,
+ override val position: Position) : IStatement, IFunctionCall {
+ override lateinit var parent: Node
+ override val expensiveToInline
+ get() = arglist.any { it !is LiteralValue }
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ target.linkParents(this)
+ arglist.forEach { it.linkParents(this) }
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "FunctionCallStatement(target=$target, pos=$position)"
+ }
+}
+
+class InlineAssembly(val assembly: String, override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = true
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+}
+
+class AnonymousScope(override var statements: MutableList,
+ override val position: Position) : INameScope, IStatement {
+ override val name: String
+ override lateinit var parent: Node
+ override val expensiveToInline
+ get() = statements.any { it.expensiveToInline }
+
+ init {
+ name = "" // make sure it's an invalid soruce code identifier so user source code can never produce it
+ sequenceNumber++
+ }
+
+ companion object {
+ private var sequenceNumber = 1
+ }
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ statements.forEach { it.linkParents(this) }
+ }
+ override fun process(processor: IAstProcessor) = processor.process(this)
+}
+
+class NopStatement(override val position: Position): IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = false
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+
+ override fun process(processor: IAstProcessor) = this
+}
+
+// the subroutine class covers both the normal user-defined subroutines,
+// and also the predefined/ROM/register-based subroutines.
+// (multiple return types can only occur for the latter type)
+class Subroutine(override val name: String,
+ val parameters: List,
+ val returntypes: List,
+ val asmParameterRegisters: List,
+ val asmReturnvaluesRegisters: List,
+ val asmClobbers: Set,
+ val asmAddress: Int?,
+ val isAsmSubroutine: Boolean,
+ override var statements: MutableList,
+ override val position: Position) : IStatement, INameScope {
+
+ var keepAlways: Boolean = false
+ override val expensiveToInline
+ get() = statements.any { it.expensiveToInline }
+
+ override lateinit var parent: Node
+ val calledBy = mutableListOf()
+ val calls = mutableSetOf()
+
+ val scopedname: String by lazy { makeScopedName(name) }
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ parameters.forEach { it.linkParents(this) }
+ statements.forEach { it.linkParents(this) }
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
+ }
+
+ fun amountOfRtsInAsm(): Int = statements
+ .asSequence()
+ .filter { it is InlineAssembly }
+ .map { (it as InlineAssembly).assembly }
+ .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
+
+ val canBeAsmSubroutine =false // TODO disabled for now, see below about problem with converting to asm subroutine
+// !isAsmSubroutine
+// && ((parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD))
+// || (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE }))
+
+ fun intoAsmSubroutine(): Subroutine {
+ // TODO turn subroutine into asm calling convention. Requires rethinking of how parameters are handled (conflicts with local vardefs now, see AstIdentifierChecker...)
+ return this // TODO
+
+// println("TO ASM $this") // TODO
+// val paramregs = if (parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE))
+// listOf(RegisterOrStatusflag(RegisterOrPair.Y, null, null))
+// else if (parameters.size == 1 && parameters[0].type in setOf(DataType.WORD, DataType.UWORD))
+// listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
+// else if (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE })
+// listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
+// else throw FatalAstException("cannot convert subroutine to asm parameters")
+//
+// val asmsub=Subroutine(
+// name,
+// parameters,
+// returntypes,
+// paramregs,
+// emptyList(),
+// emptySet(),
+// null,
+// true,
+// statements,
+// position
+// )
+// asmsub.linkParents(parent)
+// return asmsub
+ }
+}
+
+open class SubroutineParameter(val name: String,
+ val type: DataType,
+ override val position: Position) : Node {
+ override lateinit var parent: Node
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ }
+}
+
+class IfStatement(var condition: IExpression,
+ var truepart: AnonymousScope,
+ var elsepart: AnonymousScope,
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline: Boolean
+ get() = truepart.expensiveToInline || elsepart.expensiveToInline
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ condition.linkParents(this)
+ truepart.linkParents(this)
+ elsepart.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor): IStatement = processor.process(this)
+}
+
+class BranchStatement(var condition: BranchCondition,
+ var truepart: AnonymousScope,
+ var elsepart: AnonymousScope,
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline: Boolean
+ get() = truepart.expensiveToInline || elsepart.expensiveToInline
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ truepart.linkParents(this)
+ elsepart.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor): IStatement = processor.process(this)
+}
+
+class ForLoop(val loopRegister: Register?,
+ val decltype: DataType?,
+ val zeropage: Boolean,
+ val loopVar: IdentifierReference?,
+ var iterable: IExpression,
+ var body: AnonymousScope,
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = true
+
+ override fun linkParents(parent: Node) {
+ this.parent=parent
+ loopVar?.linkParents(if(decltype==null) this else body)
+ iterable.linkParents(this)
+ body.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor) = processor.process(this)
+
+ override fun toString(): String {
+ return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
+ }
+
+ companion object {
+ const val iteratorLoopcounterVarname = "prog8forloopcounter"
+ }
+}
+
+class WhileLoop(var condition: IExpression,
+ var body: AnonymousScope,
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = true
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ condition.linkParents(this)
+ body.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor): IStatement = processor.process(this)
+}
+
+class RepeatLoop(var body: AnonymousScope,
+ var untilCondition: IExpression,
+ override val position: Position) : IStatement {
+ override lateinit var parent: Node
+ override val expensiveToInline = true
+
+ override fun linkParents(parent: Node) {
+ this.parent = parent
+ untilCondition.linkParents(this)
+ body.linkParents(this)
+ }
+
+ override fun process(processor: IAstProcessor): IStatement = processor.process(this)
+}
diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt
index a27d75371..aa1b55fb5 100644
--- a/compiler/src/prog8/astvm/AstVm.kt
+++ b/compiler/src/prog8/astvm/AstVm.kt
@@ -1,6 +1,11 @@
package prog8.astvm
import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.base.initvarsSubName
+import prog8.ast.expressions.IdentifierReference
+import prog8.ast.expressions.LiteralValue
+import prog8.ast.statements.*
import prog8.compiler.RuntimeValue
import prog8.compiler.RuntimeValueRange
import prog8.compiler.target.c64.Petscii
diff --git a/compiler/src/prog8/astvm/BuiltinFunctions.kt b/compiler/src/prog8/astvm/BuiltinFunctions.kt
index 28c218242..c51263d21 100644
--- a/compiler/src/prog8/astvm/BuiltinFunctions.kt
+++ b/compiler/src/prog8/astvm/BuiltinFunctions.kt
@@ -1,6 +1,6 @@
package prog8.astvm
-import prog8.ast.DataType
+import prog8.ast.base.DataType
import prog8.compiler.RuntimeValue
import java.lang.Math.toDegrees
import java.lang.Math.toRadians
diff --git a/compiler/src/prog8/astvm/Expressions.kt b/compiler/src/prog8/astvm/Expressions.kt
index 6cd89f32d..f39f63d58 100644
--- a/compiler/src/prog8/astvm/Expressions.kt
+++ b/compiler/src/prog8/astvm/Expressions.kt
@@ -1,6 +1,14 @@
package prog8.astvm
import prog8.ast.*
+import prog8.ast.base.ArrayElementTypes
+import prog8.ast.base.DataType
+import prog8.ast.base.VarDeclType
+import prog8.ast.expressions.*
+import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
+import prog8.ast.statements.Label
+import prog8.ast.statements.Subroutine
+import prog8.ast.statements.VarDecl
import prog8.compiler.RuntimeValue
import prog8.compiler.RuntimeValueRange
import kotlin.math.abs
diff --git a/compiler/src/prog8/astvm/VariablesCreator.kt b/compiler/src/prog8/astvm/VariablesCreator.kt
index c01f55202..626990079 100644
--- a/compiler/src/prog8/astvm/VariablesCreator.kt
+++ b/compiler/src/prog8/astvm/VariablesCreator.kt
@@ -1,6 +1,10 @@
package prog8.astvm
import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.expressions.LiteralValue
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.VarDecl
import prog8.compiler.HeapValues
import prog8.compiler.RuntimeValue
diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt
index 0d2c76821..cbc4cca40 100644
--- a/compiler/src/prog8/compiler/Compiler.kt
+++ b/compiler/src/prog8/compiler/Compiler.kt
@@ -1,7 +1,11 @@
package prog8.compiler
import prog8.ast.*
-import prog8.ast.RegisterOrPair.*
+import prog8.ast.base.*
+import prog8.ast.base.RegisterOrPair.*
+import prog8.ast.expressions.*
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.*
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.branchOpcodes
@@ -383,7 +387,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
// The compiler could then convert it to a special system call
val sub = stmt.parent as? Subroutine
val scopename =
- if(sub!=null && sub.statements.filter{it !is VarDecl}.size==1)
+ if(sub!=null && sub.statements.filter{it !is VarDecl }.size==1)
sub.scopedname
else
null
@@ -1413,7 +1417,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
// @todo use convertType()????
when(targetDt) {
in ByteDatatypes ->
- if(valueDt!=DataType.BYTE && valueDt!=DataType.UBYTE)
+ if(valueDt!= DataType.BYTE && valueDt!= DataType.UBYTE)
throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
DataType.WORD -> {
when (valueDt) {
@@ -1619,11 +1623,11 @@ internal class Compiler(private val program: Program): IAstProcessor {
}
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: LiteralValue) {
- if(loopvarDt==DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB))
+ if(loopvarDt== DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB))
throw CompilerException("loop variable type doesn't match iterableValue type")
- else if(loopvarDt==DataType.UWORD && iterableValue.type != DataType.ARRAY_UW)
+ else if(loopvarDt== DataType.UWORD && iterableValue.type != DataType.ARRAY_UW)
throw CompilerException("loop variable type doesn't match iterableValue type")
- else if(loopvarDt==DataType.FLOAT && iterableValue.type != DataType.ARRAY_F)
+ else if(loopvarDt== DataType.FLOAT && iterableValue.type != DataType.ARRAY_F)
throw CompilerException("loop variable type doesn't match iterableValue type")
val numElements: Int
@@ -1645,7 +1649,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
else -> throw CompilerException("weird datatype")
}
- if(loop.loopRegister!=null && loop.loopRegister==Register.X)
+ if(loop.loopRegister!=null && loop.loopRegister== Register.X)
throw CompilerException("loopVar cannot use X register because that is used as internal stack pointer")
/**
@@ -1848,10 +1852,10 @@ internal class Compiler(private val program: Program): IAstProcessor {
val condition =
if(literalStepValue > 0) {
// if LV > last goto break
- BinaryExpression(loopVar,">", range.to, range.position)
+ BinaryExpression(loopVar, ">", range.to, range.position)
} else {
// if LV < last goto break
- BinaryExpression(loopVar,"<", range.to, range.position)
+ BinaryExpression(loopVar, "<", range.to, range.position)
}
val ifstmt = IfStatement(condition,
AnonymousScope(mutableListOf(Jump(null, null, breakLabel, range.position)), range.position),
@@ -1881,7 +1885,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
TODO("can't generate code for step other than 1 or -1 right now")
// LV++ / LV--
- val postIncr = PostIncrDecr(lvTarget, if(step==1) "++" else "--", range.position)
+ val postIncr = PostIncrDecr(lvTarget, if (step == 1) "++" else "--", range.position)
postIncr.linkParents(body)
translate(postIncr)
if(lvTarget.register!=null)
@@ -2067,10 +2071,10 @@ internal class Compiler(private val program: Program): IAstProcessor {
private fun translate(addrof: AddressOf) {
val target = addrof.identifier.targetVarDecl(program.namespace)!!
- if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes|| target.datatype==DataType.FLOAT) {
+ if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes || target.datatype== DataType.FLOAT) {
pushHeapVarAddress(addrof.identifier, false)
}
- else if(target.datatype==DataType.FLOAT) {
+ else if(target.datatype== DataType.FLOAT) {
pushFloatAddress(addrof.identifier)
}
else
diff --git a/compiler/src/prog8/compiler/RuntimeValue.kt b/compiler/src/prog8/compiler/RuntimeValue.kt
index 8b0d45203..51bf1a165 100644
--- a/compiler/src/prog8/compiler/RuntimeValue.kt
+++ b/compiler/src/prog8/compiler/RuntimeValue.kt
@@ -1,6 +1,7 @@
package prog8.compiler
-import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.expressions.LiteralValue
import prog8.compiler.target.c64.Petscii
import kotlin.math.abs
import kotlin.math.pow
@@ -174,7 +175,7 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
DataType.UBYTE, DataType.UWORD -> {
// storing a negative number in an unsigned one is done by storing the 2's complement instead
val number = abs(result.toDouble().toInt())
- if(leftDt==DataType.UBYTE)
+ if(leftDt== DataType.UBYTE)
RuntimeValue(DataType.UBYTE, (number xor 255) + 1)
else
RuntimeValue(DataType.UBYTE, (number xor 65535) + 1)
diff --git a/compiler/src/prog8/compiler/Zeropage.kt b/compiler/src/prog8/compiler/Zeropage.kt
index e247d2f7b..d980b534a 100644
--- a/compiler/src/prog8/compiler/Zeropage.kt
+++ b/compiler/src/prog8/compiler/Zeropage.kt
@@ -1,6 +1,7 @@
package prog8.compiler
-import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.base.printWarning
class ZeropageDepletedError(message: String) : Exception(message)
diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt
index 18976d28f..e7f7d2f3f 100644
--- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt
+++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt
@@ -1,6 +1,10 @@
package prog8.compiler.intermediate
-import prog8.ast.*
+import prog8.ast.antlr.escape
+import prog8.ast.base.*
+import prog8.ast.base.printWarning
+import prog8.ast.expressions.LiteralValue
+import prog8.ast.statements.VarDecl
import prog8.compiler.RuntimeValue
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
@@ -412,7 +416,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
VarDeclType.MEMORY -> {
// note that constants are all folded away, but assembly code may still refer to them
val lv = decl.value as LiteralValue
- if(lv.type!=DataType.UWORD && lv.type!=DataType.UBYTE)
+ if(lv.type!= DataType.UWORD && lv.type!= DataType.UBYTE)
throw CompilerException("expected integer memory address $lv")
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
}
diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt
index eb9d8dded..1a60630d4 100644
--- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt
+++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt
@@ -3,7 +3,10 @@ package prog8.compiler.target.c64
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
-import prog8.ast.*
+import prog8.ast.antlr.escape
+import prog8.ast.base.DataType
+import prog8.ast.base.initvarsSubName
+import prog8.ast.base.printWarning
import prog8.compiler.RuntimeValue
import prog8.compiler.*
import prog8.compiler.intermediate.*
@@ -63,7 +66,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
// make a list of all const floats that are used
for(block in program.blocks) {
- for(ins in block.instructions.filter{it.arg?.type==DataType.FLOAT}) {
+ for(ins in block.instructions.filter{it.arg?.type== DataType.FLOAT}) {
val float = ins.arg!!.numericValue().toDouble()
if(float !in globalFloatConsts)
globalFloatConsts[float] = "prog8_const_float_${globalFloatConsts.size}"
@@ -186,7 +189,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
out(" ldx #\$ff\t; init estack pointer")
out(" ; initialize the variables in each block")
for(block in program.blocks) {
- val initVarsLabel = block.instructions.firstOrNull { it is LabelInstr && it.name==initvarsSubName } as? LabelInstr
+ val initVarsLabel = block.instructions.firstOrNull { it is LabelInstr && it.name== initvarsSubName } as? LabelInstr
if(initVarsLabel!=null)
out(" jsr ${block.name}.${initVarsLabel.name}")
}
@@ -362,10 +365,10 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
private fun makeArrayFillDataUnsigned(value: RuntimeValue): List {
val array = heap.get(value.heapId!!).array!!
return when {
- value.type==DataType.ARRAY_UB ->
+ value.type== DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map { "$"+it.integer!!.toString(16).padStart(2, '0') }
- value.type==DataType.ARRAY_UW -> array.map {
+ value.type== DataType.ARRAY_UW -> array.map {
when {
it.integer!=null -> "$"+it.integer.toString(16).padStart(2, '0')
it.addressOf!=null -> symname(it.addressOf.scopedname!!, block)
diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt
index e0e3c3f2e..53fe7550a 100644
--- a/compiler/src/prog8/functions/BuiltinFunctions.kt
+++ b/compiler/src/prog8/functions/BuiltinFunctions.kt
@@ -1,6 +1,11 @@
package prog8.functions
import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.expressions.DirectMemoryRead
+import prog8.ast.expressions.IdentifierReference
+import prog8.ast.expressions.LiteralValue
+import prog8.ast.statements.VarDecl
import prog8.compiler.CompilerException
import kotlin.math.*
@@ -111,13 +116,13 @@ fun builtinFunctionReturnType(function: String, args: List, program
fun datatypeFromIterableArg(arglist: IExpression): DataType {
if(arglist is LiteralValue) {
- if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F) {
+ if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
val dt = arglist.arrayvalue!!.map {it.inferType(program)}
- if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) {
+ if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
}
- if(dt.any { it==DataType.FLOAT }) return DataType.FLOAT
- if(dt.any { it==DataType.UWORD }) return DataType.UWORD
+ if(dt.any { it== DataType.FLOAT }) return DataType.FLOAT
+ if(dt.any { it== DataType.UWORD }) return DataType.UWORD
return DataType.UBYTE
}
}
@@ -186,7 +191,7 @@ private fun oneDoubleArg(args: List, position: Position, program: P
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
- if(constval.type!=DataType.FLOAT)
+ if(constval.type!= DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
val float = constval.asNumericValue?.toDouble()!!
@@ -197,16 +202,16 @@ private fun oneDoubleArgOutputWord(args: List, position: Position,
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
- if(constval.type!=DataType.FLOAT)
+ if(constval.type!= DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
- return LiteralValue(DataType.WORD, wordvalue=function(constval.asNumericValue!!.toDouble()).toInt(), position=args[0].position)
+ return LiteralValue(DataType.WORD, wordvalue = function(constval.asNumericValue!!.toDouble()).toInt(), position = args[0].position)
}
private fun oneIntArgOutputInt(args: List, position: Position, program: Program, function: (arg: Int)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("built-in function requires one integer argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
- if(constval.type!=DataType.UBYTE && constval.type!=DataType.UWORD)
+ if(constval.type!= DataType.UBYTE && constval.type!= DataType.UWORD)
throw SyntaxError("built-in function requires one integer argument", position)
val integer = constval.asNumericValue?.toInt()!!
@@ -378,7 +383,7 @@ private fun builtinSin8(args: List, position: Position, program: Pr
throw SyntaxError("sin8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.BYTE, bytevalue = (127.0* sin(rad)).toShort(), position = position)
+ return LiteralValue(DataType.BYTE, bytevalue = (127.0 * sin(rad)).toShort(), position = position)
}
private fun builtinSin8u(args: List, position: Position, program: Program): LiteralValue {
@@ -386,7 +391,7 @@ private fun builtinSin8u(args: List, position: Position, program: P
throw SyntaxError("sin8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.UBYTE, bytevalue = (128.0+127.5*sin(rad)).toShort(), position = position)
+ return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * sin(rad)).toShort(), position = position)
}
private fun builtinCos8(args: List, position: Position, program: Program): LiteralValue {
@@ -394,7 +399,7 @@ private fun builtinCos8(args: List, position: Position, program: Pr
throw SyntaxError("cos8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.BYTE, bytevalue = (127.0* cos(rad)).toShort(), position = position)
+ return LiteralValue(DataType.BYTE, bytevalue = (127.0 * cos(rad)).toShort(), position = position)
}
private fun builtinCos8u(args: List, position: Position, program: Program): LiteralValue {
@@ -402,7 +407,7 @@ private fun builtinCos8u(args: List, position: Position, program: P
throw SyntaxError("cos8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5*cos(rad)).toShort(), position = position)
+ return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * cos(rad)).toShort(), position = position)
}
private fun builtinSin16(args: List, position: Position, program: Program): LiteralValue {
@@ -410,7 +415,7 @@ private fun builtinSin16(args: List, position: Position, program: P
throw SyntaxError("sin16 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.WORD, wordvalue = (32767.0* sin(rad)).toInt(), position = position)
+ return LiteralValue(DataType.WORD, wordvalue = (32767.0 * sin(rad)).toInt(), position = position)
}
private fun builtinSin16u(args: List, position: Position, program: Program): LiteralValue {
@@ -418,7 +423,7 @@ private fun builtinSin16u(args: List, position: Position, program:
throw SyntaxError("sin16u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5*sin(rad)).toInt(), position = position)
+ return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * sin(rad)).toInt(), position = position)
}
private fun builtinCos16(args: List, position: Position, program: Program): LiteralValue {
@@ -426,7 +431,7 @@ private fun builtinCos16(args: List, position: Position, program: P
throw SyntaxError("cos16 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.WORD, wordvalue = (32767.0* cos(rad)).toInt(), position = position)
+ return LiteralValue(DataType.WORD, wordvalue = (32767.0 * cos(rad)).toInt(), position = position)
}
private fun builtinCos16u(args: List, position: Position, program: Program): LiteralValue {
@@ -434,7 +439,7 @@ private fun builtinCos16u(args: List, position: Position, program:
throw SyntaxError("cos16u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
- return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5* cos(rad)).toInt(), position = position)
+ return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * cos(rad)).toInt(), position = position)
}
private fun numericLiteral(value: Number, position: Position): LiteralValue {
diff --git a/compiler/src/prog8/optimizing/CallGraph.kt b/compiler/src/prog8/optimizing/CallGraph.kt
index 556283bb2..e229255ba 100644
--- a/compiler/src/prog8/optimizing/CallGraph.kt
+++ b/compiler/src/prog8/optimizing/CallGraph.kt
@@ -1,6 +1,13 @@
package prog8.optimizing
import prog8.ast.*
+import prog8.ast.base.ParentSentinel
+import prog8.ast.base.VarDeclType
+import prog8.ast.base.initvarsSubName
+import prog8.ast.expressions.FunctionCall
+import prog8.ast.expressions.IdentifierReference
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.*
import prog8.compiler.loadAsmIncludeFile
@@ -98,7 +105,7 @@ class CallGraph(private val program: Program): IAstProcessor {
override fun process(subroutine: Subroutine): IStatement {
if((subroutine.name=="start" && subroutine.definingScope().name=="main")
- || subroutine.name==initvarsSubName || subroutine.definingModule().isLibraryModule) {
+ || subroutine.name== initvarsSubName || subroutine.definingModule().isLibraryModule) {
// make sure the entrypoint is mentioned in the used symbols
addNodeAndParentScopes(subroutine)
}
diff --git a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt
index 7d99a0313..52df3f12f 100644
--- a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt
+++ b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt
@@ -1,7 +1,11 @@
package prog8.optimizing
import prog8.ast.*
-import prog8.compiler.HeapValues
+import prog8.ast.base.DataType
+import prog8.ast.base.ExpressionError
+import prog8.ast.base.FatalAstException
+import prog8.ast.base.Position
+import prog8.ast.expressions.LiteralValue
import kotlin.math.pow
@@ -40,7 +44,7 @@ class ConstExprEvaluator {
if(left.asIntegerValue==null || amount.asIntegerValue==null)
throw ExpressionError("cannot compute $left >> $amount", left.position)
val result =
- if(left.type==DataType.UBYTE || left.type==DataType.UWORD)
+ if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
left.asIntegerValue.ushr(amount.asIntegerValue)
else
left.asIntegerValue.shr(amount.asIntegerValue)
diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt
index e19192319..926345753 100644
--- a/compiler/src/prog8/optimizing/ConstantFolding.kt
+++ b/compiler/src/prog8/optimizing/ConstantFolding.kt
@@ -1,7 +1,10 @@
package prog8.optimizing
import prog8.ast.*
-import prog8.compiler.CompilerException
+import prog8.ast.base.*
+import prog8.ast.expressions.*
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.*
import prog8.compiler.HeapValues
import prog8.compiler.IntegerOrAddressOf
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
@@ -75,19 +78,19 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
val eltType = rangeExpr.inferType(program)!!
if(eltType in ByteDatatypes) {
decl.value = LiteralValue(decl.datatype,
- arrayvalue = constRange.map { LiteralValue(eltType, bytevalue=it.toShort(), position = decl.value!!.position ) }
- .toTypedArray(), position=decl.value!!.position)
+ arrayvalue = constRange.map { LiteralValue(eltType, bytevalue = it.toShort(), position = decl.value!!.position) }
+ .toTypedArray(), position = decl.value!!.position)
} else {
decl.value = LiteralValue(decl.datatype,
- arrayvalue = constRange.map { LiteralValue(eltType, wordvalue= it, position = decl.value!!.position ) }
- .toTypedArray(), position=decl.value!!.position)
+ arrayvalue = constRange.map { LiteralValue(eltType, wordvalue = it, position = decl.value!!.position) }
+ .toTypedArray(), position = decl.value!!.position)
}
decl.value!!.linkParents(decl)
optimizationsDone++
return decl
}
}
- if(litval?.type==DataType.FLOAT)
+ if(litval?.type== DataType.FLOAT)
errors.add(ExpressionError("arraysize requires only integers here", litval.position))
val size = decl.arraysize?.size() ?: return decl
if ((litval==null || !litval.isArray) && rangeExpr==null) {
@@ -96,24 +99,29 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
when(decl.datatype){
DataType.ARRAY_UB -> {
if(fillvalue !in 0..255)
- errors.add(ExpressionError("ubyte value overflow", litval?.position ?: decl.position))
+ errors.add(ExpressionError("ubyte value overflow", litval?.position
+ ?: decl.position))
}
DataType.ARRAY_B -> {
if(fillvalue !in -128..127)
- errors.add(ExpressionError("byte value overflow", litval?.position ?: decl.position))
+ errors.add(ExpressionError("byte value overflow", litval?.position
+ ?: decl.position))
}
DataType.ARRAY_UW -> {
if(fillvalue !in 0..65535)
- errors.add(ExpressionError("uword value overflow", litval?.position ?: decl.position))
+ errors.add(ExpressionError("uword value overflow", litval?.position
+ ?: decl.position))
}
DataType.ARRAY_W -> {
if(fillvalue !in -32768..32767)
- errors.add(ExpressionError("word value overflow", litval?.position ?: decl.position))
+ errors.add(ExpressionError("word value overflow", litval?.position
+ ?: decl.position))
}
else -> {}
}
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
- decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval?.position ?: decl.position)
+ decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval?.position
+ ?: decl.position)
optimizationsDone++
return decl
}
@@ -124,10 +132,12 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
- errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
+ errors.add(ExpressionError("float value overflow", litval?.position
+ ?: decl.position))
else {
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
- decl.value = LiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position ?: decl.position)
+ decl.value = LiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position
+ ?: decl.position)
optimizationsDone++
return decl
}
@@ -154,7 +164,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(array.array!=null) {
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
- decl.value = LiteralValue(decl.datatype, initHeapId=heapId, position = litval.position)
+ decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
}
}
DataType.ARRAY_F -> {
@@ -385,7 +395,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
expr
} else
BinaryExpression(
- BinaryExpression(expr.left, if(expr.operator=="-") "+" else "*", subExpr.right, subExpr.position),
+ BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
expr.operator, subExpr.left, expr.position)
} else {
return if(subleftIsConst) {
@@ -394,7 +404,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
} else
BinaryExpression(
subExpr.left, expr.operator,
- BinaryExpression(expr.right, if(expr.operator=="-") "+" else "*", subExpr.right, subExpr.position),
+ BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
expr.position)
}
}
@@ -562,25 +572,25 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
val stepLiteral = iterableRange.step as? LiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
- if(rangeFrom.type!=DataType.UBYTE) {
+ if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
DataType.BYTE -> {
- if(rangeFrom.type!=DataType.BYTE) {
+ if(rangeFrom.type!= DataType.BYTE) {
// attempt to translate the iterable into byte values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
DataType.UWORD -> {
- if(rangeFrom.type!=DataType.UWORD) {
+ if(rangeFrom.type!= DataType.UWORD) {
// attempt to translate the iterable into uword values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
DataType.WORD -> {
- if(rangeFrom.type!=DataType.WORD) {
+ if(rangeFrom.type!= DataType.WORD) {
// attempt to translate the iterable into word values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
@@ -614,7 +624,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
val typesInArray = array.mapNotNull { it.inferType(program) }.toSet()
val arrayDt =
when {
- array.any { it is AddressOf} -> DataType.ARRAY_UW
+ array.any { it is AddressOf } -> DataType.ARRAY_UW
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
DataType.WORD in typesInArray -> DataType.ARRAY_W
else -> {
@@ -657,57 +667,57 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
when(assignment.singleTarget?.inferType(program, assignment)) {
DataType.UWORD -> {
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
- if(lv.type==DataType.UBYTE)
- assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
- else if(lv.type==DataType.BYTE && lv.bytevalue!!>=0)
- assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
- else if(lv.type==DataType.WORD && lv.wordvalue!!>=0)
- assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
- else if(lv.type==DataType.FLOAT) {
+ if(lv.type== DataType.UBYTE)
+ assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
+ else if(lv.type== DataType.BYTE && lv.bytevalue!!>=0)
+ assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
+ else if(lv.type== DataType.WORD && lv.wordvalue!!>=0)
+ assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
+ else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
if(floor(d)==d && d>=0 && d<=65535)
- assignment.value = LiteralValue(DataType.UWORD, wordvalue=floor(d).toInt(), position=lv.position)
+ assignment.value = LiteralValue(DataType.UWORD, wordvalue = floor(d).toInt(), position = lv.position)
}
}
DataType.UBYTE -> {
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
- if(lv.type==DataType.UWORD && lv.wordvalue!! <= 255)
- assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position=lv.position)
- else if(lv.type==DataType.BYTE && lv.bytevalue!! >=0)
- assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position=lv.position)
- else if(lv.type==DataType.FLOAT) {
+ if(lv.type== DataType.UWORD && lv.wordvalue!! <= 255)
+ assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position = lv.position)
+ else if(lv.type== DataType.BYTE && lv.bytevalue!! >=0)
+ assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position = lv.position)
+ else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
if(floor(d)==d && d >=0 && d<=255)
- assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position=lv.position)
+ assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position = lv.position)
}
}
DataType.BYTE -> {
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
- if(lv.type==DataType.UWORD && lv.wordvalue!! <= 127)
- assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position=lv.position)
- else if(lv.type==DataType.UBYTE && lv.bytevalue!! <= 127)
- assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position=lv.position)
- else if(lv.type==DataType.FLOAT) {
+ if(lv.type== DataType.UWORD && lv.wordvalue!! <= 127)
+ assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position = lv.position)
+ else if(lv.type== DataType.UBYTE && lv.bytevalue!! <= 127)
+ assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position = lv.position)
+ else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
if(floor(d)==d && d>=0 && d<=127)
- assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position)
+ assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
}
}
DataType.WORD -> {
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
- if(lv.type==DataType.UBYTE || lv.type==DataType.BYTE)
- assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.bytevalue!!.toInt(), position=lv.position)
- else if(lv.type==DataType.UWORD && lv.wordvalue!! <= 32767)
- assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.wordvalue, position=lv.position)
- else if(lv.type==DataType.FLOAT) {
+ if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE)
+ assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.bytevalue!!.toInt(), position = lv.position)
+ else if(lv.type== DataType.UWORD && lv.wordvalue!! <= 32767)
+ assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.wordvalue, position = lv.position)
+ else if(lv.type== DataType.FLOAT) {
val d = lv.floatvalue!!
if(floor(d)==d && d>=-32768 && d<=32767)
- assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position)
+ assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
}
}
DataType.FLOAT -> {
if(lv.isNumeric)
- assignment.value = LiteralValue(DataType.FLOAT, floatvalue= lv.asNumericValue?.toDouble(), position=lv.position)
+ assignment.value = LiteralValue(DataType.FLOAT, floatvalue = lv.asNumericValue?.toDouble(), position = lv.position)
}
else -> {}
}
diff --git a/compiler/src/prog8/optimizing/Extensions.kt b/compiler/src/prog8/optimizing/Extensions.kt
index a2ed77016..5dc952085 100644
--- a/compiler/src/prog8/optimizing/Extensions.kt
+++ b/compiler/src/prog8/optimizing/Extensions.kt
@@ -1,6 +1,8 @@
package prog8.optimizing
import prog8.ast.*
+import prog8.ast.base.AstException
+import prog8.ast.statements.NopStatement
import prog8.parser.ParsingFailedError
diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt
index 1a1c83012..e38b74979 100644
--- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt
+++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt
@@ -1,6 +1,13 @@
package prog8.optimizing
import prog8.ast.*
+import prog8.ast.base.AstException
+import prog8.ast.base.DataType
+import prog8.ast.base.IntegerDatatypes
+import prog8.ast.base.NumericDatatypes
+import prog8.ast.expressions.*
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.Assignment
import kotlin.math.abs
import kotlin.math.log2
@@ -336,48 +343,48 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
DataType.UBYTE -> {
if (targetDt == DataType.BYTE) {
if(value.bytevalue!! < 127)
- return Pair(true, LiteralValue(targetDt, value.bytevalue, position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
}
else if (targetDt == DataType.UWORD || targetDt == DataType.WORD)
- return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
}
DataType.BYTE -> {
if (targetDt == DataType.UBYTE) {
if(value.bytevalue!! >= 0)
- return Pair(true, LiteralValue(targetDt, value.bytevalue, position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
}
else if (targetDt == DataType.UWORD) {
if(value.bytevalue!! >= 0)
- return Pair(true, LiteralValue(targetDt, wordvalue=value.bytevalue.toInt(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue.toInt(), position = value.position))
}
- else if (targetDt == DataType.WORD) return Pair(true, LiteralValue(targetDt, wordvalue=value.bytevalue!!.toInt(), position=value.position))
+ else if (targetDt == DataType.WORD) return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
}
DataType.UWORD -> {
if (targetDt == DataType.UBYTE) {
if(value.wordvalue!! <= 255)
- return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
}
else if (targetDt == DataType.BYTE) {
if(value.wordvalue!! <= 127)
- return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
}
else if (targetDt == DataType.WORD) {
if(value.wordvalue!! <= 32767)
- return Pair(true, LiteralValue(targetDt, wordvalue=value.wordvalue, position=value.position))
+ return Pair(true, LiteralValue(targetDt, wordvalue = value.wordvalue, position = value.position))
}
}
DataType.WORD -> {
if (targetDt == DataType.UBYTE) {
if(value.wordvalue!! in 0..255)
- return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
}
else if (targetDt == DataType.BYTE) {
if(value.wordvalue!! in -128..127)
- return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
}
else if (targetDt == DataType.UWORD) {
if(value.wordvalue!! >= 0)
- return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
+ return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
}
}
else -> {}
diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt
index eafc7e222..f62885984 100644
--- a/compiler/src/prog8/optimizing/StatementOptimizer.kt
+++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt
@@ -1,6 +1,10 @@
package prog8.optimizing
import prog8.ast.*
+import prog8.ast.base.*
+import prog8.ast.expressions.*
+import prog8.ast.processing.IAstProcessor
+import prog8.ast.statements.*
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import kotlin.math.floor
@@ -419,7 +423,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
private fun hasContinueOrBreak(scope: INameScope): Boolean {
- class Searcher:IAstProcessor
+ class Searcher: IAstProcessor
{
var count=0
@@ -481,7 +485,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(bexpr!=null) {
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
if(cv==null) {
- if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
+ if(bexpr.operator=="+" && targetDt!= DataType.FLOAT) {
if (bexpr.left isSameAs bexpr.right && target isSameAs bexpr.left) {
bexpr.operator = "*"
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
diff --git a/compiler/src/prog8/parser/ModuleParsing.kt b/compiler/src/prog8/parser/ModuleParsing.kt
index 5f6219445..84ecccb73 100644
--- a/compiler/src/prog8/parser/ModuleParsing.kt
+++ b/compiler/src/prog8/parser/ModuleParsing.kt
@@ -2,6 +2,12 @@ package prog8.parser
import org.antlr.v4.runtime.*
import prog8.ast.*
+import prog8.ast.antlr.toAst
+import prog8.ast.base.Position
+import prog8.ast.base.SyntaxError
+import prog8.ast.base.checkImportedValid
+import prog8.ast.statements.Directive
+import prog8.ast.statements.DirectiveArg
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
@@ -45,8 +51,8 @@ internal fun importModule(program: Program, filePath: Path): Module {
internal fun importLibraryModule(program: Program, name: String): Module? {
val import = Directive("%import", listOf(
- DirectiveArg("", name, 42, position = Position("<<>>", 0, 0 ,0))
- ), Position("<<>>", 0, 0 ,0))
+ DirectiveArg("", name, 42, position = Position("<<>>", 0, 0, 0))
+ ), Position("<<>>", 0, 0, 0))
return executeImportDirective(program, import, Paths.get(""))
}
diff --git a/compiler/src/prog8/stackvm/Program.kt b/compiler/src/prog8/stackvm/Program.kt
index a318ff5af..975dd0bde 100644
--- a/compiler/src/prog8/stackvm/Program.kt
+++ b/compiler/src/prog8/stackvm/Program.kt
@@ -1,6 +1,9 @@
package prog8.stackvm
-import prog8.ast.*
+import prog8.ast.antlr.unescape
+import prog8.ast.base.*
+import prog8.ast.expressions.AddressOf
+import prog8.ast.expressions.IdentifierReference
import prog8.compiler.RuntimeValue
import prog8.compiler.HeapValues
import prog8.compiler.IntegerOrAddressOf
@@ -89,7 +92,7 @@ class Program (val name: String,
}
heapvalues.sortedBy { it.first }.forEach {
when(it.second) {
- DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length-1), Position("", 0, 0, 0)))
+ DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length - 1), Position("", 0, 0, 0)))
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W -> {
val numbers = it.third.substring(1, it.third.length-1).split(',')
@@ -98,8 +101,8 @@ class Program (val name: String,
if(num.startsWith("&")) {
// it's AddressOf
val scopedname = num.substring(1)
- val iref = IdentifierReference(scopedname.split('.'), Position("", 0,0,0))
- val addrOf = AddressOf(iref, Position("", 0,0,0))
+ val iref = IdentifierReference(scopedname.split('.'), Position("", 0, 0, 0))
+ val addrOf = AddressOf(iref, Position("", 0, 0, 0))
addrOf.scopedname=scopedname
IntegerOrAddressOf(null, addrOf)
} else {
diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt
index b35fe0052..2af784110 100644
--- a/compiler/src/prog8/stackvm/StackVm.kt
+++ b/compiler/src/prog8/stackvm/StackVm.kt
@@ -1,6 +1,10 @@
package prog8.stackvm
-import prog8.ast.*
+import prog8.ast.base.DataType
+import prog8.ast.base.IterableDatatypes
+import prog8.ast.base.NumericDatatypes
+import prog8.ast.base.Register
+import prog8.ast.base.initvarsSubName
import prog8.astvm.BitmapScreenPanel
import prog8.astvm.Memory
import prog8.compiler.RuntimeValue
@@ -376,7 +380,7 @@ class StackVm(private var traceOutputFile: String?) {
val value = evalstack.pop()
checkDt(value, DataType.BYTE, DataType.UBYTE)
val address = ins.arg!!.integerValue()
- if(value.type==DataType.BYTE)
+ if(value.type== DataType.BYTE)
mem.setSByte(address, value.integerValue().toShort())
else
mem.setUByte(address, value.integerValue().toShort())
@@ -386,7 +390,7 @@ class StackVm(private var traceOutputFile: String?) {
val value = evalstack.pop()
checkDt(value, DataType.WORD, DataType.UWORD)
val address = ins.arg!!.integerValue()
- if(value.type==DataType.WORD)
+ if(value.type== DataType.WORD)
mem.setSWord(address, value.integerValue())
else
mem.setUWord(address, value.integerValue())
@@ -1673,7 +1677,7 @@ class StackVm(private var traceOutputFile: String?) {
} else {
// normal variable
val variable = getVar(ins.callLabel!!)
- if(variable.type==DataType.UWORD) {
+ if(variable.type== DataType.UWORD) {
// assume the variable is a pointer (address) and get the word value from that memory location
RuntimeValue(DataType.UWORD, mem.getUWord(variable.integerValue()))
} else {
@@ -1708,7 +1712,7 @@ class StackVm(private var traceOutputFile: String?) {
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers.getValue(ins.callLabel!!)
val address = variable.first + index*5
- if(variable.second==DataType.ARRAY_F)
+ if(variable.second== DataType.ARRAY_F)
RuntimeValue(DataType.FLOAT, mem.getFloat(address))
else
throw VmExecutionException("not a proper arraysize var with float elements")
@@ -1737,13 +1741,13 @@ class StackVm(private var traceOutputFile: String?) {
val memloc = memoryPointers[varname]
if(memloc!=null) {
// variable is the name of a pointer, write the byte value to that memory location
- if(value.type==DataType.UBYTE) {
- if(memloc.second!=DataType.ARRAY_UB)
+ if(value.type== DataType.UBYTE) {
+ if(memloc.second!= DataType.ARRAY_UB)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setUByte(memloc.first, value.integerValue().toShort())
}
else {
- if(memloc.second!=DataType.ARRAY_B)
+ if(memloc.second!= DataType.ARRAY_B)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setSByte(memloc.first, value.integerValue().toShort())
}
@@ -1751,7 +1755,7 @@ class StackVm(private var traceOutputFile: String?) {
val variable = getVar(varname)
if (variable.type == DataType.UWORD) {
// assume the variable is a pointer (address) and write the byte value to that memory location
- if(value.type==DataType.UBYTE)
+ if(value.type== DataType.UBYTE)
mem.setUByte(variable.integerValue(), value.integerValue().toShort())
else
mem.setSByte(variable.integerValue(), value.integerValue().toShort())
@@ -1785,13 +1789,13 @@ class StackVm(private var traceOutputFile: String?) {
val memloc = memoryPointers[varname]
if(memloc!=null) {
// variable is the name of a pointer, write the word value to that memory location
- if(value.type==DataType.UWORD) {
- if(memloc.second!=DataType.ARRAY_UW)
+ if(value.type== DataType.UWORD) {
+ if(memloc.second!= DataType.ARRAY_UW)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setUWord(memloc.first+index*2, value.integerValue())
}
else {
- if(memloc.second!=DataType.ARRAY_W)
+ if(memloc.second!= DataType.ARRAY_W)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setSWord(memloc.first+index*2, value.integerValue())
}
@@ -1799,7 +1803,7 @@ class StackVm(private var traceOutputFile: String?) {
val variable = getVar(varname)
if (variable.type == DataType.UWORD) {
// assume the variable is a pointer (address) and write the word value to that memory location
- if(value.type==DataType.UWORD)
+ if(value.type== DataType.UWORD)
mem.setUWord(variable.integerValue()+index*2, value.integerValue())
else
mem.setSWord(variable.integerValue()+index*2, value.integerValue())
@@ -1824,7 +1828,7 @@ class StackVm(private var traceOutputFile: String?) {
val memloc = memoryPointers[varname]
if(memloc!=null) {
// variable is the name of a pointer, write the float value to that memory location
- if(memloc.second!=DataType.ARRAY_F)
+ if(memloc.second!= DataType.ARRAY_F)
throw VmExecutionException("invalid memory pointer type $memloc")
mem.setFloat(memloc.first+index*5, value.numericValue().toDouble())
} else {
@@ -2268,9 +2272,9 @@ class StackVm(private var traceOutputFile: String?) {
val numbytes = evalstack.pop().integerValue()
val bytevalue = value.integerValue().toShort()
when {
- value.type==DataType.UBYTE -> for(addr in address until address+numbytes)
+ value.type== DataType.UBYTE -> for(addr in address until address+numbytes)
mem.setUByte(addr, bytevalue)
- value.type==DataType.BYTE -> for(addr in address until address+numbytes)
+ value.type== DataType.BYTE -> for(addr in address until address+numbytes)
mem.setSByte(addr, bytevalue)
else -> throw VmExecutionException("(u)byte value expected")
}
@@ -2281,9 +2285,9 @@ class StackVm(private var traceOutputFile: String?) {
val numwords = evalstack.pop().integerValue()
val wordvalue = value.integerValue()
when {
- value.type==DataType.UWORD -> for(addr in address until address+numwords*2 step 2)
+ value.type== DataType.UWORD -> for(addr in address until address+numwords*2 step 2)
mem.setUWord(addr, wordvalue)
- value.type==DataType.WORD -> for(addr in address until address+numwords*2 step 2)
+ value.type== DataType.WORD -> for(addr in address until address+numwords*2 step 2)
mem.setSWord(addr, wordvalue)
else -> throw VmExecutionException("(u)word value expected")
}
diff --git a/compiler/test/LiteralValueTests.kt b/compiler/test/LiteralValueTests.kt
index 93f5a14dc..b0ff0c6c4 100644
--- a/compiler/test/LiteralValueTests.kt
+++ b/compiler/test/LiteralValueTests.kt
@@ -2,9 +2,9 @@ package prog8tests
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
-import prog8.ast.DataType
-import prog8.ast.LiteralValue
-import prog8.ast.Position
+import prog8.ast.base.DataType
+import prog8.ast.expressions.LiteralValue
+import prog8.ast.base.Position
import kotlin.test.*
@@ -16,7 +16,7 @@ private fun sameValueAndType(lv1: LiteralValue, lv2: LiteralValue): Boolean {
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestParserLiteralValue {
- private val dummyPos = Position("test", 0,0,0)
+ private val dummyPos = Position("test", 0, 0, 0)
@Test
fun testIdentity() {
@@ -33,99 +33,99 @@ class TestParserLiteralValue {
@Test
fun testEqualsAndNotEquals() {
- assertEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos))
- assertEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=100, position=dummyPos))
- assertEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
- assertEquals(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos), LiteralValue(DataType.UBYTE, 254, position=dummyPos))
- assertEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos))
- assertEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12345.0, position=dummyPos))
- assertEquals(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos))
- assertEquals(LiteralValue(DataType.FLOAT, floatvalue=22239.0, position=dummyPos), LiteralValue(DataType.UWORD,wordvalue=22239, position=dummyPos))
- assertEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos))
+ assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
+ assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos))
+ assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
+ assertEquals(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos))
+ assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos))
+ assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos))
+ assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
+ assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos))
+ assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos))
- assertTrue(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=100, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos), LiteralValue(DataType.UBYTE, 254, position=dummyPos)))
- assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12345.0, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=22239.0, position=dummyPos), LiteralValue(DataType.UWORD,wordvalue=22239, position=dummyPos)))
- assertTrue(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos)))
+ assertTrue(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos)))
+ assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos)))
+ assertTrue(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos)))
- assertNotEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 101, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=101, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=101.0, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.UWORD, wordvalue=245, position=dummyPos), LiteralValue(DataType.UBYTE, 246, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12346, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12346.0, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UBYTE, 9, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=9, position=dummyPos))
- assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.0, position=dummyPos))
+ assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos))
+ assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos))
- assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 101, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=101, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=101.0, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=245, position=dummyPos), LiteralValue(DataType.UBYTE, 246, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12346, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12346.0, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UBYTE, 9, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=9, position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.0, position=dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos)))
- assertTrue(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position=dummyPos), LiteralValue(DataType.STR, strvalue="hello", position=dummyPos)))
- assertFalse(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position=dummyPos), LiteralValue(DataType.STR, strvalue="bye", position=dummyPos)))
+ assertTrue(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos), LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos)))
+ assertFalse(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos), LiteralValue(DataType.STR, strvalue = "bye", position = dummyPos)))
- val lvOne = LiteralValue(DataType.UBYTE, 1, position=dummyPos)
- val lvTwo = LiteralValue(DataType.UBYTE, 2, position=dummyPos)
- val lvThree = LiteralValue(DataType.UBYTE, 3, position=dummyPos)
- val lvOneR = LiteralValue(DataType.UBYTE, 1, position=dummyPos)
- val lvTwoR = LiteralValue(DataType.UBYTE, 2, position=dummyPos)
- val lvThreeR = LiteralValue(DataType.UBYTE, 3, position=dummyPos)
- val lvFour= LiteralValue(DataType.UBYTE, 4, position=dummyPos)
- val lv1 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position=dummyPos)
- val lv2 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position=dummyPos)
- val lv3 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvFour), position=dummyPos)
+ val lvOne = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
+ val lvTwo = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
+ val lvThree = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
+ val lvOneR = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
+ val lvTwoR = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
+ val lvThreeR = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
+ val lvFour= LiteralValue(DataType.UBYTE, 4, position = dummyPos)
+ val lv1 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
+ val lv2 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
+ val lv3 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
assertEquals(lv1, lv2)
assertNotEquals(lv1, lv3)
}
@Test
fun testGreaterThan(){
- assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) > LiteralValue(DataType.UBYTE, 99, position=dummyPos))
- assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) > LiteralValue(DataType.UWORD, wordvalue=253, position=dummyPos))
- assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) > LiteralValue(DataType.FLOAT, floatvalue=99.9, position=dummyPos))
+ assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 99, position = dummyPos))
+ assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
+ assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
- assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) >= LiteralValue(DataType.UBYTE, 100, position=dummyPos))
- assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) >= LiteralValue(DataType.UWORD,wordvalue= 254, position=dummyPos))
- assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
+ assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
+ assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
+ assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
- assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) > LiteralValue(DataType.UBYTE, 100, position=dummyPos))
- assertFalse(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) > LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos))
- assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) > LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
+ assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 100, position = dummyPos))
+ assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
+ assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
- assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) >= LiteralValue(DataType.UBYTE, 101, position=dummyPos))
- assertFalse(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) >= LiteralValue(DataType.UWORD,wordvalue= 255, position=dummyPos))
- assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue=100.1, position=dummyPos))
+ assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 101, position = dummyPos))
+ assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
+ assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
}
@Test
fun testLessThan() {
- assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) < LiteralValue(DataType.UBYTE, 101, position=dummyPos))
- assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) < LiteralValue(DataType.UWORD, wordvalue=255, position=dummyPos))
- assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) < LiteralValue(DataType.FLOAT, floatvalue=100.1, position=dummyPos))
+ assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 101, position = dummyPos))
+ assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
+ assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
- assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) <= LiteralValue(DataType.UBYTE, 100, position=dummyPos))
- assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) <= LiteralValue(DataType.UWORD,wordvalue= 254, position=dummyPos))
- assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
+ assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
+ assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
+ assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
- assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) < LiteralValue(DataType.UBYTE, 100, position=dummyPos))
- assertFalse(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) < LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos))
- assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) < LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
+ assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 100, position = dummyPos))
+ assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
+ assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
- assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) <= LiteralValue(DataType.UBYTE, 99, position=dummyPos))
- assertFalse(LiteralValue(DataType.UWORD,wordvalue= 254, position=dummyPos) <= LiteralValue(DataType.UWORD,wordvalue= 253, position=dummyPos))
- assertFalse(LiteralValue(DataType.FLOAT,floatvalue= 100.0, position=dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue=99.9, position=dummyPos))
+ assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 99, position = dummyPos))
+ assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
+ assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
}
}
diff --git a/compiler/test/RuntimeValueTests.kt b/compiler/test/RuntimeValueTests.kt
index c960a0e3b..0aed3a1c7 100644
--- a/compiler/test/RuntimeValueTests.kt
+++ b/compiler/test/RuntimeValueTests.kt
@@ -2,7 +2,7 @@ package prog8tests
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
-import prog8.ast.DataType
+import prog8.ast.base.DataType
import prog8.compiler.RuntimeValue
import kotlin.test.*
diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt
index fd05b281f..c17cb44d1 100644
--- a/compiler/test/StackVMOpcodeTests.kt
+++ b/compiler/test/StackVMOpcodeTests.kt
@@ -4,10 +4,10 @@ import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.empty
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
-import prog8.ast.ByteDatatypes
-import prog8.ast.DataType
-import prog8.ast.IterableDatatypes
-import prog8.ast.WordDatatypes
+import prog8.ast.base.ByteDatatypes
+import prog8.ast.base.DataType
+import prog8.ast.base.IterableDatatypes
+import prog8.ast.base.WordDatatypes
import prog8.compiler.RuntimeValue
import prog8.compiler.HeapValues
import prog8.compiler.intermediate.Instruction
diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt
index 9a45d3daa..4c1479a33 100644
--- a/compiler/test/UnitTests.kt
+++ b/compiler/test/UnitTests.kt
@@ -5,7 +5,9 @@ import org.hamcrest.Matchers.closeTo
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
-import prog8.ast.*
+import prog8.ast.base.DataType
+import prog8.ast.base.Position
+import prog8.ast.expressions.LiteralValue
import prog8.compiler.RuntimeValue
import prog8.compiler.*
import prog8.compiler.target.c64.*
@@ -334,8 +336,8 @@ class TestPetscii {
@Test
fun testLiteralValueComparisons() {
- val ten = LiteralValue(DataType.UWORD, wordvalue=10, position=Position("", 0 ,0 ,0))
- val nine = LiteralValue(DataType.UBYTE, bytevalue=9, position=Position("", 0 ,0 ,0))
+ val ten = LiteralValue(DataType.UWORD, wordvalue = 10, position = Position("", 0, 0, 0))
+ val nine = LiteralValue(DataType.UBYTE, bytevalue = 9, position = Position("", 0, 0, 0))
assertEquals(ten, ten)
assertNotEquals(ten, nine)
assertFalse(ten != ten)
@@ -351,8 +353,8 @@ class TestPetscii {
assertTrue(ten <= ten)
assertFalse(ten < ten)
- val abc = LiteralValue(DataType.STR, strvalue = "abc", position=Position("", 0 ,0 ,0))
- val abd = LiteralValue(DataType.STR, strvalue = "abd", position=Position("", 0 ,0 ,0))
+ val abc = LiteralValue(DataType.STR, strvalue = "abc", position = Position("", 0, 0, 0))
+ val abd = LiteralValue(DataType.STR, strvalue = "abd", position = Position("", 0, 0, 0))
assertEquals(abc, abc)
assertTrue(abc!=abd)
assertFalse(abc!=abc)