mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
d9546f9dc7 | |||
2a6b0f5db7 | |||
b4e1b42cec | |||
a8898a5993 | |||
e03c68b632 | |||
a0074de12b | |||
411bedcc46 | |||
07d8caf884 | |||
c0e83ef8df | |||
4dbf4b2005 | |||
61af72b906 | |||
17be722e2b | |||
16d7927d2f | |||
55a7a5d9d5 | |||
78d7849197 | |||
d5b12fb01d | |||
31f4e378aa | |||
8a26b7b248 | |||
87c28cfdbc | |||
1f5420010d | |||
a089c48378 | |||
3e5deda46c | |||
7500c6efd0 | |||
717b5f3b07 | |||
9f6fa60bf1 | |||
1e9586f635 | |||
44f9d5e69e | |||
7c9b8f7d43 | |||
845a99d623 | |||
3d7a4bf81a | |||
d4b3e35bd2 | |||
a59f7c75dc | |||
44fe2369d6 | |||
aaaab2cfcf | |||
9a3dab20dc | |||
20379b5927 | |||
34dcce67e4 | |||
0c7f107d01 | |||
1f89571aa5 | |||
7eed1ebbf8 | |||
12cb7d7abe | |||
c9b16dcbd9 | |||
dcab6d00bb | |||
a85743f241 | |||
14cabde5cf | |||
cc078503e3 | |||
2a0c3377f9 | |||
16454f5560 | |||
c1343a78f1 | |||
9d0c65c682 | |||
9e6408244f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,8 +24,6 @@ __pycache__/
|
|||||||
parser.out
|
parser.out
|
||||||
parsetab.py
|
parsetab.py
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
compiler/src/prog8_kotlin.jar
|
|
||||||
compiler/src/compiled_java
|
|
||||||
.attach_pid*
|
.attach_pid*
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
|
@ -21,6 +21,8 @@ which aims to provide many conveniences over raw assembly code (even when using
|
|||||||
- automatic variable allocations, automatic string variables and string sharing
|
- automatic variable allocations, automatic string variables and string sharing
|
||||||
- constant folding in expressions (compile-time evaluation)
|
- constant folding in expressions (compile-time evaluation)
|
||||||
- conditional branches
|
- conditional branches
|
||||||
|
- when statement to provide a 'jump table' alternative to if/elseif chains
|
||||||
|
- structs to group together sets of variables and manipulate them at once
|
||||||
- automatic type conversions
|
- automatic type conversions
|
||||||
- floating point operations (uses the C64 Basic ROM routines for this)
|
- floating point operations (uses the C64 Basic ROM routines for this)
|
||||||
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||||
@ -33,7 +35,9 @@ Rapid edit-compile-run-debug cycle:
|
|||||||
- option to automatically run the program in the Vice emulator
|
- option to automatically run the program in the Vice emulator
|
||||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||||
|
- the compiler includes a virtual machine that can execute compiled code directy on the
|
||||||
|
host system without having to actually convert it to assembly to run on a real 6502.
|
||||||
|
This allows for very quick experimentation and debugging
|
||||||
|
|
||||||
It is mainly targeted at the Commodore-64 machine at this time.
|
It is mainly targeted at the Commodore-64 machine at this time.
|
||||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
||||||
|
@ -76,17 +76,6 @@ artifacts {
|
|||||||
archives shadowJar
|
archives shadowJar
|
||||||
}
|
}
|
||||||
|
|
||||||
task p8vmScript(type: CreateStartScripts) {
|
|
||||||
mainClassName = "prog8.vm.stackvm.MainKt"
|
|
||||||
applicationName = "p8vm"
|
|
||||||
outputDir = new File(project.buildDir, 'scripts')
|
|
||||||
classpath = jar.outputs.files + project.configurations.runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationDistribution.into("bin") {
|
|
||||||
from(p8vmScript)
|
|
||||||
fileMode = 0755
|
|
||||||
}
|
|
||||||
|
|
||||||
// To create a fat-jar use the 'create_compiler_jar' script for now
|
// To create a fat-jar use the 'create_compiler_jar' script for now
|
||||||
// @todo investigate https://imperceptiblethoughts.com/shadow/introduction/
|
// @todo investigate https://imperceptiblethoughts.com/shadow/introduction/
|
||||||
|
@ -1 +1 @@
|
|||||||
1.9
|
1.20
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package prog8
|
package prog8
|
||||||
|
|
||||||
|
import prog8.compiler.compileProgram
|
||||||
import prog8.vm.astvm.AstVm
|
import prog8.vm.astvm.AstVm
|
||||||
import prog8.vm.stackvm.stackVmMain
|
import prog8.vm.stackvm.stackVmMain
|
||||||
import prog8.compiler.*
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
@ -1,18 +1,167 @@
|
|||||||
package prog8.ast
|
package prog8.ast
|
||||||
|
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.NameError
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.base.ParentSentinel
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.statements.*
|
||||||
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.compiler.HeapValues
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
interface Node {
|
||||||
|
val position: Position
|
||||||
|
var parent: Node // will be linked correctly later (late init)
|
||||||
|
fun linkParents(parent: Node)
|
||||||
|
|
||||||
|
fun definingModule(): Module {
|
||||||
|
if(this is Module)
|
||||||
|
return this
|
||||||
|
return findParentNode<Module>(this)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
|
||||||
|
|
||||||
|
fun definingScope(): INameScope {
|
||||||
|
val scope = findParentNode<INameScope>(this)
|
||||||
|
if(scope!=null) {
|
||||||
|
return scope
|
||||||
|
}
|
||||||
|
if(this is Label && this.name.startsWith("builtin::")) {
|
||||||
|
return BuiltinFunctionScopePlaceholder
|
||||||
|
}
|
||||||
|
if(this is GlobalNamespace)
|
||||||
|
return this
|
||||||
|
throw FatalAstException("scope missing from $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFunctionCall {
|
||||||
|
var target: IdentifierReference
|
||||||
|
var arglist: MutableList<Expression>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface INameScope {
|
||||||
|
val name: String
|
||||||
|
val position: Position
|
||||||
|
val statements: MutableList<Statement>
|
||||||
|
val parent: Node
|
||||||
|
|
||||||
|
fun linkParents(parent: Node)
|
||||||
|
|
||||||
|
fun subScopes(): Map<String, INameScope> {
|
||||||
|
val subscopes = mutableMapOf<String, INameScope>()
|
||||||
|
for(stmt in statements) {
|
||||||
|
when(stmt) {
|
||||||
|
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
||||||
|
is ForLoop -> subscopes[stmt.body.name] = stmt.body
|
||||||
|
is RepeatLoop -> subscopes[stmt.body.name] = stmt.body
|
||||||
|
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||||
|
is BranchStatement -> {
|
||||||
|
subscopes[stmt.truepart.name] = stmt.truepart
|
||||||
|
if(stmt.elsepart.containsCodeOrVars())
|
||||||
|
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||||
|
}
|
||||||
|
is IfStatement -> {
|
||||||
|
subscopes[stmt.truepart.name] = stmt.truepart
|
||||||
|
if(stmt.elsepart.containsCodeOrVars())
|
||||||
|
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||||
|
}
|
||||||
|
is WhenStatement -> {
|
||||||
|
stmt.choices.forEach { subscopes[it.statements.name] = it.statements }
|
||||||
|
}
|
||||||
|
is INameScope -> subscopes[stmt.name] = stmt
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subscopes
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLabelOrVariable(name: String): Statement? {
|
||||||
|
// this is called A LOT and could perhaps be optimized a bit more,
|
||||||
|
// but adding a memoization cache didn't make much of a practical runtime difference
|
||||||
|
for (stmt in statements) {
|
||||||
|
if (stmt is VarDecl && stmt.name==name) return stmt
|
||||||
|
if (stmt is Label && stmt.name==name) return stmt
|
||||||
|
if (stmt is AnonymousScope) {
|
||||||
|
val sub = stmt.getLabelOrVariable(name)
|
||||||
|
if(sub!=null)
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allDefinedSymbols(): List<Pair<String, Statement>> {
|
||||||
|
return statements.mapNotNull {
|
||||||
|
when (it) {
|
||||||
|
is Label -> it.name to it
|
||||||
|
is VarDecl -> it.name to it
|
||||||
|
is Subroutine -> it.name to it
|
||||||
|
is Block -> it.name to it
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
||||||
|
if(scopedName.size>1) {
|
||||||
|
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
||||||
|
// try the struct first.
|
||||||
|
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
||||||
|
val struct = thing?.struct
|
||||||
|
if (struct != null) {
|
||||||
|
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
||||||
|
// return ref to the mangled name variable
|
||||||
|
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
||||||
|
return thing.definingScope().getLabelOrVariable(mangled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
||||||
|
for(module in localContext.definingModule().program.modules) {
|
||||||
|
var scope: INameScope? = module
|
||||||
|
for(name in scopedName.dropLast(1)) {
|
||||||
|
scope = scope?.subScopes()?.get(name)
|
||||||
|
if(scope==null)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if(scope!=null) {
|
||||||
|
val result = scope.getLabelOrVariable(scopedName.last())
|
||||||
|
if(result!=null)
|
||||||
|
return result
|
||||||
|
return scope.subScopes()[scopedName.last()] as Statement?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
// unqualified name, find the scope the localContext is in, look in that first
|
||||||
|
var statementScope = localContext
|
||||||
|
while(statementScope !is ParentSentinel) {
|
||||||
|
val localScope = statementScope.definingScope()
|
||||||
|
val result = localScope.getLabelOrVariable(scopedName[0])
|
||||||
|
if (result != null)
|
||||||
|
return result
|
||||||
|
val subscope = localScope.subScopes()[scopedName[0]] as Statement?
|
||||||
|
if (subscope != null)
|
||||||
|
return subscope
|
||||||
|
// not found in this scope, look one higher up
|
||||||
|
statementScope = statementScope.parent
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||||
|
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||||
|
|
||||||
|
fun remove(stmt: Statement) {
|
||||||
|
if(!statements.remove(stmt))
|
||||||
|
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||||
|
|
||||||
class Program(val name: String, val modules: MutableList<Module>) {
|
class Program(val name: String, val modules: MutableList<Module>) {
|
||||||
@ -35,7 +184,7 @@ class Program(val name: String, val modules: MutableList<Module>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Module(override val name: String,
|
class Module(override val name: String,
|
||||||
override var statements: MutableList<IStatement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position,
|
override val position: Position,
|
||||||
val isLibraryModule: Boolean,
|
val isLibraryModule: Boolean,
|
||||||
val source: Path) : Node, INameScope {
|
val source: Path) : Node, INameScope {
|
||||||
@ -59,14 +208,14 @@ class Module(override val name: String,
|
|||||||
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||||
override val name = "<<<global>>>"
|
override val name = "<<<global>>>"
|
||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||||
override val statements = mutableListOf<IStatement>()
|
override val statements = mutableListOf<Statement>()
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
modules.forEach { it.linkParents(this) }
|
modules.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>, localContext: Node): IStatement? {
|
override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
|
||||||
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
||||||
// builtin functions always exist, return a dummy localContext for them
|
// builtin functions always exist, return a dummy localContext for them
|
||||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
||||||
@ -74,6 +223,20 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
return builtinPlaceholder
|
return builtinPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(scopedName.size>1) {
|
||||||
|
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
||||||
|
// try the struct first.
|
||||||
|
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
||||||
|
val struct = thing?.struct
|
||||||
|
if (struct != null) {
|
||||||
|
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
||||||
|
// return ref to the mangled name variable
|
||||||
|
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
||||||
|
return thing.definingScope().getLabelOrVariable(mangled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val stmt = localContext.definingModule().lookup(scopedName, localContext)
|
val stmt = localContext.definingModule().lookup(scopedName, localContext)
|
||||||
return when (stmt) {
|
return when (stmt) {
|
||||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||||
@ -86,7 +249,11 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
object BuiltinFunctionScopePlaceholder : INameScope {
|
object BuiltinFunctionScopePlaceholder : INameScope {
|
||||||
override val name = "<<builtin-functions-scope-placeholder>>"
|
override val name = "<<builtin-functions-scope-placeholder>>"
|
||||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||||
override var statements = mutableListOf<IStatement>()
|
override var statements = mutableListOf<Statement>()
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// prefix for struct member variables
|
||||||
|
internal fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName"
|
||||||
|
@ -1,197 +0,0 @@
|
|||||||
package prog8.ast
|
|
||||||
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
|
|
||||||
interface Node {
|
|
||||||
val position: Position
|
|
||||||
var parent: Node // will be linked correctly later (late init)
|
|
||||||
fun linkParents(parent: Node)
|
|
||||||
|
|
||||||
fun definingModule(): Module {
|
|
||||||
if(this is Module)
|
|
||||||
return this
|
|
||||||
return findParentNode<Module>(this)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
|
|
||||||
|
|
||||||
fun definingScope(): INameScope {
|
|
||||||
val scope = findParentNode<INameScope>(this)
|
|
||||||
if(scope!=null) {
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
if(this is Label && this.name.startsWith("builtin::")) {
|
|
||||||
return BuiltinFunctionScopePlaceholder
|
|
||||||
}
|
|
||||||
if(this is GlobalNamespace)
|
|
||||||
return this
|
|
||||||
throw FatalAstException("scope missing from $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IStatement : Node {
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) : IStatement
|
|
||||||
fun accept(visitor: IAstVisitor)
|
|
||||||
fun makeScopedName(name: String): String {
|
|
||||||
// easy way out is to always return the full scoped name.
|
|
||||||
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
|
||||||
// and like this, we can cache the name even,
|
|
||||||
// like in a lazy property on the statement object itself (label, subroutine, vardecl)
|
|
||||||
val scope = mutableListOf<String>()
|
|
||||||
var statementScope = this.parent
|
|
||||||
while(statementScope !is ParentSentinel && statementScope !is Module) {
|
|
||||||
if(statementScope is INameScope) {
|
|
||||||
scope.add(0, statementScope.name)
|
|
||||||
}
|
|
||||||
statementScope = statementScope.parent
|
|
||||||
}
|
|
||||||
if(name.isNotEmpty())
|
|
||||||
scope.add(name)
|
|
||||||
return scope.joinToString(".")
|
|
||||||
}
|
|
||||||
|
|
||||||
val expensiveToInline: Boolean
|
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
|
||||||
if(this is Block)
|
|
||||||
return this
|
|
||||||
return findParentNode<Block>(this)!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFunctionCall {
|
|
||||||
var target: IdentifierReference
|
|
||||||
var arglist: MutableList<IExpression>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface INameScope {
|
|
||||||
val name: String
|
|
||||||
val position: Position
|
|
||||||
val statements: MutableList<IStatement>
|
|
||||||
val parent: Node
|
|
||||||
|
|
||||||
fun linkParents(parent: Node)
|
|
||||||
|
|
||||||
fun subScopes(): Map<String, INameScope> {
|
|
||||||
val subscopes = mutableMapOf<String, INameScope>()
|
|
||||||
for(stmt in statements) {
|
|
||||||
when(stmt) {
|
|
||||||
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<Pair<String, IStatement>> {
|
|
||||||
return statements.mapNotNull {
|
|
||||||
when (it) {
|
|
||||||
is Label -> it.name to it
|
|
||||||
is VarDecl -> it.name to it
|
|
||||||
is Subroutine -> it.name to it
|
|
||||||
is Block -> it.name to it
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lookup(scopedName: List<String>, localContext: Node) : IStatement? {
|
|
||||||
if(scopedName.size>1) {
|
|
||||||
// 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 accept(visitor: IAstModifyingVisitor): IExpression
|
|
||||||
fun accept(visitor: IAstVisitor)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,16 +3,16 @@ package prog8.ast.antlr
|
|||||||
import org.antlr.v4.runtime.IntStream
|
import org.antlr.v4.runtime.IntStream
|
||||||
import org.antlr.v4.runtime.ParserRuleContext
|
import org.antlr.v4.runtime.ParserRuleContext
|
||||||
import org.antlr.v4.runtime.tree.TerminalNode
|
import org.antlr.v4.runtime.tree.TerminalNode
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
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.compiler.target.c64.Petscii
|
||||||
import prog8.parser.CustomLexer
|
import prog8.parser.CustomLexer
|
||||||
import prog8.parser.prog8Parser
|
import prog8.parser.prog8Parser
|
||||||
|
import java.io.CharConversionException
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
/***************** Antlr Extension methods to create AST ****************/
|
/***************** Antlr Extension methods to create AST ****************/
|
||||||
@ -39,7 +39,7 @@ private fun ParserRuleContext.toPosition() : Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : IStatement {
|
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : Statement {
|
||||||
val directive = directive()?.toAst()
|
val directive = directive()?.toAst()
|
||||||
if(directive!=null) return directive
|
if(directive!=null) return directive
|
||||||
|
|
||||||
@ -50,74 +50,104 @@ private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : ISt
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : IStatement =
|
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : Statement =
|
||||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
|
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<IStatement> =
|
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<Statement> =
|
||||||
statement().asSequence().map { it.toAst() }.toMutableList()
|
statement().asSequence().map { it.toAst() }.toMutableList()
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.StatementContext.toAst() : IStatement {
|
private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||||
vardecl()?.let {
|
vardecl()?.let { return it.toAst() }
|
||||||
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 {
|
varinitializer()?.let {
|
||||||
val vd = it.vardecl()
|
val vd = it.vardecl()
|
||||||
return VarDecl(VarDeclType.VAR,
|
return VarDecl(
|
||||||
vd.datatype().toAst(),
|
VarDeclType.VAR,
|
||||||
vd.ZEROPAGE() != null,
|
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||||
|
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
vd.arrayindex()?.toAst(),
|
vd.arrayindex()?.toAst(),
|
||||||
vd.identifier().text,
|
vd.varname.text,
|
||||||
|
null,
|
||||||
it.expression().toAst(),
|
it.expression().toAst(),
|
||||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
it.toPosition())
|
it.toPosition()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
structvarinitializer()?.let {
|
||||||
|
val vd = it.structvardecl()
|
||||||
|
return VarDecl(
|
||||||
|
VarDeclType.VAR,
|
||||||
|
DataType.STRUCT,
|
||||||
|
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||||
|
null,
|
||||||
|
vd.varname.text,
|
||||||
|
vd.structname.text,
|
||||||
|
it.expression().toAst(),
|
||||||
|
isArray = false,
|
||||||
|
autogeneratedDontRemove = false,
|
||||||
|
position = it.toPosition()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
structvardecl()?.let {
|
||||||
|
return VarDecl(
|
||||||
|
VarDeclType.VAR,
|
||||||
|
DataType.STRUCT,
|
||||||
|
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||||
|
null,
|
||||||
|
it.varname.text,
|
||||||
|
it.structname.text,
|
||||||
|
null,
|
||||||
|
isArray = false,
|
||||||
|
autogeneratedDontRemove = false,
|
||||||
|
position = it.toPosition()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constdecl()?.let {
|
constdecl()?.let {
|
||||||
val cvarinit = it.varinitializer()
|
val cvarinit = it.varinitializer()
|
||||||
val vd = cvarinit.vardecl()
|
val vd = cvarinit.vardecl()
|
||||||
return VarDecl(VarDeclType.CONST,
|
return VarDecl(
|
||||||
vd.datatype().toAst(),
|
VarDeclType.CONST,
|
||||||
vd.ZEROPAGE() != null,
|
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||||
|
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
vd.arrayindex()?.toAst(),
|
vd.arrayindex()?.toAst(),
|
||||||
vd.identifier().text,
|
vd.varname.text,
|
||||||
|
null,
|
||||||
cvarinit.expression().toAst(),
|
cvarinit.expression().toAst(),
|
||||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
cvarinit.toPosition())
|
cvarinit.toPosition()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryvardecl()?.let {
|
memoryvardecl()?.let {
|
||||||
val mvarinit = it.varinitializer()
|
val mvarinit = it.varinitializer()
|
||||||
val vd = mvarinit.vardecl()
|
val vd = mvarinit.vardecl()
|
||||||
return VarDecl(VarDeclType.MEMORY,
|
return VarDecl(
|
||||||
vd.datatype().toAst(),
|
VarDeclType.MEMORY,
|
||||||
vd.ZEROPAGE() != null,
|
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||||
|
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
vd.arrayindex()?.toAst(),
|
vd.arrayindex()?.toAst(),
|
||||||
vd.identifier().text,
|
vd.varname.text,
|
||||||
|
null,
|
||||||
mvarinit.expression().toAst(),
|
mvarinit.expression().toAst(),
|
||||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
mvarinit.toPosition())
|
mvarinit.toPosition()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
assignment()?.let {
|
assignment()?.let {
|
||||||
return Assignment(it.assign_targets().toAst(), null, it.expression().toAst(), it.toPosition())
|
return Assignment(it.assign_target().toAst(), null, it.expression().toAst(), it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
augassignment()?.let {
|
augassignment()?.let {
|
||||||
return Assignment(listOf(it.assign_target().toAst()),
|
return Assignment(it.assign_target().toAst(),
|
||||||
it.operator.text,
|
it.operator.text,
|
||||||
it.expression().toAst(),
|
it.expression().toAst(),
|
||||||
it.toPosition())
|
it.toPosition())
|
||||||
@ -175,13 +205,16 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
|||||||
val whenstmt = whenstmt()?.toAst()
|
val whenstmt = whenstmt()?.toAst()
|
||||||
if(whenstmt!=null) return whenstmt
|
if(whenstmt!=null) return whenstmt
|
||||||
|
|
||||||
|
structdecl()?.let {
|
||||||
|
return StructDecl(it.identifier().text,
|
||||||
|
it.vardecl().map { vd->vd.toAst() }.toMutableList(),
|
||||||
|
toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.Assign_targetsContext.toAst(): List<AssignTarget> = assign_target().map { it.toAst() }
|
private fun prog8Parser.AsmsubroutineContext.toAst(): Statement {
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement {
|
|
||||||
val name = identifier().text
|
val name = identifier().text
|
||||||
val address = asmsub_address()?.address?.toAst()?.number?.toInt()
|
val address = asmsub_address()?.address?.toAst()?.number?.toInt()
|
||||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||||
@ -218,15 +251,19 @@ private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
|||||||
|
|
||||||
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||||
= asmsub_param().map {
|
= asmsub_param().map {
|
||||||
AsmSubroutineParameter(it.vardecl().identifier().text, it.vardecl().datatype().toAst(),
|
val vardecl = it.vardecl()
|
||||||
it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition())
|
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
||||||
|
AsmSubroutineParameter(vardecl.varname.text, datatype,
|
||||||
|
it.registerorpair()?.toAst(),
|
||||||
|
it.statusregister()?.toAst(),
|
||||||
|
!it.stack?.text.isNullOrEmpty(), toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): IStatement {
|
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||||
val location = scoped_identifier().toAst()
|
val location = scoped_identifier().toAst()
|
||||||
return if(expression_list() == null)
|
return if(expression_list() == null)
|
||||||
FunctionCallStatement(location, mutableListOf(), toPosition())
|
FunctionCallStatement(location, mutableListOf(), toPosition())
|
||||||
@ -249,8 +286,7 @@ private fun prog8Parser.InlineasmContext.toAst() =
|
|||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ReturnstmtContext.toAst() : Return {
|
private fun prog8Parser.ReturnstmtContext.toAst() : Return {
|
||||||
val values = expression_list()
|
return Return(expression()?.toAst(), toPosition())
|
||||||
return Return(values?.toAst() ?: emptyList(), toPosition())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
|
private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
|
||||||
@ -260,7 +296,7 @@ private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.LabeldefContext.toAst(): IStatement =
|
private fun prog8Parser.LabeldefContext.toAst(): Statement =
|
||||||
Label(children[0].text, toPosition())
|
Label(children[0].text, toPosition())
|
||||||
|
|
||||||
|
|
||||||
@ -285,7 +321,8 @@ private fun prog8Parser.Sub_return_partContext.toAst(): List<DataType> {
|
|||||||
|
|
||||||
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||||
vardecl().map {
|
vardecl().map {
|
||||||
SubroutineParameter(it.identifier().text, it.datatype().toAst(), it.toPosition())
|
val datatype = it.datatype()?.toAst() ?: DataType.STRUCT
|
||||||
|
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -372,30 +409,30 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
||||||
|
|
||||||
val litval = literalvalue()
|
val litval = literalvalue()
|
||||||
if(litval!=null) {
|
if(litval!=null) {
|
||||||
val booleanlit = litval.booleanliteral()?.toAst()
|
val booleanlit = litval.booleanliteral()?.toAst()
|
||||||
return if(booleanlit!=null) {
|
return if(booleanlit!=null) {
|
||||||
LiteralValue.fromBoolean(booleanlit, litval.toPosition())
|
NumericLiteralValue.fromBoolean(booleanlit, litval.toPosition())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val intLit = litval.integerliteral()?.toAst()
|
val intLit = litval.integerliteral()?.toAst()
|
||||||
when {
|
when {
|
||||||
intLit!=null -> when(intLit.datatype) {
|
intLit!=null -> when(intLit.datatype) {
|
||||||
DataType.UBYTE -> LiteralValue(DataType.UBYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
|
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, intLit.number.toShort(), litval.toPosition())
|
||||||
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
|
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, intLit.number.toShort(), litval.toPosition())
|
||||||
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
|
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, intLit.number.toInt(), litval.toPosition())
|
||||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
|
DataType.WORD -> NumericLiteralValue(DataType.WORD, intLit.number.toInt(), litval.toPosition())
|
||||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = intLit.number.toDouble(), position = litval.toPosition())
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, intLit.number.toDouble(), litval.toPosition())
|
||||||
else -> throw FatalAstException("invalid datatype for numeric literal")
|
else -> throw FatalAstException("invalid datatype for numeric literal")
|
||||||
}
|
}
|
||||||
litval.floatliteral()!=null -> LiteralValue(DataType.FLOAT, floatvalue = litval.floatliteral().toAst(), position = litval.toPosition())
|
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
|
||||||
litval.stringliteral()!=null -> LiteralValue(DataType.STR, strvalue = unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
|
litval.stringliteral()!=null -> ReferenceLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
|
||||||
litval.charliteral()!=null -> {
|
litval.charliteral()!=null -> {
|
||||||
try {
|
try {
|
||||||
LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], position = litval.toPosition())
|
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
|
||||||
} catch (ce: CharConversionException) {
|
} catch (ce: CharConversionException) {
|
||||||
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
|
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
|
||||||
}
|
}
|
||||||
@ -404,7 +441,11 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
|||||||
val array = litval.arrayliteral()?.toAst()
|
val array = litval.arrayliteral()?.toAst()
|
||||||
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
|
// 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.
|
// the ConstantFolder takes care of that and converts the type if needed.
|
||||||
LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
|
ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition())
|
||||||
|
}
|
||||||
|
litval.structliteral()!=null -> {
|
||||||
|
val values = litval.structliteral().expression().map { it.toAst() }
|
||||||
|
StructLiteralValue(values, litval.toPosition())
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("invalid parsed literal")
|
else -> throw FatalAstException("invalid parsed literal")
|
||||||
}
|
}
|
||||||
@ -427,7 +468,7 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
|||||||
if(funcall!=null) return funcall
|
if(funcall!=null) return funcall
|
||||||
|
|
||||||
if (rangefrom!=null && rangeto!=null) {
|
if (rangefrom!=null && rangeto!=null) {
|
||||||
val step = rangestep?.toAst() ?: LiteralValue(DataType.UBYTE, 1, position = toPosition())
|
val step = rangestep?.toAst() ?: NumericLiteralValue(DataType.UBYTE, 1, toPosition())
|
||||||
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,10 +519,9 @@ private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayliteralContext.toAst() : Array<IExpression> =
|
private fun prog8Parser.ArrayliteralContext.toAst() : Array<Expression> =
|
||||||
expression().map { it.toAst() }.toTypedArray()
|
expression().map { it.toAst() }.toTypedArray()
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
||||||
val condition = expression().toAst()
|
val condition = expression().toAst()
|
||||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
@ -492,7 +532,7 @@ private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
|||||||
return IfStatement(condition, trueScope, elseScope, toPosition())
|
return IfStatement(condition, trueScope, elseScope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.Else_partContext.toAst(): MutableList<IStatement> {
|
private fun prog8Parser.Else_partContext.toAst(): MutableList<Statement> {
|
||||||
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +553,7 @@ private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf
|
|||||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||||
val loopregister = register()?.toAst()
|
val loopregister = register()?.toAst()
|
||||||
val datatype = datatype()?.toAst()
|
val datatype = datatype()?.toAst()
|
||||||
val zeropage = ZEROPAGE()!=null
|
val zeropage = if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE
|
||||||
val loopvar = identifier()?.toAst()
|
val loopvar = identifier()?.toAst()
|
||||||
val iterable = expression()!!.toAst()
|
val iterable = expression()!!.toAst()
|
||||||
val scope =
|
val scope =
|
||||||
@ -554,13 +594,28 @@ private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
|
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
|
||||||
val value = expression()?.toAst()
|
val values = expression_list()?.toAst()
|
||||||
val stmt = statement()?.toAst()
|
val stmt = statement()?.toAst()
|
||||||
val stmt_block = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
|
val stmt_block = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
|
||||||
if(stmt!=null)
|
if(stmt!=null)
|
||||||
stmt_block.add(stmt)
|
stmt_block.add(stmt)
|
||||||
val scope = AnonymousScope(stmt_block, toPosition())
|
val scope = AnonymousScope(stmt_block, toPosition())
|
||||||
return WhenChoice(value, scope, toPosition())
|
return WhenChoice(values, scope, toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prog8Parser.VardeclContext.toAst(): VarDecl {
|
||||||
|
return VarDecl(
|
||||||
|
VarDeclType.VAR,
|
||||||
|
datatype()?.toAst() ?: DataType.STRUCT,
|
||||||
|
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
|
arrayindex()?.toAst(),
|
||||||
|
varname.text,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ARRAYSIG() != null || arrayindex() != null,
|
||||||
|
false,
|
||||||
|
toPosition()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
||||||
@ -588,3 +643,4 @@ internal fun unescape(str: String, position: Position): String {
|
|||||||
}
|
}
|
||||||
return result.joinToString("")
|
return result.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,18 +5,19 @@ import prog8.ast.Node
|
|||||||
/**************************** AST Data classes ****************************/
|
/**************************** AST Data classes ****************************/
|
||||||
|
|
||||||
enum class DataType {
|
enum class DataType {
|
||||||
UBYTE,
|
UBYTE, // pass by value
|
||||||
BYTE,
|
BYTE, // pass by value
|
||||||
UWORD,
|
UWORD, // pass by value
|
||||||
WORD,
|
WORD, // pass by value
|
||||||
FLOAT,
|
FLOAT, // pass by value
|
||||||
STR,
|
STR, // pass by reference
|
||||||
STR_S,
|
STR_S, // pass by reference
|
||||||
ARRAY_UB,
|
ARRAY_UB, // pass by reference
|
||||||
ARRAY_B,
|
ARRAY_B, // pass by reference
|
||||||
ARRAY_UW,
|
ARRAY_UW, // pass by reference
|
||||||
ARRAY_W,
|
ARRAY_W, // pass by reference
|
||||||
ARRAY_F;
|
ARRAY_F, // pass by reference
|
||||||
|
STRUCT; // pass by reference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is the type assignable to the given other type?
|
* is the type assignable to the given other type?
|
||||||
@ -24,26 +25,33 @@ enum class DataType {
|
|||||||
infix fun isAssignableTo(targetType: DataType) =
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
// what types are assignable to others without loss of precision?
|
// what types are assignable to others without loss of precision?
|
||||||
when(this) {
|
when(this) {
|
||||||
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
UBYTE -> targetType in setOf(UBYTE, UWORD, WORD, FLOAT)
|
||||||
BYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
BYTE -> targetType in setOf(BYTE, UBYTE, UWORD, WORD, FLOAT)
|
||||||
UWORD -> targetType == UWORD || targetType == FLOAT
|
UWORD -> targetType in setOf(UWORD, FLOAT)
|
||||||
WORD -> targetType == WORD || targetType==UWORD || targetType == FLOAT
|
WORD -> targetType in setOf(WORD, UWORD, FLOAT)
|
||||||
FLOAT -> targetType == FLOAT
|
FLOAT -> targetType == FLOAT
|
||||||
STR -> targetType == STR || targetType==STR_S
|
STR -> targetType == STR || targetType==STR_S
|
||||||
STR_S -> targetType == STR || targetType==STR_S
|
STR_S -> targetType == STR || targetType==STR_S
|
||||||
in ArrayDatatypes -> targetType === this
|
in ArrayDatatypes -> targetType == this
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
||||||
|
|
||||||
infix fun biggerThan(other: DataType) =
|
infix fun largerThan(other: DataType) =
|
||||||
when(this) {
|
when(this) {
|
||||||
in ByteDatatypes -> false
|
in ByteDatatypes -> false
|
||||||
in WordDatatypes -> other in ByteDatatypes
|
in WordDatatypes -> other in ByteDatatypes
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infix fun equalsSize(other: DataType) =
|
||||||
|
when(this) {
|
||||||
|
in ByteDatatypes -> other in ByteDatatypes
|
||||||
|
in WordDatatypes -> other in WordDatatypes
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Register {
|
enum class Register {
|
||||||
@ -89,17 +97,19 @@ enum class VarDeclType {
|
|||||||
MEMORY
|
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 ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
|
||||||
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
|
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
|
||||||
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, 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 NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||||
val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
|
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 ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
||||||
|
val IterableDatatypes = setOf(
|
||||||
|
DataType.STR, DataType.STR_S,
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||||
|
DataType.ARRAY_F)
|
||||||
|
val PassByValueDatatypes = NumericDatatypes
|
||||||
|
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
|
||||||
val ArrayElementTypes = mapOf(
|
val ArrayElementTypes = mapOf(
|
||||||
DataType.ARRAY_B to DataType.BYTE,
|
DataType.ARRAY_B to DataType.BYTE,
|
||||||
DataType.ARRAY_UB to DataType.UBYTE,
|
DataType.ARRAY_UB to DataType.UBYTE,
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package prog8.ast.base
|
package prog8.ast.base
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.ForLoop
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
|
||||||
|
|
||||||
|
|
||||||
// the name of the subroutine that should be called for every block to initialize its variables
|
// the name of the subroutine that should be called for every block to initialize its variables
|
||||||
@ -16,6 +15,12 @@ internal const val initvarsSubName="prog8_init_vars"
|
|||||||
internal const val autoHeapValuePrefix = "auto_heap_value_"
|
internal const val autoHeapValuePrefix = "auto_heap_value_"
|
||||||
|
|
||||||
|
|
||||||
|
internal fun Program.removeNopsFlattenAnonScopes() {
|
||||||
|
val flattener = FlattenAnonymousScopesAndRemoveNops()
|
||||||
|
flattener.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
||||||
val checker = AstChecker(this, compilerOptions)
|
val checker = AstChecker(this, compilerOptions)
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
@ -24,7 +29,7 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
|||||||
|
|
||||||
|
|
||||||
internal fun Program.reorderStatements() {
|
internal fun Program.reorderStatements() {
|
||||||
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
|
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace, heap)
|
||||||
initvalueCreator.visit(this)
|
initvalueCreator.visit(this)
|
||||||
|
|
||||||
val checker = StatementReorderer(this)
|
val checker = StatementReorderer(this)
|
||||||
@ -45,40 +50,12 @@ internal fun Program.checkRecursion() {
|
|||||||
|
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers() {
|
internal fun Program.checkIdentifiers() {
|
||||||
val checker = AstIdentifiersChecker(namespace)
|
val checker = AstIdentifiersChecker(this)
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
|
|
||||||
if(modules.map {it.name}.toSet().size != modules.size) {
|
if(modules.map {it.name}.toSet().size != modules.size) {
|
||||||
throw FatalAstException("modules should all be unique")
|
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)
|
printErrors(checker.result(), name)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
package prog8.ast.expressions
|
package prog8.ast.expressions
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.ArrayIndex
|
||||||
|
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.IntegerOrAddressOf
|
import prog8.compiler.IntegerOrAddressOf
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
@ -12,13 +19,43 @@ import prog8.functions.BuiltinFunctions
|
|||||||
import prog8.functions.NotConstArgumentException
|
import prog8.functions.NotConstArgumentException
|
||||||
import prog8.functions.builtinFunctionReturnType
|
import prog8.functions.builtinFunctionReturnType
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.floor
|
|
||||||
|
|
||||||
|
|
||||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
|
|
||||||
|
|
||||||
class PrefixExpression(val operator: String, var expression: IExpression, override val position: Position) : IExpression {
|
sealed class Expression: Node {
|
||||||
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
|
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
||||||
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
|
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this here and move it into CallGraph instead
|
||||||
|
abstract fun inferType(program: Program): DataType?
|
||||||
|
|
||||||
|
infix fun isSameAs(other: Expression): Boolean {
|
||||||
|
if(this===other)
|
||||||
|
return true
|
||||||
|
when(this) {
|
||||||
|
is RegisterExpr ->
|
||||||
|
return (other is RegisterExpr && other.register==register)
|
||||||
|
is IdentifierReference ->
|
||||||
|
return (other is IdentifierReference && other.nameInSource==nameInSource)
|
||||||
|
is PrefixExpression ->
|
||||||
|
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
||||||
|
is BinaryExpression ->
|
||||||
|
return (other is BinaryExpression && other.operator==operator
|
||||||
|
&& other.left isSameAs left
|
||||||
|
&& other.right isSameAs right)
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||||
|
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||||
|
}
|
||||||
|
else -> return other==this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixExpression(val operator: String, var expression: Expression, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -26,10 +63,10 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
|
|||||||
expression.linkParents(this)
|
expression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): LiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? = expression.inferType(program)
|
override fun inferType(program: Program): DataType? = expression.inferType(program)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -37,7 +74,7 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BinaryExpression(var left: IExpression, var operator: String, var right: IExpression, override val position: Position) : IExpression {
|
class BinaryExpression(var left: Expression, var operator: String, var right: Expression, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -51,11 +88,11 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
|||||||
}
|
}
|
||||||
|
|
||||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
// 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 constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
|
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): DataType? {
|
||||||
val leftDt = left.inferType(program)
|
val leftDt = left.inferType(program)
|
||||||
val rightDt = right.inferType(program)
|
val rightDt = right.inferType(program)
|
||||||
@ -148,7 +185,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun commonDatatype(leftDt: DataType, rightDt: DataType,
|
fun commonDatatype(leftDt: DataType, rightDt: DataType,
|
||||||
left: IExpression, right: IExpression): Pair<DataType, IExpression?> {
|
left: Expression, right: Expression): Pair<DataType, Expression?> {
|
||||||
// byte + byte -> byte
|
// byte + byte -> byte
|
||||||
// byte + word -> word
|
// byte + word -> word
|
||||||
// word + byte -> word
|
// word + byte -> word
|
||||||
@ -169,7 +206,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
|||||||
DataType.UWORD -> Pair(DataType.UWORD, left)
|
DataType.UWORD -> Pair(DataType.UWORD, left)
|
||||||
DataType.WORD -> Pair(DataType.WORD, left)
|
DataType.WORD -> Pair(DataType.WORD, left)
|
||||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
else -> Pair(leftDt, null) // non-numeric datatype
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
@ -179,7 +216,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
|||||||
DataType.UWORD -> Pair(DataType.WORD, left)
|
DataType.UWORD -> Pair(DataType.WORD, left)
|
||||||
DataType.WORD -> Pair(DataType.WORD, left)
|
DataType.WORD -> Pair(DataType.WORD, left)
|
||||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
else -> Pair(leftDt, null) // non-numeric datatype
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -189,7 +226,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
|||||||
DataType.UWORD -> Pair(DataType.UWORD, null)
|
DataType.UWORD -> Pair(DataType.UWORD, null)
|
||||||
DataType.WORD -> Pair(DataType.WORD, left)
|
DataType.WORD -> Pair(DataType.WORD, left)
|
||||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
else -> Pair(leftDt, null) // non-numeric datatype
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
@ -199,20 +236,20 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
|||||||
DataType.UWORD -> Pair(DataType.WORD, right)
|
DataType.UWORD -> Pair(DataType.WORD, right)
|
||||||
DataType.WORD -> Pair(DataType.WORD, null)
|
DataType.WORD -> Pair(DataType.WORD, null)
|
||||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
else -> Pair(leftDt, null) // non-numeric datatype
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
Pair(DataType.FLOAT, right)
|
Pair(DataType.FLOAT, right)
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("non-numeric datatype $leftDt")
|
else -> Pair(leftDt, null) // non-numeric datatype
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayIndexedExpression(val identifier: IdentifierReference,
|
class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||||
var arrayspec: ArrayIndex,
|
var arrayspec: ArrayIndex,
|
||||||
override val position: Position) : IExpression {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -220,10 +257,10 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
|
|||||||
arrayspec.linkParents(this)
|
arrayspec.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): LiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
|
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
|
||||||
|
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): DataType? {
|
||||||
val target = identifier.targetStatement(program.namespace)
|
val target = identifier.targetStatement(program.namespace)
|
||||||
@ -231,11 +268,7 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
|
|||||||
return when (target.datatype) {
|
return when (target.datatype) {
|
||||||
in NumericDatatypes -> null
|
in NumericDatatypes -> null
|
||||||
in StringDatatypes -> DataType.UBYTE
|
in StringDatatypes -> DataType.UBYTE
|
||||||
DataType.ARRAY_UB -> DataType.UBYTE
|
in ArrayDatatypes -> ArrayElementTypes[target.datatype]
|
||||||
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")
|
else -> throw FatalAstException("invalid dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +280,7 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TypecastExpression(var expression: IExpression, var type: DataType, val implicit: Boolean, override val position: Position) : IExpression {
|
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -257,9 +290,9 @@ class TypecastExpression(var expression: IExpression, var type: DataType, val im
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? = type
|
override fun inferType(program: Program): DataType? = type
|
||||||
override fun constValue(program: Program): LiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val cv = expression.constValue(program) ?: return null
|
val cv = expression.constValue(program) ?: return null
|
||||||
return cv.cast(type)
|
return cv.cast(type)
|
||||||
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
||||||
@ -271,7 +304,7 @@ class TypecastExpression(var expression: IExpression, var type: DataType, val im
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AddressOf(val identifier: IdentifierReference, override val position: Position) : IExpression {
|
data class AddressOf(val identifier: IdentifierReference, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -280,14 +313,14 @@ data class AddressOf(val identifier: IdentifierReference, override val position:
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scopedname: String? = null // will be set in a later state by the compiler
|
var scopedname: String? = null // will be set in a later state by the compiler
|
||||||
override fun constValue(program: Program): LiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun referencesIdentifier(name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program) = DataType.UWORD
|
override fun inferType(program: Program) = DataType.UWORD
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DirectMemoryRead(var addressExpression: IExpression, override val position: Position) : IExpression {
|
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -297,165 +330,218 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program): DataType? = DataType.UBYTE
|
override fun inferType(program: Program): DataType? = DataType.UBYTE
|
||||||
override fun constValue(program: Program): LiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "DirectMemoryRead($addressExpression)"
|
return "DirectMemoryRead($addressExpression)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class LiteralValue(val type: DataType,
|
class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||||
val bytevalue: Short? = null,
|
val number: Number, // can be byte, word or float depending on the type
|
||||||
val wordvalue: Int? = null,
|
override val position: Position) : Expression() {
|
||||||
val floatvalue: Double? = null,
|
|
||||||
val strvalue: String? = null,
|
|
||||||
val arrayvalue: Array<IExpression>? = null,
|
|
||||||
initHeapId: Int? =null,
|
|
||||||
override val position: Position) : IExpression {
|
|
||||||
override lateinit var parent: Node
|
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 {
|
companion object {
|
||||||
fun fromBoolean(bool: Boolean, position: Position) =
|
fun fromBoolean(bool: Boolean, position: Position) =
|
||||||
LiteralValue(DataType.UBYTE, bytevalue = if (bool) 1 else 0, position = position)
|
NumericLiteralValue(DataType.UBYTE, if (bool) 1 else 0, position)
|
||||||
|
|
||||||
fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
|
fun optimalNumeric(value: Number, position: Position): NumericLiteralValue {
|
||||||
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) {
|
return if(value is Double) {
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = value, position = position)
|
NumericLiteralValue(DataType.FLOAT, value, position)
|
||||||
} else {
|
} else {
|
||||||
when (val intval = value.toInt()) {
|
when (val intval = value.toInt()) {
|
||||||
in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = intval.toShort(), position = position)
|
in 0..255 -> NumericLiteralValue(DataType.UBYTE, intval, position)
|
||||||
in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = intval.toShort(), position = position)
|
in -128..127 -> NumericLiteralValue(DataType.BYTE, intval, position)
|
||||||
in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = intval, position = position)
|
in 0..65535 -> NumericLiteralValue(DataType.UWORD, intval, position)
|
||||||
in -32768..32767 -> LiteralValue(DataType.WORD, wordvalue = intval, position = position)
|
in -32768..32767 -> NumericLiteralValue(DataType.WORD, intval, position)
|
||||||
else -> LiteralValue(DataType.FLOAT, floatvalue = intval.toDouble(), position = position)
|
else -> NumericLiteralValue(DataType.FLOAT, intval.toDouble(), position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optimalInteger(value: Number, position: Position): LiteralValue {
|
fun optimalInteger(value: Int, position: Position): NumericLiteralValue {
|
||||||
val intval = value.toInt()
|
return when (value) {
|
||||||
if(intval.toDouble() != value.toDouble())
|
in 0..255 -> NumericLiteralValue(DataType.UBYTE, value, position)
|
||||||
throw FatalAstException("value is not an integer: $value")
|
in -128..127 -> NumericLiteralValue(DataType.BYTE, value, position)
|
||||||
return when (intval) {
|
in 0..65535 -> NumericLiteralValue(DataType.UWORD, value, position)
|
||||||
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")
|
else -> throw FatalAstException("integer overflow: $value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
val asBooleanValue: Boolean = number!=0
|
||||||
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) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
arrayvalue?.forEach {it.linkParents(this)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): LiteralValue? {
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
if(arrayvalue!=null) {
|
override fun constValue(program: Program) = this
|
||||||
for(v in arrayvalue) {
|
|
||||||
if(v.constValue(program)==null) return null
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
}
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
|
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
|
||||||
|
|
||||||
|
override fun inferType(program: Program) = type
|
||||||
|
|
||||||
|
override fun hashCode(): Int = type.hashCode() * 31 xor number.hashCode()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is NumericLiteralValue)
|
||||||
|
return false
|
||||||
|
return number.toDouble()==other.number.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
||||||
|
|
||||||
|
fun cast(targettype: DataType): NumericLiteralValue? {
|
||||||
|
if(type==targettype)
|
||||||
return this
|
return this
|
||||||
|
val numval = number.toDouble()
|
||||||
|
when(type) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if(targettype== DataType.BYTE && numval <= 127)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if(targettype== DataType.WORD || targettype== DataType.UWORD)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
if(targettype== DataType.FLOAT)
|
||||||
|
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(targettype== DataType.UBYTE && numval >= 0)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if(targettype== DataType.UWORD && numval >= 0)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
if(targettype== DataType.WORD)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
if(targettype== DataType.FLOAT)
|
||||||
|
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(targettype== DataType.BYTE && numval <= 127)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if(targettype== DataType.UBYTE && numval <= 255)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if(targettype== DataType.WORD && numval <= 32767)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
if(targettype== DataType.FLOAT)
|
||||||
|
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(targettype== DataType.BYTE && numval >= -128 && numval <=127)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if(targettype== DataType.UBYTE && numval >= 0 && numval <= 255)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if(targettype== DataType.UWORD && numval >=0)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
if(targettype== DataType.FLOAT)
|
||||||
|
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
if (targettype == DataType.BYTE && numval >= -128 && numval <=127)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if (targettype == DataType.UBYTE && numval >=0 && numval <= 255)
|
||||||
|
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||||
|
if (targettype == DataType.WORD && numval >= -32768 && numval <= 32767)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
if (targettype == DataType.UWORD && numval >=0 && numval <= 65535)
|
||||||
|
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return null // invalid type conversion from $this to $targettype
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StructLiteralValue(var values: List<Expression>,
|
||||||
|
override val position: Position): Expression() {
|
||||||
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
this.parent=parent
|
||||||
|
values.forEach { it.linkParents(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
||||||
|
override fun inferType(program: Program) = DataType.STRUCT
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "struct{ ${values.joinToString(", ")} }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReferenceLiteralValue(val type: DataType, // only reference types allowed here
|
||||||
|
val str: String? = null,
|
||||||
|
val array: Array<Expression>? = null,
|
||||||
|
// actually, at the moment, we don't have struct literals in the language
|
||||||
|
initHeapId: Int? =null,
|
||||||
|
override val position: Position) : Expression() {
|
||||||
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
override fun referencesIdentifiers(vararg name: String) = array?.any { it.referencesIdentifiers(*name) } ?: false
|
||||||
|
|
||||||
|
val isString = type in StringDatatypes
|
||||||
|
val isArray = type in ArrayDatatypes
|
||||||
|
var heapId = initHeapId
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
when(type){
|
||||||
|
in StringDatatypes ->
|
||||||
|
if(str==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
|
||||||
|
in ArrayDatatypes ->
|
||||||
|
if(array==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
|
||||||
|
else -> throw FatalAstException("invalid type $type")
|
||||||
|
}
|
||||||
|
if(array==null && str==null && heapId==null)
|
||||||
|
throw FatalAstException("literal ref value without actual value")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
this.parent = parent
|
||||||
|
array?.forEach {it.linkParents(this)}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
|
// note that we can't handle arrays that only contain constant numbers here anymore
|
||||||
|
// so they're not treated as constants anymore
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val vstr = when(type) {
|
val valueStr = when(type) {
|
||||||
DataType.UBYTE -> "ubyte:$bytevalue"
|
in StringDatatypes -> "'${escape(str!!)}'"
|
||||||
DataType.BYTE -> "byte:$bytevalue"
|
in ArrayDatatypes -> "$array"
|
||||||
DataType.UWORD -> "uword:$wordvalue"
|
else -> throw FatalAstException("weird ref type")
|
||||||
DataType.WORD -> "word:$wordvalue"
|
|
||||||
DataType.FLOAT -> "float:$floatvalue"
|
|
||||||
in StringDatatypes -> "str:$strvalue"
|
|
||||||
in ArrayDatatypes -> "array:$arrayvalue"
|
|
||||||
else -> throw FatalAstException("weird datatype")
|
|
||||||
}
|
}
|
||||||
return "LiteralValue($vstr)"
|
return "ReferenceValueLiteral($type, $valueStr)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inferType(program: Program) = type
|
override fun inferType(program: Program) = type
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
val bh = bytevalue?.hashCode() ?: 0x10001234
|
val sh = str?.hashCode() ?: 0x00014567
|
||||||
val wh = wordvalue?.hashCode() ?: 0x01002345
|
val ah = array?.hashCode() ?: 0x11119876
|
||||||
val fh = floatvalue?.hashCode() ?: 0x00103456
|
return sh * 31 xor ah xor type.hashCode()
|
||||||
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is LiteralValue)
|
if(other==null || other !is ReferenceLiteralValue)
|
||||||
return false
|
return false
|
||||||
if(isNumeric && other.isNumeric)
|
|
||||||
return asNumericValue?.toDouble()==other.asNumericValue?.toDouble()
|
|
||||||
if(isArray && other.isArray)
|
if(isArray && other.isArray)
|
||||||
return arrayvalue!!.contentEquals(other.arrayvalue!!) && heapId==other.heapId
|
return array!!.contentEquals(other.array!!) && heapId==other.heapId
|
||||||
if(isString && other.isString)
|
if(isString && other.isString)
|
||||||
return strvalue==other.strvalue && heapId==other.heapId
|
return str==other.str && heapId==other.heapId
|
||||||
|
|
||||||
if(type!=other.type)
|
if(type!=other.type)
|
||||||
return false
|
return false
|
||||||
@ -463,76 +549,17 @@ open class LiteralValue(val type: DataType,
|
|||||||
return compareTo(other) == 0
|
return compareTo(other) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: LiteralValue): Int {
|
operator fun compareTo(other: ReferenceLiteralValue): 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)
|
throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cast(targettype: DataType): LiteralValue? {
|
fun cast(targettype: DataType): ReferenceLiteralValue? {
|
||||||
if(type==targettype)
|
if(type==targettype)
|
||||||
return this
|
return this
|
||||||
when(type) {
|
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 -> {
|
in StringDatatypes -> {
|
||||||
if(targettype in StringDatatypes)
|
if(targettype in StringDatatypes)
|
||||||
return this
|
return ReferenceLiteralValue(targettype, str, initHeapId = heapId, position = position)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@ -540,22 +567,22 @@ open class LiteralValue(val type: DataType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addToHeap(heap: HeapValues) {
|
fun addToHeap(heap: HeapValues) {
|
||||||
if(heapId==null) {
|
if (heapId != null) return
|
||||||
if (strvalue != null) {
|
if (str != null) {
|
||||||
heapId = heap.addString(type, strvalue)
|
heapId = heap.addString(type, str)
|
||||||
}
|
}
|
||||||
else if (arrayvalue!=null) {
|
else if (array!=null) {
|
||||||
if(arrayvalue.any {it is AddressOf }) {
|
if(array.any {it is AddressOf }) {
|
||||||
val intArrayWithAddressOfs = arrayvalue.map {
|
val intArrayWithAddressOfs = array.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is AddressOf -> IntegerOrAddressOf(null, it)
|
is AddressOf -> IntegerOrAddressOf(null, it)
|
||||||
is LiteralValue -> IntegerOrAddressOf(it.asIntegerValue, null)
|
is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null)
|
||||||
else -> throw FatalAstException("invalid datatype in array")
|
else -> throw FatalAstException("invalid datatype in array")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
|
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
|
||||||
} else {
|
} else {
|
||||||
val valuesInArray = arrayvalue.map { (it as LiteralValue).asNumericValue!! }
|
val valuesInArray = array.map { (it as NumericLiteralValue).number }
|
||||||
heapId = if(type== DataType.ARRAY_F) {
|
heapId = if(type== DataType.ARRAY_F) {
|
||||||
val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
|
val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
|
||||||
heap.addDoublesArray(doubleArray)
|
heap.addDoublesArray(doubleArray)
|
||||||
@ -566,13 +593,12 @@ open class LiteralValue(val type: DataType,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RangeExpr(var from: IExpression,
|
class RangeExpr(var from: Expression,
|
||||||
var to: IExpression,
|
var to: Expression,
|
||||||
var step: IExpression,
|
var step: Expression,
|
||||||
override val position: Position) : IExpression {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -582,10 +608,10 @@ class RangeExpr(var from: IExpression,
|
|||||||
step.linkParents(this)
|
step.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): LiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
|
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): DataType? {
|
||||||
val fromDt=from.inferType(program)
|
val fromDt=from.inferType(program)
|
||||||
val toDt=to.inferType(program)
|
val toDt=to.inferType(program)
|
||||||
@ -605,30 +631,32 @@ class RangeExpr(var from: IExpression,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun size(): Int? {
|
fun size(): Int? {
|
||||||
val fromLv = (from as? LiteralValue)
|
val fromLv = (from as? NumericLiteralValue)
|
||||||
val toLv = (to as? LiteralValue)
|
val toLv = (to as? NumericLiteralValue)
|
||||||
if(fromLv==null || toLv==null)
|
if(fromLv==null || toLv==null)
|
||||||
return null
|
return null
|
||||||
return toConstantIntegerRange()?.count()
|
return toConstantIntegerRange()?.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toConstantIntegerRange(): IntProgression? {
|
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 fromVal: Int
|
||||||
val toVal: Int
|
val toVal: Int
|
||||||
if(fromLv.isString && toLv.isString) {
|
val fromRlv = from as? ReferenceLiteralValue
|
||||||
|
val toRlv = to as? ReferenceLiteralValue
|
||||||
|
if(fromRlv!=null && fromRlv.isString && toRlv!=null && toRlv.isString) {
|
||||||
// string range -> int range over petscii values
|
// string range -> int range over petscii values
|
||||||
fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt()
|
fromVal = Petscii.encodePetscii(fromRlv.str!!, true)[0].toInt()
|
||||||
toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt()
|
toVal = Petscii.encodePetscii(toRlv.str!!, true)[0].toInt()
|
||||||
} else {
|
} else {
|
||||||
|
val fromLv = from as? NumericLiteralValue
|
||||||
|
val toLv = to as? NumericLiteralValue
|
||||||
|
if(fromLv==null || toLv==null)
|
||||||
|
return null // non-constant range
|
||||||
// integer range
|
// integer range
|
||||||
fromVal = (from as LiteralValue).asIntegerValue!!
|
fromVal = fromLv.number.toInt()
|
||||||
toVal = (to as LiteralValue).asIntegerValue!!
|
toVal = toLv.number.toInt()
|
||||||
}
|
}
|
||||||
val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1
|
val stepVal = (step as? NumericLiteralValue)?.number?.toInt() ?: 1
|
||||||
return when {
|
return when {
|
||||||
fromVal <= toVal -> when {
|
fromVal <= toVal -> when {
|
||||||
stepVal <= 0 -> IntRange.EMPTY
|
stepVal <= 0 -> IntRange.EMPTY
|
||||||
@ -644,17 +672,17 @@ class RangeExpr(var from: IExpression,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterExpr(val register: Register, override val position: Position) : IExpression {
|
class RegisterExpr(val register: Register, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): LiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String): Boolean = false
|
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "RegisterExpr(register=$register, pos=$position)"
|
return "RegisterExpr(register=$register, pos=$position)"
|
||||||
}
|
}
|
||||||
@ -662,7 +690,7 @@ class RegisterExpr(val register: Register, override val position: Position) : IE
|
|||||||
override fun inferType(program: Program) = DataType.UBYTE
|
override fun inferType(program: Program) = DataType.UBYTE
|
||||||
}
|
}
|
||||||
|
|
||||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : IExpression {
|
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
fun targetStatement(namespace: INameScope) =
|
fun targetStatement(namespace: INameScope) =
|
||||||
@ -678,7 +706,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): LiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val node = program.namespace.lookup(nameInSource, this)
|
val node = program.namespace.lookup(nameInSource, this)
|
||||||
?: throw UndefinedSymbolError(this)
|
?: throw UndefinedSymbolError(this)
|
||||||
val vardecl = node as? VarDecl
|
val vardecl = node as? VarDecl
|
||||||
@ -696,7 +724,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
|
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name // @todo is this correct all the time?
|
||||||
|
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): DataType? {
|
||||||
val targetStmt = targetStatement(program.namespace)
|
val targetStmt = targetStatement(program.namespace)
|
||||||
@ -709,13 +737,18 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
|
|
||||||
fun heapId(namespace: INameScope): Int {
|
fun heapId(namespace: INameScope): Int {
|
||||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
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")
|
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
||||||
|
return when (value) {
|
||||||
|
is IdentifierReference -> value.heapId(namespace)
|
||||||
|
is ReferenceLiteralValue -> value.heapId ?: throw FatalAstException("refLv is not on the heap: $value")
|
||||||
|
else -> throw FatalAstException("requires a reference value")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCall(override var target: IdentifierReference,
|
class FunctionCall(override var target: IdentifierReference,
|
||||||
override var arglist: MutableList<IExpression>,
|
override var arglist: MutableList<Expression>,
|
||||||
override val position: Position) : IExpression, IFunctionCall {
|
override val position: Position) : Expression(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -726,12 +759,12 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
|
|
||||||
override fun constValue(program: Program) = constValue(program, true)
|
override fun constValue(program: Program) = constValue(program, true)
|
||||||
|
|
||||||
private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
|
private fun constValue(program: Program, withDatatypeCheck: Boolean): NumericLiteralValue? {
|
||||||
// if the function is a built-in function and the args are consts, should try to const-evaluate!
|
// 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!
|
// lenghts of arrays and strings are constants that are determined at compile time!
|
||||||
if(target.nameInSource.size>1) return null
|
if(target.nameInSource.size>1) return null
|
||||||
try {
|
try {
|
||||||
var resultValue: LiteralValue? = null
|
var resultValue: NumericLiteralValue? = null
|
||||||
val func = BuiltinFunctions[target.nameInSource[0]]
|
val func = BuiltinFunctions[target.nameInSource[0]]
|
||||||
if(func!=null) {
|
if(func!=null) {
|
||||||
val exprfunc = func.constExpressionFunc
|
val exprfunc = func.constExpressionFunc
|
||||||
@ -762,7 +795,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)}
|
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || arglist.any{it.referencesIdentifiers(*name)}
|
||||||
|
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): DataType? {
|
||||||
val constVal = constValue(program ,false)
|
val constVal = constValue(program ,false)
|
||||||
@ -784,8 +817,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
return stmt.returntypes[0]
|
return stmt.returntypes[0]
|
||||||
return null // has multiple return types... so not a single resulting datatype possible
|
return null // has multiple return types... so not a single resulting datatype possible
|
||||||
}
|
}
|
||||||
is Label -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
return null // calling something we don't recognise...
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +1,41 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.autoHeapValuePrefix
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstModifyingVisitor {
|
internal class AstIdentifiersChecker(private val program: Program) : IAstModifyingVisitor {
|
||||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||||
|
|
||||||
private var blocks: MutableMap<String, Block> = mutableMapOf()
|
private var blocks = mutableMapOf<String, Block>()
|
||||||
|
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||||
|
|
||||||
internal fun result(): List<AstException> {
|
internal fun result(): List<AstException> {
|
||||||
return checkResult
|
return checkResult
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: IStatement) {
|
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||||
checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
|
checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
|
vardeclsToAdd.clear()
|
||||||
blocks.clear() // blocks may be redefined within a different module
|
blocks.clear() // blocks may be redefined within a different module
|
||||||
super.visit(module)
|
super.visit(module)
|
||||||
|
// add any new vardecls to the various scopes
|
||||||
|
for((where, decls) in vardeclsToAdd) {
|
||||||
|
where.statements.addAll(0, decls)
|
||||||
|
decls.forEach { it.linkParents(where as Node) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block): IStatement {
|
override fun visit(block: Block): Statement {
|
||||||
val existing = blocks[block.name]
|
val existing = blocks[block.name]
|
||||||
if(existing!=null)
|
if(existing!=null)
|
||||||
nameError(block.name, block.position, existing)
|
nameError(block.name, block.position, existing)
|
||||||
@ -36,7 +45,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
return super.visit(block)
|
return super.visit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): IExpression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||||
val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, false, functionCall.position)
|
val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, false, functionCall.position)
|
||||||
@ -46,7 +55,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
return super.visit(functionCall)
|
return super.visit(functionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): IStatement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
// first, check if there are datatype errors on the vardecl
|
// first, check if there are datatype errors on the vardecl
|
||||||
decl.datatypeErrors.forEach { checkResult.add(it) }
|
decl.datatypeErrors.forEach { checkResult.add(it) }
|
||||||
|
|
||||||
@ -55,14 +64,40 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
||||||
|
|
||||||
val existing = namespace.lookup(listOf(decl.name), decl)
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
|
// and include the original decl as well.
|
||||||
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
|
if(decl.structHasBeenFlattened)
|
||||||
|
return super.visit(decl) // don't do this multiple times
|
||||||
|
|
||||||
|
if(decl.struct==null) {
|
||||||
|
checkResult.add(NameError("undefined struct type", decl.position))
|
||||||
|
return super.visit(decl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes})
|
||||||
|
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
||||||
|
|
||||||
|
if(decl.value is NumericLiteralValue) {
|
||||||
|
checkResult.add(ExpressionError("you cannot initialize a struct using a single value", decl.position))
|
||||||
|
return super.visit(decl)
|
||||||
|
}
|
||||||
|
|
||||||
|
val decls = decl.flattenStructMembers()
|
||||||
|
decls.add(decl)
|
||||||
|
val result = AnonymousScope(decls, decl.position)
|
||||||
|
result.linkParents(decl.parent)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
|
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): IStatement {
|
override fun visit(subroutine: Subroutine): Statement {
|
||||||
if(subroutine.name in BuiltinFunctions) {
|
if(subroutine.name in BuiltinFunctions) {
|
||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
||||||
@ -70,7 +105,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
||||||
checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
|
checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
|
||||||
|
|
||||||
val existing = namespace.lookup(listOf(subroutine.name), subroutine)
|
val existing = program.namespace.lookup(listOf(subroutine.name), subroutine)
|
||||||
if (existing != null && existing !== subroutine)
|
if (existing != null && existing !== subroutine)
|
||||||
nameError(subroutine.name, subroutine.position, existing)
|
nameError(subroutine.name, subroutine.position, existing)
|
||||||
|
|
||||||
@ -98,8 +133,8 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
subroutine.parameters
|
subroutine.parameters
|
||||||
.filter { it.name !in namesInSub }
|
.filter { it.name !in namesInSub }
|
||||||
.forEach {
|
.forEach {
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, it.type, false, null, it.name, null,
|
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.DONTCARE, null, it.name, null, null,
|
||||||
isArray = false, autoGenerated = true, position = subroutine.position)
|
isArray = false, autogeneratedDontRemove = true, position = subroutine.position)
|
||||||
vardecl.linkParents(subroutine)
|
vardecl.linkParents(subroutine)
|
||||||
subroutine.statements.add(0, vardecl)
|
subroutine.statements.add(0, vardecl)
|
||||||
}
|
}
|
||||||
@ -109,19 +144,19 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
return super.visit(subroutine)
|
return super.visit(subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(label: Label): IStatement {
|
override fun visit(label: Label): Statement {
|
||||||
if(label.name in BuiltinFunctions) {
|
if(label.name in BuiltinFunctions) {
|
||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
||||||
} else {
|
} else {
|
||||||
val existing = namespace.lookup(listOf(label.name), label)
|
val existing = program.namespace.lookup(listOf(label.name), label)
|
||||||
if (existing != null && existing !== label)
|
if (existing != null && existing !== label)
|
||||||
nameError(label.name, label.position, existing)
|
nameError(label.name, label.position, existing)
|
||||||
}
|
}
|
||||||
return super.visit(label)
|
return super.visit(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): IStatement {
|
override fun visit(forLoop: ForLoop): Statement {
|
||||||
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
|
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
|
||||||
// rather than reusing an already declared loopvar from an outer scope.
|
// rather than reusing an already declared loopvar from an outer scope.
|
||||||
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
||||||
@ -137,8 +172,8 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
||||||
if(existing==null) {
|
if(existing==null) {
|
||||||
// create the local scoped for loop variable itself
|
// create the local scoped for loop variable itself
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null,
|
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null, null,
|
||||||
isArray = false, autoGenerated = true, position = forLoop.loopVar.position)
|
isArray = false, autogeneratedDontRemove = true, position = forLoop.loopVar.position)
|
||||||
vardecl.linkParents(forLoop.body)
|
vardecl.linkParents(forLoop.body)
|
||||||
forLoop.body.statements.add(0, vardecl)
|
forLoop.body.statements.add(0, vardecl)
|
||||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||||
@ -150,8 +185,8 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
|
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
|
||||||
if(existing==null) {
|
if(existing==null) {
|
||||||
// create loop iteration counter variable (without value, to avoid an assignment)
|
// create loop iteration counter variable (without value, to avoid an assignment)
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, ForLoop.iteratorLoopcounterVarname, null,
|
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, ForLoop.iteratorLoopcounterVarname, null, null,
|
||||||
isArray = false, autoGenerated = true, position = forLoop.loopVar.position)
|
isArray = false, autogeneratedDontRemove = true, position = forLoop.loopVar.position)
|
||||||
vardecl.linkParents(forLoop.body)
|
vardecl.linkParents(forLoop.body)
|
||||||
forLoop.body.statements.add(0, vardecl)
|
forLoop.body.statements.add(0, vardecl)
|
||||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||||
@ -167,50 +202,100 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
|||||||
return super.visit(assignTarget)
|
return super.visit(assignTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(returnStmt: Return): IStatement {
|
override fun visit(returnStmt: Return): Statement {
|
||||||
if(returnStmt.values.isNotEmpty()) {
|
if(returnStmt.value!=null) {
|
||||||
// possibly adjust any literal values returned, into the desired returning data type
|
// possibly adjust any literal values returned, into the desired returning data type
|
||||||
val subroutine = returnStmt.definingSubroutine()!!
|
val subroutine = returnStmt.definingSubroutine()!!
|
||||||
if(subroutine.returntypes.size!=returnStmt.values.size)
|
if(subroutine.returntypes.size!=1)
|
||||||
return returnStmt // mismatch in number of return values, error will be printed later.
|
return returnStmt // mismatch in number of return values, error will be printed later.
|
||||||
val newValues = mutableListOf<IExpression>()
|
val newValue: Expression
|
||||||
for(returnvalue in returnStmt.values.zip(subroutine.returntypes)) {
|
val lval = returnStmt.value as? NumericLiteralValue
|
||||||
val lval = returnvalue.first as? LiteralValue
|
|
||||||
if(lval!=null) {
|
if(lval!=null) {
|
||||||
val adjusted = lval.cast(returnvalue.second)
|
val adjusted = lval.cast(subroutine.returntypes.single())
|
||||||
if(adjusted!=null && adjusted !== lval)
|
newValue = if(adjusted!=null && adjusted !== lval) adjusted else lval
|
||||||
newValues.add(adjusted)
|
} else {
|
||||||
else
|
newValue = returnStmt.value!!
|
||||||
newValues.add(lval)
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
newValues.add(returnvalue.first)
|
returnStmt.value = newValue
|
||||||
}
|
|
||||||
returnStmt.values = newValues
|
|
||||||
}
|
}
|
||||||
return super.visit(returnStmt)
|
return super.visit(returnStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
||||||
internal val anonymousVariablesFromHeap = mutableMapOf<String, Pair<LiteralValue, VarDecl>>()
|
if(refLiteral.parent !is VarDecl) {
|
||||||
|
return makeIdentifierFromRefLv(refLiteral)
|
||||||
|
|
||||||
override fun visit(literalValue: LiteralValue): LiteralValue {
|
|
||||||
if(literalValue.heapId!=null && literalValue.parent !is VarDecl) {
|
|
||||||
// a literal value that's not declared as a variable, which refers to something on the heap.
|
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value!
|
|
||||||
val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "$autoHeapValuePrefix${literalValue.heapId}", literalValue,
|
|
||||||
isArray = false, autoGenerated = false, position = literalValue.position)
|
|
||||||
anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable)
|
|
||||||
}
|
}
|
||||||
return super.visit(literalValue)
|
return super.visit(refLiteral)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(addressOf: AddressOf): IExpression {
|
private fun makeIdentifierFromRefLv(refLiteral: ReferenceLiteralValue): IdentifierReference {
|
||||||
|
// a referencetype literal value that's not declared as a variable
|
||||||
|
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
||||||
|
refLiteral.addToHeap(program.heap)
|
||||||
|
val variable = VarDecl.createAuto(refLiteral, program.heap)
|
||||||
|
addVarDecl(refLiteral.definingScope(), variable)
|
||||||
|
// replace the reference literal by a identifier reference
|
||||||
|
val identifier = IdentifierReference(listOf(variable.name), variable.position)
|
||||||
|
identifier.parent = refLiteral.parent
|
||||||
|
return identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(addressOf: AddressOf): Expression {
|
||||||
// register the scoped name of the referenced identifier
|
// register the scoped name of the referenced identifier
|
||||||
val variable= addressOf.identifier.targetVarDecl(namespace) ?: return addressOf
|
val variable= addressOf.identifier.targetVarDecl(program.namespace) ?: return addressOf
|
||||||
addressOf.scopedname = variable.scopedname
|
addressOf.scopedname = variable.scopedname
|
||||||
return super.visit(addressOf)
|
return super.visit(addressOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(structDecl: StructDecl): Statement {
|
||||||
|
for(member in structDecl.statements){
|
||||||
|
val decl = member as? VarDecl
|
||||||
|
if(decl!=null && decl.datatype !in NumericDatatypes)
|
||||||
|
checkResult.add(SyntaxError("structs can only contain numerical types", decl.position))
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.visit(structDecl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
|
return when {
|
||||||
|
expr.left is ReferenceLiteralValue ->
|
||||||
|
processBinaryExprWithReferenceVal(expr.left as ReferenceLiteralValue, expr.right, expr)
|
||||||
|
expr.right is ReferenceLiteralValue ->
|
||||||
|
processBinaryExprWithReferenceVal(expr.right as ReferenceLiteralValue, expr.left, expr)
|
||||||
|
else -> super.visit(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processBinaryExprWithReferenceVal(refLv: ReferenceLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
||||||
|
// expressions on strings or arrays
|
||||||
|
if(refLv.isString) {
|
||||||
|
val constvalue = operand.constValue(program)
|
||||||
|
if(constvalue!=null) {
|
||||||
|
if (expr.operator == "*") {
|
||||||
|
// repeat a string a number of times
|
||||||
|
return ReferenceLiteralValue(refLv.inferType(program),
|
||||||
|
refLv.str!!.repeat(constvalue.number.toInt()), null, null, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(expr.operator == "+" && operand is ReferenceLiteralValue) {
|
||||||
|
if (operand.isString) {
|
||||||
|
// concatenate two strings
|
||||||
|
return ReferenceLiteralValue(refLv.inferType(program),
|
||||||
|
"${refLv.str}${operand.str}", null, null, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
||||||
|
if(scope !in vardeclsToAdd)
|
||||||
|
vardeclsToAdd[scope] = mutableListOf()
|
||||||
|
val declList = vardeclsToAdd.getValue(scope)
|
||||||
|
if(declList.all{it.name!=variable.name})
|
||||||
|
declList.add(variable)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
@ -13,38 +14,38 @@ interface IAstModifyingVisitor {
|
|||||||
module.statements = module.statements.asSequence().map { it.accept(this) }.toMutableList()
|
module.statements = module.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(expr: PrefixExpression): IExpression {
|
fun visit(expr: PrefixExpression): Expression {
|
||||||
expr.expression = expr.expression.accept(this)
|
expr.expression = expr.expression.accept(this)
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(expr: BinaryExpression): IExpression {
|
fun visit(expr: BinaryExpression): Expression {
|
||||||
expr.left = expr.left.accept(this)
|
expr.left = expr.left.accept(this)
|
||||||
expr.right = expr.right.accept(this)
|
expr.right = expr.right.accept(this)
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(directive: Directive): IStatement {
|
fun visit(directive: Directive): Statement {
|
||||||
return directive
|
return directive
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(block: Block): IStatement {
|
fun visit(block: Block): Statement {
|
||||||
block.statements = block.statements.asSequence().map { it.accept(this) }.toMutableList()
|
block.statements = block.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(decl: VarDecl): IStatement {
|
fun visit(decl: VarDecl): Statement {
|
||||||
decl.value = decl.value?.accept(this)
|
decl.value = decl.value?.accept(this)
|
||||||
decl.arraysize?.accept(this)
|
decl.arraysize?.accept(this)
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(subroutine: Subroutine): IStatement {
|
fun visit(subroutine: Subroutine): Statement {
|
||||||
subroutine.statements = subroutine.statements.asSequence().map { it.accept(this) }.toMutableList()
|
subroutine.statements = subroutine.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(functionCall: FunctionCall): IExpression {
|
fun visit(functionCall: FunctionCall): Expression {
|
||||||
val newtarget = functionCall.target.accept(this)
|
val newtarget = functionCall.target.accept(this)
|
||||||
if(newtarget is IdentifierReference)
|
if(newtarget is IdentifierReference)
|
||||||
functionCall.target = newtarget
|
functionCall.target = newtarget
|
||||||
@ -52,7 +53,7 @@ interface IAstModifyingVisitor {
|
|||||||
return functionCall
|
return functionCall
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
val newtarget = functionCallStatement.target.accept(this)
|
val newtarget = functionCallStatement.target.accept(this)
|
||||||
if(newtarget is IdentifierReference)
|
if(newtarget is IdentifierReference)
|
||||||
functionCallStatement.target = newtarget
|
functionCallStatement.target = newtarget
|
||||||
@ -60,13 +61,13 @@ interface IAstModifyingVisitor {
|
|||||||
return functionCallStatement
|
return functionCallStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(identifier: IdentifierReference): IExpression {
|
fun visit(identifier: IdentifierReference): Expression {
|
||||||
// note: this is an identifier that is used in an expression.
|
// note: this is an identifier that is used in an expression.
|
||||||
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
|
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
|
||||||
return identifier
|
return identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(jump: Jump): IStatement {
|
fun visit(jump: Jump): Statement {
|
||||||
if(jump.identifier!=null) {
|
if(jump.identifier!=null) {
|
||||||
val ident = jump.identifier.accept(this)
|
val ident = jump.identifier.accept(this)
|
||||||
if(ident is IdentifierReference && ident!==jump.identifier) {
|
if(ident is IdentifierReference && ident!==jump.identifier) {
|
||||||
@ -76,84 +77,88 @@ interface IAstModifyingVisitor {
|
|||||||
return jump
|
return jump
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(ifStatement: IfStatement): IStatement {
|
fun visit(ifStatement: IfStatement): Statement {
|
||||||
ifStatement.condition = ifStatement.condition.accept(this)
|
ifStatement.condition = ifStatement.condition.accept(this)
|
||||||
ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope
|
ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope
|
||||||
ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope
|
ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope
|
||||||
return ifStatement
|
return ifStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(branchStatement: BranchStatement): IStatement {
|
fun visit(branchStatement: BranchStatement): Statement {
|
||||||
branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope
|
branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope
|
||||||
branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope
|
branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope
|
||||||
return branchStatement
|
return branchStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(range: RangeExpr): IExpression {
|
fun visit(range: RangeExpr): Expression {
|
||||||
range.from = range.from.accept(this)
|
range.from = range.from.accept(this)
|
||||||
range.to = range.to.accept(this)
|
range.to = range.to.accept(this)
|
||||||
range.step = range.step.accept(this)
|
range.step = range.step.accept(this)
|
||||||
return range
|
return range
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(label: Label): IStatement {
|
fun visit(label: Label): Statement {
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(literalValue: LiteralValue): LiteralValue {
|
fun visit(literalValue: NumericLiteralValue): NumericLiteralValue {
|
||||||
if(literalValue.arrayvalue!=null) {
|
|
||||||
for(av in literalValue.arrayvalue.withIndex()) {
|
|
||||||
val newvalue = av.value.accept(this)
|
|
||||||
literalValue.arrayvalue[av.index] = newvalue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return literalValue
|
return literalValue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(assignment: Assignment): IStatement {
|
fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
||||||
assignment.targets = assignment.targets.map { it.accept(this) }
|
if(refLiteral.array!=null) {
|
||||||
|
for(av in refLiteral.array.withIndex()) {
|
||||||
|
val newvalue = av.value.accept(this)
|
||||||
|
refLiteral.array[av.index] = newvalue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refLiteral
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(assignment: Assignment): Statement {
|
||||||
|
assignment.target = assignment.target.accept(this)
|
||||||
assignment.value = assignment.value.accept(this)
|
assignment.value = assignment.value.accept(this)
|
||||||
return assignment
|
return assignment
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(postIncrDecr: PostIncrDecr): IStatement {
|
fun visit(postIncrDecr: PostIncrDecr): Statement {
|
||||||
postIncrDecr.target = postIncrDecr.target.accept(this)
|
postIncrDecr.target = postIncrDecr.target.accept(this)
|
||||||
return postIncrDecr
|
return postIncrDecr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(contStmt: Continue): IStatement {
|
fun visit(contStmt: Continue): Statement {
|
||||||
return contStmt
|
return contStmt
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(breakStmt: Break): IStatement {
|
fun visit(breakStmt: Break): Statement {
|
||||||
return breakStmt
|
return breakStmt
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(forLoop: ForLoop): IStatement {
|
fun visit(forLoop: ForLoop): Statement {
|
||||||
forLoop.loopVar?.accept(this)
|
forLoop.loopVar?.accept(this)
|
||||||
forLoop.iterable = forLoop.iterable.accept(this)
|
forLoop.iterable = forLoop.iterable.accept(this)
|
||||||
forLoop.body = forLoop.body.accept(this) as AnonymousScope
|
forLoop.body = forLoop.body.accept(this) as AnonymousScope
|
||||||
return forLoop
|
return forLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(whileLoop: WhileLoop): IStatement {
|
fun visit(whileLoop: WhileLoop): Statement {
|
||||||
whileLoop.condition = whileLoop.condition.accept(this)
|
whileLoop.condition = whileLoop.condition.accept(this)
|
||||||
whileLoop.body = whileLoop.body.accept(this) as AnonymousScope
|
whileLoop.body = whileLoop.body.accept(this) as AnonymousScope
|
||||||
return whileLoop
|
return whileLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop): IStatement {
|
fun visit(repeatLoop: RepeatLoop): Statement {
|
||||||
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
||||||
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
||||||
return repeatLoop
|
return repeatLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(returnStmt: Return): IStatement {
|
fun visit(returnStmt: Return): Statement {
|
||||||
returnStmt.values = returnStmt.values.map { it.accept(this) }
|
returnStmt.value = returnStmt.value?.accept(this)
|
||||||
return returnStmt
|
return returnStmt
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
|
fun visit(arrayIndexedExpression: ArrayIndexedExpression): Expression {
|
||||||
arrayIndexedExpression.identifier.accept(this)
|
arrayIndexedExpression.identifier.accept(this)
|
||||||
arrayIndexedExpression.arrayspec.accept(this)
|
arrayIndexedExpression.arrayspec.accept(this)
|
||||||
return arrayIndexedExpression
|
return arrayIndexedExpression
|
||||||
@ -166,17 +171,17 @@ interface IAstModifyingVisitor {
|
|||||||
return assignTarget
|
return assignTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(scope: AnonymousScope): IStatement {
|
fun visit(scope: AnonymousScope): Statement {
|
||||||
scope.statements = scope.statements.asSequence().map { it.accept(this) }.toMutableList()
|
scope.statements = scope.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(typecast: TypecastExpression): IExpression {
|
fun visit(typecast: TypecastExpression): Expression {
|
||||||
typecast.expression = typecast.expression.accept(this)
|
typecast.expression = typecast.expression.accept(this)
|
||||||
return typecast
|
return typecast
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(memread: DirectMemoryRead): IExpression {
|
fun visit(memread: DirectMemoryRead): Expression {
|
||||||
memread.addressExpression = memread.addressExpression.accept(this)
|
memread.addressExpression = memread.addressExpression.accept(this)
|
||||||
return memread
|
return memread
|
||||||
}
|
}
|
||||||
@ -185,35 +190,45 @@ interface IAstModifyingVisitor {
|
|||||||
memwrite.addressExpression = memwrite.addressExpression.accept(this)
|
memwrite.addressExpression = memwrite.addressExpression.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(addressOf: AddressOf): IExpression {
|
fun visit(addressOf: AddressOf): Expression {
|
||||||
addressOf.identifier.accept(this)
|
addressOf.identifier.accept(this)
|
||||||
return addressOf
|
return addressOf
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(inlineAssembly: InlineAssembly): IStatement {
|
fun visit(inlineAssembly: InlineAssembly): Statement {
|
||||||
return inlineAssembly
|
return inlineAssembly
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr): IExpression {
|
fun visit(registerExpr: RegisterExpr): Expression {
|
||||||
return registerExpr
|
return registerExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): IStatement {
|
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): Statement {
|
||||||
return builtinFunctionStatementPlaceholder
|
return builtinFunctionStatementPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(nopStatement: NopStatement): IStatement {
|
fun visit(nopStatement: NopStatement): Statement {
|
||||||
return nopStatement
|
return nopStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(whenStatement: WhenStatement): IStatement {
|
fun visit(whenStatement: WhenStatement): Statement {
|
||||||
whenStatement.condition.accept(this)
|
whenStatement.condition.accept(this)
|
||||||
whenStatement.choices.forEach { it.accept(this) }
|
whenStatement.choices.forEach { it.accept(this) }
|
||||||
return whenStatement
|
return whenStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(whenChoice: WhenChoice) {
|
fun visit(whenChoice: WhenChoice) {
|
||||||
whenChoice.value?.accept(this)
|
whenChoice.values?.forEach { it.accept(this) }
|
||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(structDecl: StructDecl): Statement {
|
||||||
|
structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList()
|
||||||
|
return structDecl
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(structLv: StructLiteralValue): Expression {
|
||||||
|
structLv.values = structLv.values.map { it.accept(this) }
|
||||||
|
return structLv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
@ -75,12 +76,15 @@ interface IAstVisitor {
|
|||||||
fun visit(label: Label) {
|
fun visit(label: Label) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(literalValue: LiteralValue) {
|
fun visit(numLiteral: NumericLiteralValue) {
|
||||||
literalValue.arrayvalue?.let { it.forEach { v->v.accept(this) }}
|
}
|
||||||
|
|
||||||
|
fun visit(refLiteral: ReferenceLiteralValue) {
|
||||||
|
refLiteral.array?.let { it.forEach { v->v.accept(this) }}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(assignment: Assignment) {
|
fun visit(assignment: Assignment) {
|
||||||
assignment.targets.forEach { it.accept(this) }
|
assignment.target.accept(this)
|
||||||
assignment.value.accept(this)
|
assignment.value.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +115,7 @@ interface IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun visit(returnStmt: Return) {
|
fun visit(returnStmt: Return) {
|
||||||
returnStmt.values.forEach { it.accept(this) }
|
returnStmt.value?.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
@ -163,7 +167,15 @@ interface IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun visit(whenChoice: WhenChoice) {
|
fun visit(whenChoice: WhenChoice) {
|
||||||
whenChoice.value?.accept(this)
|
whenChoice.values?.forEach { it.accept(this) }
|
||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(structDecl: StructDecl) {
|
||||||
|
structDecl.statements.forEach { it.accept(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(structLv: StructLiteralValue) {
|
||||||
|
structLv.values.forEach { it.accept(this) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
import prog8.ast.base.printWarning
|
import prog8.ast.base.printWarning
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
|
import prog8.ast.statements.Statement
|
||||||
|
|
||||||
internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
||||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
||||||
@ -17,7 +18,7 @@ internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
|||||||
*/
|
*/
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
super.visit(module)
|
super.visit(module)
|
||||||
val newStatements : MutableList<IStatement> = mutableListOf()
|
val newStatements : MutableList<Statement> = mutableListOf()
|
||||||
|
|
||||||
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
||||||
for (sourceStmt in module.statements) {
|
for (sourceStmt in module.statements) {
|
||||||
|
@ -1,17 +1,54 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import kotlin.comparisons.nullsLast
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.base.initvarsSubName
|
||||||
import prog8.ast.base.printWarning
|
import prog8.ast.base.printWarning
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.FunctionCall
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
|
fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||||
|
val identifier = structAssignment.target.identifier!!
|
||||||
|
val identifierName = identifier.nameInSource.single()
|
||||||
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
val struct = targetVar.struct!!
|
||||||
|
when {
|
||||||
|
structAssignment.value is IdentifierReference -> {
|
||||||
|
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||||
|
if (sourceVar.struct == null)
|
||||||
|
throw FatalAstException("can only assign arrays or structs to structs")
|
||||||
|
// struct memberwise copy
|
||||||
|
val sourceStruct = sourceVar.struct!!
|
||||||
|
if(sourceStruct!==targetVar.struct) {
|
||||||
|
// structs are not the same in assignment
|
||||||
|
return listOf() // error will be printed elsewhere
|
||||||
|
}
|
||||||
|
return struct.statements.zip(sourceStruct.statements).map { member ->
|
||||||
|
val targetDecl = member.first as VarDecl
|
||||||
|
val sourceDecl = member.second as VarDecl
|
||||||
|
if(targetDecl.name != sourceDecl.name)
|
||||||
|
throw FatalAstException("struct member mismatch")
|
||||||
|
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
||||||
|
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
|
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
||||||
|
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
||||||
|
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
|
||||||
|
null, sourceIdref, member.second.position)
|
||||||
|
assign.linkParents(structAssignment)
|
||||||
|
assign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
structAssignment.value is StructLiteralValue -> {
|
||||||
|
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
||||||
|
}
|
||||||
|
else -> throw FatalAstException("strange struct value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(private val program: Program): IAstModifyingVisitor {
|
internal class StatementReorderer(private val program: Program): IAstModifyingVisitor {
|
||||||
// Reorders the statements in a way the compiler needs.
|
// Reorders the statements in a way the compiler needs.
|
||||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||||
@ -60,7 +97,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
module.statements.addAll(0, directives)
|
module.statements.addAll(0, directives)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block): IStatement {
|
override fun visit(block: Block): Statement {
|
||||||
|
|
||||||
val subroutines = block.statements.filterIsInstance<Subroutine>()
|
val subroutines = block.statements.filterIsInstance<Subroutine>()
|
||||||
var numSubroutinesAtEnd = 0
|
var numSubroutinesAtEnd = 0
|
||||||
@ -91,7 +128,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
&& stmtBeforeFirstSub !is Jump
|
&& stmtBeforeFirstSub !is Jump
|
||||||
&& stmtBeforeFirstSub !is Subroutine
|
&& stmtBeforeFirstSub !is Subroutine
|
||||||
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
|
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
|
||||||
val ret = Return(emptyList(), stmtBeforeFirstSub.position)
|
val ret = Return(null, stmtBeforeFirstSub.position)
|
||||||
ret.linkParents(block)
|
ret.linkParents(block)
|
||||||
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret)
|
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret)
|
||||||
}
|
}
|
||||||
@ -124,7 +161,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
return super.visit(block)
|
return super.visit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): IStatement {
|
override fun visit(subroutine: Subroutine): Statement {
|
||||||
super.visit(subroutine)
|
super.visit(subroutine)
|
||||||
|
|
||||||
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
||||||
@ -139,7 +176,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
// and if an assembly block doesn't contain a rts/rti
|
// and if an assembly block doesn't contain a rts/rti
|
||||||
if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) {
|
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)
|
val returnStmt = Return(null, subroutine.position)
|
||||||
returnStmt.linkParents(subroutine)
|
returnStmt.linkParents(subroutine)
|
||||||
subroutine.statements.add(returnStmt)
|
subroutine.statements.add(returnStmt)
|
||||||
}
|
}
|
||||||
@ -149,7 +186,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): IExpression {
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
|
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
|
||||||
@ -172,30 +209,68 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
return super.visit(expr)
|
return super.visit(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): IStatement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
val target=assignment.singleTarget
|
val assg = super.visit(assignment)
|
||||||
if(target!=null) {
|
if(assg !is Assignment)
|
||||||
|
return assg
|
||||||
|
|
||||||
// see if a typecast is needed to convert the value's type into the proper target type
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
val valuetype = assignment.value.inferType(program)
|
val valuetype = assg.value.inferType(program)
|
||||||
val targettype = target.inferType(program, assignment)
|
val targettype = assg.target.inferType(program, assg)
|
||||||
if(targettype!=null && valuetype!=null && valuetype!=targettype) {
|
if(targettype!=null && valuetype!=null) {
|
||||||
if(valuetype isAssignableTo targettype) {
|
if(valuetype!=targettype) {
|
||||||
assignment.value = TypecastExpression(assignment.value, targettype, true, assignment.value.position)
|
if (valuetype isAssignableTo targettype) {
|
||||||
assignment.value.linkParents(assignment)
|
assg.value = TypecastExpression(assg.value, targettype, true, assg.value.position)
|
||||||
|
assg.value.linkParents(assg)
|
||||||
}
|
}
|
||||||
// if they're not assignable, we'll get a proper error later from the AstChecker
|
// if they're not assignable, we'll get a proper error later from the AstChecker
|
||||||
}
|
}
|
||||||
} else TODO("multi-target assign")
|
|
||||||
|
|
||||||
return super.visit(assignment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
// struct assignments will be flattened (if it's not a struct literal)
|
||||||
|
if(valuetype==DataType.STRUCT && targettype==DataType.STRUCT) {
|
||||||
|
if(assg.value is StructLiteralValue)
|
||||||
|
return assg // do NOT flatten it at this point!! (the compiler will take care if it, later, if needed)
|
||||||
|
|
||||||
|
val assignments = flattenStructAssignmentFromIdentifier(assg, program) // 'structvar1 = structvar2'
|
||||||
|
return if(assignments.isEmpty()) {
|
||||||
|
// something went wrong (probably incompatible struct types)
|
||||||
|
// we'll get an error later from the AstChecker
|
||||||
|
assg
|
||||||
|
} else {
|
||||||
|
val scope = AnonymousScope(assignments.toMutableList(), assg.position)
|
||||||
|
scope.linkParents(assg.parent)
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(assg.aug_op!=null) {
|
||||||
|
// transform augmented assg into normal assg so we have one case less to deal with later
|
||||||
|
val newTarget: Expression =
|
||||||
|
when {
|
||||||
|
assg.target.register != null -> RegisterExpr(assg.target.register!!, assg.target.position)
|
||||||
|
assg.target.identifier != null -> assg.target.identifier!!
|
||||||
|
assg.target.arrayindexed != null -> assg.target.arrayindexed!!
|
||||||
|
assg.target.memoryAddress != null -> DirectMemoryRead(assg.target.memoryAddress!!.addressExpression, assg.value.position)
|
||||||
|
else -> throw FatalAstException("strange assg")
|
||||||
|
}
|
||||||
|
|
||||||
|
val expression = BinaryExpression(newTarget, assg.aug_op.substringBeforeLast('='), assg.value, assg.position)
|
||||||
|
expression.linkParents(assg.parent)
|
||||||
|
val convertedAssignment = Assignment(assg.target, null, expression, assg.position)
|
||||||
|
convertedAssignment.linkParents(assg.parent)
|
||||||
|
return super.visit(convertedAssignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return assg
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
||||||
return super.visit(functionCallStatement)
|
return super.visit(functionCallStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): IExpression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
||||||
return super.visit(functionCall)
|
return super.visit(functionCall)
|
||||||
}
|
}
|
||||||
@ -246,29 +321,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortConstantAssignmentSequence(first: Assignment, stmtIter: MutableIterator<IStatement>): Pair<List<Assignment>, IStatement?> {
|
override fun visit(typecast: TypecastExpression): Expression {
|
||||||
val sequence= mutableListOf(first)
|
|
||||||
var trailing: IStatement? = null
|
|
||||||
while(stmtIter.hasNext()) {
|
|
||||||
val next = stmtIter.next()
|
|
||||||
if(next is Assignment) {
|
|
||||||
val constValue = next.value.constValue(program)
|
|
||||||
if(constValue==null) {
|
|
||||||
trailing = next
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sequence.add(next)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trailing=next
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val sorted = sequence.sortedWith(compareBy({it.value.inferType(program)}, {it.singleTarget?.shortString(true)}))
|
|
||||||
return Pair(sorted, trailing)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression): IExpression {
|
|
||||||
// warn about any implicit type casts to Float, because that may not be intended
|
// warn about any implicit type casts to Float, because that may not be intended
|
||||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
||||||
@ -276,10 +329,90 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
return super.visit(typecast)
|
return super.visit(typecast)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
override fun visit(whenStatement: WhenStatement): Statement {
|
||||||
// sort the choices in low-to-high value order
|
// make sure all choices are just for one single value
|
||||||
|
val choices = whenStatement.choices.toList()
|
||||||
|
for(choice in choices) {
|
||||||
|
if(choice.values==null || choice.values.size==1)
|
||||||
|
continue
|
||||||
|
for(v in choice.values) {
|
||||||
|
val newchoice=WhenChoice(listOf(v), choice.statements, choice.position)
|
||||||
|
newchoice.parent = choice.parent
|
||||||
|
whenStatement.choices.add(newchoice)
|
||||||
|
}
|
||||||
|
whenStatement.choices.remove(choice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the choices in low-to-high value order (nulls last)
|
||||||
whenStatement.choices
|
whenStatement.choices
|
||||||
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.value?.constValue(program)?.asIntegerValue}))
|
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.values?.single()?.constValue(program)?.number?.toInt()}))
|
||||||
return super.visit(whenStatement)
|
return super.visit(whenStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(memread: DirectMemoryRead): Expression {
|
||||||
|
// make sure the memory address is an uword
|
||||||
|
val dt = memread.addressExpression.inferType(program)
|
||||||
|
if(dt!=DataType.UWORD) {
|
||||||
|
val literaladdr = memread.addressExpression as? NumericLiteralValue
|
||||||
|
if(literaladdr!=null) {
|
||||||
|
memread.addressExpression = literaladdr.cast(DataType.UWORD)!!
|
||||||
|
} else {
|
||||||
|
memread.addressExpression = TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||||
|
memread.addressExpression.parent = memread
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(memread)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(memwrite: DirectMemoryWrite) {
|
||||||
|
val dt = memwrite.addressExpression.inferType(program)
|
||||||
|
if(dt!=DataType.UWORD) {
|
||||||
|
val literaladdr = memwrite.addressExpression as? NumericLiteralValue
|
||||||
|
if(literaladdr!=null) {
|
||||||
|
memwrite.addressExpression = literaladdr.cast(DataType.UWORD)!!
|
||||||
|
} else {
|
||||||
|
memwrite.addressExpression = TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||||
|
memwrite.addressExpression.parent = memwrite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visit(memwrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(structLv: StructLiteralValue): Expression {
|
||||||
|
val litval = super.visit(structLv)
|
||||||
|
if(litval !is StructLiteralValue)
|
||||||
|
return litval
|
||||||
|
|
||||||
|
val decl = litval.parent as? VarDecl
|
||||||
|
if(decl != null) {
|
||||||
|
val struct = decl.struct
|
||||||
|
if(struct != null) {
|
||||||
|
addTypecastsIfNeeded(litval, struct)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val assign = litval.parent as? Assignment
|
||||||
|
if (assign != null) {
|
||||||
|
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
||||||
|
if(decl2 != null) {
|
||||||
|
val struct = decl2.struct
|
||||||
|
if(struct != null) {
|
||||||
|
addTypecastsIfNeeded(litval, struct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return litval
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addTypecastsIfNeeded(structLv: StructLiteralValue, struct: StructDecl) {
|
||||||
|
structLv.values = struct.statements.zip(structLv.values).map {
|
||||||
|
val memberDt = (it.first as VarDecl).datatype
|
||||||
|
val valueDt = it.second.inferType(program)
|
||||||
|
if (valueDt != memberDt)
|
||||||
|
TypecastExpression(it.second, memberDt, true, it.second.position)
|
||||||
|
else
|
||||||
|
it.second
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.autoHeapValuePrefix
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.AddressOf
|
|
||||||
import prog8.ast.expressions.FunctionCall
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.LiteralValue
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.HeapValues
|
||||||
|
|
||||||
internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor {
|
|
||||||
|
internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope, private val heap: HeapValues): IAstModifyingVisitor {
|
||||||
// For VarDecls that declare an initialization value:
|
// For VarDecls that declare an initialization value:
|
||||||
// Replace the vardecl with an assignment (to set the initial 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.
|
// and add a new vardecl with the default constant value of that type (usually zero) to the scope.
|
||||||
@ -21,22 +21,31 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
|||||||
|
|
||||||
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
|
||||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableMap<String, VarDecl>>()
|
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
vardeclsToAdd.clear()
|
vardeclsToAdd.clear()
|
||||||
super.visit(module)
|
super.visit(module)
|
||||||
|
|
||||||
// add any new vardecls to the various scopes
|
// add any new vardecls to the various scopes
|
||||||
for(decl in vardeclsToAdd)
|
for((where, decls) in vardeclsToAdd) {
|
||||||
for(d in decl.value) {
|
where.statements.addAll(0, decls)
|
||||||
d.value.linkParents(decl.key as Node)
|
decls.forEach { it.linkParents(where as Node) }
|
||||||
decl.key.statements.add(0, d.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): IStatement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
|
|
||||||
|
if(decl.isArray && decl.value==null) {
|
||||||
|
// array datatype without initialization value, add list of zeros
|
||||||
|
val arraysize = decl.arraysize!!.size()!!
|
||||||
|
val array = ReferenceLiteralValue(decl.datatype, null,
|
||||||
|
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
|
||||||
|
null, decl.position)
|
||||||
|
array.addToHeap(heap)
|
||||||
|
decl.value = array
|
||||||
|
}
|
||||||
|
|
||||||
if(decl.type!= VarDeclType.VAR || decl.value==null)
|
if(decl.type!= VarDeclType.VAR || decl.value==null)
|
||||||
return decl
|
return decl
|
||||||
|
|
||||||
@ -45,13 +54,13 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
|||||||
addVarDecl(scope, decl.asDefaultValueDecl(null))
|
addVarDecl(scope, decl.asDefaultValueDecl(null))
|
||||||
val declvalue = decl.value!!
|
val declvalue = decl.value!!
|
||||||
val value =
|
val value =
|
||||||
if(declvalue is LiteralValue) {
|
if(declvalue is NumericLiteralValue) {
|
||||||
val converted = declvalue.cast(decl.datatype)
|
val converted = declvalue.cast(decl.datatype)
|
||||||
converted ?: declvalue
|
converted ?: declvalue
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
declvalue
|
declvalue
|
||||||
val identifierName = listOf(decl.name) // // TODO this was: (scoped name) decl.scopedname.split(".")
|
val identifierName = listOf(decl.name) // this was: (scoped name) decl.scopedname.split(".")
|
||||||
return VariableInitializationAssignment(
|
return VariableInitializationAssignment(
|
||||||
AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
|
AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
|
||||||
null,
|
null,
|
||||||
@ -59,35 +68,36 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
|||||||
decl.position
|
decl.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): IExpression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
val targetStatement = functionCall.target.targetSubroutine(namespace)
|
val targetStatement = functionCall.target.targetSubroutine(namespace)
|
||||||
if(targetStatement!=null) {
|
if(targetStatement!=null) {
|
||||||
var node: Node = functionCall
|
var node: Node = functionCall
|
||||||
while(node !is IStatement)
|
while(node !is Statement)
|
||||||
node=node.parent
|
node=node.parent
|
||||||
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
|
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
|
||||||
}
|
}
|
||||||
return functionCall
|
return functionCall
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
|
val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
|
||||||
if(targetStatement!=null)
|
if(targetStatement!=null)
|
||||||
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
|
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
|
||||||
return functionCallStatement
|
return functionCallStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<IExpression>, parent: IStatement) {
|
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<Expression>, parent: Statement) {
|
||||||
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
|
// 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)) {
|
for(argparam in subroutine.parameters.withIndex().zip(arglist)) {
|
||||||
if(argparam.first.value.type== DataType.UWORD || argparam.first.value.type in StringDatatypes) {
|
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type in StringDatatypes) {
|
||||||
if(argparam.second is AddressOf)
|
if(argparam.second is AddressOf)
|
||||||
continue
|
continue
|
||||||
val idref = argparam.second as? IdentifierReference
|
val idref = argparam.second as? IdentifierReference
|
||||||
val strvalue = argparam.second as? LiteralValue
|
val strvalue = argparam.second as? ReferenceLiteralValue
|
||||||
if(idref!=null) {
|
if(idref!=null) {
|
||||||
val variable = idref.targetVarDecl(namespace)
|
val variable = idref.targetVarDecl(namespace)
|
||||||
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
|
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
|
||||||
@ -99,18 +109,15 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
|||||||
}
|
}
|
||||||
else if(strvalue!=null) {
|
else if(strvalue!=null) {
|
||||||
if(strvalue.isString) {
|
if(strvalue.isString) {
|
||||||
|
// add a vardecl so that the autovar can be resolved in later lookups
|
||||||
|
val variable = VarDecl.createAuto(strvalue, heap)
|
||||||
|
addVarDecl(strvalue.definingScope(), variable)
|
||||||
// replace the argument with &autovar
|
// replace the argument with &autovar
|
||||||
val autoVarName = "$autoHeapValuePrefix${strvalue.heapId}"
|
val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position)
|
||||||
val autoHeapvarRef = IdentifierReference(listOf(autoVarName), strvalue.position)
|
|
||||||
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
||||||
pointerExpr.scopedname = parent.makeScopedName(autoVarName)
|
pointerExpr.scopedname = parent.makeScopedName(variable.name)
|
||||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||||
arglist[argparam.first.index] = pointerExpr
|
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)
|
|
||||||
// println("MADE ANONVAR $variable") // XXX
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,8 +126,10 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
|||||||
|
|
||||||
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
||||||
if(scope !in vardeclsToAdd)
|
if(scope !in vardeclsToAdd)
|
||||||
vardeclsToAdd[scope] = mutableMapOf()
|
vardeclsToAdd[scope] = mutableListOf()
|
||||||
vardeclsToAdd.getValue(scope)[variable.name]=variable
|
val declList = vardeclsToAdd.getValue(scope)
|
||||||
|
if(declList.all{it.name!=variable.name})
|
||||||
|
declList.add(variable)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,40 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement {
|
sealed class Statement : Node {
|
||||||
|
abstract fun accept(visitor: IAstModifyingVisitor) : Statement
|
||||||
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
|
fun makeScopedName(name: String): String {
|
||||||
|
// easy way out is to always return the full scoped name.
|
||||||
|
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||||
|
// and like this, we can cache the name even,
|
||||||
|
// like in a lazy property on the statement object itself (label, subroutine, vardecl)
|
||||||
|
val scope = mutableListOf<String>()
|
||||||
|
var statementScope = this.parent
|
||||||
|
while(statementScope !is ParentSentinel && statementScope !is Module) {
|
||||||
|
if(statementScope is INameScope) {
|
||||||
|
scope.add(0, statementScope.name)
|
||||||
|
}
|
||||||
|
statementScope = statementScope.parent
|
||||||
|
}
|
||||||
|
if(name.isNotEmpty())
|
||||||
|
scope.add(name)
|
||||||
|
return scope.joinToString(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val expensiveToInline: Boolean
|
||||||
|
|
||||||
|
fun definingBlock(): Block {
|
||||||
|
if(this is Block)
|
||||||
|
return this
|
||||||
|
return findParentNode<Block>(this)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
@ -25,9 +54,9 @@ data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusf
|
|||||||
|
|
||||||
class Block(override val name: String,
|
class Block(override val name: String,
|
||||||
val address: Int?,
|
val address: Int?,
|
||||||
override var statements: MutableList<IStatement>,
|
override var statements: MutableList<Statement>,
|
||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : IStatement, INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
get() = statements.any { it.expensiveToInline }
|
get() = statements.any { it.expensiveToInline }
|
||||||
@ -47,7 +76,7 @@ class Block(override val name: String,
|
|||||||
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
|
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : IStatement {
|
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -68,7 +97,7 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : IStatement {
|
data class Label(val name: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -86,24 +115,24 @@ data class Label(val name: String, override val position: Position) : IStatement
|
|||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Return(var values: List<IExpression>, override val position: Position) : IStatement {
|
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = values.any { it !is LiteralValue }
|
override val expensiveToInline = value!=null && value !is NumericLiteralValue
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
values.forEach {it.linkParents(this)}
|
value?.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Return(values: $values, pos=$position)"
|
return "Return($value, pos=$position)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReturnFromIrq(override val position: Position) : Return(emptyList(), position) {
|
class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
@ -112,7 +141,7 @@ class ReturnFromIrq(override val position: Position) : Return(emptyList(), posit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Continue(override val position: Position) : IStatement {
|
class Continue(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -124,7 +153,7 @@ class Continue(override val position: Position) : IStatement {
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Break(override val position: Position) : IStatement {
|
class Break(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -136,18 +165,51 @@ class Break(override val position: Position) : IStatement {
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class ZeropageWish {
|
||||||
|
REQUIRE_ZEROPAGE,
|
||||||
|
PREFER_ZEROPAGE,
|
||||||
|
DONTCARE,
|
||||||
|
NOT_IN_ZEROPAGE
|
||||||
|
}
|
||||||
|
|
||||||
class VarDecl(val type: VarDeclType,
|
class VarDecl(val type: VarDeclType,
|
||||||
private val declaredDatatype: DataType,
|
private val declaredDatatype: DataType,
|
||||||
val zeropage: Boolean,
|
val zeropage: ZeropageWish,
|
||||||
var arraysize: ArrayIndex?,
|
var arraysize: ArrayIndex?,
|
||||||
val name: String,
|
val name: String,
|
||||||
var value: IExpression?,
|
private val structName: String?,
|
||||||
|
var value: Expression?,
|
||||||
val isArray: Boolean,
|
val isArray: Boolean,
|
||||||
val autoGenerated: Boolean,
|
val autogeneratedDontRemove: Boolean,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
|
||||||
|
private set
|
||||||
|
var structHasBeenFlattened = false // set later
|
||||||
|
private set
|
||||||
|
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
get() = value!=null && value !is LiteralValue
|
get() = value!=null && value !is NumericLiteralValue
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createAuto(refLv: ReferenceLiteralValue, heap: HeapValues): VarDecl {
|
||||||
|
if(refLv.heapId==null)
|
||||||
|
throw FatalAstException("can only create autovar for a ref lv that has a heapid $refLv")
|
||||||
|
|
||||||
|
val autoVarName = "$autoHeapValuePrefix${refLv.heapId}"
|
||||||
|
|
||||||
|
return if(refLv.isArray) {
|
||||||
|
val declaredType = ArrayElementTypes.getValue(refLv.type)
|
||||||
|
val arraysize = ArrayIndex.forArray(refLv, heap)
|
||||||
|
VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, refLv,
|
||||||
|
isArray = true, autogeneratedDontRemove = true, position = refLv.position)
|
||||||
|
} else {
|
||||||
|
VarDecl(VarDeclType.VAR, refLv.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, refLv,
|
||||||
|
isArray = false, autogeneratedDontRemove = true, position = refLv.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
|
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
|
||||||
val datatype =
|
val datatype =
|
||||||
@ -160,7 +222,7 @@ class VarDecl(val type: VarDeclType,
|
|||||||
DataType.FLOAT -> DataType.ARRAY_F
|
DataType.FLOAT -> DataType.ARRAY_F
|
||||||
else -> {
|
else -> {
|
||||||
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
|
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
|
||||||
DataType.UBYTE
|
DataType.ARRAY_UB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +230,11 @@ class VarDecl(val type: VarDeclType,
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
arraysize?.linkParents(this)
|
arraysize?.linkParents(this)
|
||||||
value?.linkParents(this)
|
value?.linkParents(this)
|
||||||
|
if(structName!=null) {
|
||||||
|
val structStmt = definingScope().lookup(listOf(structName), this)
|
||||||
|
if(structStmt!=null)
|
||||||
|
struct = definingScope().lookup(listOf(structName), this) as StructDecl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
@ -176,26 +243,47 @@ class VarDecl(val type: VarDeclType,
|
|||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, array=$isArray, value=$value, pos=$position)"
|
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asDefaultValueDecl(parent: Node?): VarDecl {
|
fun asDefaultValueDecl(parent: Node?): VarDecl {
|
||||||
val constValue = when(declaredDatatype) {
|
val constValue = when(declaredDatatype) {
|
||||||
DataType.UBYTE -> LiteralValue(DataType.UBYTE, 0, position = position)
|
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
|
||||||
DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position = position)
|
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
|
||||||
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = 0, position = position)
|
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
|
||||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = 0, position = position)
|
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
|
||||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = 0.0, position = position)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
|
||||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
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)
|
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, constValue, isArray, false, position)
|
||||||
if(parent!=null)
|
if(parent!=null)
|
||||||
decl.linkParents(parent)
|
decl.linkParents(parent)
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun flattenStructMembers(): MutableList<Statement> {
|
||||||
|
val result = struct!!.statements.withIndex().map {
|
||||||
|
val member = it.value as VarDecl
|
||||||
|
val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null
|
||||||
|
VarDecl(
|
||||||
|
VarDeclType.VAR,
|
||||||
|
member.datatype,
|
||||||
|
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||||
|
member.arraysize,
|
||||||
|
mangledStructMemberName(name, member.name),
|
||||||
|
struct!!.name,
|
||||||
|
initvalue,
|
||||||
|
member.isArray,
|
||||||
|
true,
|
||||||
|
member.position
|
||||||
|
) as Statement
|
||||||
|
}.toMutableList()
|
||||||
|
structHasBeenFlattened = true
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayIndex(var index: IExpression, override val position: Position) : Node {
|
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -204,9 +292,9 @@ class ArrayIndex(var index: IExpression, override val position: Position) : Node
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun forArray(v: LiteralValue, heap: HeapValues): ArrayIndex {
|
fun forArray(v: ReferenceLiteralValue, heap: HeapValues): ArrayIndex {
|
||||||
val arraySize = v.arrayvalue?.size ?: heap.get(v.heapId!!).arraysize
|
val arraySize = v.array?.size ?: heap.get(v.heapId!!).arraysize
|
||||||
return ArrayIndex(LiteralValue.optimalNumeric(arraySize, v.position), v.position)
|
return ArrayIndex(NumericLiteralValue.optimalNumeric(arraySize, v.position), v.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,17 +309,17 @@ class ArrayIndex(var index: IExpression, override val position: Position) : Node
|
|||||||
return("ArrayIndex($index, pos=$position)")
|
return("ArrayIndex($index, pos=$position)")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun size() = (index as? LiteralValue)?.asIntegerValue
|
fun size() = (index as? NumericLiteralValue)?.number?.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Assignment(var targets: List<AssignTarget>, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
|
open class Assignment(var target: AssignTarget, val aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
get() = value !is LiteralValue
|
get() = value !is NumericLiteralValue
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
targets.forEach { it.linkParents(this) }
|
this.target.linkParents(this)
|
||||||
value.linkParents(this)
|
value.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,19 +327,14 @@ open class Assignment(var targets: List<AssignTarget>, val aug_op : String?, var
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("Assignment(augop: $aug_op, targets: $targets, value: $value, pos=$position)")
|
return("Assignment(augop: $aug_op, target: $target, 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,
|
// 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.
|
// or just a regular assignment. It may optimize the initialization step from this.
|
||||||
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: IExpression, position: Position)
|
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: Expression, position: Position)
|
||||||
: Assignment(listOf(target), aug_op, value, position)
|
: Assignment(target, aug_op, value, position)
|
||||||
|
|
||||||
data class AssignTarget(val register: Register?,
|
data class AssignTarget(val register: Register?,
|
||||||
val identifier: IdentifierReference?,
|
val identifier: IdentifierReference?,
|
||||||
@ -271,7 +354,7 @@ data class AssignTarget(val register: Register?,
|
|||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromExpr(expr: IExpression): AssignTarget {
|
fun fromExpr(expr: Expression): AssignTarget {
|
||||||
return when (expr) {
|
return when (expr) {
|
||||||
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
|
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
|
||||||
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
|
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
|
||||||
@ -282,7 +365,7 @@ data class AssignTarget(val register: Register?,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inferType(program: Program, stmt: IStatement): DataType? {
|
fun inferType(program: Program, stmt: Statement): DataType? {
|
||||||
if(register!=null)
|
if(register!=null)
|
||||||
return DataType.UBYTE
|
return DataType.UBYTE
|
||||||
|
|
||||||
@ -303,23 +386,7 @@ data class AssignTarget(val register: Register?,
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shortString(withTypePrefix: Boolean=false): String {
|
infix fun isSameAs(value: Expression): Boolean {
|
||||||
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 {
|
return when {
|
||||||
this.memoryAddress!=null -> false
|
this.memoryAddress!=null -> false
|
||||||
this.register!=null -> value is RegisterExpr && value.register==register
|
this.register!=null -> value is RegisterExpr && value.register==register
|
||||||
@ -374,7 +441,7 @@ data class AssignTarget(val register: Register?,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : IStatement {
|
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -394,7 +461,7 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
|||||||
class Jump(val address: Int?,
|
class Jump(val address: Int?,
|
||||||
val identifier: IdentifierReference?,
|
val identifier: IdentifierReference?,
|
||||||
val generatedLabel: String?, // used in code generation scenarios
|
val generatedLabel: String?, // used in code generation scenarios
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -412,11 +479,11 @@ class Jump(val address: Int?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCallStatement(override var target: IdentifierReference,
|
class FunctionCallStatement(override var target: IdentifierReference,
|
||||||
override var arglist: MutableList<IExpression>,
|
override var arglist: MutableList<Expression>,
|
||||||
override val position: Position) : IStatement, IFunctionCall {
|
override val position: Position) : Statement(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
get() = arglist.any { it !is LiteralValue }
|
get() = arglist.any { it !is NumericLiteralValue }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -432,7 +499,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InlineAssembly(val assembly: String, override val position: Position) : IStatement {
|
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
override val expensiveToInline = true
|
||||||
|
|
||||||
@ -444,8 +511,8 @@ class InlineAssembly(val assembly: String, override val position: Position) : IS
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnonymousScope(override var statements: MutableList<IStatement>,
|
class AnonymousScope(override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : INameScope, IStatement {
|
override val position: Position) : INameScope, Statement() {
|
||||||
override val name: String
|
override val name: String
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
@ -469,7 +536,7 @@ class AnonymousScope(override var statements: MutableList<IStatement>,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NopStatement(override val position: Position): IStatement {
|
class NopStatement(override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
|
|
||||||
@ -479,6 +546,14 @@ class NopStatement(override val position: Position): IStatement {
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun insteadOf(stmt: Statement): NopStatement {
|
||||||
|
val nop = NopStatement(stmt.position)
|
||||||
|
nop.parent = stmt.parent
|
||||||
|
return nop
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the subroutine class covers both the normal user-defined subroutines,
|
// the subroutine class covers both the normal user-defined subroutines,
|
||||||
@ -492,8 +567,8 @@ class Subroutine(override val name: String,
|
|||||||
val asmClobbers: Set<Register>,
|
val asmClobbers: Set<Register>,
|
||||||
val asmAddress: Int?,
|
val asmAddress: Int?,
|
||||||
val isAsmSubroutine: Boolean,
|
val isAsmSubroutine: Boolean,
|
||||||
override var statements: MutableList<IStatement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : IStatement, INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
var keepAlways: Boolean = false
|
var keepAlways: Boolean = false
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
@ -569,10 +644,10 @@ open class SubroutineParameter(val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IfStatement(var condition: IExpression,
|
class IfStatement(var condition: Expression,
|
||||||
var truepart: AnonymousScope,
|
var truepart: AnonymousScope,
|
||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
override val expensiveToInline: Boolean
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
||||||
@ -591,7 +666,7 @@ class IfStatement(var condition: IExpression,
|
|||||||
class BranchStatement(var condition: BranchCondition,
|
class BranchStatement(var condition: BranchCondition,
|
||||||
var truepart: AnonymousScope,
|
var truepart: AnonymousScope,
|
||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
override val expensiveToInline: Boolean
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
||||||
@ -608,11 +683,11 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
|
|
||||||
class ForLoop(val loopRegister: Register?,
|
class ForLoop(val loopRegister: Register?,
|
||||||
val decltype: DataType?,
|
val decltype: DataType?,
|
||||||
val zeropage: Boolean,
|
val zeropage: ZeropageWish,
|
||||||
val loopVar: IdentifierReference?,
|
val loopVar: IdentifierReference?,
|
||||||
var iterable: IExpression,
|
var iterable: Expression,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
override val expensiveToInline = true
|
||||||
|
|
||||||
@ -635,9 +710,9 @@ class ForLoop(val loopRegister: Register?,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhileLoop(var condition: IExpression,
|
class WhileLoop(var condition: Expression,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
override val expensiveToInline = true
|
||||||
|
|
||||||
@ -652,8 +727,8 @@ class WhileLoop(var condition: IExpression,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RepeatLoop(var body: AnonymousScope,
|
class RepeatLoop(var body: AnonymousScope,
|
||||||
var untilCondition: IExpression,
|
var untilCondition: Expression,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
override val expensiveToInline = true
|
||||||
|
|
||||||
@ -667,9 +742,9 @@ class RepeatLoop(var body: AnonymousScope,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhenStatement(val condition: IExpression,
|
class WhenStatement(val condition: Expression,
|
||||||
val choices: MutableList<WhenChoice>,
|
val choices: MutableList<WhenChoice>,
|
||||||
override val position: Position): IStatement {
|
override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
override val expensiveToInline: Boolean = true
|
||||||
|
|
||||||
@ -679,35 +754,40 @@ class WhenStatement(val condition: IExpression,
|
|||||||
choices.forEach { it.linkParents(this) }
|
choices.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun choiceValues(program: Program): List<Pair<Int?, WhenChoice>> {
|
fun choiceValues(program: Program): List<Pair<List<Int>?, WhenChoice>> {
|
||||||
// only gives sensible results when the choices are all valid (constant integers)
|
// only gives sensible results when the choices are all valid (constant integers)
|
||||||
return choices
|
val result = mutableListOf<Pair<List<Int>?, WhenChoice>>()
|
||||||
.map {
|
for(choice in choices) {
|
||||||
val cv = it.value?.constValue(program)
|
if(choice.values==null)
|
||||||
if(cv==null)
|
result.add(null to choice)
|
||||||
null to it
|
else {
|
||||||
|
val values = choice.values.map { it.constValue(program)?.number?.toInt() }
|
||||||
|
if(values.contains(null))
|
||||||
|
result.add(null to choice)
|
||||||
else
|
else
|
||||||
cv.asNumericValue!!.toInt() to it
|
result.add(values.filterNotNull() to choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhenChoice(val value: IExpression?, // if null, this is the 'else' part
|
class WhenChoice(val values: List<Expression>?, // if null, this is the 'else' part
|
||||||
val statements: AnonymousScope,
|
val statements: AnonymousScope,
|
||||||
override val position: Position) : Node {
|
override val position: Position) : Node {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
value?.linkParents(this)
|
values?.forEach { it.linkParents(this) }
|
||||||
statements.linkParents(this)
|
statements.linkParents(this)
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Choice($value at $position)"
|
return "Choice($values at $position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
@ -715,7 +795,26 @@ class WhenChoice(val value: IExpression?, // if null, this is the 'el
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DirectMemoryWrite(var addressExpression: IExpression, override val position: Position) : Node {
|
class StructDecl(override val name: String,
|
||||||
|
override var statements: MutableList<Statement>, // actually, only vardecls here
|
||||||
|
override val position: Position): Statement(), INameScope {
|
||||||
|
|
||||||
|
override lateinit var parent: Node
|
||||||
|
override val expensiveToInline: Boolean = true
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
this.parent = parent
|
||||||
|
this.statements.forEach { it.linkParents(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val numberOfElements: Int
|
||||||
|
get() = this.statements.size
|
||||||
|
|
||||||
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DirectMemoryWrite(var addressExpression: Expression, override val position: Position) : Node {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.antlr.escape
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.IStatement
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.antlr.escape
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.NumericDatatypes
|
||||||
|
import prog8.ast.base.StringDatatypes
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
|
||||||
var scopelevel = 0
|
private var scopelevel = 0
|
||||||
|
|
||||||
fun indent(s: String) = " ".repeat(scopelevel) + s
|
private fun indent(s: String) = " ".repeat(scopelevel) + s
|
||||||
fun outputln(text: String) = output(text + "\n")
|
private fun outputln(text: String) = output(text + "\n")
|
||||||
fun outputlni(s: Any) = outputln(indent(s.toString()))
|
private fun outputlni(s: Any) = outputln(indent(s.toString()))
|
||||||
fun outputi(s: Any) = output(indent(s.toString()))
|
private fun outputi(s: Any) = output(indent(s.toString()))
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
override fun visit(program: Program) {
|
||||||
outputln("============= PROGRAM ${program.name} (FROM AST) ===============")
|
outputln("============= PROGRAM ${program.name} (FROM AST) ===============")
|
||||||
@ -76,7 +78,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
output("\n")
|
output("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun datatypeString(dt: DataType): String {
|
private fun datatypeString(dt: DataType): String {
|
||||||
return when(dt) {
|
return when(dt) {
|
||||||
in NumericDatatypes -> dt.toString().toLowerCase()
|
in NumericDatatypes -> dt.toString().toLowerCase()
|
||||||
in StringDatatypes -> dt.toString().toLowerCase()
|
in StringDatatypes -> dt.toString().toLowerCase()
|
||||||
@ -85,21 +87,30 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
DataType.ARRAY_UW -> "uword["
|
DataType.ARRAY_UW -> "uword["
|
||||||
DataType.ARRAY_W -> "word["
|
DataType.ARRAY_W -> "word["
|
||||||
DataType.ARRAY_F -> "float["
|
DataType.ARRAY_F -> "float["
|
||||||
else -> "?????"
|
DataType.STRUCT -> "" // the name of the struct is enough
|
||||||
|
else -> "?????2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(structDecl: StructDecl) {
|
||||||
|
outputln("struct ${structDecl.name} {")
|
||||||
|
scopelevel++
|
||||||
|
for(decl in structDecl.statements) {
|
||||||
|
outputi("")
|
||||||
|
decl.accept(this)
|
||||||
|
output("\n")
|
||||||
|
}
|
||||||
|
scopelevel--
|
||||||
|
outputlni("}")
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
if(decl.autoGenerated) {
|
|
||||||
// skip autogenerated vardecl
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
when(decl.type) {
|
when(decl.type) {
|
||||||
VarDeclType.VAR -> {}
|
VarDeclType.VAR -> {}
|
||||||
VarDeclType.CONST -> output("const ")
|
VarDeclType.CONST -> output("const ")
|
||||||
VarDeclType.MEMORY -> output("&")
|
VarDeclType.MEMORY -> output("&")
|
||||||
}
|
}
|
||||||
|
output(decl.struct?.name ?: "")
|
||||||
output(datatypeString(decl.datatype))
|
output(datatypeString(decl.datatype))
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
decl.arraysize!!.index.accept(this)
|
decl.arraysize!!.index.accept(this)
|
||||||
@ -107,7 +118,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
if(decl.isArray)
|
if(decl.isArray)
|
||||||
output("]")
|
output("]")
|
||||||
|
|
||||||
if(decl.zeropage)
|
if(decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||||
output(" @zp")
|
output(" @zp")
|
||||||
output(" ${decl.name} ")
|
output(" ${decl.name} ")
|
||||||
if(decl.value!=null) {
|
if(decl.value!=null) {
|
||||||
@ -126,7 +137,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
true==param.second.stack -> "stack"
|
true==param.second.stack -> "stack"
|
||||||
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
||||||
param.second.statusflag!=null -> param.second.statusflag.toString()
|
param.second.statusflag!=null -> param.second.statusflag.toString()
|
||||||
else -> "?????"
|
else -> "?????1"
|
||||||
}
|
}
|
||||||
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
|
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
|
||||||
if(param.first!==subroutine.parameters.last())
|
if(param.first!==subroutine.parameters.last())
|
||||||
@ -167,10 +178,10 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputStatements(statements: List<IStatement>) {
|
private fun outputStatements(statements: List<Statement>) {
|
||||||
for(stmt in statements) {
|
for(stmt in statements) {
|
||||||
if(stmt is VarDecl && stmt.autoGenerated)
|
if(stmt is VarDecl && stmt.autogeneratedDontRemove)
|
||||||
continue // skip autogenerated decls
|
continue // skip autogenerated decls (to avoid generating a newline)
|
||||||
outputi("")
|
outputi("")
|
||||||
stmt.accept(this)
|
stmt.accept(this)
|
||||||
output("\n")
|
output("\n")
|
||||||
@ -243,27 +254,51 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
output("${label.name}:")
|
output("${label.name}:")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(literalValue: LiteralValue) {
|
override fun visit(numLiteral: NumericLiteralValue) {
|
||||||
when {
|
output(numLiteral.number.toString())
|
||||||
literalValue.isNumeric -> output(literalValue.asNumericValue.toString())
|
|
||||||
literalValue.isString -> output("\"${escape(literalValue.strvalue!!)}\"")
|
|
||||||
literalValue.isArray -> {
|
|
||||||
if(literalValue.arrayvalue!=null) {
|
|
||||||
output("[")
|
|
||||||
for (v in literalValue.arrayvalue) {
|
|
||||||
v.accept(this)
|
|
||||||
if (v !== literalValue.arrayvalue.last())
|
|
||||||
output(", ")
|
|
||||||
}
|
}
|
||||||
output("]")
|
|
||||||
|
override fun visit(refLiteral: ReferenceLiteralValue) {
|
||||||
|
when {
|
||||||
|
refLiteral.isString -> output("\"${escape(refLiteral.str!!)}\"")
|
||||||
|
refLiteral.isArray -> {
|
||||||
|
if(refLiteral.array!=null) {
|
||||||
|
outputListMembers(refLiteral.array.asSequence(), '[', ']')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun outputListMembers(array: Sequence<Expression>, openchar: Char, closechar: Char) {
|
||||||
|
var counter = 0
|
||||||
|
output(openchar.toString())
|
||||||
|
scopelevel++
|
||||||
|
for (v in array) {
|
||||||
|
v.accept(this)
|
||||||
|
if (v !== array.last())
|
||||||
|
output(", ")
|
||||||
|
counter++
|
||||||
|
if (counter > 16) {
|
||||||
|
outputln("")
|
||||||
|
outputi("")
|
||||||
|
counter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scopelevel--
|
||||||
|
output(closechar.toString())
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment) {
|
override fun visit(assignment: Assignment) {
|
||||||
assignment.singleTarget!!.accept(this)
|
if(assignment is VariableInitializationAssignment) {
|
||||||
if(assignment.aug_op!=null)
|
val targetVar = assignment.target.identifier?.targetVarDecl(program.namespace)
|
||||||
|
if(targetVar?.struct != null) {
|
||||||
|
// skip STRUCT init assignments
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assignment.target.accept(this)
|
||||||
|
if (assignment.aug_op != null)
|
||||||
output(" ${assignment.aug_op} ")
|
output(" ${assignment.aug_op} ")
|
||||||
else
|
else
|
||||||
output(" = ")
|
output(" = ")
|
||||||
@ -287,7 +322,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
output("for ")
|
output("for ")
|
||||||
if(forLoop.decltype!=null) {
|
if(forLoop.decltype!=null) {
|
||||||
output(datatypeString(forLoop.decltype))
|
output(datatypeString(forLoop.decltype))
|
||||||
if (forLoop.zeropage)
|
if (forLoop.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || forLoop.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||||
output(" @zp ")
|
output(" @zp ")
|
||||||
else
|
else
|
||||||
output(" ")
|
output(" ")
|
||||||
@ -318,11 +353,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(returnStmt: Return) {
|
override fun visit(returnStmt: Return) {
|
||||||
output("return ")
|
output("return ")
|
||||||
for(v in returnStmt.values) {
|
returnStmt.value?.accept(this)
|
||||||
v.accept(this)
|
|
||||||
if(v!==returnStmt.values.last())
|
|
||||||
output(", ")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
@ -351,8 +382,9 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression) {
|
override fun visit(typecast: TypecastExpression) {
|
||||||
|
output("(")
|
||||||
typecast.expression.accept(this)
|
typecast.expression.accept(this)
|
||||||
output(" as ${datatypeString(typecast.type)} ")
|
output(" as ${datatypeString(typecast.type)}) ")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead) {
|
override fun visit(memread: DirectMemoryRead) {
|
||||||
@ -397,11 +429,15 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whenChoice: WhenChoice) {
|
override fun visit(whenChoice: WhenChoice) {
|
||||||
if(whenChoice.value==null)
|
if(whenChoice.values==null)
|
||||||
outputi("else -> ")
|
outputi("else -> ")
|
||||||
else {
|
else {
|
||||||
outputi("")
|
outputi("")
|
||||||
whenChoice.value.accept(this)
|
for(value in whenChoice.values) {
|
||||||
|
value.accept(this)
|
||||||
|
if(value !== whenChoice.values.last())
|
||||||
|
output(",")
|
||||||
|
}
|
||||||
output(" -> ")
|
output(" -> ")
|
||||||
}
|
}
|
||||||
if(whenChoice.statements.statements.size==1)
|
if(whenChoice.statements.statements.size==1)
|
||||||
@ -410,7 +446,12 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
outputln("")
|
outputln("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(structLv: StructLiteralValue) {
|
||||||
|
outputListMembers(structLv.values.asSequence(), '{', '}')
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
override fun visit(nopStatement: NopStatement) {
|
||||||
TODO("NOP???")
|
output("; NOP @ ${nopStatement.position} $nopStatement")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.RegisterOrPair.*
|
import prog8.ast.base.RegisterOrPair.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.mangledStructMemberName
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.intermediate.IntermediateProgram
|
import prog8.compiler.intermediate.IntermediateProgram
|
||||||
import prog8.compiler.intermediate.Opcode
|
import prog8.compiler.intermediate.Opcode
|
||||||
@ -115,7 +117,7 @@ class HeapValues {
|
|||||||
|
|
||||||
fun get(heapId: Int): HeapValue {
|
fun get(heapId: Int): HeapValue {
|
||||||
return heap[heapId] ?:
|
return heap[heapId] ?:
|
||||||
throw IllegalArgumentException("heapId not found in heap")
|
throw IllegalArgumentException("heapId $heapId not found in heap")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allEntries() = heap.entries
|
fun allEntries() = heap.entries
|
||||||
@ -179,8 +181,8 @@ internal class Compiler(private val program: Program) {
|
|||||||
processVariables(subscope.value)
|
processVariables(subscope.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(statements: List<IStatement>) {
|
private fun translate(statements: List<Statement>) {
|
||||||
for (stmt: IStatement in statements) {
|
for (stmt: Statement in statements) {
|
||||||
generatedLabelSequenceNumber++
|
generatedLabelSequenceNumber++
|
||||||
when (stmt) {
|
when (stmt) {
|
||||||
is Label -> translate(stmt)
|
is Label -> translate(stmt)
|
||||||
@ -213,6 +215,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
is NopStatement -> {}
|
is NopStatement -> {}
|
||||||
is InlineAssembly -> translate(stmt)
|
is InlineAssembly -> translate(stmt)
|
||||||
is WhenStatement -> translate(stmt)
|
is WhenStatement -> translate(stmt)
|
||||||
|
is StructDecl -> {}
|
||||||
else -> TODO("translate statement $stmt to stackvm")
|
else -> TODO("translate statement $stmt to stackvm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,7 +488,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeLabel(scopeStmt: IStatement, postfix: String): String {
|
private fun makeLabel(scopeStmt: Statement, postfix: String): String {
|
||||||
generatedLabelSequenceNumber++
|
generatedLabelSequenceNumber++
|
||||||
return "${scopeStmt.makeScopedName("")}.<s-$generatedLabelSequenceNumber-$postfix>"
|
return "${scopeStmt.makeScopedName("")}.<s-$generatedLabelSequenceNumber-$postfix>"
|
||||||
}
|
}
|
||||||
@ -550,7 +553,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
prog.instr(Opcode.NOP)
|
prog.instr(Opcode.NOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(expr: IExpression) {
|
private fun translate(expr: Expression) {
|
||||||
when(expr) {
|
when(expr) {
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.name)
|
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.name)
|
||||||
@ -591,22 +594,24 @@ internal class Compiler(private val program: Program) {
|
|||||||
is TypecastExpression -> translate(expr)
|
is TypecastExpression -> translate(expr)
|
||||||
is DirectMemoryRead -> translate(expr)
|
is DirectMemoryRead -> translate(expr)
|
||||||
is AddressOf -> translate(expr)
|
is AddressOf -> translate(expr)
|
||||||
|
is StructLiteralValue -> throw CompilerException("a struct Lv should have been flattened as assignments")
|
||||||
else -> {
|
else -> {
|
||||||
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
|
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
|
||||||
when(lv.type) {
|
when(lv.type) {
|
||||||
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, RuntimeValue(lv.type, lv.bytevalue!!))
|
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, RuntimeValue(lv.type, lv.number.toShort()))
|
||||||
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, RuntimeValue(lv.type, lv.wordvalue!!))
|
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, RuntimeValue(lv.type, lv.number.toInt()))
|
||||||
DataType.FLOAT -> prog.instr(Opcode.PUSH_FLOAT, RuntimeValue(lv.type, lv.floatvalue!!))
|
DataType.FLOAT -> prog.instr(Opcode.PUSH_FLOAT, RuntimeValue(lv.type, lv.number.toDouble()))
|
||||||
in StringDatatypes -> {
|
// TODO what about these ref types:
|
||||||
if(lv.heapId==null)
|
// in StringDatatypes -> {
|
||||||
throw CompilerException("string should have been moved into heap ${lv.position}")
|
// if(lv.heapId==null)
|
||||||
TODO("push address of string with PUSH_ADDR_HEAPVAR")
|
// throw CompilerException("string should have been moved into heap ${lv.position}")
|
||||||
}
|
// TODO("push address of string with PUSH_ADDR_HEAPVAR")
|
||||||
in ArrayDatatypes -> {
|
// }
|
||||||
if(lv.heapId==null)
|
// in ArrayDatatypes -> {
|
||||||
throw CompilerException("array should have been moved into heap ${lv.position}")
|
// if(lv.heapId==null)
|
||||||
TODO("push address of array with PUSH_ADDR_HEAPVAR")
|
// throw CompilerException("array should have been moved into heap ${lv.position}")
|
||||||
}
|
// TODO("push address of array with PUSH_ADDR_HEAPVAR")
|
||||||
|
// }
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,11 +678,11 @@ internal class Compiler(private val program: Program) {
|
|||||||
throw CompilerException("const ref should have been const-folded away")
|
throw CompilerException("const ref should have been const-folded away")
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
when (target.datatype) {
|
when (target.datatype) {
|
||||||
DataType.UBYTE -> prog.instr(Opcode.PUSH_MEM_UB, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
|
DataType.UBYTE -> prog.instr(Opcode.PUSH_MEM_UB, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
|
||||||
DataType.BYTE-> prog.instr(Opcode.PUSH_MEM_B, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
|
DataType.BYTE-> prog.instr(Opcode.PUSH_MEM_B, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
|
||||||
DataType.UWORD -> prog.instr(Opcode.PUSH_MEM_UW, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
|
DataType.UWORD -> prog.instr(Opcode.PUSH_MEM_UW, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
|
||||||
DataType.WORD -> prog.instr(Opcode.PUSH_MEM_W, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
|
DataType.WORD -> prog.instr(Opcode.PUSH_MEM_W, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
|
||||||
DataType.FLOAT -> prog.instr(Opcode.PUSH_MEM_FLOAT, RuntimeValue(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!))
|
DataType.FLOAT -> prog.instr(Opcode.PUSH_MEM_FLOAT, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number))
|
||||||
else -> throw CompilerException("invalid datatype for memory variable expression: $target")
|
else -> throw CompilerException("invalid datatype for memory variable expression: $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -713,7 +718,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateBuiltinFunctionCall(funcname: String, args: List<IExpression>) {
|
private fun translateBuiltinFunctionCall(funcname: String, args: List<Expression>) {
|
||||||
// some builtin functions are implemented directly as vm opcodes
|
// some builtin functions are implemented directly as vm opcodes
|
||||||
|
|
||||||
if(funcname == "swap") {
|
if(funcname == "swap") {
|
||||||
@ -743,7 +748,8 @@ internal class Compiler(private val program: Program) {
|
|||||||
val arg=args.single()
|
val arg=args.single()
|
||||||
when (arg.inferType(program)) {
|
when (arg.inferType(program)) {
|
||||||
DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str")
|
DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str")
|
||||||
else -> throw CompilerException("wrong datatype for len()")
|
in ArrayDatatypes -> throw CompilerException("len() of an array type should have been const-folded")
|
||||||
|
else -> throw CompilerException("wrong datatype for len() $arg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"any", "all" -> {
|
"any", "all" -> {
|
||||||
@ -896,7 +902,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateSwap(args: List<IExpression>) {
|
private fun translateSwap(args: List<Expression>) {
|
||||||
// swap(x,y) is treated differently, it's not a normal function call
|
// swap(x,y) is treated differently, it's not a normal function call
|
||||||
if (args.size != 2)
|
if (args.size != 2)
|
||||||
throw AstException("swap requires 2 arguments")
|
throw AstException("swap requires 2 arguments")
|
||||||
@ -919,7 +925,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position) {
|
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<Expression>, callPosition: Position) {
|
||||||
// evaluate the arguments and assign them into the subroutine's argument variables.
|
// evaluate the arguments and assign them into the subroutine's argument variables.
|
||||||
var restoreX = Register.X in subroutine.asmClobbers
|
var restoreX = Register.X in subroutine.asmClobbers
|
||||||
if(restoreX)
|
if(restoreX)
|
||||||
@ -966,7 +972,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position, restoreXinitial: Boolean): Boolean {
|
private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List<Expression>, callPosition: Position, restoreXinitial: Boolean): Boolean {
|
||||||
var restoreX = restoreXinitial
|
var restoreX = restoreXinitial
|
||||||
if (subroutine.parameters.size != subroutine.asmParameterRegisters.size)
|
if (subroutine.parameters.size != subroutine.asmParameterRegisters.size)
|
||||||
TODO("no support yet for mix of register and non-register subroutine arguments")
|
TODO("no support yet for mix of register and non-register subroutine arguments")
|
||||||
@ -982,7 +988,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
} else {
|
} else {
|
||||||
when (arg.second.registerOrPair!!) {
|
when (arg.second.registerOrPair!!) {
|
||||||
A -> {
|
A -> {
|
||||||
val assign = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, arg.first, callPosition)
|
val assign = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, arg.first, callPosition)
|
||||||
assign.linkParents(arguments[0].parent)
|
assign.linkParents(arguments[0].parent)
|
||||||
translate(assign)
|
translate(assign)
|
||||||
}
|
}
|
||||||
@ -991,12 +997,12 @@ internal class Compiler(private val program: Program) {
|
|||||||
prog.instr(Opcode.RSAVEX)
|
prog.instr(Opcode.RSAVEX)
|
||||||
restoreX = true
|
restoreX = true
|
||||||
}
|
}
|
||||||
val assign = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, arg.first, callPosition)
|
val assign = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, arg.first, callPosition)
|
||||||
assign.linkParents(arguments[0].parent)
|
assign.linkParents(arguments[0].parent)
|
||||||
translate(assign)
|
translate(assign)
|
||||||
}
|
}
|
||||||
Y -> {
|
Y -> {
|
||||||
val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, arg.first, callPosition)
|
val assign = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, arg.first, callPosition)
|
||||||
assign.linkParents(arguments[0].parent)
|
assign.linkParents(arguments[0].parent)
|
||||||
translate(assign)
|
translate(assign)
|
||||||
}
|
}
|
||||||
@ -1005,15 +1011,15 @@ internal class Compiler(private val program: Program) {
|
|||||||
prog.instr(Opcode.RSAVEX)
|
prog.instr(Opcode.RSAVEX)
|
||||||
restoreX = true
|
restoreX = true
|
||||||
}
|
}
|
||||||
val valueA: IExpression
|
val valueA: Expression
|
||||||
val valueX: IExpression
|
val valueX: Expression
|
||||||
val paramDt = arg.first.inferType(program)
|
val paramDt = arg.first.inferType(program)
|
||||||
when (paramDt) {
|
when (paramDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
valueA = arg.first
|
valueA = arg.first
|
||||||
valueX = LiteralValue.optimalInteger(0, callPosition)
|
valueX = NumericLiteralValue.optimalInteger(0, callPosition)
|
||||||
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
|
val assignA = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, valueA, callPosition)
|
||||||
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
|
val assignX = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, valueX, callPosition)
|
||||||
assignA.linkParents(arguments[0].parent)
|
assignA.linkParents(arguments[0].parent)
|
||||||
assignX.linkParents(arguments[0].parent)
|
assignX.linkParents(arguments[0].parent)
|
||||||
translate(assignA)
|
translate(assignA)
|
||||||
@ -1028,15 +1034,15 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AY -> {
|
AY -> {
|
||||||
val valueA: IExpression
|
val valueA: Expression
|
||||||
val valueY: IExpression
|
val valueY: Expression
|
||||||
val paramDt = arg.first.inferType(program)
|
val paramDt = arg.first.inferType(program)
|
||||||
when (paramDt) {
|
when (paramDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
valueA = arg.first
|
valueA = arg.first
|
||||||
valueY = LiteralValue.optimalInteger(0, callPosition)
|
valueY = NumericLiteralValue.optimalInteger(0, callPosition)
|
||||||
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
|
val assignA = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, valueA, callPosition)
|
||||||
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
|
val assignY = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, valueY, callPosition)
|
||||||
assignA.linkParents(arguments[0].parent)
|
assignA.linkParents(arguments[0].parent)
|
||||||
assignY.linkParents(arguments[0].parent)
|
assignY.linkParents(arguments[0].parent)
|
||||||
translate(assignA)
|
translate(assignA)
|
||||||
@ -1055,15 +1061,15 @@ internal class Compiler(private val program: Program) {
|
|||||||
prog.instr(Opcode.RSAVEX)
|
prog.instr(Opcode.RSAVEX)
|
||||||
restoreX = true
|
restoreX = true
|
||||||
}
|
}
|
||||||
val valueX: IExpression
|
val valueX: Expression
|
||||||
val valueY: IExpression
|
val valueY: Expression
|
||||||
val paramDt = arg.first.inferType(program)
|
val paramDt = arg.first.inferType(program)
|
||||||
when (paramDt) {
|
when (paramDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
valueX = arg.first
|
valueX = arg.first
|
||||||
valueY = LiteralValue.optimalInteger(0, callPosition)
|
valueY = NumericLiteralValue.optimalInteger(0, callPosition)
|
||||||
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
|
val assignX = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, valueX, callPosition)
|
||||||
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
|
val assignY = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, valueY, callPosition)
|
||||||
assignX.linkParents(arguments[0].parent)
|
assignX.linkParents(arguments[0].parent)
|
||||||
assignY.linkParents(arguments[0].parent)
|
assignY.linkParents(arguments[0].parent)
|
||||||
translate(assignX)
|
translate(assignX)
|
||||||
@ -1251,10 +1257,10 @@ internal class Compiler(private val program: Program) {
|
|||||||
prog.instr(opcode)
|
prog.instr(opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateBitshiftedOperator(operator: String, leftDt: DataType, amount: LiteralValue?) {
|
private fun translateBitshiftedOperator(operator: String, leftDt: DataType, amount: NumericLiteralValue?) {
|
||||||
if(amount?.asIntegerValue == null)
|
if(amount?.number?.toInt() == null)
|
||||||
throw FatalAstException("bitshift operators should only have constant integer value as right operand")
|
throw FatalAstException("bitshift operators should only have constant integer value as right operand")
|
||||||
var shifts=amount.asIntegerValue
|
var shifts=amount.number.toInt()
|
||||||
if(shifts<0)
|
if(shifts<0)
|
||||||
throw FatalAstException("bitshift value should be >= 0")
|
throw FatalAstException("bitshift value should be >= 0")
|
||||||
|
|
||||||
@ -1380,7 +1386,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stmt.target.memoryAddress != null -> {
|
stmt.target.memoryAddress != null -> {
|
||||||
val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
|
val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
|
||||||
if(address!=null) {
|
if(address!=null) {
|
||||||
when(stmt.operator) {
|
when(stmt.operator) {
|
||||||
"++" -> prog.instr(Opcode.INC_MEMORY, RuntimeValue(DataType.UWORD, address))
|
"++" -> prog.instr(Opcode.INC_MEMORY, RuntimeValue(DataType.UWORD, address))
|
||||||
@ -1400,17 +1406,30 @@ internal class Compiler(private val program: Program) {
|
|||||||
|
|
||||||
private fun translate(stmt: Assignment) {
|
private fun translate(stmt: Assignment) {
|
||||||
prog.line(stmt.position)
|
prog.line(stmt.position)
|
||||||
translate(stmt.value)
|
if(stmt.value is StructLiteralValue) {
|
||||||
|
// flatten into individual struct member assignments
|
||||||
val assignTarget= stmt.singleTarget
|
val identifier = stmt.target.identifier!!
|
||||||
if(assignTarget==null) {
|
val identifierName = identifier.nameInSource.single()
|
||||||
// we're dealing with multiple return values
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
translateMultiReturnAssignment(stmt)
|
val struct = targetVar.struct!!
|
||||||
|
val sourcevalues = (stmt.value as StructLiteralValue).values
|
||||||
|
val assignments = struct.statements.zip(sourcevalues).map { member ->
|
||||||
|
val decl = member.first as VarDecl
|
||||||
|
val mangled = mangledStructMemberName(identifierName, decl.name)
|
||||||
|
val idref = IdentifierReference(listOf(mangled), stmt.position)
|
||||||
|
val assign = Assignment(AssignTarget(null, idref, null, null, stmt.position),
|
||||||
|
null, member.second, member.second.position)
|
||||||
|
assign.linkParents(stmt)
|
||||||
|
assign
|
||||||
|
}
|
||||||
|
assignments.forEach { translate(it) }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translate(stmt.value)
|
||||||
|
|
||||||
val valueDt = stmt.value.inferType(program)
|
val valueDt = stmt.value.inferType(program)
|
||||||
val targetDt = assignTarget.inferType(program, stmt)
|
val targetDt = stmt.target.inferType(program, stmt)
|
||||||
if(valueDt!=targetDt) {
|
if(valueDt!=targetDt) {
|
||||||
// convert value to target datatype if possible
|
// convert value to target datatype if possible
|
||||||
// @todo use convertType()????
|
// @todo use convertType()????
|
||||||
@ -1461,45 +1480,38 @@ internal class Compiler(private val program: Program) {
|
|||||||
throw CompilerException("augmented assignment should have been converted to regular assignment already")
|
throw CompilerException("augmented assignment should have been converted to regular assignment already")
|
||||||
|
|
||||||
// pop the result value back into the assignment target
|
// pop the result value back into the assignment target
|
||||||
val datatype = assignTarget.inferType(program, stmt)!!
|
val datatype = stmt.target.inferType(program, stmt)!!
|
||||||
popValueIntoTarget(assignTarget, datatype)
|
popValueIntoTarget(stmt.target, datatype)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pushHeapVarAddress(value: IExpression, removeLastOpcode: Boolean) {
|
private fun pushHeapVarAddress(value: Expression, removeLastOpcode: Boolean) {
|
||||||
when (value) {
|
if (value is IdentifierReference) {
|
||||||
is LiteralValue -> throw CompilerException("can only push address of string or array (value on the heap)")
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val vardecl = value.targetVarDecl(program.namespace)!!
|
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||||
if(removeLastOpcode) prog.removeLastInstruction()
|
if(removeLastOpcode) prog.removeLastInstruction()
|
||||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("can only take address of a literal string value or a string/array variable")
|
else throw CompilerException("can only take address of a literal string value or a string/array variable")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pushFloatAddress(value: IExpression) {
|
private fun pushFloatAddress(value: Expression) {
|
||||||
when (value) {
|
if (value is IdentifierReference) {
|
||||||
is LiteralValue -> throw CompilerException("can only push address of float that is a variable on the heap")
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val vardecl = value.targetVarDecl(program.namespace)!!
|
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
|
else throw CompilerException("can only take address of a the float as constant literal or variable")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
private fun pushStructAddress(value: Expression) {
|
||||||
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(program.namespace)
|
if (value is IdentifierReference) {
|
||||||
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
// notice that the mangled name of the first struct member is the start address of this struct var
|
||||||
// this is the only case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||||
// the return values are already on the stack (the subroutine call puts them there)
|
val firstStructMember = (vardecl.struct!!.statements.first() as VarDecl).name
|
||||||
if(stmt.targets.size!=targetStmt.asmReturnvaluesRegisters.size)
|
val firstVarName = listOf(vardecl.name, firstStructMember)
|
||||||
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
|
// find the flattened var that belongs to this first struct member
|
||||||
for(target in stmt.targets) {
|
val firstVar = value.definingScope().lookup(firstVarName, value) as VarDecl
|
||||||
val dt = target.inferType(program, stmt)
|
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = firstVar.scopedname) // TODO
|
||||||
popValueIntoTarget(target, dt!!)
|
|
||||||
}
|
}
|
||||||
} else throw CompilerException("can only use multiple assignment targets on an asmsub call")
|
else throw CompilerException("can only take address of a the float as constant literal or variable")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
|
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
|
||||||
@ -1514,7 +1526,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
val opcode = opcodePopmem(datatype)
|
val opcode = opcodePopmem(datatype)
|
||||||
val address = target.value?.constValue(program)!!.asIntegerValue!!
|
val address = target.value?.constValue(program)!!.number.toInt()
|
||||||
prog.instr(opcode, RuntimeValue(DataType.UWORD, address))
|
prog.instr(opcode, RuntimeValue(DataType.UWORD, address))
|
||||||
}
|
}
|
||||||
VarDeclType.CONST -> throw CompilerException("cannot assign to const")
|
VarDeclType.CONST -> throw CompilerException("cannot assign to const")
|
||||||
@ -1524,7 +1536,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
|
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
|
||||||
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
|
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
|
||||||
assignTarget.memoryAddress != null -> {
|
assignTarget.memoryAddress != null -> {
|
||||||
val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.asIntegerValue
|
val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
|
||||||
if(address!=null) {
|
if(address!=null) {
|
||||||
// const integer address given
|
// const integer address given
|
||||||
prog.instr(Opcode.POP_MEM_BYTE, arg= RuntimeValue(DataType.UWORD, address))
|
prog.instr(Opcode.POP_MEM_BYTE, arg= RuntimeValue(DataType.UWORD, address))
|
||||||
@ -1538,9 +1550,8 @@ internal class Compiler(private val program: Program) {
|
|||||||
|
|
||||||
private fun translate(stmt: Return) {
|
private fun translate(stmt: Return) {
|
||||||
// put the return values on the stack, in reversed order. The caller will accept them.
|
// put the return values on the stack, in reversed order. The caller will accept them.
|
||||||
for(value in stmt.values.reversed()) {
|
if(stmt.value!=null)
|
||||||
translate(value)
|
translate(stmt.value!!)
|
||||||
}
|
|
||||||
prog.line(stmt.position)
|
prog.line(stmt.position)
|
||||||
prog.instr(Opcode.RETURN)
|
prog.instr(Opcode.RETURN)
|
||||||
}
|
}
|
||||||
@ -1552,17 +1563,19 @@ internal class Compiler(private val program: Program) {
|
|||||||
private fun translate(loop: ForLoop) {
|
private fun translate(loop: ForLoop) {
|
||||||
if(loop.body.containsNoCodeNorVars()) return
|
if(loop.body.containsNoCodeNorVars()) return
|
||||||
prog.line(loop.position)
|
prog.line(loop.position)
|
||||||
val loopVarName: String
|
val loopVarName: String?
|
||||||
val loopVarDt: DataType
|
val loopRegister: Register?
|
||||||
|
val loopvalueDt: DataType
|
||||||
|
|
||||||
if(loop.loopRegister!=null) {
|
if(loop.loopRegister!=null) {
|
||||||
val reg = loop.loopRegister
|
loopVarName = null
|
||||||
loopVarName = reg.name
|
loopRegister = loop.loopRegister
|
||||||
loopVarDt = DataType.UBYTE
|
loopvalueDt = DataType.UBYTE
|
||||||
} else {
|
} else {
|
||||||
val loopvar = loop.loopVar!!.targetVarDecl(program.namespace)!!
|
val loopvar = loop.loopVar!!.targetVarDecl(program.namespace)!!
|
||||||
loopVarName = loopvar.scopedname
|
loopVarName = loopvar.scopedname
|
||||||
loopVarDt = loopvar.datatype
|
loopvalueDt = loopvar.datatype
|
||||||
|
loopRegister = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if(loop.iterable is RangeExpr) {
|
if(loop.iterable is RangeExpr) {
|
||||||
@ -1575,7 +1588,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
throw CompilerException("loop over just 1 value should have been optimized away")
|
throw CompilerException("loop over just 1 value should have been optimized away")
|
||||||
if((range.last-range.first) % range.step != 0)
|
if((range.last-range.first) % range.step != 0)
|
||||||
throw CompilerException("range first and last must be exactly inclusive")
|
throw CompilerException("range first and last must be exactly inclusive")
|
||||||
when (loopVarDt) {
|
when (loopvalueDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (range.first < 0 || range.first > 255 || range.last < 0 || range.last > 255)
|
if (range.first < 0 || range.first > 255 || range.last < 0 || range.last > 255)
|
||||||
throw CompilerException("range out of bounds for ubyte")
|
throw CompilerException("range out of bounds for ubyte")
|
||||||
@ -1594,7 +1607,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
}
|
}
|
||||||
else -> throw CompilerException("range must be byte or word")
|
else -> throw CompilerException("range must be byte or word")
|
||||||
}
|
}
|
||||||
translateForOverConstantRange(loopVarName, loopVarDt, range, loop.body)
|
translateForOverConstantRange(loopVarName, loopRegister, loopvalueDt, range, loop.body)
|
||||||
} else {
|
} else {
|
||||||
// loop over a range where one or more of the start, last or step values is not a constant
|
// loop over a range where one or more of the start, last or step values is not a constant
|
||||||
if(loop.loopRegister!=null) {
|
if(loop.loopRegister!=null) {
|
||||||
@ -1610,18 +1623,19 @@ internal class Compiler(private val program: Program) {
|
|||||||
loop.iterable is IdentifierReference -> {
|
loop.iterable is IdentifierReference -> {
|
||||||
val idRef = loop.iterable as IdentifierReference
|
val idRef = loop.iterable as IdentifierReference
|
||||||
val vardecl = idRef.targetVarDecl(program.namespace)!!
|
val vardecl = idRef.targetVarDecl(program.namespace)!!
|
||||||
val iterableValue = vardecl.value as LiteralValue
|
// TODO check loop over iterable or not
|
||||||
if(iterableValue.type !in IterableDatatypes)
|
// val iterableValue = vardecl.value as LiteralValue
|
||||||
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
|
// if(iterableValue.type !in IterableDatatypes)
|
||||||
translateForOverIterableVar(loop, loopVarDt, iterableValue)
|
// throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
|
||||||
|
translateForOverIterableVar(loop, loopvalueDt, vardecl.value as ReferenceLiteralValue)
|
||||||
}
|
}
|
||||||
loop.iterable is LiteralValue -> throw CompilerException("literal value in loop must have been moved to heap already $loop")
|
// TODO what's this: loop.iterable is LiteralValue -> throw CompilerException("literal value in loop must have been moved to heap already $loop")
|
||||||
else -> throw CompilerException("loopvar is something strange ${loop.iterable}")
|
else -> throw CompilerException("loopvar is something strange ${loop.iterable}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: LiteralValue) {
|
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: ReferenceLiteralValue) {
|
||||||
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")
|
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)
|
||||||
@ -1633,16 +1647,16 @@ internal class Compiler(private val program: Program) {
|
|||||||
when(iterableValue.type) {
|
when(iterableValue.type) {
|
||||||
!in IterableDatatypes -> throw CompilerException("non-iterableValue type")
|
!in IterableDatatypes -> throw CompilerException("non-iterableValue type")
|
||||||
DataType.STR, DataType.STR_S -> {
|
DataType.STR, DataType.STR_S -> {
|
||||||
numElements = iterableValue.strvalue!!.length
|
numElements = iterableValue.str!!.length
|
||||||
if(numElements>255) throw CompilerException("string length > 255")
|
if(numElements>255) throw CompilerException("string length > 255")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
numElements = iterableValue.arrayvalue?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
|
numElements = iterableValue.array?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
|
||||||
if(numElements>255) throw CompilerException("string length > 255")
|
if(numElements>255) throw CompilerException("string length > 255")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
numElements = iterableValue.arrayvalue?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
|
numElements = iterableValue.array?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
|
||||||
if(numElements>255) throw CompilerException("string length > 255")
|
if(numElements>255) throw CompilerException("string length > 255")
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
@ -1686,7 +1700,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
AssignTarget(null, loop.loopVar!!.copy(), null, null, loop.position)
|
AssignTarget(null, loop.loopVar!!.copy(), null, null, loop.position)
|
||||||
val arrayspec = ArrayIndex(IdentifierReference(listOf(ForLoop.iteratorLoopcounterVarname), loop.position), loop.position)
|
val arrayspec = ArrayIndex(IdentifierReference(listOf(ForLoop.iteratorLoopcounterVarname), loop.position), loop.position)
|
||||||
val assignLv = Assignment(
|
val assignLv = Assignment(
|
||||||
listOf(assignTarget), null,
|
assignTarget, null,
|
||||||
ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position),
|
ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position),
|
||||||
loop.position)
|
loop.position)
|
||||||
assignLv.linkParents(loop.body)
|
assignLv.linkParents(loop.body)
|
||||||
@ -1706,7 +1720,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
continueStmtLabelStack.pop()
|
continueStmtLabelStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverConstantRange(varname: String, varDt: DataType, range: IntProgression, body: AnonymousScope) {
|
private fun translateForOverConstantRange(varname: String?, loopregister: Register?, varDt: DataType, range: IntProgression, body: AnonymousScope) {
|
||||||
/**
|
/**
|
||||||
* for LV in start..last { body }
|
* for LV in start..last { body }
|
||||||
* (and we already know that the range is not empty, and first and last are exactly inclusive.)
|
* (and we already know that the range is not empty, and first and last are exactly inclusive.)
|
||||||
@ -1734,7 +1748,9 @@ internal class Compiler(private val program: Program) {
|
|||||||
breakStmtLabelStack.push(breakLabel)
|
breakStmtLabelStack.push(breakLabel)
|
||||||
|
|
||||||
prog.instr(opcodePush(varDt), RuntimeValue(varDt, range.first))
|
prog.instr(opcodePush(varDt), RuntimeValue(varDt, range.first))
|
||||||
prog.instr(opcodePopvar(varDt), callLabel = varname)
|
val variableLabel = varname ?: loopregister?.name
|
||||||
|
|
||||||
|
prog.instr(opcodePopvar(varDt), callLabel = variableLabel)
|
||||||
prog.label(loopLabel)
|
prog.label(loopLabel)
|
||||||
translate(body)
|
translate(body)
|
||||||
prog.label(continueLabel)
|
prog.label(continueLabel)
|
||||||
@ -1742,25 +1758,25 @@ internal class Compiler(private val program: Program) {
|
|||||||
when {
|
when {
|
||||||
range.step in 1..numberOfIncDecsForOptimize -> {
|
range.step in 1..numberOfIncDecsForOptimize -> {
|
||||||
repeat(range.step) {
|
repeat(range.step) {
|
||||||
prog.instr(opcodeIncvar(varDt), callLabel = varname)
|
prog.instr(opcodeIncvar(varDt), callLabel = variableLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
range.step in -1 downTo -numberOfIncDecsForOptimize -> {
|
range.step in -1 downTo -numberOfIncDecsForOptimize -> {
|
||||||
repeat(abs(range.step)) {
|
repeat(abs(range.step)) {
|
||||||
prog.instr(opcodeDecvar(varDt), callLabel = varname)
|
prog.instr(opcodeDecvar(varDt), callLabel = variableLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
range.step>numberOfIncDecsForOptimize -> {
|
range.step>numberOfIncDecsForOptimize -> {
|
||||||
prog.instr(opcodePushvar(varDt), callLabel = varname)
|
prog.instr(opcodePushvar(varDt), callLabel = variableLabel)
|
||||||
prog.instr(opcodePush(varDt), RuntimeValue(varDt, range.step))
|
prog.instr(opcodePush(varDt), RuntimeValue(varDt, range.step))
|
||||||
prog.instr(opcodeAdd(varDt))
|
prog.instr(opcodeAdd(varDt))
|
||||||
prog.instr(opcodePopvar(varDt), callLabel = varname)
|
prog.instr(opcodePopvar(varDt), callLabel = variableLabel)
|
||||||
}
|
}
|
||||||
range.step<numberOfIncDecsForOptimize -> {
|
range.step<numberOfIncDecsForOptimize -> {
|
||||||
prog.instr(opcodePushvar(varDt), callLabel = varname)
|
prog.instr(opcodePushvar(varDt), callLabel = variableLabel)
|
||||||
prog.instr(opcodePush(varDt), RuntimeValue(varDt, abs(range.step)))
|
prog.instr(opcodePush(varDt), RuntimeValue(varDt, abs(range.step)))
|
||||||
prog.instr(opcodeSub(varDt))
|
prog.instr(opcodeSub(varDt))
|
||||||
prog.instr(opcodePopvar(varDt), callLabel = varname)
|
prog.instr(opcodePopvar(varDt), callLabel = variableLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1768,7 +1784,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
// optimize for the for loop that counts to 0
|
// optimize for the for loop that counts to 0
|
||||||
prog.instr(if(range.first>0) Opcode.BPOS else Opcode.BNEG, callLabel = loopLabel)
|
prog.instr(if(range.first>0) Opcode.BPOS else Opcode.BNEG, callLabel = loopLabel)
|
||||||
} else {
|
} else {
|
||||||
prog.instr(opcodePushvar(varDt), callLabel = varname)
|
prog.instr(opcodePushvar(varDt), callLabel = variableLabel)
|
||||||
val checkValue =
|
val checkValue =
|
||||||
when (varDt) {
|
when (varDt) {
|
||||||
DataType.UBYTE -> (range.last + range.step) and 255
|
DataType.UBYTE -> (range.last + range.step) and 255
|
||||||
@ -1827,14 +1843,14 @@ internal class Compiler(private val program: Program) {
|
|||||||
AssignTarget(register, null, null, null, range.position)
|
AssignTarget(register, null, null, null, range.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
val startAssignment = Assignment(listOf(makeAssignmentTarget()), null, range.from, range.position)
|
val startAssignment = Assignment(makeAssignmentTarget(), null, range.from, range.position)
|
||||||
startAssignment.linkParents(body)
|
startAssignment.linkParents(body)
|
||||||
translate(startAssignment)
|
translate(startAssignment)
|
||||||
|
|
||||||
val loopLabel = makeLabel(body, "loop")
|
val loopLabel = makeLabel(body, "loop")
|
||||||
val continueLabel = makeLabel(body, "continue")
|
val continueLabel = makeLabel(body, "continue")
|
||||||
val breakLabel = makeLabel(body, "break")
|
val breakLabel = makeLabel(body, "break")
|
||||||
val literalStepValue = (range.step as? LiteralValue)?.asNumericValue?.toInt()
|
val literalStepValue = (range.step as? NumericLiteralValue)?.number?.toInt()
|
||||||
|
|
||||||
continueStmtLabelStack.push(continueLabel)
|
continueStmtLabelStack.push(continueLabel)
|
||||||
breakStmtLabelStack.push(breakLabel)
|
breakStmtLabelStack.push(breakLabel)
|
||||||
@ -2005,7 +2021,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
DataType.UBYTE -> when(sourceDt) {
|
DataType.UBYTE -> when(sourceDt) {
|
||||||
DataType.UBYTE -> {}
|
DataType.UBYTE -> {}
|
||||||
DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UB)
|
DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UB)
|
||||||
DataType.UWORD-> prog.instr(Opcode.CAST_UW_TO_UB)
|
DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_UB)
|
||||||
DataType.WORD-> prog.instr(Opcode.CAST_W_TO_UB)
|
DataType.WORD-> prog.instr(Opcode.CAST_W_TO_UB)
|
||||||
DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_UB)
|
DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_UB)
|
||||||
else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check")
|
else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check")
|
||||||
@ -2048,7 +2064,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
|
|
||||||
private fun translate(memread: DirectMemoryRead) {
|
private fun translate(memread: DirectMemoryRead) {
|
||||||
// for now, only a single memory location (ubyte) is read at a time.
|
// for now, only a single memory location (ubyte) is read at a time.
|
||||||
val address = memread.addressExpression.constValue(program)?.asIntegerValue
|
val address = memread.addressExpression.constValue(program)?.number?.toInt()
|
||||||
if(address!=null) {
|
if(address!=null) {
|
||||||
prog.instr(Opcode.PUSH_MEM_UB, arg = RuntimeValue(DataType.UWORD, address))
|
prog.instr(Opcode.PUSH_MEM_UB, arg = RuntimeValue(DataType.UWORD, address))
|
||||||
} else {
|
} else {
|
||||||
@ -2060,7 +2076,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
private fun translate(memwrite: DirectMemoryWrite) {
|
private fun translate(memwrite: DirectMemoryWrite) {
|
||||||
// for now, only a single memory location (ubyte) is written at a time.
|
// for now, only a single memory location (ubyte) is written at a time.
|
||||||
// TODO inline this function (it's only used once)
|
// TODO inline this function (it's only used once)
|
||||||
val address = memwrite.addressExpression.constValue(program)?.asIntegerValue
|
val address = memwrite.addressExpression.constValue(program)?.number?.toInt()
|
||||||
if(address!=null) {
|
if(address!=null) {
|
||||||
prog.instr(Opcode.POP_MEM_BYTE, arg = RuntimeValue(DataType.UWORD, address))
|
prog.instr(Opcode.POP_MEM_BYTE, arg = RuntimeValue(DataType.UWORD, address))
|
||||||
} else {
|
} else {
|
||||||
@ -2077,6 +2093,9 @@ internal class Compiler(private val program: Program) {
|
|||||||
else if(target.datatype== DataType.FLOAT) {
|
else if(target.datatype== DataType.FLOAT) {
|
||||||
pushFloatAddress(addrof.identifier)
|
pushFloatAddress(addrof.identifier)
|
||||||
}
|
}
|
||||||
|
else if(target.datatype == DataType.STRUCT) {
|
||||||
|
pushStructAddress(addrof.identifier)
|
||||||
|
}
|
||||||
else
|
else
|
||||||
throw CompilerException("cannot take memory pointer $addrof")
|
throw CompilerException("cannot take memory pointer $addrof")
|
||||||
}
|
}
|
||||||
@ -2095,24 +2114,20 @@ internal class Compiler(private val program: Program) {
|
|||||||
val endOfWhenLabel = makeLabel(whenstmt, "when_end")
|
val endOfWhenLabel = makeLabel(whenstmt, "when_end")
|
||||||
|
|
||||||
val choiceLabels = mutableListOf<String>()
|
val choiceLabels = mutableListOf<String>()
|
||||||
var previousValue = 0
|
|
||||||
for(choice in whenstmt.choiceValues(program)) {
|
for(choice in whenstmt.choiceValues(program)) {
|
||||||
val choiceVal = choice.first
|
if(choice.first==null) {
|
||||||
if(choiceVal==null) {
|
|
||||||
// the else clause
|
// the else clause
|
||||||
translate(choice.second.statements)
|
translate(choice.second.statements)
|
||||||
} else {
|
} else {
|
||||||
val subtract = choiceVal-previousValue
|
val choiceVal = choice.first!!.single()
|
||||||
previousValue = choiceVal
|
val rval = RuntimeValue(conditionDt!!, choiceVal)
|
||||||
if (conditionDt in ByteDatatypes) {
|
if (conditionDt in ByteDatatypes) {
|
||||||
prog.instr(Opcode.DUP_B)
|
prog.instr(Opcode.DUP_B)
|
||||||
prog.instr(Opcode.PUSH_BYTE, RuntimeValue(conditionDt!!, subtract))
|
prog.instr(opcodeCompare(conditionDt), rval)
|
||||||
prog.instr(opcodeCompare(conditionDt))
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
prog.instr(Opcode.DUP_W)
|
prog.instr(Opcode.DUP_W)
|
||||||
prog.instr(Opcode.PUSH_WORD, RuntimeValue(conditionDt!!, subtract))
|
prog.instr(opcodeCompare(conditionDt), rval)
|
||||||
prog.instr(opcodeCompare(conditionDt))
|
|
||||||
}
|
}
|
||||||
val choiceLabel = makeLabel(whenstmt, "choice_$choiceVal")
|
val choiceLabel = makeLabel(whenstmt, "choice_$choiceVal")
|
||||||
choiceLabels.add(choiceLabel)
|
choiceLabels.add(choiceLabel)
|
||||||
@ -2122,12 +2137,12 @@ internal class Compiler(private val program: Program) {
|
|||||||
prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel)
|
prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel)
|
||||||
|
|
||||||
for(choice in whenstmt.choices.zip(choiceLabels)) {
|
for(choice in whenstmt.choices.zip(choiceLabels)) {
|
||||||
// TODO the various code blocks here, don't forget to jump to the end label at their eind
|
|
||||||
prog.label(choice.second)
|
prog.label(choice.second)
|
||||||
prog.instr(Opcode.NOP)
|
translate(choice.first.statements)
|
||||||
prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel)
|
prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prog.removeLastInstruction() // remove the last jump, that can fall through to here
|
||||||
prog.label(endOfWhenLabel)
|
prog.label(endOfWhenLabel)
|
||||||
|
|
||||||
if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE)
|
if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE)
|
||||||
|
@ -2,12 +2,9 @@ package prog8.compiler
|
|||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.checkIdentifiers
|
|
||||||
import prog8.ast.base.checkValid
|
|
||||||
import prog8.ast.base.reorderStatements
|
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.target.c64.AsmGen
|
import prog8.compiler.target.c64.AsmGen
|
||||||
import prog8.compiler.target.c64.C64Zeropage
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
import prog8.optimizer.constantFold
|
import prog8.optimizer.constantFold
|
||||||
import prog8.optimizer.optimizeStatements
|
import prog8.optimizer.optimizeStatements
|
||||||
import prog8.optimizer.simplifyExpressions
|
import prog8.optimizer.simplifyExpressions
|
||||||
@ -17,7 +14,6 @@ import prog8.parser.importModule
|
|||||||
import prog8.parser.moduleName
|
import prog8.parser.moduleName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.lang.Exception
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
@ -63,6 +59,12 @@ fun compileProgram(filepath: Path,
|
|||||||
}
|
}
|
||||||
//println(" time2: $time2")
|
//println(" time2: $time2")
|
||||||
val time3 = measureTimeMillis {
|
val time3 = measureTimeMillis {
|
||||||
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
|
|
||||||
|
// if you want to print the AST, do it before shuffling the statements around below
|
||||||
|
//printAst(programAst)
|
||||||
|
|
||||||
|
|
||||||
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
||||||
}
|
}
|
||||||
//println(" time3: $time3")
|
//println(" time3: $time3")
|
||||||
@ -84,12 +86,10 @@ fun compileProgram(filepath: Path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
programAst.checkValid(compilerOptions) // check if final tree is valid
|
programAst.checkValid(compilerOptions) // check if final tree is valid
|
||||||
programAst.checkRecursion() // check if there are recursive subroutine calls
|
programAst.checkRecursion() // check if there are recursive subroutine calls
|
||||||
|
|
||||||
// printAst(programAst)
|
|
||||||
// namespace.debugPrint()
|
|
||||||
|
|
||||||
if(generateVmCode) {
|
if(generateVmCode) {
|
||||||
// compile the syntax tree into stackvmProg form, and optimize that
|
// compile the syntax tree into stackvmProg form, and optimize that
|
||||||
val compiler = Compiler(programAst)
|
val compiler = Compiler(programAst)
|
||||||
@ -106,7 +106,7 @@ fun compileProgram(filepath: Path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (writeAssembly) {
|
if (writeAssembly) {
|
||||||
val zeropage = C64Zeropage(compilerOptions)
|
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
|
||||||
intermediate.allocateZeropage(zeropage)
|
intermediate.allocateZeropage(zeropage)
|
||||||
val assembly = AsmGen(compilerOptions, intermediate, programAst.heap, zeropage).compileToAssembly(optimize)
|
val assembly = AsmGen(compilerOptions, intermediate, programAst.heap, zeropage).compileToAssembly(optimize)
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
@ -144,7 +144,7 @@ fun compileProgram(filepath: Path,
|
|||||||
|
|
||||||
fun printAst(programAst: Program) {
|
fun printAst(programAst: Program) {
|
||||||
println()
|
println()
|
||||||
val printer = AstToSourceCode(::print)
|
val printer = AstToSourceCode(::print, programAst)
|
||||||
printer.visit(programAst)
|
printer.visit(programAst)
|
||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.printWarning
|
|
||||||
|
|
||||||
|
|
||||||
class ZeropageDepletedError(message: String) : Exception(message)
|
class ZeropageDepletedError(message: String) : Exception(message)
|
||||||
|
@ -2,57 +2,52 @@ package prog8.compiler.intermediate
|
|||||||
|
|
||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.printWarning
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.LiteralValue
|
import prog8.ast.expressions.ReferenceLiteralValue
|
||||||
|
import prog8.ast.statements.StructDecl
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.vm.RuntimeValue
|
import prog8.ast.statements.ZeropageWish
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.CompilerException
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
import prog8.compiler.ZeropageDepletedError
|
import prog8.compiler.ZeropageDepletedError
|
||||||
|
import prog8.vm.RuntimeValue
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) {
|
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) {
|
||||||
|
|
||||||
|
class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?)
|
||||||
|
class Variable(val scopedname: String, val value: RuntimeValue, val params: VariableParameters)
|
||||||
|
|
||||||
class ProgramBlock(val name: String,
|
class ProgramBlock(val name: String,
|
||||||
var address: Int?,
|
var address: Int?,
|
||||||
val instructions: MutableList<Instruction> = mutableListOf(),
|
val instructions: MutableList<Instruction> = mutableListOf(),
|
||||||
val variables: MutableMap<String, RuntimeValue> = mutableMapOf(), // names are fully scoped
|
val variables: MutableList<Variable> = mutableListOf(),
|
||||||
val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(),
|
val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(),
|
||||||
val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
|
val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
|
||||||
val force_output: Boolean)
|
val force_output: Boolean)
|
||||||
{
|
|
||||||
val numVariables: Int
|
|
||||||
get() { return variables.size }
|
|
||||||
val numInstructions: Int
|
|
||||||
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
|
|
||||||
val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
||||||
val blocks = mutableListOf<ProgramBlock>()
|
val blocks = mutableListOf<ProgramBlock>()
|
||||||
val memory = mutableMapOf<Int, List<RuntimeValue>>()
|
val memory = mutableMapOf<Int, List<RuntimeValue>>()
|
||||||
private lateinit var currentBlock: ProgramBlock
|
private lateinit var currentBlock: ProgramBlock
|
||||||
|
|
||||||
val numVariables: Int
|
|
||||||
get() = blocks.sumBy { it.numVariables }
|
|
||||||
val numInstructions: Int
|
|
||||||
get() = blocks.sumBy { it.numInstructions }
|
|
||||||
|
|
||||||
fun allocateZeropage(zeropage: Zeropage) {
|
fun allocateZeropage(zeropage: Zeropage) {
|
||||||
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
|
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
|
||||||
var notAllocated = 0
|
var notAllocated = 0
|
||||||
for(block in blocks) {
|
for(block in blocks) {
|
||||||
val zpVariables = block.variables.filter { it.key in block.variablesMarkedForZeropage }
|
val zpVariables = block.variables.filter { it.params.zp==ZeropageWish.REQUIRE_ZEROPAGE || it.params.zp==ZeropageWish.PREFER_ZEROPAGE }
|
||||||
if (zpVariables.isNotEmpty()) {
|
if (zpVariables.isNotEmpty()) {
|
||||||
for (variable in zpVariables) {
|
for (variable in zpVariables) {
|
||||||
|
if(variable.params.zp==ZeropageWish.NOT_IN_ZEROPAGE || variable.params.memberOfStruct!=null)
|
||||||
|
throw CompilerException("zp conflict")
|
||||||
try {
|
try {
|
||||||
val address = zeropage.allocate(variable.key, variable.value.type, null)
|
val address = zeropage.allocate(variable.scopedname, variable.value.type, null)
|
||||||
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type)
|
allocatedZeropageVariables[variable.scopedname] = Pair(address, variable.value.type)
|
||||||
} catch (x: ZeropageDepletedError) {
|
} catch (x: ZeropageDepletedError) {
|
||||||
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}")
|
printWarning(x.toString() + " variable ${variable.scopedname} type ${variable.value.type}")
|
||||||
notAllocated++
|
notAllocated++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,39 +388,53 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
|||||||
fun variable(scopedname: String, decl: VarDecl) {
|
fun variable(scopedname: String, decl: VarDecl) {
|
||||||
when(decl.type) {
|
when(decl.type) {
|
||||||
VarDeclType.VAR -> {
|
VarDeclType.VAR -> {
|
||||||
|
// var decls that are defined inside of a StructDecl are skipped in the output
|
||||||
|
// because every occurrence of the members will have a separate mangled vardecl for that occurrence
|
||||||
|
if(decl.parent is StructDecl)
|
||||||
|
return
|
||||||
|
|
||||||
|
val valueparams = VariableParameters(decl.zeropage, decl.struct)
|
||||||
val value = when(decl.datatype) {
|
val value = when(decl.datatype) {
|
||||||
in NumericDatatypes -> RuntimeValue(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
|
in NumericDatatypes -> {
|
||||||
|
RuntimeValue(decl.datatype, (decl.value as NumericLiteralValue).number)
|
||||||
|
}
|
||||||
in StringDatatypes -> {
|
in StringDatatypes -> {
|
||||||
val litval = (decl.value as LiteralValue)
|
val litval = (decl.value as ReferenceLiteralValue)
|
||||||
if(litval.heapId==null)
|
if(litval.heapId==null)
|
||||||
throw CompilerException("string should already be in the heap")
|
throw CompilerException("string should already be in the heap")
|
||||||
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
||||||
}
|
}
|
||||||
in ArrayDatatypes -> {
|
in ArrayDatatypes -> {
|
||||||
val litval = (decl.value as LiteralValue)
|
val litval = (decl.value as? ReferenceLiteralValue)
|
||||||
if(litval.heapId==null)
|
if(litval!=null && litval.heapId==null)
|
||||||
throw CompilerException("array should already be in the heap")
|
throw CompilerException("array should already be in the heap")
|
||||||
|
if(litval!=null){
|
||||||
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
||||||
|
} else {
|
||||||
|
throw CompilerException("initialization value expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.STRUCT -> {
|
||||||
|
// struct variables have been flattened already
|
||||||
|
return
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
}
|
}
|
||||||
currentBlock.variables[scopedname] = value
|
currentBlock.variables.add(Variable(scopedname, value, valueparams))
|
||||||
if(decl.zeropage)
|
|
||||||
currentBlock.variablesMarkedForZeropage.add(scopedname)
|
|
||||||
}
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
// note that constants are all folded away, but assembly code may still refer to them
|
// note that constants are all folded away, but assembly code may still refer to them
|
||||||
val lv = decl.value as LiteralValue
|
val lv = decl.value as NumericLiteralValue
|
||||||
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")
|
throw CompilerException("expected integer memory address $lv")
|
||||||
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
|
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
|
||||||
}
|
}
|
||||||
VarDeclType.CONST -> {
|
VarDeclType.CONST -> {
|
||||||
// note that constants are all folded away, but assembly code may still refer to them (if their integers)
|
// note that constants are all folded away, but assembly code may still refer to them (if their integers)
|
||||||
// floating point constants are not generated at all!!
|
// floating point constants are not generated at all!!
|
||||||
val lv = decl.value as LiteralValue
|
val lv = decl.value as NumericLiteralValue
|
||||||
if(lv.type in IntegerDatatypes)
|
if(lv.type in IntegerDatatypes)
|
||||||
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
|
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -459,10 +468,14 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
|||||||
|
|
||||||
fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) {
|
fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) {
|
||||||
out.println("; stackVM program code for '$name'")
|
out.println("; stackVM program code for '$name'")
|
||||||
out.println("%memory")
|
writeMemory(out)
|
||||||
if(memory.isNotEmpty())
|
writeHeap(out)
|
||||||
TODO("add support for writing/reading initial memory values")
|
for(blk in blocks) {
|
||||||
out.println("%end_memory")
|
writeBlock(out, blk, embeddedLabels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeHeap(out: PrintStream) {
|
||||||
out.println("%heap")
|
out.println("%heap")
|
||||||
heap.allEntries().forEach {
|
heap.allEntries().forEach {
|
||||||
out.print("${it.key} ${it.value.type.name.toLowerCase()} ")
|
out.print("${it.key} ${it.value.type.name.toLowerCase()} ")
|
||||||
@ -491,24 +504,29 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.println("%end_heap")
|
out.println("%end_heap")
|
||||||
for(blk in blocks) {
|
}
|
||||||
|
|
||||||
|
private fun writeBlock(out: PrintStream, blk: ProgramBlock, embeddedLabels: Boolean) {
|
||||||
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")
|
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")
|
||||||
|
|
||||||
out.println("%variables")
|
out.println("%variables")
|
||||||
for(variable in blk.variables) {
|
for (variable in blk.variables) {
|
||||||
|
if(variable.params.zp==ZeropageWish.REQUIRE_ZEROPAGE)
|
||||||
|
throw CompilerException("zp conflict")
|
||||||
val valuestr = variable.value.toString()
|
val valuestr = variable.value.toString()
|
||||||
out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr")
|
val struct = if(variable.params.memberOfStruct==null) "" else "struct=${variable.params.memberOfStruct.name}"
|
||||||
|
out.println("${variable.scopedname} ${variable.value.type.name.toLowerCase()} $valuestr zp=${variable.params.zp} s=$struct")
|
||||||
}
|
}
|
||||||
out.println("%end_variables")
|
out.println("%end_variables")
|
||||||
out.println("%memorypointers")
|
out.println("%memorypointers")
|
||||||
for(iconst in blk.memoryPointers) {
|
for (iconst in blk.memoryPointers) {
|
||||||
out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
||||||
}
|
}
|
||||||
out.println("%end_memorypointers")
|
out.println("%end_memorypointers")
|
||||||
out.println("%instructions")
|
out.println("%instructions")
|
||||||
val labels = blk.labels.entries.associateBy({it.value}) {it.key}
|
val labels = blk.labels.entries.associateBy({ it.value }) { it.key }
|
||||||
for(instr in blk.instructions) {
|
for (instr in blk.instructions) {
|
||||||
if(!embeddedLabels) {
|
if (!embeddedLabels) {
|
||||||
val label = labels[instr]
|
val label = labels[instr]
|
||||||
if (label != null)
|
if (label != null)
|
||||||
out.println("$label:")
|
out.println("$label:")
|
||||||
@ -520,5 +538,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
|||||||
|
|
||||||
out.println("%end_block")
|
out.println("%end_block")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun writeMemory(out: PrintStream) {
|
||||||
|
out.println("%memory")
|
||||||
|
if (memory.isNotEmpty())
|
||||||
|
TODO("add support for writing/reading initial memory values")
|
||||||
|
out.println("%end_memory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,13 @@ package prog8.compiler.target.c64
|
|||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.base.initvarsSubName
|
||||||
import prog8.vm.RuntimeValue
|
import prog8.ast.statements.ZeropageWish
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.intermediate.*
|
import prog8.compiler.intermediate.Instruction
|
||||||
|
import prog8.compiler.intermediate.IntermediateProgram
|
||||||
|
import prog8.compiler.intermediate.LabelInstr
|
||||||
|
import prog8.compiler.intermediate.Opcode
|
||||||
|
import prog8.vm.RuntimeValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
@ -44,8 +48,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
// Convert invalid label names (such as "<anon-1>") to something that's allowed.
|
// Convert invalid label names (such as "<anon-1>") to something that's allowed.
|
||||||
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
||||||
for(block in program.blocks) {
|
for(block in program.blocks) {
|
||||||
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
val newvars = block.variables.map { IntermediateProgram.Variable(symname(it.scopedname, block), it.value, it.params) }.toMutableList()
|
||||||
val newvarsZeropaged = block.variablesMarkedForZeropage.map{symname(it, block)}.toMutableSet()
|
|
||||||
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
|
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
|
||||||
val newinstructions = block.instructions.asSequence().map {
|
val newinstructions = block.instructions.asSequence().map {
|
||||||
when {
|
when {
|
||||||
@ -66,8 +69,6 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
newMempointers,
|
newMempointers,
|
||||||
newlabels,
|
newlabels,
|
||||||
force_output = block.force_output)
|
force_output = block.force_output)
|
||||||
newblock.variablesMarkedForZeropage.clear()
|
|
||||||
newblock.variablesMarkedForZeropage.addAll(newvarsZeropaged)
|
|
||||||
newblocks.add(newblock)
|
newblocks.add(newblock)
|
||||||
}
|
}
|
||||||
program.blocks.clear()
|
program.blocks.clear()
|
||||||
@ -146,7 +147,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
return name.replace("-", "")
|
return name.replace("-", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFloatFill(flt: Mflpt5): String {
|
private fun makeFloatFill(flt: MachineDefinition.Mflpt5): String {
|
||||||
val b0 = "$"+flt.b0.toString(16).padStart(2, '0')
|
val b0 = "$"+flt.b0.toString(16).padStart(2, '0')
|
||||||
val b1 = "$"+flt.b1.toString(16).padStart(2, '0')
|
val b1 = "$"+flt.b1.toString(16).padStart(2, '0')
|
||||||
val b2 = "$"+flt.b2.toString(16).padStart(2, '0')
|
val b2 = "$"+flt.b2.toString(16).padStart(2, '0')
|
||||||
@ -164,7 +165,8 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
out("\n.cpu '6502'\n.enc 'none'\n")
|
out("\n.cpu '6502'\n.enc 'none'\n")
|
||||||
|
|
||||||
if(program.loadAddress==0) // fix load address
|
if(program.loadAddress==0) // fix load address
|
||||||
program.loadAddress = if(options.launcher==LauncherType.BASIC) BASIC_LOAD_ADDRESS else RAW_LOAD_ADDRESS
|
program.loadAddress = if(options.launcher==LauncherType.BASIC)
|
||||||
|
MachineDefinition.BASIC_LOAD_ADDRESS else MachineDefinition.RAW_LOAD_ADDRESS
|
||||||
|
|
||||||
when {
|
when {
|
||||||
options.launcher == LauncherType.BASIC -> {
|
options.launcher == LauncherType.BASIC -> {
|
||||||
@ -220,7 +222,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
|
|
||||||
// the global list of all floating point constants for the whole program
|
// the global list of all floating point constants for the whole program
|
||||||
for(flt in globalFloatConsts) {
|
for(flt in globalFloatConsts) {
|
||||||
val floatFill = makeFloatFill(Mflpt5.fromNumber(flt.key))
|
val floatFill = makeFloatFill(MachineDefinition.Mflpt5.fromNumber(flt.key))
|
||||||
out("${flt.value}\t.byte $floatFill ; float ${flt.key}")
|
out("${flt.value}\t.byte $floatFill ; float ${flt.key}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,16 +239,17 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
|
|
||||||
// deal with zeropage variables
|
// deal with zeropage variables
|
||||||
for(variable in blk.variables) {
|
for(variable in blk.variables) {
|
||||||
val sym = symname(blk.name+"."+variable.key, null)
|
val sym = symname(blk.name+"."+variable.scopedname, null)
|
||||||
val zpVar = program.allocatedZeropageVariables[sym]
|
val zpVar = program.allocatedZeropageVariables[sym]
|
||||||
if(zpVar==null) {
|
if(zpVar==null) {
|
||||||
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
|
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
|
||||||
if(variable.value.type in zeropage.allowedDatatypes && variable.value.type != DataType.FLOAT) {
|
if(variable.params.zp != ZeropageWish.NOT_IN_ZEROPAGE &&
|
||||||
|
variable.value.type in zeropage.allowedDatatypes
|
||||||
|
&& variable.value.type != DataType.FLOAT) {
|
||||||
try {
|
try {
|
||||||
val address = zeropage.allocate(sym, variable.value.type, null)
|
val address = zeropage.allocate(sym, variable.value.type, null)
|
||||||
out("${variable.key} = $address\t; auto zp ${variable.value.type}")
|
out("${variable.scopedname} = $address\t; auto zp ${variable.value.type}")
|
||||||
// make sure we add the var to the set of zpvars for this block
|
// make sure we add the var to the set of zpvars for this block
|
||||||
blk.variablesMarkedForZeropage.add(variable.key)
|
|
||||||
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type)
|
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type)
|
||||||
} catch (x: ZeropageDepletedError) {
|
} catch (x: ZeropageDepletedError) {
|
||||||
// leave it as it is.
|
// leave it as it is.
|
||||||
@ -255,7 +258,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// it was already allocated on the zp
|
// it was already allocated on the zp
|
||||||
out("${variable.key} = ${zpVar.first}\t; zp ${zpVar.second}")
|
out("${variable.scopedname} = ${zpVar.first}\t; zp ${zpVar.second}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,77 +292,97 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
|
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
|
||||||
// these are the non-zeropage variables
|
val uniqueNames = block.variables.map { it.scopedname }.toSet()
|
||||||
val sortedVars = block.variables.filter{it.key !in block.variablesMarkedForZeropage}.toList().sortedBy { it.second.type }
|
if (uniqueNames.size != block.variables.size)
|
||||||
for (v in sortedVars) {
|
throw AssemblyError("not all variables have unique names")
|
||||||
when (v.second.type) {
|
|
||||||
DataType.UBYTE -> out("${v.first}\t.byte 0")
|
// these are the non-zeropage variables.
|
||||||
DataType.BYTE -> out("${v.first}\t.char 0")
|
// first get all the flattened struct members, they MUST remain in order
|
||||||
DataType.UWORD -> out("${v.first}\t.word 0")
|
out("; flattened struct members")
|
||||||
DataType.WORD -> out("${v.first}\t.sint 0")
|
val (structMembers, normalVars) = block.variables.partition { it.params.memberOfStruct!=null }
|
||||||
DataType.FLOAT -> out("${v.first}\t.byte 0,0,0,0,0 ; float")
|
structMembers.forEach { vardecl2asm(it.scopedname, it.value, it.params) }
|
||||||
|
|
||||||
|
// leave outsort the other variables by type
|
||||||
|
out("; other variables sorted by type")
|
||||||
|
val sortedVars = normalVars.sortedBy { it.value.type }
|
||||||
|
for (variable in sortedVars) {
|
||||||
|
val sym = symname(block.name + "." + variable.scopedname, null)
|
||||||
|
if(sym in program.allocatedZeropageVariables)
|
||||||
|
continue // skip the ones that already belong in the zero page
|
||||||
|
vardecl2asm(variable.scopedname, variable.value, variable.params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun vardecl2asm(varname: String, value: RuntimeValue, parameters: IntermediateProgram.VariableParameters) {
|
||||||
|
when (value.type) {
|
||||||
|
DataType.UBYTE -> out("$varname\t.byte 0")
|
||||||
|
DataType.BYTE -> out("$varname\t.char 0")
|
||||||
|
DataType.UWORD -> out("$varname\t.word 0")
|
||||||
|
DataType.WORD -> out("$varname\t.sint 0")
|
||||||
|
DataType.FLOAT -> out("$varname\t.byte 0,0,0,0,0 ; float")
|
||||||
DataType.STR, DataType.STR_S -> {
|
DataType.STR, DataType.STR_S -> {
|
||||||
val rawStr = heap.get(v.second.heapId!!).str!!
|
val rawStr = heap.get(value.heapId!!).str!!
|
||||||
val bytes = encodeStr(rawStr, v.second.type).map { "$" + it.toString(16).padStart(2, '0') }
|
val bytes = encodeStr(rawStr, value.type).map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
out("${v.first}\t; ${v.second.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
|
out("$varname\t; ${value.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
|
||||||
for (chunk in bytes.chunked(16))
|
for (chunk in bytes.chunked(16))
|
||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
// unsigned integer byte arraysize
|
// unsigned integer byte arraysize
|
||||||
val data = makeArrayFillDataUnsigned(v.second)
|
val data = makeArrayFillDataUnsigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.byte ${data.joinToString()}")
|
out("$varname\t.byte ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
// signed integer byte arraysize
|
// signed integer byte arraysize
|
||||||
val data = makeArrayFillDataSigned(v.second)
|
val data = makeArrayFillDataSigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.char ${data.joinToString()}")
|
out("$varname\t.char ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .char " + chunk.joinToString())
|
out(" .char " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
// unsigned word arraysize
|
// unsigned word arraysize
|
||||||
val data = makeArrayFillDataUnsigned(v.second)
|
val data = makeArrayFillDataUnsigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.word ${data.joinToString()}")
|
out("$varname\t.word ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .word " + chunk.joinToString())
|
out(" .word " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
// signed word arraysize
|
// signed word arraysize
|
||||||
val data = makeArrayFillDataSigned(v.second)
|
val data = makeArrayFillDataSigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.sint ${data.joinToString()}")
|
out("$varname\t.sint ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .sint " + chunk.joinToString())
|
out(" .sint " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
// float arraysize
|
// float arraysize
|
||||||
val array = heap.get(v.second.heapId!!).doubleArray!!
|
val array = heap.get(value.heapId!!).doubleArray!!
|
||||||
val floatFills = array.map { makeFloatFill(Mflpt5.fromNumber(it)) }
|
val floatFills = array.map { makeFloatFill(MachineDefinition.Mflpt5.fromNumber(it)) }
|
||||||
out(v.first)
|
out(varname)
|
||||||
for(f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
out(" .byte ${f.second} ; float ${f.first}")
|
out(" .byte ${f.second} ; float ${f.first}")
|
||||||
}
|
}
|
||||||
|
DataType.STRUCT -> throw AssemblyError("vars of type STRUCT should have been removed because flattened")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun encodeStr(str: String, dt: DataType): List<Short> {
|
private fun encodeStr(str: String, dt: DataType): List<Short> {
|
||||||
return when(dt) {
|
return when(dt) {
|
||||||
@ -554,14 +577,14 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_UW -> AsmFragment(" lda $variable+${index*2} | bne + | dec $variable+${index*2+1} |+ | dec $variable+${index*2}")
|
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_UW -> AsmFragment(" lda $variable+${index*2} | bne + | dec $variable+${index*2+1} |+ | dec $variable+${index*2}")
|
||||||
Opcode.INC_INDEXED_VAR_FLOAT -> AsmFragment(
|
Opcode.INC_INDEXED_VAR_FLOAT -> AsmFragment(
|
||||||
"""
|
"""
|
||||||
lda #<($variable+${index*Mflpt5.MemorySize})
|
lda #<($variable+${index* MachineDefinition.Mflpt5.MemorySize})
|
||||||
ldy #>($variable+${index*Mflpt5.MemorySize})
|
ldy #>($variable+${index* MachineDefinition.Mflpt5.MemorySize})
|
||||||
jsr c64flt.inc_var_f
|
jsr c64flt.inc_var_f
|
||||||
""")
|
""")
|
||||||
Opcode.DEC_INDEXED_VAR_FLOAT -> AsmFragment(
|
Opcode.DEC_INDEXED_VAR_FLOAT -> AsmFragment(
|
||||||
"""
|
"""
|
||||||
lda #<($variable+${index*Mflpt5.MemorySize})
|
lda #<($variable+${index* MachineDefinition.Mflpt5.MemorySize})
|
||||||
ldy #>($variable+${index*Mflpt5.MemorySize})
|
ldy #>($variable+${index* MachineDefinition.Mflpt5.MemorySize})
|
||||||
jsr c64flt.dec_var_f
|
jsr c64flt.dec_var_f
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@ -571,8 +594,8 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
|||||||
|
|
||||||
private fun sameIndexedVarOperation(variable: String, indexVar: String, ins: Instruction): AsmFragment? {
|
private fun sameIndexedVarOperation(variable: String, indexVar: String, ins: Instruction): AsmFragment? {
|
||||||
// an in place operation that consists of a push-value / op / push-index-var / pop-into-indexed-var
|
// an in place operation that consists of a push-value / op / push-index-var / pop-into-indexed-var
|
||||||
val saveX = " stx ${C64Zeropage.SCRATCH_B1} |"
|
val saveX = " stx ${MachineDefinition.C64Zeropage.SCRATCH_B1} |"
|
||||||
val restoreX = " | ldx ${C64Zeropage.SCRATCH_B1}"
|
val restoreX = " | ldx ${MachineDefinition.C64Zeropage.SCRATCH_B1}"
|
||||||
val loadXWord: String
|
val loadXWord: String
|
||||||
val loadX: String
|
val loadX: String
|
||||||
|
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
|
|
||||||
|
|
||||||
|
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
fun optimizeAssembly(lines: MutableList<String>): Int {
|
fun optimizeAssembly(lines: MutableList<String>): Int {
|
||||||
|
|
||||||
@ -24,10 +29,19 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeLines = optimizeCmpSequence(linesByFour)
|
||||||
|
if(removeLines.isNotEmpty()) {
|
||||||
|
for (i in removeLines.reversed())
|
||||||
|
lines.removeAt(i)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
removeLines = optimizeStoreLoadSame(linesByFour)
|
removeLines = optimizeStoreLoadSame(linesByFour)
|
||||||
if(removeLines.isNotEmpty()) {
|
if(removeLines.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
for (i in removeLines.reversed())
|
||||||
lines.removeAt(i)
|
lines.removeAt(i)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +50,7 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
|
|||||||
if(removeLines.isNotEmpty()) {
|
if(removeLines.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
for (i in removeLines.reversed())
|
||||||
lines.removeAt(i)
|
lines.removeAt(i)
|
||||||
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,15 +59,36 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
|
|||||||
return numberOfOptimizations
|
return numberOfOptimizations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
||||||
|
// the when statement (on bytes) generates a sequence of:
|
||||||
|
// lda $ce01,x
|
||||||
|
// cmp #$20
|
||||||
|
// beq check_prog8_s72choice_32
|
||||||
|
// lda $ce01,x
|
||||||
|
// cmp #$21
|
||||||
|
// beq check_prog8_s73choice_33
|
||||||
|
// the repeated lda can be removed
|
||||||
|
val removeLines = mutableListOf<Int>()
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
if(lines[0].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x" &&
|
||||||
|
lines[1].value.trim().startsWith("cmp ") &&
|
||||||
|
lines[2].value.trim().startsWith("beq ") &&
|
||||||
|
lines[3].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x") {
|
||||||
|
removeLines.add(lines[3].index) // remove the second lda
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removeLines
|
||||||
|
}
|
||||||
|
|
||||||
fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
||||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||||
// this is a lot harder for word values because the instruction sequence varies.
|
// this is a lot harder for word values because the instruction sequence varies.
|
||||||
val removeLines = mutableListOf<Int>()
|
val removeLines = mutableListOf<Int>()
|
||||||
for(lines in linesByFour) {
|
for(lines in linesByFour) {
|
||||||
if(lines[0].value.trim()=="sta ${ESTACK_LO.toHex()},x" &&
|
if(lines[0].value.trim()=="sta $ESTACK_LO_HEX,x" &&
|
||||||
lines[1].value.trim()=="dex" &&
|
lines[1].value.trim()=="dex" &&
|
||||||
lines[2].value.trim()=="inx" &&
|
lines[2].value.trim()=="inx" &&
|
||||||
lines[3].value.trim()=="lda ${ESTACK_LO.toHex()},x") {
|
lines[3].value.trim()=="lda $ESTACK_LO_HEX,x") {
|
||||||
removeLines.add(lines[0].index)
|
removeLines.add(lines[0].index)
|
||||||
removeLines.add(lines[1].index)
|
removeLines.add(lines[1].index)
|
||||||
removeLines.add(lines[2].index)
|
removeLines.add(lines[2].index)
|
||||||
@ -128,7 +164,7 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||||
// all lines (that aren't empty or comments) in sliding pairs of 2
|
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
||||||
|
@ -3,8 +3,16 @@ package prog8.compiler.target.c64
|
|||||||
import prog8.ast.base.printWarning
|
import prog8.ast.base.printWarning
|
||||||
import prog8.compiler.intermediate.Instruction
|
import prog8.compiler.intermediate.Instruction
|
||||||
import prog8.compiler.intermediate.Opcode
|
import prog8.compiler.intermediate.Opcode
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
internal class AsmPattern(val sequence: List<Opcode>, val altSequence: List<Opcode>?=null, val asm: (List<Instruction>)->String?)
|
internal class AsmPattern(val sequence: List<Opcode>, val altSequence: List<Opcode>?=null, val asm: (List<Instruction>)->String?)
|
||||||
|
|
||||||
internal fun loadAFromIndexedByVar(idxVarInstr: Instruction, readArrayInstr: Instruction): String {
|
internal fun loadAFromIndexedByVar(idxVarInstr: Instruction, readArrayInstr: Instruction): String {
|
||||||
@ -1368,7 +1376,7 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
},
|
},
|
||||||
// floatvar = floatarray[index]
|
// floatvar = floatarray[index]
|
||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.POP_VAR_FLOAT)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.POP_VAR_FLOAT)) { segment ->
|
||||||
val index = intVal(segment[0]) * Mflpt5.MemorySize
|
val index = intVal(segment[0]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
"""
|
"""
|
||||||
lda #<${segment[1].callLabel}+$index
|
lda #<${segment[1].callLabel}+$index
|
||||||
ldy #>${segment[1].callLabel}+$index
|
ldy #>${segment[1].callLabel}+$index
|
||||||
@ -1567,7 +1575,7 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
},
|
},
|
||||||
// memfloat = floatarray[index]
|
// memfloat = floatarray[index]
|
||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.POP_MEM_FLOAT)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.POP_MEM_FLOAT)) { segment ->
|
||||||
val index = intVal(segment[0]) * Mflpt5.MemorySize
|
val index = intVal(segment[0]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
"""
|
"""
|
||||||
lda #<${segment[1].callLabel}+$index
|
lda #<${segment[1].callLabel}+$index
|
||||||
ldy #>${segment[1].callLabel}+$index
|
ldy #>${segment[1].callLabel}+$index
|
||||||
@ -1581,7 +1589,7 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
// floatarray[idxbyte] = float
|
// floatarray[idxbyte] = float
|
||||||
AsmPattern(listOf(Opcode.PUSH_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
||||||
val floatConst = getFloatConst(segment[0].arg!!)
|
val floatConst = getFloatConst(segment[0].arg!!)
|
||||||
val index = intVal(segment[1]) * Mflpt5.MemorySize
|
val index = intVal(segment[1]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
"""
|
"""
|
||||||
lda #<$floatConst
|
lda #<$floatConst
|
||||||
ldy #>$floatConst
|
ldy #>$floatConst
|
||||||
@ -1594,7 +1602,7 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
},
|
},
|
||||||
// floatarray[idxbyte] = floatvar
|
// floatarray[idxbyte] = floatvar
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
||||||
val index = intVal(segment[1]) * Mflpt5.MemorySize
|
val index = intVal(segment[1]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
"""
|
"""
|
||||||
lda #<${segment[0].callLabel}
|
lda #<${segment[0].callLabel}
|
||||||
ldy #>${segment[0].callLabel}
|
ldy #>${segment[0].callLabel}
|
||||||
@ -1607,7 +1615,7 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
},
|
},
|
||||||
// floatarray[idxbyte] = memfloat
|
// floatarray[idxbyte] = memfloat
|
||||||
AsmPattern(listOf(Opcode.PUSH_MEM_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_MEM_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
||||||
val index = intVal(segment[1]) * Mflpt5.MemorySize
|
val index = intVal(segment[1]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
"""
|
"""
|
||||||
lda #<${hexVal(segment[0])}
|
lda #<${hexVal(segment[0])}
|
||||||
ldy #>${hexVal(segment[0])}
|
ldy #>${hexVal(segment[0])}
|
||||||
@ -1620,8 +1628,8 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
},
|
},
|
||||||
// floatarray[idx2] = floatarray[idx1]
|
// floatarray[idx2] = floatarray[idx1]
|
||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
|
||||||
val index1 = intVal(segment[0]) * Mflpt5.MemorySize
|
val index1 = intVal(segment[0]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
val index2 = intVal(segment[2]) * Mflpt5.MemorySize
|
val index2 = intVal(segment[2]) * MachineDefinition.Mflpt5.MemorySize
|
||||||
"""
|
"""
|
||||||
lda #<(${segment[1].callLabel}+$index1)
|
lda #<(${segment[1].callLabel}+$index1)
|
||||||
ldy #>(${segment[1].callLabel}+$index1)
|
ldy #>(${segment[1].callLabel}+$index1)
|
||||||
@ -1756,16 +1764,16 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
// push word var as (u)byte
|
// push word var as (u)byte
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_UB),
|
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_UB),
|
||||||
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_B)) { segment ->
|
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_W_TO_B)) { segment ->
|
||||||
" lda ${segment[0].callLabel} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${segment[0].callLabel} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push uword var as (u)byte
|
// push uword var as (u)byte
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_UB),
|
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_UB),
|
||||||
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_B)) { segment ->
|
listOf(Opcode.PUSH_VAR_WORD, Opcode.CAST_UW_TO_B)) { segment ->
|
||||||
" lda ${segment[0].callLabel} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${segment[0].callLabel} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push msb(word var)
|
// push msb(word var)
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.MSB)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.MSB)) { segment ->
|
||||||
" lda ${segment[0].callLabel}+1 | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${segment[0].callLabel}+1 | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
|
|
||||||
// set a register pair to a certain memory address (of a variable)
|
// set a register pair to a certain memory address (of a variable)
|
||||||
@ -1782,29 +1790,29 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
// push memory byte | bytevalue
|
// push memory byte | bytevalue
|
||||||
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITOR_BYTE),
|
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITOR_BYTE),
|
||||||
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITOR_BYTE)) { segment ->
|
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITOR_BYTE)) { segment ->
|
||||||
" lda ${hexVal(segment[0])} | ora #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${hexVal(segment[0])} | ora #${hexVal(segment[1])} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push memory byte & bytevalue
|
// push memory byte & bytevalue
|
||||||
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITAND_BYTE),
|
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITAND_BYTE),
|
||||||
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITAND_BYTE)) { segment ->
|
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITAND_BYTE)) { segment ->
|
||||||
" lda ${hexVal(segment[0])} | and #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${hexVal(segment[0])} | and #${hexVal(segment[1])} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push memory byte ^ bytevalue
|
// push memory byte ^ bytevalue
|
||||||
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE),
|
AsmPattern(listOf(Opcode.PUSH_MEM_B, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE),
|
||||||
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE)) { segment ->
|
listOf(Opcode.PUSH_MEM_UB, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE)) { segment ->
|
||||||
" lda ${hexVal(segment[0])} | eor #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${hexVal(segment[0])} | eor #${hexVal(segment[1])} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push var byte | bytevalue
|
// push var byte | bytevalue
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.BITOR_BYTE)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.BITOR_BYTE)) { segment ->
|
||||||
" lda ${segment[0].callLabel} | ora #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${segment[0].callLabel} | ora #${hexVal(segment[1])} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push var byte & bytevalue
|
// push var byte & bytevalue
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.BITAND_BYTE)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.BITAND_BYTE)) { segment ->
|
||||||
" lda ${segment[0].callLabel} | and #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${segment[0].callLabel} | and #${hexVal(segment[1])} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
// push var byte ^ bytevalue
|
// push var byte ^ bytevalue
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.BITXOR_BYTE)) { segment ->
|
||||||
" lda ${segment[0].callLabel} | eor #${hexVal(segment[1])} | sta ${ESTACK_LO.toHex()},x | dex "
|
" lda ${segment[0].callLabel} | eor #${hexVal(segment[1])} | sta $ESTACK_LO_HEX,x | dex "
|
||||||
},
|
},
|
||||||
|
|
||||||
// push memory word | wordvalue
|
// push memory word | wordvalue
|
||||||
@ -1813,10 +1821,10 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
"""
|
"""
|
||||||
lda ${hexVal(segment[0])}
|
lda ${hexVal(segment[0])}
|
||||||
ora #<${hexVal(segment[1])}
|
ora #<${hexVal(segment[1])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${hexValPlusOne(segment[0])}
|
lda ${hexValPlusOne(segment[0])}
|
||||||
ora #>${hexVal(segment[1])}
|
ora #>${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -1826,10 +1834,10 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
"""
|
"""
|
||||||
lda ${hexVal(segment[0])}
|
lda ${hexVal(segment[0])}
|
||||||
and #<${hexVal(segment[1])}
|
and #<${hexVal(segment[1])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${hexValPlusOne(segment[0])}
|
lda ${hexValPlusOne(segment[0])}
|
||||||
and #>${hexVal(segment[1])}
|
and #>${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -1839,10 +1847,10 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
"""
|
"""
|
||||||
lda ${hexVal(segment[0])}
|
lda ${hexVal(segment[0])}
|
||||||
eor #<${hexVal(segment[1])}
|
eor #<${hexVal(segment[1])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${hexValPlusOne(segment[0])}
|
lda ${hexValPlusOne(segment[0])}
|
||||||
eor #>${hexVal(segment[1])}
|
eor #>${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -1851,10 +1859,10 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
"""
|
"""
|
||||||
lda ${segment[0].callLabel}
|
lda ${segment[0].callLabel}
|
||||||
ora #<${hexVal(segment[1])}
|
ora #<${hexVal(segment[1])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${segment[0].callLabel}+1
|
lda ${segment[0].callLabel}+1
|
||||||
ora #>${hexVal(segment[1])}
|
ora #>${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -1863,10 +1871,10 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
"""
|
"""
|
||||||
lda ${segment[0].callLabel}
|
lda ${segment[0].callLabel}
|
||||||
and #<${hexVal(segment[1])}
|
and #<${hexVal(segment[1])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${segment[0].callLabel}+1
|
lda ${segment[0].callLabel}+1
|
||||||
and #>${hexVal(segment[1])}
|
and #>${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -1875,10 +1883,10 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
"""
|
"""
|
||||||
lda ${segment[0].callLabel}
|
lda ${segment[0].callLabel}
|
||||||
eor #<${hexVal(segment[1])}
|
eor #<${hexVal(segment[1])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${segment[0].callLabel}+1
|
lda ${segment[0].callLabel}+1
|
||||||
eor #>${hexVal(segment[1])}
|
eor #>${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -1971,27 +1979,27 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.MKWORD)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.MKWORD)) { segment ->
|
||||||
"""
|
"""
|
||||||
lda ${segment[0].callLabel}
|
lda ${segment[0].callLabel}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${segment[1].callLabel}
|
lda ${segment[1].callLabel}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.MKWORD)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.MKWORD)) { segment ->
|
||||||
"""
|
"""
|
||||||
lda #${hexVal(segment[0])}
|
lda #${hexVal(segment[0])}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${segment[1].callLabel}
|
lda ${segment[1].callLabel}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.MKWORD)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.MKWORD)) { segment ->
|
||||||
"""
|
"""
|
||||||
lda ${segment[0].callLabel}
|
lda ${segment[0].callLabel}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda #${hexVal(segment[1])}
|
lda #${hexVal(segment[1])}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
@ -2032,28 +2040,28 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.ADD_B), listOf(Opcode.PUSH_BYTE, Opcode.ADD_UB)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.ADD_B), listOf(Opcode.PUSH_BYTE, Opcode.ADD_UB)) { segment ->
|
||||||
val amount = segment[0].arg!!.integerValue()
|
val amount = segment[0].arg!!.integerValue()
|
||||||
if (amount in 1..2) {
|
if (amount in 1..2) {
|
||||||
" inc ${(ESTACK_LO + 1).toHex()},x | ".repeat(amount)
|
" inc $ESTACK_LO_PLUS1_HEX,x | ".repeat(amount)
|
||||||
} else
|
} else
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.ADD_UW), listOf(Opcode.PUSH_WORD, Opcode.ADD_W)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.ADD_UW), listOf(Opcode.PUSH_WORD, Opcode.ADD_W)) { segment ->
|
||||||
val amount = segment[0].arg!!.integerValue()
|
val amount = segment[0].arg!!.integerValue()
|
||||||
if (amount in 1..2) {
|
if (amount in 1..2) {
|
||||||
" inc ${(ESTACK_LO + 1).toHex()},x | bne + | inc ${(ESTACK_HI + 1).toHex()},x |+ | ".repeat(amount)
|
" inc $ESTACK_LO_PLUS1_HEX,x | bne + | inc $ESTACK_HI_PLUS1_HEX,x |+ | ".repeat(amount)
|
||||||
} else
|
} else
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.SUB_B), listOf(Opcode.PUSH_BYTE, Opcode.SUB_UB)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.SUB_B), listOf(Opcode.PUSH_BYTE, Opcode.SUB_UB)) { segment ->
|
||||||
val amount = segment[0].arg!!.integerValue()
|
val amount = segment[0].arg!!.integerValue()
|
||||||
if (amount in 1..2) {
|
if (amount in 1..2) {
|
||||||
" dec ${(ESTACK_LO + 1).toHex()},x | ".repeat(amount)
|
" dec $ESTACK_LO_PLUS1_HEX,x | ".repeat(amount)
|
||||||
} else
|
} else
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.SUB_UW), listOf(Opcode.PUSH_WORD, Opcode.SUB_W)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.SUB_UW), listOf(Opcode.PUSH_WORD, Opcode.SUB_W)) { segment ->
|
||||||
val amount = segment[0].arg!!.integerValue()
|
val amount = segment[0].arg!!.integerValue()
|
||||||
if (amount in 1..2) {
|
if (amount in 1..2) {
|
||||||
" lda ${(ESTACK_LO + 1).toHex()},x | bne + | dec ${(ESTACK_HI + 1).toHex()},x |+ | dec ${(ESTACK_LO + 1).toHex()},x | ".repeat(amount)
|
" lda $ESTACK_LO_PLUS1_HEX,x | bne + | dec $ESTACK_HI_PLUS1_HEX,x |+ | dec $ESTACK_LO_PLUS1_HEX,x | ".repeat(amount)
|
||||||
} else
|
} else
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
@ -2063,7 +2071,20 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.CMP_B), listOf(Opcode.PUSH_VAR_BYTE, Opcode.CMP_UB)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.CMP_B), listOf(Opcode.PUSH_VAR_BYTE, Opcode.CMP_UB)) { segment ->
|
||||||
// this pattern is encountered as part of the loop bound condition in for loops (var + cmp + jz/jnz)
|
// this pattern is encountered as part of the loop bound condition in for loops (var + cmp + jz/jnz)
|
||||||
val cmpval = segment[1].arg!!.integerValue()
|
val cmpval = segment[1].arg!!.integerValue()
|
||||||
|
when(segment[0].callLabel) {
|
||||||
|
"A" -> {
|
||||||
|
" cmp #$cmpval "
|
||||||
|
}
|
||||||
|
"X" -> {
|
||||||
|
" cpx #$cmpval "
|
||||||
|
}
|
||||||
|
"Y" -> {
|
||||||
|
" cpy #$cmpval "
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
" lda ${segment[0].callLabel} | cmp #$cmpval "
|
" lda ${segment[0].callLabel} | cmp #$cmpval "
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CMP_W), listOf(Opcode.PUSH_VAR_WORD, Opcode.CMP_UW)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.CMP_W), listOf(Opcode.PUSH_VAR_WORD, Opcode.CMP_UW)) { segment ->
|
||||||
// this pattern is encountered as part of the loop bound condition in for loops (var + cmp + jz/jnz)
|
// this pattern is encountered as part of the loop bound condition in for loops (var + cmp + jz/jnz)
|
||||||
@ -2093,14 +2114,14 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.MUL_B), listOf(Opcode.PUSH_BYTE, Opcode.MUL_UB)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.MUL_B), listOf(Opcode.PUSH_BYTE, Opcode.MUL_UB)) { segment ->
|
||||||
val amount = segment[0].arg!!.integerValue()
|
val amount = segment[0].arg!!.integerValue()
|
||||||
val result = optimizedIntMultiplicationsOnStack(segment[1], amount)
|
val result = optimizedIntMultiplicationsOnStack(segment[1], amount)
|
||||||
result ?: " lda #${hexVal(segment[0])} | sta ${ESTACK_LO.toHex()},x | dex | jsr prog8_lib.mul_byte"
|
result ?: " lda #${hexVal(segment[0])} | sta $ESTACK_LO_HEX,x | dex | jsr prog8_lib.mul_byte"
|
||||||
},
|
},
|
||||||
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.MUL_W), listOf(Opcode.PUSH_WORD, Opcode.MUL_UW)) { segment ->
|
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.MUL_W), listOf(Opcode.PUSH_WORD, Opcode.MUL_UW)) { segment ->
|
||||||
val amount = segment[0].arg!!.integerValue()
|
val amount = segment[0].arg!!.integerValue()
|
||||||
val result = optimizedIntMultiplicationsOnStack(segment[1], amount)
|
val result = optimizedIntMultiplicationsOnStack(segment[1], amount)
|
||||||
if (result != null) result else {
|
if (result != null) result else {
|
||||||
val value = hexVal(segment[0])
|
val value = hexVal(segment[0])
|
||||||
" lda #<$value | sta ${ESTACK_LO.toHex()},x | lda #>$value | sta ${ESTACK_HI.toHex()},x | dex | jsr prog8_lib.mul_word"
|
" lda #<$value | sta $ESTACK_LO_HEX,x | lda #>$value | sta $ESTACK_HI_HEX,x | dex | jsr prog8_lib.mul_word"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2289,6 +2310,24 @@ internal val patterns = listOf<AsmPattern>(
|
|||||||
ldx ${C64Zeropage.SCRATCH_REG_X}
|
ldx ${C64Zeropage.SCRATCH_REG_X}
|
||||||
"""
|
"""
|
||||||
} else null
|
} else null
|
||||||
|
},
|
||||||
|
|
||||||
|
AsmPattern(listOf(Opcode.DUP_W, Opcode.CMP_UW),
|
||||||
|
listOf(Opcode.DUP_W, Opcode.CMP_W)) { segment ->
|
||||||
|
"""
|
||||||
|
lda $ESTACK_HI_PLUS1_HEX,x
|
||||||
|
cmp #>${segment[1].arg!!.integerValue().toHex()}
|
||||||
|
bne +
|
||||||
|
lda $ESTACK_LO_PLUS1_HEX,x
|
||||||
|
cmp #<${segment[1].arg!!.integerValue().toHex()}
|
||||||
|
; bne + not necessary?
|
||||||
|
; lda #0 not necessary?
|
||||||
|
+
|
||||||
|
"""
|
||||||
|
},
|
||||||
|
AsmPattern(listOf(Opcode.DUP_B, Opcode.CMP_UB),
|
||||||
|
listOf(Opcode.DUP_B, Opcode.CMP_B)) { segment ->
|
||||||
|
""" lda $ESTACK_LO_PLUS1_HEX,x | cmp #${segment[1].arg!!.integerValue().toHex()} """
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -1,242 +0,0 @@
|
|||||||
package prog8.compiler.target.c64
|
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
|
||||||
import prog8.compiler.CompilerException
|
|
||||||
import prog8.compiler.Zeropage
|
|
||||||
import prog8.compiler.ZeropageType
|
|
||||||
import java.awt.Color
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
|
|
||||||
// 5-byte cbm MFLPT format limitations:
|
|
||||||
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
|
||||||
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
|
||||||
|
|
||||||
const val BASIC_LOAD_ADDRESS = 0x0801
|
|
||||||
const val RAW_LOAD_ADDRESS = 0xc000
|
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
|
||||||
const val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive
|
|
||||||
const val ESTACK_HI = 0xcf00 // $cf00-$cfff inclusive
|
|
||||||
|
|
||||||
|
|
||||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SCRATCH_B1 = 0x02
|
|
||||||
const val SCRATCH_REG = 0x03 // temp storage for a register
|
|
||||||
const val SCRATCH_REG_X = 0xfa // temp storage for register X (the evaluation stack pointer)
|
|
||||||
const val SCRATCH_W1 = 0xfb // $fb+$fc
|
|
||||||
const val SCRATCH_W2 = 0xfd // $fd+$fe
|
|
||||||
}
|
|
||||||
|
|
||||||
override val exitProgramStrategy: ExitProgramStrategy = when(options.zeropage) {
|
|
||||||
ZeropageType.BASICSAFE -> ExitProgramStrategy.CLEAN_EXIT
|
|
||||||
ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
if(options.floats && options.zeropage!=ZeropageType.FLOATSAFE && options.zeropage!=ZeropageType.BASICSAFE)
|
|
||||||
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe'")
|
|
||||||
|
|
||||||
if(options.zeropage == ZeropageType.FULL) {
|
|
||||||
free.addAll(0x04 .. 0xf9)
|
|
||||||
free.add(0xff)
|
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1+1, SCRATCH_W2, SCRATCH_W2+1))
|
|
||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
|
||||||
} else {
|
|
||||||
if(options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
|
||||||
free.addAll(listOf(0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
|
||||||
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
|
||||||
0x22, 0x23, 0x24, 0x25,
|
|
||||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
|
||||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
|
||||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
|
||||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
|
||||||
// 0x90-0xfa is 'kernel work storage area'
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.zeropage == ZeropageType.FLOATSAFE) {
|
|
||||||
// remove the zero page locations used for floating point operations from the free list
|
|
||||||
free.removeAll(listOf(
|
|
||||||
0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xf
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the other free Zp addresses,
|
|
||||||
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully:
|
|
||||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e,
|
|
||||||
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
|
|
||||||
0xb5, 0xb6, 0xf7, 0xf8, 0xf9))
|
|
||||||
}
|
|
||||||
assert(SCRATCH_B1 !in free)
|
|
||||||
assert(SCRATCH_REG !in free)
|
|
||||||
assert(SCRATCH_REG_X !in free)
|
|
||||||
assert(SCRATCH_W1 !in free)
|
|
||||||
assert(SCRATCH_W2 !in free)
|
|
||||||
|
|
||||||
for(reserved in options.zpReserved)
|
|
||||||
reserve(reserved)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val MemorySize = 5
|
|
||||||
|
|
||||||
val zero = Mflpt5(0, 0,0,0,0)
|
|
||||||
fun fromNumber(num: Number): Mflpt5 {
|
|
||||||
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
|
||||||
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
|
||||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
|
||||||
|
|
||||||
val flt = num.toDouble()
|
|
||||||
if(flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
|
||||||
throw CompilerException("floating point number out of 5-byte mflpt range: $this")
|
|
||||||
if(flt==0.0)
|
|
||||||
return zero
|
|
||||||
|
|
||||||
val sign = if(flt<0.0) 0x80L else 0x00L
|
|
||||||
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
|
||||||
var mantissa = flt.absoluteValue
|
|
||||||
|
|
||||||
// if mantissa is too large, shift right and adjust exponent
|
|
||||||
while(mantissa >= 0x100000000) {
|
|
||||||
mantissa /= 2.0
|
|
||||||
exponent ++
|
|
||||||
}
|
|
||||||
// if mantissa is too small, shift left and adjust exponent
|
|
||||||
while(mantissa < 0x80000000) {
|
|
||||||
mantissa *= 2.0
|
|
||||||
exponent --
|
|
||||||
}
|
|
||||||
|
|
||||||
return when {
|
|
||||||
exponent<0 -> zero // underflow, use zero instead
|
|
||||||
exponent>255 -> throw CompilerException("floating point overflow: $this")
|
|
||||||
exponent==0 -> zero
|
|
||||||
else -> {
|
|
||||||
val mantLong = mantissa.toLong()
|
|
||||||
Mflpt5(
|
|
||||||
exponent.toShort(),
|
|
||||||
(mantLong.and(0x7f000000L) ushr 24).or(sign).toShort(),
|
|
||||||
(mantLong.and(0x00ff0000L) ushr 16).toShort(),
|
|
||||||
(mantLong.and(0x0000ff00L) ushr 8).toShort(),
|
|
||||||
(mantLong.and(0x000000ffL)).toShort())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toDouble(): Double {
|
|
||||||
if(this == zero) return 0.0
|
|
||||||
val exp = b0 - 128
|
|
||||||
val sign = (b1.toInt() and 0x80) > 0
|
|
||||||
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
|
||||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
|
||||||
return if(sign) -result else result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Charset {
|
|
||||||
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
|
|
||||||
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
|
|
||||||
|
|
||||||
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
|
|
||||||
|
|
||||||
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
|
|
||||||
transparent.createGraphics().drawImage(img, 0, 0, null)
|
|
||||||
|
|
||||||
val black = Color(0,0,0).rgb
|
|
||||||
val nopixel = Color(0,0,0,0).rgb
|
|
||||||
for(y in 0 until transparent.height) {
|
|
||||||
for(x in 0 until transparent.width) {
|
|
||||||
val col = transparent.getRGB(x, y)
|
|
||||||
if(col==black)
|
|
||||||
transparent.setRGB(x, y, nopixel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val numColumns = transparent.width / 8
|
|
||||||
val charImages = (0..255).map {
|
|
||||||
val charX = it % numColumns
|
|
||||||
val charY = it/ numColumns
|
|
||||||
transparent.getSubimage(charX*8, charY*8, 8, 8)
|
|
||||||
}
|
|
||||||
return charImages.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
val normalChars = scanChars(normalImg)
|
|
||||||
val shiftedChars = scanChars(shiftedImg)
|
|
||||||
|
|
||||||
private val coloredNormalChars = mutableMapOf<Short, Array<BufferedImage>>()
|
|
||||||
|
|
||||||
fun getColoredChar(screenCode: Short, color: Short): BufferedImage {
|
|
||||||
val colorIdx = (color % Colors.palette.size).toShort()
|
|
||||||
val chars = coloredNormalChars[colorIdx]
|
|
||||||
if(chars!=null)
|
|
||||||
return chars[screenCode.toInt()]
|
|
||||||
|
|
||||||
val coloredChars = mutableListOf<BufferedImage>()
|
|
||||||
val transparent = Color(0,0,0,0).rgb
|
|
||||||
val rgb = Colors.palette[colorIdx.toInt()].rgb
|
|
||||||
for(c in normalChars) {
|
|
||||||
val colored = c.copy()
|
|
||||||
for(y in 0 until colored.height)
|
|
||||||
for(x in 0 until colored.width) {
|
|
||||||
if(colored.getRGB(x, y)!=transparent) {
|
|
||||||
colored.setRGB(x, y, rgb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coloredChars.add(colored)
|
|
||||||
}
|
|
||||||
coloredNormalChars[colorIdx] = coloredChars.toTypedArray()
|
|
||||||
return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BufferedImage.copy(): BufferedImage {
|
|
||||||
val bcopy = BufferedImage(this.width, this.height, this.type)
|
|
||||||
val g = bcopy.graphics
|
|
||||||
g.drawImage(this, 0, 0, null)
|
|
||||||
g.dispose()
|
|
||||||
return bcopy
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
object Colors {
|
|
||||||
val palette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
|
||||||
Color(0x000000), // 0 = black
|
|
||||||
Color(0xFFFFFF), // 1 = white
|
|
||||||
Color(0x813338), // 2 = red
|
|
||||||
Color(0x75cec8), // 3 = cyan
|
|
||||||
Color(0x8e3c97), // 4 = purple
|
|
||||||
Color(0x56ac4d), // 5 = green
|
|
||||||
Color(0x2e2c9b), // 6 = blue
|
|
||||||
Color(0xedf171), // 7 = yellow
|
|
||||||
Color(0x8e5029), // 8 = orange
|
|
||||||
Color(0x553800), // 9 = brown
|
|
||||||
Color(0xc46c71), // 10 = light red
|
|
||||||
Color(0x4a4a4a), // 11 = dark grey
|
|
||||||
Color(0x7b7b7b), // 12 = medium grey
|
|
||||||
Color(0xa9ff9f), // 13 = light green
|
|
||||||
Color(0x706deb), // 14 = light blue
|
|
||||||
Color(0xb2b2b2) // 15 = light grey
|
|
||||||
)
|
|
||||||
}
|
|
250
compiler/src/prog8/compiler/target/c64/MachineDefinition.kt
Normal file
250
compiler/src/prog8/compiler/target/c64/MachineDefinition.kt
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.CompilerException
|
||||||
|
import prog8.compiler.Zeropage
|
||||||
|
import prog8.compiler.ZeropageType
|
||||||
|
import java.awt.Color
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
object MachineDefinition {
|
||||||
|
|
||||||
|
// 5-byte cbm MFLPT format limitations:
|
||||||
|
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||||
|
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
|
|
||||||
|
const val BASIC_LOAD_ADDRESS = 0x0801
|
||||||
|
const val RAW_LOAD_ADDRESS = 0xc000
|
||||||
|
|
||||||
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
|
// and some heavily used string constants derived from the two values above
|
||||||
|
const val ESTACK_LO_VALUE = 0xce00 // $ce00-$ceff inclusive
|
||||||
|
const val ESTACK_HI_VALUE = 0xcf00 // $cf00-$cfff inclusive
|
||||||
|
const val ESTACK_LO_HEX = "\$ce00"
|
||||||
|
const val ESTACK_LO_PLUS1_HEX = "\$ce01"
|
||||||
|
const val ESTACK_LO_PLUS2_HEX = "\$ce02"
|
||||||
|
const val ESTACK_HI_HEX = "\$cf00"
|
||||||
|
const val ESTACK_HI_PLUS1_HEX = "\$cf01"
|
||||||
|
const val ESTACK_HI_PLUS2_HEX = "\$cf02"
|
||||||
|
|
||||||
|
|
||||||
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SCRATCH_B1 = 0x02
|
||||||
|
const val SCRATCH_REG = 0x03 // temp storage for a register
|
||||||
|
const val SCRATCH_REG_X = 0xfa // temp storage for register X (the evaluation stack pointer)
|
||||||
|
const val SCRATCH_W1 = 0xfb // $fb+$fc
|
||||||
|
const val SCRATCH_W2 = 0xfd // $fd+$fe
|
||||||
|
}
|
||||||
|
|
||||||
|
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
|
||||||
|
ZeropageType.BASICSAFE -> ExitProgramStrategy.CLEAN_EXIT
|
||||||
|
ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage != ZeropageType.FLOATSAFE && options.zeropage != ZeropageType.BASICSAFE)
|
||||||
|
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe'")
|
||||||
|
|
||||||
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
|
free.addAll(0x04..0xf9)
|
||||||
|
free.add(0xff)
|
||||||
|
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||||
|
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||||
|
} else {
|
||||||
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
|
free.addAll(listOf(0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||||
|
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||||
|
0x22, 0x23, 0x24, 0x25,
|
||||||
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||||
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||||
|
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||||
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
|
// 0x90-0xfa is 'kernel work storage area'
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
|
// remove the zero page locations used for floating point operations from the free list
|
||||||
|
free.removeAll(listOf(
|
||||||
|
0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xf
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the other free Zp addresses,
|
||||||
|
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully:
|
||||||
|
free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e,
|
||||||
|
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
|
||||||
|
0xb5, 0xb6, 0xf7, 0xf8, 0xf9))
|
||||||
|
}
|
||||||
|
assert(SCRATCH_B1 !in free)
|
||||||
|
assert(SCRATCH_REG !in free)
|
||||||
|
assert(SCRATCH_REG_X !in free)
|
||||||
|
assert(SCRATCH_W1 !in free)
|
||||||
|
assert(SCRATCH_W2 !in free)
|
||||||
|
|
||||||
|
for (reserved in options.zpReserved)
|
||||||
|
reserve(reserved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MemorySize = 5
|
||||||
|
|
||||||
|
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||||
|
fun fromNumber(num: Number): Mflpt5 {
|
||||||
|
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
||||||
|
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||||
|
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||||
|
|
||||||
|
val flt = num.toDouble()
|
||||||
|
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||||
|
throw CompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||||
|
if (flt == 0.0)
|
||||||
|
return zero
|
||||||
|
|
||||||
|
val sign = if (flt < 0.0) 0x80L else 0x00L
|
||||||
|
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
||||||
|
var mantissa = flt.absoluteValue
|
||||||
|
|
||||||
|
// if mantissa is too large, shift right and adjust exponent
|
||||||
|
while (mantissa >= 0x100000000) {
|
||||||
|
mantissa /= 2.0
|
||||||
|
exponent++
|
||||||
|
}
|
||||||
|
// if mantissa is too small, shift left and adjust exponent
|
||||||
|
while (mantissa < 0x80000000) {
|
||||||
|
mantissa *= 2.0
|
||||||
|
exponent--
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
exponent < 0 -> zero // underflow, use zero instead
|
||||||
|
exponent > 255 -> throw CompilerException("floating point overflow: $this")
|
||||||
|
exponent == 0 -> zero
|
||||||
|
else -> {
|
||||||
|
val mantLong = mantissa.toLong()
|
||||||
|
Mflpt5(
|
||||||
|
exponent.toShort(),
|
||||||
|
(mantLong.and(0x7f000000L) ushr 24).or(sign).toShort(),
|
||||||
|
(mantLong.and(0x00ff0000L) ushr 16).toShort(),
|
||||||
|
(mantLong.and(0x0000ff00L) ushr 8).toShort(),
|
||||||
|
(mantLong.and(0x000000ffL)).toShort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toDouble(): Double {
|
||||||
|
if (this == zero) return 0.0
|
||||||
|
val exp = b0 - 128
|
||||||
|
val sign = (b1.toInt() and 0x80) > 0
|
||||||
|
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
||||||
|
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||||
|
return if (sign) -result else result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Charset {
|
||||||
|
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
|
||||||
|
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
|
||||||
|
|
||||||
|
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
|
||||||
|
|
||||||
|
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
|
||||||
|
transparent.createGraphics().drawImage(img, 0, 0, null)
|
||||||
|
|
||||||
|
val black = Color(0, 0, 0).rgb
|
||||||
|
val nopixel = Color(0, 0, 0, 0).rgb
|
||||||
|
for (y in 0 until transparent.height) {
|
||||||
|
for (x in 0 until transparent.width) {
|
||||||
|
val col = transparent.getRGB(x, y)
|
||||||
|
if (col == black)
|
||||||
|
transparent.setRGB(x, y, nopixel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val numColumns = transparent.width / 8
|
||||||
|
val charImages = (0..255).map {
|
||||||
|
val charX = it % numColumns
|
||||||
|
val charY = it / numColumns
|
||||||
|
transparent.getSubimage(charX * 8, charY * 8, 8, 8)
|
||||||
|
}
|
||||||
|
return charImages.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
val normalChars = scanChars(normalImg)
|
||||||
|
val shiftedChars = scanChars(shiftedImg)
|
||||||
|
|
||||||
|
private val coloredNormalChars = mutableMapOf<Short, Array<BufferedImage>>()
|
||||||
|
|
||||||
|
fun getColoredChar(screenCode: Short, color: Short): BufferedImage {
|
||||||
|
val colorIdx = (color % colorPalette.size).toShort()
|
||||||
|
val chars = coloredNormalChars[colorIdx]
|
||||||
|
if (chars != null)
|
||||||
|
return chars[screenCode.toInt()]
|
||||||
|
|
||||||
|
val coloredChars = mutableListOf<BufferedImage>()
|
||||||
|
val transparent = Color(0, 0, 0, 0).rgb
|
||||||
|
val rgb = colorPalette[colorIdx.toInt()].rgb
|
||||||
|
for (c in normalChars) {
|
||||||
|
val colored = c.copy()
|
||||||
|
for (y in 0 until colored.height)
|
||||||
|
for (x in 0 until colored.width) {
|
||||||
|
if (colored.getRGB(x, y) != transparent) {
|
||||||
|
colored.setRGB(x, y, rgb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coloredChars.add(colored)
|
||||||
|
}
|
||||||
|
coloredNormalChars[colorIdx] = coloredChars.toTypedArray()
|
||||||
|
return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BufferedImage.copy(): BufferedImage {
|
||||||
|
val bcopy = BufferedImage(this.width, this.height, this.type)
|
||||||
|
val g = bcopy.graphics
|
||||||
|
g.drawImage(this, 0, 0, null)
|
||||||
|
g.dispose()
|
||||||
|
return bcopy
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||||
|
Color(0x000000), // 0 = black
|
||||||
|
Color(0xFFFFFF), // 1 = white
|
||||||
|
Color(0x813338), // 2 = red
|
||||||
|
Color(0x75cec8), // 3 = cyan
|
||||||
|
Color(0x8e3c97), // 4 = purple
|
||||||
|
Color(0x56ac4d), // 5 = green
|
||||||
|
Color(0x2e2c9b), // 6 = blue
|
||||||
|
Color(0xedf171), // 7 = yellow
|
||||||
|
Color(0x8e5029), // 8 = orange
|
||||||
|
Color(0x553800), // 9 = brown
|
||||||
|
Color(0xc46c71), // 10 = light red
|
||||||
|
Color(0x4a4a4a), // 11 = dark grey
|
||||||
|
Color(0x7b7b7b), // 12 = medium grey
|
||||||
|
Color(0xa9ff9f), // 13 = light green
|
||||||
|
Color(0x706deb), // 14 = light blue
|
||||||
|
Color(0xb2b2b2) // 15 = light grey
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
@ -1086,5 +1086,38 @@ class Petscii {
|
|||||||
val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase
|
val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase
|
||||||
return screencode.map { decodeTable[it.toInt()] }.joinToString("")
|
return screencode.map { decodeTable[it.toInt()] }.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Short {
|
||||||
|
val code = when {
|
||||||
|
petscii_code <= 0x1f -> petscii_code + 128
|
||||||
|
petscii_code <= 0x3f -> petscii_code.toInt()
|
||||||
|
petscii_code <= 0x5f -> petscii_code - 64
|
||||||
|
petscii_code <= 0x7f -> petscii_code - 32
|
||||||
|
petscii_code <= 0x9f -> petscii_code + 64
|
||||||
|
petscii_code <= 0xbf -> petscii_code - 64
|
||||||
|
petscii_code <= 0xfe -> petscii_code - 128
|
||||||
|
petscii_code == 255.toShort() -> 95
|
||||||
|
else -> throw CharConversionException("petscii code out of range")
|
||||||
|
}
|
||||||
|
if(inverseVideo)
|
||||||
|
return (code or 0x80).toShort()
|
||||||
|
return code.toShort()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scr2petscii(screencode: Short): Short {
|
||||||
|
val petscii = when {
|
||||||
|
screencode <= 0x1f -> screencode + 64
|
||||||
|
screencode <= 0x3f -> screencode.toInt()
|
||||||
|
screencode <= 0x5d -> screencode +123
|
||||||
|
screencode == 0x5e.toShort() -> 255
|
||||||
|
screencode == 0x5f.toShort() -> 223
|
||||||
|
screencode <= 0x7f -> screencode + 64
|
||||||
|
screencode <= 0xbf -> screencode - 128
|
||||||
|
screencode <= 0xfe -> screencode - 64
|
||||||
|
screencode == 255.toShort() -> 191
|
||||||
|
else -> throw CharConversionException("screencode out of range")
|
||||||
|
}
|
||||||
|
return petscii.toShort()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,20 @@ import prog8.compiler.intermediate.Instruction
|
|||||||
import prog8.compiler.intermediate.IntermediateProgram
|
import prog8.compiler.intermediate.IntermediateProgram
|
||||||
import prog8.compiler.intermediate.LabelInstr
|
import prog8.compiler.intermediate.LabelInstr
|
||||||
import prog8.compiler.intermediate.Opcode
|
import prog8.compiler.intermediate.Opcode
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS2_HEX
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.vm.stackvm.Syscall
|
import prog8.vm.stackvm.Syscall
|
||||||
import prog8.vm.stackvm.syscallsForStackVm
|
import prog8.vm.stackvm.syscallsForStackVm
|
||||||
|
|
||||||
|
|
||||||
|
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
private var breakpointCounter = 0
|
private var breakpointCounter = 0
|
||||||
|
|
||||||
internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.ProgramBlock): String? {
|
internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.ProgramBlock): String? {
|
||||||
@ -61,13 +70,32 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.RRESTOREX -> " sta ${C64Zeropage.SCRATCH_REG} | pla | tax | lda ${C64Zeropage.SCRATCH_REG}"
|
Opcode.RRESTOREX -> " sta ${C64Zeropage.SCRATCH_REG} | pla | tax | lda ${C64Zeropage.SCRATCH_REG}"
|
||||||
Opcode.DISCARD_BYTE -> " inx"
|
Opcode.DISCARD_BYTE -> " inx"
|
||||||
Opcode.DISCARD_WORD -> " inx"
|
Opcode.DISCARD_WORD -> " inx"
|
||||||
|
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
|
||||||
Opcode.DUP_B -> {
|
Opcode.DUP_B -> {
|
||||||
" dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x"
|
" lda $ESTACK_LO_PLUS1_HEX,x | sta $ESTACK_LO_HEX,x | dex | ;DUP_B "
|
||||||
}
|
}
|
||||||
Opcode.DUP_W -> {
|
Opcode.DUP_W -> {
|
||||||
" dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x | lda ${(ESTACK_HI+1).toHex()},x | sta ${ESTACK_HI.toHex()},x "
|
" lda $ESTACK_LO_PLUS1_HEX,x | sta $ESTACK_LO_HEX,x | lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_HI_HEX,x | dex "
|
||||||
}
|
}
|
||||||
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
|
|
||||||
|
Opcode.CMP_B, Opcode.CMP_UB -> {
|
||||||
|
" inx | lda $ESTACK_LO_HEX,x | cmp #${ins.arg!!.integerValue().toHex()} | ;CMP_B "
|
||||||
|
}
|
||||||
|
|
||||||
|
Opcode.CMP_W, Opcode.CMP_UW -> {
|
||||||
|
"""
|
||||||
|
inx
|
||||||
|
lda $ESTACK_HI_HEX,x
|
||||||
|
cmp #>${ins.arg!!.integerValue().toHex()}
|
||||||
|
bne +
|
||||||
|
lda $ESTACK_LO_HEX,x
|
||||||
|
cmp #<${ins.arg.integerValue().toHex()}
|
||||||
|
; bne + not necessary?
|
||||||
|
; lda #0 not necessary?
|
||||||
|
+
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it.
|
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it.
|
||||||
Opcode.INCLUDE_FILE -> {
|
Opcode.INCLUDE_FILE -> {
|
||||||
val offset = if(ins.arg==null) "" else ", ${ins.arg.integerValue()}"
|
val offset = if(ins.arg==null) "" else ", ${ins.arg.integerValue()}"
|
||||||
@ -108,11 +136,11 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode.PUSH_BYTE -> {
|
Opcode.PUSH_BYTE -> {
|
||||||
" lda #${hexVal(ins)} | sta ${ESTACK_LO.toHex()},x | dex"
|
" lda #${hexVal(ins)} | sta $ESTACK_LO_HEX,x | dex"
|
||||||
}
|
}
|
||||||
Opcode.PUSH_WORD -> {
|
Opcode.PUSH_WORD -> {
|
||||||
val value = hexVal(ins)
|
val value = hexVal(ins)
|
||||||
" lda #<$value | sta ${ESTACK_LO.toHex()},x | lda #>$value | sta ${ESTACK_HI.toHex()},x | dex"
|
" lda #<$value | sta $ESTACK_LO_HEX,x | lda #>$value | sta $ESTACK_HI_HEX,x | dex"
|
||||||
}
|
}
|
||||||
Opcode.PUSH_FLOAT -> {
|
Opcode.PUSH_FLOAT -> {
|
||||||
val floatConst = getFloatConst(ins.arg!!)
|
val floatConst = getFloatConst(ins.arg!!)
|
||||||
@ -121,28 +149,28 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.PUSH_VAR_BYTE -> {
|
Opcode.PUSH_VAR_BYTE -> {
|
||||||
when(ins.callLabel) {
|
when(ins.callLabel) {
|
||||||
"X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself. You should probably not use the X register (or only in trivial assignments)")
|
"X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself. You should probably not use the X register (or only in trivial assignments)")
|
||||||
"A" -> " sta ${ESTACK_LO.toHex()},x | dex"
|
"A" -> " sta $ESTACK_LO_HEX,x | dex"
|
||||||
"Y" -> " tya | sta ${ESTACK_LO.toHex()},x | dex"
|
"Y" -> " tya | sta $ESTACK_LO_HEX,x | dex"
|
||||||
else -> " lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | dex"
|
else -> " lda ${ins.callLabel} | sta $ESTACK_LO_HEX,x | dex"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.PUSH_VAR_WORD -> {
|
Opcode.PUSH_VAR_WORD -> {
|
||||||
" lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda ${ins.callLabel}+1 | sta ${ESTACK_HI.toHex()},x | dex"
|
" lda ${ins.callLabel} | sta $ESTACK_LO_HEX,x | lda ${ins.callLabel}+1 | sta $ESTACK_HI_HEX,x | dex"
|
||||||
}
|
}
|
||||||
Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr c64flt.push_float"
|
Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr c64flt.push_float"
|
||||||
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> {
|
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> {
|
||||||
"""
|
"""
|
||||||
lda ${hexVal(ins)}
|
lda ${hexVal(ins)}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> {
|
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> {
|
||||||
"""
|
"""
|
||||||
lda ${hexVal(ins)}
|
lda ${hexVal(ins)}
|
||||||
sta ${ESTACK_LO.toHex()},x
|
sta $ESTACK_LO_HEX,x
|
||||||
lda ${hexValPlusOne(ins)}
|
lda ${hexValPlusOne(ins)}
|
||||||
sta ${ESTACK_HI.toHex()},x
|
sta $ESTACK_HI_HEX,x
|
||||||
dex
|
dex
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -151,43 +179,43 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
}
|
}
|
||||||
Opcode.PUSH_MEMREAD -> {
|
Opcode.PUSH_MEMREAD -> {
|
||||||
"""
|
"""
|
||||||
lda ${(ESTACK_LO+1).toHex()},x
|
lda $ESTACK_LO_PLUS1_HEX,x
|
||||||
sta (+) +1
|
sta (+) +1
|
||||||
lda ${(ESTACK_HI+1).toHex()},x
|
lda $ESTACK_HI_PLUS1_HEX,x
|
||||||
sta (+) +2
|
sta (+) +2
|
||||||
+ lda 65535 ; modified
|
+ lda 65535 ; modified
|
||||||
sta ${(ESTACK_LO+1).toHex()},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode.PUSH_REGAY_WORD -> {
|
Opcode.PUSH_REGAY_WORD -> {
|
||||||
" sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex "
|
" sta $ESTACK_LO_HEX,x | tya | sta $ESTACK_HI_HEX,x | dex "
|
||||||
}
|
}
|
||||||
Opcode.PUSH_ADDR_HEAPVAR -> {
|
Opcode.PUSH_ADDR_HEAPVAR -> {
|
||||||
" lda #<${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda #>${ins.callLabel} | sta ${ESTACK_HI.toHex()},x | dex"
|
" lda #<${ins.callLabel} | sta $ESTACK_LO_HEX,x | lda #>${ins.callLabel} | sta $ESTACK_HI_HEX,x | dex"
|
||||||
}
|
}
|
||||||
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
||||||
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
||||||
Opcode.POP_REGAY_WORD -> {
|
Opcode.POP_REGAY_WORD -> {
|
||||||
" inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x "
|
" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x "
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode.READ_INDEXED_VAR_BYTE -> {
|
Opcode.READ_INDEXED_VAR_BYTE -> {
|
||||||
"""
|
"""
|
||||||
ldy ${(ESTACK_LO+1).toHex()},x
|
ldy $ESTACK_LO_PLUS1_HEX,x
|
||||||
lda ${ins.callLabel},y
|
lda ${ins.callLabel},y
|
||||||
sta ${(ESTACK_LO+1).toHex()},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.READ_INDEXED_VAR_WORD -> {
|
Opcode.READ_INDEXED_VAR_WORD -> {
|
||||||
"""
|
"""
|
||||||
lda ${(ESTACK_LO+1).toHex()},x
|
lda $ESTACK_LO_PLUS1_HEX,x
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
lda ${ins.callLabel},y
|
lda ${ins.callLabel},y
|
||||||
sta ${(ESTACK_LO+1).toHex()},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
lda ${ins.callLabel}+1,y
|
lda ${ins.callLabel}+1,y
|
||||||
sta ${(ESTACK_HI+1).toHex()},x
|
sta $ESTACK_HI_PLUS1_HEX,x
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.READ_INDEXED_VAR_FLOAT -> {
|
Opcode.READ_INDEXED_VAR_FLOAT -> {
|
||||||
@ -200,22 +228,22 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.WRITE_INDEXED_VAR_BYTE -> {
|
Opcode.WRITE_INDEXED_VAR_BYTE -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
ldy ${ESTACK_LO.toHex()},x
|
ldy $ESTACK_LO_HEX,x
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta ${ins.callLabel},y
|
sta ${ins.callLabel},y
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.WRITE_INDEXED_VAR_WORD -> {
|
Opcode.WRITE_INDEXED_VAR_WORD -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
asl a
|
asl a
|
||||||
tay
|
tay
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta ${ins.callLabel},y
|
sta ${ins.callLabel},y
|
||||||
lda ${ESTACK_HI.toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta ${ins.callLabel}+1,y
|
sta ${ins.callLabel}+1,y
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -229,16 +257,16 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.POP_MEM_BYTE -> {
|
Opcode.POP_MEM_BYTE -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta ${hexVal(ins)}
|
sta ${hexVal(ins)}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.POP_MEM_WORD -> {
|
Opcode.POP_MEM_WORD -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta ${hexVal(ins)}
|
sta ${hexVal(ins)}
|
||||||
lda ${ESTACK_HI.toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta ${hexValPlusOne(ins)}
|
sta ${hexValPlusOne(ins)}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -248,12 +276,12 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.POP_MEMWRITE -> {
|
Opcode.POP_MEMWRITE -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta (+) +1
|
sta (+) +1
|
||||||
lda ${ESTACK_HI.toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) +2
|
sta (+) +2
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
+ sta 65535 ; modified
|
+ sta 65535 ; modified
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -261,13 +289,13 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.POP_VAR_BYTE -> {
|
Opcode.POP_VAR_BYTE -> {
|
||||||
when (ins.callLabel) {
|
when (ins.callLabel) {
|
||||||
"X" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself")
|
"X" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself")
|
||||||
"A" -> " inx | lda ${ESTACK_LO.toHex()},x"
|
"A" -> " inx | lda $ESTACK_LO_HEX,x"
|
||||||
"Y" -> " inx | ldy ${ESTACK_LO.toHex()},x"
|
"Y" -> " inx | ldy $ESTACK_LO_HEX,x"
|
||||||
else -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${ins.callLabel}"
|
else -> " inx | lda $ESTACK_LO_HEX,x | sta ${ins.callLabel}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.POP_VAR_WORD -> {
|
Opcode.POP_VAR_WORD -> {
|
||||||
" inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x | sta ${ins.callLabel} | sty ${ins.callLabel}+1"
|
" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x | sta ${ins.callLabel} | sty ${ins.callLabel}+1"
|
||||||
}
|
}
|
||||||
Opcode.POP_VAR_FLOAT -> {
|
Opcode.POP_VAR_FLOAT -> {
|
||||||
" lda #<${ins.callLabel} | ldy #>${ins.callLabel} | jsr c64flt.pop_float"
|
" lda #<${ins.callLabel} | ldy #>${ins.callLabel} | jsr c64flt.pop_float"
|
||||||
@ -294,9 +322,9 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.POP_INC_MEMORY -> {
|
Opcode.POP_INC_MEMORY -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta (+) +1
|
sta (+) +1
|
||||||
lda ${ESTACK_HI.toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) +2
|
sta (+) +2
|
||||||
+ inc 65535 ; modified
|
+ inc 65535 ; modified
|
||||||
"""
|
"""
|
||||||
@ -304,9 +332,9 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.POP_DEC_MEMORY -> {
|
Opcode.POP_DEC_MEMORY -> {
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO.toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
sta (+) +1
|
sta (+) +1
|
||||||
lda ${ESTACK_HI.toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) +2
|
sta (+) +2
|
||||||
+ dec 65535 ; modified
|
+ dec 65535 ; modified
|
||||||
"""
|
"""
|
||||||
@ -331,8 +359,8 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
}
|
}
|
||||||
Opcode.INC_MEMORY -> " inc ${hexVal(ins)}"
|
Opcode.INC_MEMORY -> " inc ${hexVal(ins)}"
|
||||||
Opcode.DEC_MEMORY -> " dec ${hexVal(ins)}"
|
Opcode.DEC_MEMORY -> " dec ${hexVal(ins)}"
|
||||||
Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB -> " inx | txa | pha | lda ${ESTACK_LO.toHex()},x | tax | inc ${ins.callLabel},x | pla | tax"
|
Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB -> " inx | txa | pha | lda $ESTACK_LO_HEX,x | tax | inc ${ins.callLabel},x | pla | tax"
|
||||||
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB -> " inx | txa | pha | lda ${ESTACK_LO.toHex()},x | tax | dec ${ins.callLabel},x | pla | tax"
|
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB -> " inx | txa | pha | lda $ESTACK_LO_HEX,x | tax | dec ${ins.callLabel},x | pla | tax"
|
||||||
|
|
||||||
Opcode.NEG_B -> " jsr prog8_lib.neg_b"
|
Opcode.NEG_B -> " jsr prog8_lib.neg_b"
|
||||||
Opcode.NEG_W -> " jsr prog8_lib.neg_w"
|
Opcode.NEG_W -> " jsr prog8_lib.neg_w"
|
||||||
@ -343,9 +371,9 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.POW_F -> " jsr c64flt.pow_f"
|
Opcode.POW_F -> " jsr c64flt.pow_f"
|
||||||
Opcode.INV_BYTE -> {
|
Opcode.INV_BYTE -> {
|
||||||
"""
|
"""
|
||||||
lda ${(ESTACK_LO + 1).toHex()},x
|
lda $ESTACK_LO_PLUS1_HEX,x
|
||||||
eor #255
|
eor #255
|
||||||
sta ${(ESTACK_LO + 1).toHex()},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.INV_WORD -> " jsr prog8_lib.inv_word"
|
Opcode.INV_WORD -> " jsr prog8_lib.inv_word"
|
||||||
@ -387,7 +415,7 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
val label = ins.callLabel ?: hexVal(ins)
|
val label = ins.callLabel ?: hexVal(ins)
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${(ESTACK_LO).toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
beq $label
|
beq $label
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -395,9 +423,9 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
val label = ins.callLabel ?: hexVal(ins)
|
val label = ins.callLabel ?: hexVal(ins)
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${(ESTACK_LO).toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
beq $label
|
beq $label
|
||||||
lda ${(ESTACK_HI).toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
beq $label
|
beq $label
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -405,7 +433,7 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
val label = ins.callLabel ?: hexVal(ins)
|
val label = ins.callLabel ?: hexVal(ins)
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${(ESTACK_LO).toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
bne $label
|
bne $label
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -413,9 +441,9 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
val label = ins.callLabel ?: hexVal(ins)
|
val label = ins.callLabel ?: hexVal(ins)
|
||||||
"""
|
"""
|
||||||
inx
|
inx
|
||||||
lda ${(ESTACK_LO).toHex()},x
|
lda $ESTACK_LO_HEX,x
|
||||||
bne $label
|
bne $label
|
||||||
lda ${(ESTACK_HI).toHex()},x
|
lda $ESTACK_HI_HEX,x
|
||||||
bne $label
|
bne $label
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -435,27 +463,27 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2w"
|
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2w"
|
||||||
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
|
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
|
||||||
Opcode.CAST_F_TO_W -> " jsr c64flt.stack_float2w"
|
Opcode.CAST_F_TO_W -> " jsr c64flt.stack_float2w"
|
||||||
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${(ESTACK_HI+1).toHex()},x" // clear the msb
|
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta $ESTACK_HI_PLUS1_HEX,x" // clear the msb
|
||||||
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda ${(ESTACK_LO+1).toHex()},x | ${signExtendA("${(ESTACK_HI+1).toHex()},x")}" // sign extend the lsb
|
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda $ESTACK_LO_PLUS1_HEX,x | ${signExtendA("$ESTACK_HI_PLUS1_HEX,x")}" // sign extend the lsb
|
||||||
Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x"
|
Opcode.MSB -> " lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x"
|
||||||
Opcode.MKWORD -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${(ESTACK_HI+1).toHex()},x "
|
Opcode.MKWORD -> " inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x "
|
||||||
|
|
||||||
Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better (pattern with more opcodes)
|
Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better (pattern with more opcodes)
|
||||||
"""
|
"""
|
||||||
lda ${(ESTACK_LO + 2).toHex()},x
|
lda $ESTACK_LO_PLUS2_HEX,x
|
||||||
clc
|
clc
|
||||||
adc ${(ESTACK_LO + 1).toHex()},x
|
adc $ESTACK_LO_PLUS1_HEX,x
|
||||||
inx
|
inx
|
||||||
sta ${(ESTACK_LO + 1).toHex()},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.SUB_UB, Opcode.SUB_B -> { // TODO inline better (pattern with more opcodes)
|
Opcode.SUB_UB, Opcode.SUB_B -> { // TODO inline better (pattern with more opcodes)
|
||||||
"""
|
"""
|
||||||
lda ${(ESTACK_LO + 2).toHex()},x
|
lda $ESTACK_LO_PLUS2_HEX,x
|
||||||
sec
|
sec
|
||||||
sbc ${(ESTACK_LO + 1).toHex()},x
|
sbc $ESTACK_LO_PLUS1_HEX,x
|
||||||
inx
|
inx
|
||||||
sta ${(ESTACK_LO + 1).toHex()},x
|
sta $ESTACK_LO_PLUS1_HEX,x
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
Opcode.ADD_W, Opcode.ADD_UW -> " jsr prog8_lib.add_w"
|
Opcode.ADD_W, Opcode.ADD_UW -> " jsr prog8_lib.add_w"
|
||||||
@ -519,12 +547,12 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
|
|||||||
Opcode.LESSEQ_W -> " jsr prog8_lib.lesseq_w"
|
Opcode.LESSEQ_W -> " jsr prog8_lib.lesseq_w"
|
||||||
Opcode.LESSEQ_F -> " jsr c64flt.lesseq_f"
|
Opcode.LESSEQ_F -> " jsr c64flt.lesseq_f"
|
||||||
|
|
||||||
Opcode.SHIFTEDL_BYTE -> " asl ${(ESTACK_LO+1).toHex()},x"
|
Opcode.SHIFTEDL_BYTE -> " asl $ESTACK_LO_PLUS1_HEX,x"
|
||||||
Opcode.SHIFTEDL_WORD -> " asl ${(ESTACK_LO+1).toHex()},x | rol ${(ESTACK_HI+1).toHex()},x"
|
Opcode.SHIFTEDL_WORD -> " asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x"
|
||||||
Opcode.SHIFTEDR_SBYTE -> " lda ${(ESTACK_LO+1).toHex()},x | asl a | ror ${(ESTACK_LO+1).toHex()},x"
|
Opcode.SHIFTEDR_SBYTE -> " lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x"
|
||||||
Opcode.SHIFTEDR_UBYTE -> " lsr ${(ESTACK_LO+1).toHex()},x"
|
Opcode.SHIFTEDR_UBYTE -> " lsr $ESTACK_LO_PLUS1_HEX,x"
|
||||||
Opcode.SHIFTEDR_SWORD -> " lda ${(ESTACK_HI+1).toHex()},x | asl a | ror ${(ESTACK_HI+1).toHex()},x | ror ${(ESTACK_LO+1).toHex()},x"
|
Opcode.SHIFTEDR_SWORD -> " lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x"
|
||||||
Opcode.SHIFTEDR_UWORD -> " lsr ${(ESTACK_HI+1).toHex()},x | ror ${(ESTACK_LO+1).toHex()},x"
|
Opcode.SHIFTEDR_UWORD -> " lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x"
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
package prog8.functions
|
package prog8.functions
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.DirectMemoryRead
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.LiteralValue
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.CompilerException
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
|
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
|
||||||
|
|
||||||
|
|
||||||
|
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
|
||||||
|
|
||||||
|
|
||||||
class FunctionSignature(val pure: Boolean, // does it have side effects?
|
class FunctionSignature(val pure: Boolean, // does it have side effects?
|
||||||
val parameters: List<BuiltinFunctionParam>,
|
val parameters: List<BuiltinFunctionParam>,
|
||||||
val returntype: DataType?,
|
val returntype: DataType?,
|
||||||
val constExpressionFunc: ((args: List<IExpression>, position: Position, program: Program) -> LiteralValue)? = null)
|
val constExpressionFunc: ConstExpressionCaller? = null)
|
||||||
|
|
||||||
|
|
||||||
val BuiltinFunctions = mapOf(
|
val BuiltinFunctions = mapOf(
|
||||||
@ -27,9 +28,9 @@ val BuiltinFunctions = mapOf(
|
|||||||
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||||
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||||
// these few have a return value depending on the argument(s):
|
// these few have a return value depending on the argument(s):
|
||||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.max()!! }}, // type depends on args
|
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, _ -> collectionArgNeverConst(a, p) }, // type depends on args
|
||||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.min()!! }}, // type depends on args
|
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, _ -> collectionArgNeverConst(a, p) }, // type depends on args
|
||||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.sum() }}, // type depends on args
|
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, _ -> collectionArgNeverConst(a, p) }, // type depends on args
|
||||||
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
@ -51,12 +52,12 @@ val BuiltinFunctions = mapOf(
|
|||||||
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||||
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||||
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||||
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.FLOAT, ::builtinAvg),
|
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.FLOAT) { a, p, _ -> collectionArgNeverConst(a, p) },
|
||||||
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||||
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||||
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.any { v -> v != 0.0} }},
|
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, _ -> collectionArgNeverConst(a, p) },
|
||||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.all { v -> v != 0.0} }},
|
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, _ -> collectionArgNeverConst(a, p) },
|
||||||
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
||||||
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
||||||
"mkword" to FunctionSignature(true, listOf(
|
"mkword" to FunctionSignature(true, listOf(
|
||||||
@ -112,12 +113,12 @@ val BuiltinFunctions = mapOf(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
fun builtinFunctionReturnType(function: String, args: List<IExpression>, program: Program): DataType? {
|
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): DataType? {
|
||||||
|
|
||||||
fun datatypeFromIterableArg(arglist: IExpression): DataType {
|
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
||||||
if(arglist is LiteralValue) {
|
if(arglist is ReferenceLiteralValue) {
|
||||||
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)}
|
val dt = arglist.array!!.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")
|
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
|
||||||
}
|
}
|
||||||
@ -127,8 +128,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(arglist is IdentifierReference) {
|
if(arglist is IdentifierReference) {
|
||||||
val dt = arglist.inferType(program)
|
return when(val dt = arglist.inferType(program)) {
|
||||||
return when(dt) {
|
|
||||||
in NumericDatatypes -> dt!!
|
in NumericDatatypes -> dt!!
|
||||||
in StringDatatypes -> dt!!
|
in StringDatatypes -> dt!!
|
||||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt!!)
|
in ArrayDatatypes -> ArrayElementTypes.getValue(dt!!)
|
||||||
@ -145,8 +145,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
|
|||||||
|
|
||||||
return when (function) {
|
return when (function) {
|
||||||
"abs" -> {
|
"abs" -> {
|
||||||
val dt = args.single().inferType(program)
|
when(val dt = args.single().inferType(program)) {
|
||||||
when(dt) {
|
|
||||||
in ByteDatatypes -> DataType.UBYTE
|
in ByteDatatypes -> DataType.UBYTE
|
||||||
in WordDatatypes -> DataType.UWORD
|
in WordDatatypes -> DataType.UWORD
|
||||||
DataType.FLOAT -> DataType.FLOAT
|
DataType.FLOAT -> DataType.FLOAT
|
||||||
@ -154,8 +153,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"max", "min" -> {
|
"max", "min" -> {
|
||||||
val dt = datatypeFromIterableArg(args.single())
|
when(val dt = datatypeFromIterableArg(args.single())) {
|
||||||
when(dt) {
|
|
||||||
in NumericDatatypes -> dt
|
in NumericDatatypes -> dt
|
||||||
in StringDatatypes -> DataType.UBYTE
|
in StringDatatypes -> DataType.UBYTE
|
||||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
||||||
@ -187,182 +185,98 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
|
|||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||||
|
|
||||||
|
|
||||||
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
|
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val float = getFloatArg(constval, args[0].position)
|
val float = constval.number.toDouble()
|
||||||
return numericLiteral(function(float), args[0].position)
|
return numericLiteral(function(float), args[0].position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFloatArg(v: LiteralValue, position: Position): Double {
|
private fun oneDoubleArgOutputWord(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||||
val nv = v.asNumericValue ?: throw SyntaxError("numerical argument required", position)
|
|
||||||
return nv.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
|
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val float = getFloatArg(constval, args[0].position)
|
val float = constval.number.toDouble()
|
||||||
return LiteralValue(DataType.WORD, wordvalue = function(float).toInt(), position = args[0].position)
|
return NumericLiteralValue(DataType.WORD, function(float).toInt(), args[0].position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): LiteralValue {
|
private fun oneIntArgOutputInt(args: List<Expression>, position: Position, program: Program, function: (arg: Int)->Number): NumericLiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one integer argument", position)
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
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)
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
|
|
||||||
val integer = constval.asNumericValue?.toInt()!!
|
val integer = constval.number.toInt()
|
||||||
return numericLiteral(function(integer).toInt(), args[0].position)
|
return numericLiteral(function(integer).toInt(), args[0].position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position,
|
private fun collectionArgNeverConst(args: List<Expression>, position: Position): NumericLiteralValue {
|
||||||
program: Program,
|
|
||||||
function: (arg: Collection<Double>)->Number): LiteralValue {
|
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||||
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
|
|
||||||
|
|
||||||
val result = if(iterable.arrayvalue != null) {
|
// max/min/sum etc only work on arrays and these are never considered to be const for these functions
|
||||||
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
|
|
||||||
if(null in constants)
|
|
||||||
throw NotConstArgumentException()
|
throw NotConstArgumentException()
|
||||||
function(constants.map { it!!.toDouble() }).toDouble()
|
|
||||||
} else {
|
|
||||||
when(iterable.type) {
|
|
||||||
DataType.UBYTE, DataType.UWORD, DataType.FLOAT -> throw SyntaxError("function expects an iterable type", position)
|
|
||||||
else -> {
|
|
||||||
val heapId = iterable.heapId ?: throw FatalAstException("iterable value should be on the heap")
|
|
||||||
val array = program.heap.get(heapId).array ?: throw SyntaxError("function expects an iterable type", position)
|
|
||||||
function(array.map {
|
|
||||||
if(it.integer!=null)
|
|
||||||
it.integer.toDouble()
|
|
||||||
else
|
|
||||||
throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return numericLiteral(result, args[0].position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position,
|
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
program: Program,
|
|
||||||
function: (arg: Collection<Double>)->Boolean): LiteralValue {
|
|
||||||
if(args.size!=1)
|
|
||||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
|
||||||
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
|
|
||||||
|
|
||||||
val result = if(iterable.arrayvalue != null) {
|
|
||||||
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
|
|
||||||
if(null in constants)
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
function(constants.map { it!!.toDouble() })
|
|
||||||
} else {
|
|
||||||
val array = program.heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
|
|
||||||
function(array.map {
|
|
||||||
if(it.integer!=null)
|
|
||||||
it.integer.toDouble()
|
|
||||||
else
|
|
||||||
throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return LiteralValue.fromBoolean(result, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
|
||||||
// 1 arg, type = float or int, result type= isSameAs as argument type
|
// 1 arg, type = float or int, result type= isSameAs as argument type
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("abs requires one numeric argument", position)
|
throw SyntaxError("abs requires one numeric argument", position)
|
||||||
|
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val number = constval.asNumericValue
|
return when (constval.type) {
|
||||||
return when (number) {
|
in IntegerDatatypes -> numericLiteral(abs(constval.number.toInt()), args[0].position)
|
||||||
is Int, is Byte, is Short -> numericLiteral(abs(number.toInt()), args[0].position)
|
DataType.FLOAT -> numericLiteral(abs(constval.number.toDouble()), args[0].position)
|
||||||
is Double -> numericLiteral(abs(number.toDouble()), args[0].position)
|
|
||||||
else -> throw SyntaxError("abs requires one numeric argument", position)
|
else -> throw SyntaxError("abs requires one numeric argument", position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinAvg(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if(args.size!=1)
|
|
||||||
throw SyntaxError("avg requires array argument", position)
|
|
||||||
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
|
|
||||||
val result = if(iterable.arrayvalue!=null) {
|
|
||||||
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
|
|
||||||
if (null in constants)
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
(constants.map { it!!.toDouble() }).average()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val heapId = iterable.heapId!!
|
|
||||||
val integerarray = program.heap.get(heapId).array
|
|
||||||
if(integerarray!=null) {
|
|
||||||
if (integerarray.all { it.integer != null }) {
|
|
||||||
integerarray.map { it.integer!! }.average()
|
|
||||||
} else {
|
|
||||||
throw ExpressionError("cannot avg() over array that does not only contain constant numerical values", position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val doublearray = program.heap.get(heapId).doubleArray
|
|
||||||
doublearray?.average() ?: throw SyntaxError("avg requires array argument", position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return numericLiteral(result, args[0].position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("strlen requires one argument", position)
|
throw SyntaxError("strlen requires one argument", position)
|
||||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
if(argument.type !in StringDatatypes)
|
if(argument.type !in StringDatatypes)
|
||||||
throw SyntaxError("strlen must have string argument", position)
|
throw SyntaxError("strlen must have string argument", position)
|
||||||
val string = argument.strvalue!!
|
|
||||||
val zeroIdx = string.indexOf('\u0000')
|
throw NotConstArgumentException() // this function is not considering the string argument a constant
|
||||||
return if(zeroIdx>=0)
|
|
||||||
LiteralValue.optimalInteger(zeroIdx, position=position)
|
|
||||||
else
|
|
||||||
LiteralValue.optimalInteger(string.length, position=position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("len requires one argument", position)
|
throw SyntaxError("len requires one argument", position)
|
||||||
var argument = args[0].constValue(program)
|
val constArg = args[0].constValue(program)
|
||||||
if(argument==null) {
|
if(constArg!=null)
|
||||||
|
throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||||
|
|
||||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||||
val arraySize = directMemVar?.arraysize?.size()
|
var arraySize = directMemVar?.arraysize?.size()
|
||||||
if(arraySize != null)
|
if(arraySize != null)
|
||||||
return LiteralValue.optimalInteger(arraySize, position)
|
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||||
if(args[0] !is IdentifierReference)
|
if(args[0] !is IdentifierReference)
|
||||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||||
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
|
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||||
val argValue = (target as? VarDecl)?.value
|
|
||||||
argument = argValue?.constValue(program)
|
return when(target.datatype) {
|
||||||
?: throw NotConstArgumentException()
|
|
||||||
}
|
|
||||||
return when(argument.type) {
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
|
arraySize = target.arraysize!!.size()!!
|
||||||
if(arraySize>256)
|
if(arraySize>256)
|
||||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||||
LiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
|
arraySize = target.arraysize!!.size()!!
|
||||||
if(arraySize>256)
|
if(arraySize>256)
|
||||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||||
LiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
in StringDatatypes -> {
|
in StringDatatypes -> {
|
||||||
val str = argument.strvalue!!
|
val refLv = target.value as ReferenceLiteralValue
|
||||||
if(str.length>255)
|
if(refLv.str!!.length>255)
|
||||||
throw CompilerException("string length exceeds byte limit ${argument.position}")
|
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
||||||
LiteralValue.optimalInteger(str.length, args[0].position)
|
NumericLiteralValue.optimalInteger(refLv.str.length, args[0].position)
|
||||||
}
|
}
|
||||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
@ -370,80 +284,80 @@ private fun builtinLen(args: List<IExpression>, position: Position, program: Pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 2)
|
if (args.size != 2)
|
||||||
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
||||||
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val result = (constMsb.asIntegerValue!! shl 8) or constLsb.asIntegerValue!!
|
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
||||||
return LiteralValue(DataType.UWORD, wordvalue = result, position = position)
|
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinSin8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin8 requires one argument", position)
|
throw SyntaxError("sin8 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * sin(rad)).toShort(), position = position)
|
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin8u requires one argument", position)
|
throw SyntaxError("sin8u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * sin(rad)).toShort(), position = position)
|
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos8 requires one argument", position)
|
throw SyntaxError("cos8 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * cos(rad)).toShort(), position = position)
|
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos8u requires one argument", position)
|
throw SyntaxError("cos8u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * cos(rad)).toShort(), position = position)
|
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin16 requires one argument", position)
|
throw SyntaxError("sin16 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * sin(rad)).toInt(), position = position)
|
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin16u requires one argument", position)
|
throw SyntaxError("sin16u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * sin(rad)).toInt(), position = position)
|
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinCos16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos16 requires one argument", position)
|
throw SyntaxError("cos16 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * cos(rad)).toInt(), position = position)
|
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos16u requires one argument", position)
|
throw SyntaxError("cos16u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * cos(rad)).toInt(), position = position)
|
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun numericLiteral(value: Number, position: Position): LiteralValue {
|
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
|
||||||
val floatNum=value.toDouble()
|
val floatNum=value.toDouble()
|
||||||
val tweakedValue: Number =
|
val tweakedValue: Number =
|
||||||
if(floatNum== floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
|
if(floatNum== floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
|
||||||
@ -452,11 +366,11 @@ private fun numericLiteral(value: Number, position: Position): LiteralValue {
|
|||||||
floatNum
|
floatNum
|
||||||
|
|
||||||
return when(tweakedValue) {
|
return when(tweakedValue) {
|
||||||
is Int -> LiteralValue.optimalNumeric(value.toInt(), position)
|
is Int -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
||||||
is Short -> LiteralValue.optimalNumeric(value.toInt(), position)
|
is Short -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
||||||
is Byte -> LiteralValue(DataType.UBYTE, bytevalue = value.toShort(), position = position)
|
is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position)
|
||||||
is Double -> LiteralValue(DataType.FLOAT, floatvalue = value.toDouble(), position = position)
|
is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||||
is Float -> LiteralValue(DataType.FLOAT, floatvalue = value.toDouble(), position = position)
|
is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||||
else -> throw FatalAstException("invalid number type ${value::class}")
|
else -> throw FatalAstException("invalid number type ${value::class}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.ParentSentinel
|
import prog8.ast.base.ParentSentinel
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.base.initvarsSubName
|
||||||
@ -17,7 +21,8 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
val usedSymbols = mutableSetOf<IStatement>()
|
// TODO add dataflow graph: what statements use what variables
|
||||||
|
val usedSymbols = mutableSetOf<Statement>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
visit(program)
|
visit(program)
|
||||||
@ -92,11 +97,11 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
super.visit(identifier)
|
super.visit(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addNodeAndParentScopes(stmt: IStatement) {
|
private fun addNodeAndParentScopes(stmt: Statement) {
|
||||||
usedSymbols.add(stmt)
|
usedSymbols.add(stmt)
|
||||||
var node: Node=stmt
|
var node: Node=stmt
|
||||||
do {
|
do {
|
||||||
if(node is INameScope && node is IStatement) {
|
if(node is INameScope && node is Statement) {
|
||||||
usedSymbols.add(node)
|
usedSymbols.add(node)
|
||||||
}
|
}
|
||||||
node=node.parent
|
node=node.parent
|
||||||
@ -104,7 +109,12 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
override fun visit(subroutine: Subroutine) {
|
||||||
if((subroutine.name=="start" && subroutine.definingScope().name=="main")
|
val alwaysKeepSubroutines = setOf(
|
||||||
|
Pair("main", "start"),
|
||||||
|
Pair("irq", "irq")
|
||||||
|
)
|
||||||
|
|
||||||
|
if(Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|
||||||
|| subroutine.name== initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
|| subroutine.name== initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
||||||
// make sure the entrypoint is mentioned in the used symbols
|
// make sure the entrypoint is mentioned in the used symbols
|
||||||
addNodeAndParentScopes(subroutine)
|
addNodeAndParentScopes(subroutine)
|
||||||
@ -113,10 +123,14 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
if(decl.autoGenerated || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
|
if(decl.autogeneratedDontRemove || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
|
||||||
// make sure autogenerated vardecls are in the used symbols
|
// make sure autogenerated vardecls are in the used symbols
|
||||||
addNodeAndParentScopes(decl)
|
addNodeAndParentScopes(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(decl.datatype==DataType.STRUCT)
|
||||||
|
addNodeAndParentScopes(decl)
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +167,11 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
super.visit(jump)
|
super.visit(jump)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(structDecl: StructDecl) {
|
||||||
|
usedSymbols.add(structDecl)
|
||||||
|
usedSymbols.addAll(structDecl.statements)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(inlineAssembly: InlineAssembly) {
|
override fun visit(inlineAssembly: InlineAssembly) {
|
||||||
// parse inline asm for subroutine calls (jmp, jsr)
|
// parse inline asm for subroutine calls (jmp, jsr)
|
||||||
val scope = inlineAssembly.definingScope()
|
val scope = inlineAssembly.definingScope()
|
||||||
@ -160,7 +179,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
super.visit(inlineAssembly)
|
super.visit(inlineAssembly)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scanAssemblyCode(asm: String, context: IStatement, scope: INameScope) {
|
private fun scanAssemblyCode(asm: String, context: Statement, scope: INameScope) {
|
||||||
val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
asm.lines().forEach { line ->
|
asm.lines().forEach { line ->
|
||||||
@ -186,6 +205,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
if (matches2 != null) {
|
if (matches2 != null) {
|
||||||
val target= matches2.groups[2]?.value
|
val target= matches2.groups[2]?.value
|
||||||
if (target != null && (target[0].isLetter() || target[0] == '_')) {
|
if (target != null && (target[0].isLetter() || target[0] == '_')) {
|
||||||
|
if(target.contains('.')) {
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
||||||
@ -196,4 +216,5 @@ class CallGraph(private val program: Program): IAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.base.ExpressionError
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.base.Position
|
|
||||||
import prog8.ast.expressions.LiteralValue
|
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
class ConstExprEvaluator {
|
class ConstExprEvaluator {
|
||||||
|
|
||||||
fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression {
|
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
||||||
return when(operator) {
|
return when(operator) {
|
||||||
"+" -> plus(left, right)
|
"+" -> plus(left, right)
|
||||||
"-" -> minus(left, right)
|
"-" -> minus(left, right)
|
||||||
@ -25,200 +22,188 @@ class ConstExprEvaluator {
|
|||||||
"and" -> logicaland(left, right)
|
"and" -> logicaland(left, right)
|
||||||
"or" -> logicalor(left, right)
|
"or" -> logicalor(left, right)
|
||||||
"xor" -> logicalxor(left, right)
|
"xor" -> logicalxor(left, right)
|
||||||
"<" -> LiteralValue.fromBoolean(left < right, left.position)
|
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
|
||||||
">" -> LiteralValue.fromBoolean(left > right, left.position)
|
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
|
||||||
"<=" -> LiteralValue.fromBoolean(left <= right, left.position)
|
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
|
||||||
">=" -> LiteralValue.fromBoolean(left >= right, left.position)
|
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
|
||||||
"==" -> LiteralValue.fromBoolean(left == right, left.position)
|
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
|
||||||
"!=" -> LiteralValue.fromBoolean(left != right, left.position)
|
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
|
||||||
"<<" -> shiftedleft(left, right)
|
"<<" -> shiftedleft(left, right)
|
||||||
">>" -> shiftedright(left, right)
|
">>" -> shiftedright(left, right)
|
||||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shiftedright(left: LiteralValue, amount: LiteralValue): IExpression {
|
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||||
if(left.asIntegerValue==null || amount.asIntegerValue==null)
|
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||||
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
||||||
val result =
|
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)
|
left.number.toInt().ushr(amount.number.toInt())
|
||||||
else
|
else
|
||||||
left.asIntegerValue.shr(amount.asIntegerValue)
|
left.number.toInt().shr(amount.number.toInt())
|
||||||
return LiteralValue.fromNumber(result, left.type, left.position)
|
return NumericLiteralValue(left.type, result, left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shiftedleft(left: LiteralValue, amount: LiteralValue): IExpression {
|
private fun shiftedleft(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||||
if(left.asIntegerValue==null || amount.asIntegerValue==null)
|
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||||
throw ExpressionError("cannot compute $left << $amount", left.position)
|
throw ExpressionError("cannot compute $left << $amount", left.position)
|
||||||
val result = left.asIntegerValue.shl(amount.asIntegerValue)
|
val result = left.number.toInt().shl(amount.number.toInt())
|
||||||
return LiteralValue.fromNumber(result, left.type, left.position)
|
return NumericLiteralValue(left.type, result, left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logicalxor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute $left locical-bitxor $right"
|
val error = "cannot compute $left locical-bitxor $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean((left.asIntegerValue != 0) xor (right.asIntegerValue != 0), left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue.fromBoolean((left.asIntegerValue != 0) xor (right.floatvalue != 0.0), left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toDouble() != 0.0), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean((left.floatvalue != 0.0) xor (right.asIntegerValue != 0), left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toInt() != 0), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue.fromBoolean((left.floatvalue != 0.0) xor (right.floatvalue != 0.0), left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toDouble() != 0.0), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logicalor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun logicalor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute $left locical-or $right"
|
val error = "cannot compute $left locical-or $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 || right.asIntegerValue != 0, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
|
||||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 || right.floatvalue != 0.0, left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toDouble() != 0.0, left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 || right.asIntegerValue != 0, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toInt() != 0, left.position)
|
||||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 || right.floatvalue != 0.0, left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toDouble() != 0.0, left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logicaland(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun logicaland(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute $left locical-and $right"
|
val error = "cannot compute $left locical-and $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 && right.asIntegerValue != 0, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
|
||||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue != 0 && right.floatvalue != 0.0, left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toDouble() != 0.0, left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 && right.asIntegerValue != 0, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toInt() != 0, left.position)
|
||||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue != 0.0 && right.floatvalue != 0.0, left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toDouble() != 0.0, left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwisexor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun bitwisexor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.asIntegerValue!=null) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() xor (right.asIntegerValue and 255)).toShort(), position = left.position)
|
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toShort(), left.position)
|
||||||
}
|
}
|
||||||
} else if(left.type== DataType.UWORD) {
|
} else if(left.type== DataType.UWORD) {
|
||||||
if(right.asIntegerValue!=null) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! xor right.asIntegerValue, position = left.position)
|
return NumericLiteralValue(DataType.UWORD, left.number.toInt() xor right.number.toInt(), left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun bitwiseor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.asIntegerValue!=null) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
|
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
|
||||||
}
|
}
|
||||||
} else if(left.type== DataType.UWORD) {
|
} else if(left.type== DataType.UWORD) {
|
||||||
if(right.asIntegerValue!=null) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
|
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.asIntegerValue!=null) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return LiteralValue(DataType.UBYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
|
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
|
||||||
}
|
}
|
||||||
} else if(left.type== DataType.UWORD) {
|
} else if(left.type== DataType.UWORD) {
|
||||||
if(right.asIntegerValue!=null) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return LiteralValue(DataType.UWORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
|
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun power(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun power(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot calculate $left ** $right"
|
val error = "cannot calculate $left ** $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue.toDouble().pow(right.asIntegerValue), left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue.toDouble().pow(right.floatvalue), position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number.toDouble()), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue.pow(right.asIntegerValue), position = left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toInt()), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue.pow(right.floatvalue), position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toDouble()), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun plus(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun plus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot add $left and $right"
|
val error = "cannot add $left and $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue + right.asIntegerValue, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue + right.floatvalue, position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.asIntegerValue, position = left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toInt(), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue + right.floatvalue, position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
|
||||||
}
|
|
||||||
left.isString -> when {
|
|
||||||
right.isString -> {
|
|
||||||
val newStr = left.strvalue!! + right.strvalue!!
|
|
||||||
if(newStr.length > 255) throw ExpressionError("string too long", left.position)
|
|
||||||
LiteralValue(DataType.STR, strvalue = newStr, position = left.position)
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun minus(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun minus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot subtract $left and $right"
|
val error = "cannot subtract $left and $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue - right.asIntegerValue, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue - right.floatvalue, position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue - right.asIntegerValue, position = left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toInt(), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue - right.floatvalue, position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun multiply(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun multiply(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue.optimalNumeric(left.asIntegerValue * right.asIntegerValue, left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue * right.floatvalue, position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
||||||
right.isString -> {
|
|
||||||
if(right.strvalue!!.length * left.asIntegerValue > 255) throw ExpressionError("string too long", left.position)
|
|
||||||
LiteralValue(DataType.STR, strvalue = right.strvalue.repeat(left.asIntegerValue), position = left.position)
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue * right.asIntegerValue, position = left.position)
|
right.type in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toInt(), left.position)
|
||||||
right.floatvalue!=null -> LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue * right.floatvalue, position = left.position)
|
right.type == DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
@ -228,29 +213,29 @@ class ConstExprEvaluator {
|
|||||||
private fun divideByZeroError(pos: Position): Unit =
|
private fun divideByZeroError(pos: Position): Unit =
|
||||||
throw ExpressionError("division by zero", pos)
|
throw ExpressionError("division by zero", pos)
|
||||||
|
|
||||||
private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun divide(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot divide $left by $right"
|
val error = "cannot divide $left by $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> {
|
right.type in IntegerDatatypes -> {
|
||||||
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
val result: Int = left.asIntegerValue / right.asIntegerValue
|
val result: Int = left.number.toInt() / right.number.toInt()
|
||||||
LiteralValue.optimalNumeric(result, left.position)
|
NumericLiteralValue.optimalNumeric(result, left.position)
|
||||||
}
|
}
|
||||||
right.floatvalue!=null -> {
|
right.type == DataType.FLOAT -> {
|
||||||
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue / right.floatvalue, position = left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> {
|
right.type in IntegerDatatypes -> {
|
||||||
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.asIntegerValue, position = left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toInt(), left.position)
|
||||||
}
|
}
|
||||||
right.floatvalue!=null -> {
|
right.type == DataType.FLOAT -> {
|
||||||
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.floatvalue, position = left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
@ -258,28 +243,28 @@ class ConstExprEvaluator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun remainder(left: LiteralValue, right: LiteralValue): LiteralValue {
|
private fun remainder(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
val error = "cannot compute remainder of $left by $right"
|
val error = "cannot compute remainder of $left by $right"
|
||||||
return when {
|
return when {
|
||||||
left.asIntegerValue!=null -> when {
|
left.type in IntegerDatatypes -> when {
|
||||||
right.asIntegerValue!=null -> {
|
right.type in IntegerDatatypes -> {
|
||||||
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
LiteralValue.optimalNumeric(left.asIntegerValue.toDouble() % right.asIntegerValue.toDouble(), left.position)
|
NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
|
||||||
}
|
}
|
||||||
right.floatvalue!=null -> {
|
right.type == DataType.FLOAT -> {
|
||||||
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue % right.floatvalue, position = left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
left.floatvalue!=null -> when {
|
left.type == DataType.FLOAT -> when {
|
||||||
right.asIntegerValue!=null -> {
|
right.type in IntegerDatatypes -> {
|
||||||
if(right.asIntegerValue==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.asIntegerValue, position = left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toInt(), left.position)
|
||||||
}
|
}
|
||||||
right.floatvalue!=null -> {
|
right.type == DataType.FLOAT -> {
|
||||||
if(right.floatvalue==0.0) divideByZeroError(right.position)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.floatvalue, position = left.position)
|
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.IntegerOrAddressOf
|
import prog8.compiler.IntegerOrAddressOf
|
||||||
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
var errors : MutableList<AstException> = mutableListOf()
|
var errors : MutableList<AstException> = mutableListOf()
|
||||||
|
|
||||||
private val reportedErrorMessages = mutableSetOf<String>()
|
private val reportedErrorMessages = mutableSetOf<String>()
|
||||||
|
|
||||||
fun addError(x: AstException) {
|
fun addError(x: AstException) {
|
||||||
@ -26,30 +26,31 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): IStatement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) {
|
// TODO: use call tree for this?
|
||||||
|
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||||
errors.add(ExpressionError("recursive var declaration", decl.position))
|
errors.add(ExpressionError("recursive var declaration", decl.position))
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
||||||
val litval = decl.value as? LiteralValue
|
val refLv = decl.value as? ReferenceLiteralValue
|
||||||
if(litval!=null && litval.isArray && litval.heapId!=null)
|
if(refLv!=null && refLv.isArray && refLv.heapId!=null)
|
||||||
fixupArrayTypeOnHeap(decl, litval)
|
fixupArrayTypeOnHeap(decl, refLv)
|
||||||
|
|
||||||
if(decl.isArray){
|
if(decl.isArray){
|
||||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
||||||
if(decl.arraysize==null) {
|
if(decl.arraysize==null) {
|
||||||
val arrayval = (decl.value as? LiteralValue)?.arrayvalue
|
val arrayval = (decl.value as? ReferenceLiteralValue)?.array
|
||||||
if(arrayval!=null) {
|
if(arrayval!=null) {
|
||||||
decl.arraysize = ArrayIndex(LiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
|
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(decl.arraysize?.size()==null) {
|
else if(decl.arraysize?.size()==null) {
|
||||||
val size = decl.arraysize!!.index.accept(this)
|
val size = decl.arraysize!!.index.accept(this)
|
||||||
if(size is LiteralValue) {
|
if(size is NumericLiteralValue) {
|
||||||
decl.arraysize = ArrayIndex(size, decl.position)
|
decl.arraysize = ArrayIndex(size, decl.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
}
|
}
|
||||||
@ -59,14 +60,22 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
when(decl.datatype) {
|
when(decl.datatype) {
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
||||||
if (litval != null && litval.type in IntegerDatatypes) {
|
val litval = decl.value as? NumericLiteralValue
|
||||||
val newValue = LiteralValue(DataType.FLOAT, floatvalue = litval.asNumericValue!!.toDouble(), position = litval.position)
|
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||||
|
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
||||||
decl.value = newValue
|
decl.value = newValue
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return decl
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
in StringDatatypes -> {
|
||||||
|
// nothing to do for strings
|
||||||
|
}
|
||||||
|
DataType.STRUCT -> {
|
||||||
|
// struct defintions don't have anything else in them
|
||||||
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
val numericLv = decl.value as? NumericLiteralValue
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array (will be put on heap later)
|
// convert the initializer range expression to an actual array (will be put on heap later)
|
||||||
@ -77,69 +86,66 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program)!!
|
val eltType = rangeExpr.inferType(program)!!
|
||||||
if(eltType in ByteDatatypes) {
|
if(eltType in ByteDatatypes) {
|
||||||
decl.value = LiteralValue(decl.datatype,
|
decl.value = ReferenceLiteralValue(decl.datatype,
|
||||||
arrayvalue = constRange.map { LiteralValue(eltType, bytevalue = it.toShort(), position = decl.value!!.position) }
|
array = constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }
|
||||||
.toTypedArray(), position = decl.value!!.position)
|
.toTypedArray(), position = decl.value!!.position)
|
||||||
} else {
|
} else {
|
||||||
decl.value = LiteralValue(decl.datatype,
|
decl.value = ReferenceLiteralValue(decl.datatype,
|
||||||
arrayvalue = constRange.map { LiteralValue(eltType, wordvalue = it, position = decl.value!!.position) }
|
array = constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }
|
||||||
.toTypedArray(), position = decl.value!!.position)
|
.toTypedArray(), position = decl.value!!.position)
|
||||||
}
|
}
|
||||||
decl.value!!.linkParents(decl)
|
decl.value!!.linkParents(decl)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return decl
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(litval?.type== DataType.FLOAT)
|
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||||
errors.add(ExpressionError("arraysize requires only integers here", litval.position))
|
errors.add(ExpressionError("arraysize requires only integers here", numericLv.position))
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
val size = decl.arraysize?.size() ?: return decl
|
||||||
if ((litval==null || !litval.isArray) && rangeExpr==null) {
|
if (rangeExpr==null && numericLv!=null) {
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||||
val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0
|
val fillvalue = numericLv.number.toInt()
|
||||||
when(decl.datatype){
|
when(decl.datatype){
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
if(fillvalue !in 0..255)
|
if(fillvalue !in 0..255)
|
||||||
errors.add(ExpressionError("ubyte value overflow", litval?.position
|
errors.add(ExpressionError("ubyte value overflow", numericLv.position))
|
||||||
?: decl.position))
|
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
if(fillvalue !in -128..127)
|
if(fillvalue !in -128..127)
|
||||||
errors.add(ExpressionError("byte value overflow", litval?.position
|
errors.add(ExpressionError("byte value overflow", numericLv.position))
|
||||||
?: decl.position))
|
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
if(fillvalue !in 0..65535)
|
if(fillvalue !in 0..65535)
|
||||||
errors.add(ExpressionError("uword value overflow", litval?.position
|
errors.add(ExpressionError("uword value overflow", numericLv.position))
|
||||||
?: decl.position))
|
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
if(fillvalue !in -32768..32767)
|
if(fillvalue !in -32768..32767)
|
||||||
errors.add(ExpressionError("word value overflow", litval?.position
|
errors.add(ExpressionError("word value overflow", numericLv.position))
|
||||||
?: decl.position))
|
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
|
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
|
||||||
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval?.position
|
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = numericLv.position)
|
||||||
?: decl.position)
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return decl
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
val size = decl.arraysize?.size() ?: return decl
|
||||||
if (litval==null || !litval.isArray) {
|
val litval = decl.value as? NumericLiteralValue
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
if(litval==null) {
|
||||||
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
|
// there's no initialization value, but the size is known, so we're ok.
|
||||||
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
|
return super.visit(decl)
|
||||||
errors.add(ExpressionError("float value overflow", litval?.position
|
} else {
|
||||||
?: decl.position))
|
// arraysize initializer is a single int, and we know the size.
|
||||||
|
val fillvalue = litval.number.toDouble()
|
||||||
|
if (fillvalue < FLOAT_MAX_NEGATIVE || fillvalue > FLOAT_MAX_POSITIVE)
|
||||||
|
errors.add(ExpressionError("float value overflow", litval.position))
|
||||||
else {
|
else {
|
||||||
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
|
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
|
||||||
decl.value = LiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position
|
decl.value = ReferenceLiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval.position)
|
||||||
?: decl.position)
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return decl
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +158,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) {
|
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: ReferenceLiteralValue) {
|
||||||
// fix the type of the array value that's on the heap, to match the vardecl.
|
// fix the type of the array value that's on the heap, to match the vardecl.
|
||||||
// notice that checking the bounds of the actual values is not done here, but in the AstChecker later.
|
// notice that checking the bounds of the actual values is not done here, but in the AstChecker later.
|
||||||
|
|
||||||
@ -164,7 +170,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
if(array.array!=null) {
|
if(array.array!=null) {
|
||||||
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, 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 = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
@ -172,9 +178,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
// convert a non-float array to floats
|
// convert a non-float array to floats
|
||||||
val doubleArray = array.array.map { it.integer!!.toDouble() }.toDoubleArray()
|
val doubleArray = array.array.map { it.integer!!.toDouble() }.toDoubleArray()
|
||||||
program.heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
|
program.heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
|
||||||
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
|
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType.STRUCT -> {
|
||||||
|
// leave it alone for structs.
|
||||||
|
}
|
||||||
else -> throw FatalAstException("invalid array vardecl type ${decl.datatype}")
|
else -> throw FatalAstException("invalid array vardecl type ${decl.datatype}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,22 +191,25 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
/**
|
/**
|
||||||
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
||||||
*/
|
*/
|
||||||
override fun visit(identifier: IdentifierReference): IExpression {
|
override fun visit(identifier: IdentifierReference): Expression {
|
||||||
return try {
|
return try {
|
||||||
val cval = identifier.constValue(program) ?: return identifier
|
val cval = identifier.constValue(program) ?: return identifier
|
||||||
return if(cval.isNumeric) {
|
return when {
|
||||||
val copy = LiteralValue(cval.type, cval.bytevalue, cval.wordvalue, cval.floatvalue, null, cval.arrayvalue, position = identifier.position)
|
cval.type in NumericDatatypes -> {
|
||||||
|
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
|
||||||
copy.parent = identifier.parent
|
copy.parent = identifier.parent
|
||||||
copy
|
copy
|
||||||
} else
|
}
|
||||||
identifier
|
cval.type in PassByReferenceDatatypes -> TODO("ref type $identifier")
|
||||||
|
else -> identifier
|
||||||
|
}
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
addError(ax)
|
addError(ax)
|
||||||
identifier
|
identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): IExpression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
return try {
|
return try {
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
typeCastConstArguments(functionCall)
|
typeCastConstArguments(functionCall)
|
||||||
@ -208,7 +220,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
typeCastConstArguments(functionCallStatement)
|
typeCastConstArguments(functionCallStatement)
|
||||||
return functionCallStatement
|
return functionCallStatement
|
||||||
@ -232,7 +244,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
override fun visit(memread: DirectMemoryRead): Expression {
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
||||||
val addrOf = memread.addressExpression as? AddressOf
|
val addrOf = memread.addressExpression as? AddressOf
|
||||||
if(addrOf!=null)
|
if(addrOf!=null)
|
||||||
@ -245,43 +257,36 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||||
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||||
*/
|
*/
|
||||||
override fun visit(expr: PrefixExpression): IExpression {
|
override fun visit(expr: PrefixExpression): Expression {
|
||||||
return try {
|
return try {
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
|
|
||||||
val subexpr = expr.expression
|
val subexpr = expr.expression
|
||||||
if (subexpr is LiteralValue) {
|
if (subexpr is NumericLiteralValue) {
|
||||||
// accept prefixed literal values (such as -3, not true)
|
// accept prefixed literal values (such as -3, not true)
|
||||||
return when {
|
return when {
|
||||||
expr.operator == "+" -> subexpr
|
expr.operator == "+" -> subexpr
|
||||||
expr.operator == "-" -> when {
|
expr.operator == "-" -> when {
|
||||||
subexpr.asIntegerValue!= null -> {
|
subexpr.type in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
LiteralValue.optimalNumeric(-subexpr.asIntegerValue, subexpr.position)
|
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
|
||||||
}
|
}
|
||||||
subexpr.floatvalue != null -> {
|
subexpr.type == DataType.FLOAT -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
LiteralValue(DataType.FLOAT, floatvalue = -subexpr.floatvalue, position = subexpr.position)
|
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||||
}
|
}
|
||||||
expr.operator == "~" -> when {
|
expr.operator == "~" -> when {
|
||||||
subexpr.asIntegerValue != null -> {
|
subexpr.type in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
LiteralValue.optimalNumeric(subexpr.asIntegerValue.inv(), subexpr.position)
|
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||||
}
|
}
|
||||||
expr.operator == "not" -> when {
|
expr.operator == "not" -> {
|
||||||
subexpr.asIntegerValue != null -> {
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
LiteralValue.fromBoolean(subexpr.asIntegerValue == 0, subexpr.position)
|
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
|
||||||
}
|
|
||||||
subexpr.floatvalue != null -> {
|
|
||||||
optimizationsDone++
|
|
||||||
LiteralValue.fromBoolean(subexpr.floatvalue == 0.0, subexpr.position)
|
|
||||||
}
|
|
||||||
else -> throw ExpressionError("can not take logical not of $subexpr", subexpr.position)
|
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(expr.operator, subexpr.position)
|
else -> throw ExpressionError(expr.operator, subexpr.position)
|
||||||
}
|
}
|
||||||
@ -310,9 +315,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
* (X / c1) * c2 -> X / (c2/c1)
|
* (X / c1) * c2 -> X / (c2/c1)
|
||||||
* (X + c1) - c2 -> X + (c1-c2)
|
* (X + c1) - c2 -> X + (c1-c2)
|
||||||
*/
|
*/
|
||||||
override fun visit(expr: BinaryExpression): IExpression {
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
return try {
|
return try {
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
|
|
||||||
|
if(expr.left is ReferenceLiteralValue || expr.right is ReferenceLiteralValue)
|
||||||
|
TODO("binexpr with reference litval")
|
||||||
|
|
||||||
val leftconst = expr.left.constValue(program)
|
val leftconst = expr.left.constValue(program)
|
||||||
val rightconst = expr.right.constValue(program)
|
val rightconst = expr.right.constValue(program)
|
||||||
|
|
||||||
@ -333,12 +342,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// const fold when both operands are a const
|
// const fold when both operands are a const
|
||||||
val evaluator = ConstExprEvaluator()
|
|
||||||
return when {
|
return when {
|
||||||
leftconst != null && rightconst != null -> {
|
leftconst != null && rightconst != null -> {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
|
val evaluator = ConstExprEvaluator()
|
||||||
evaluator.evaluate(leftconst, expr.operator, rightconst)
|
evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> expr
|
else -> expr
|
||||||
}
|
}
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
@ -352,7 +362,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
leftIsConst: Boolean,
|
leftIsConst: Boolean,
|
||||||
rightIsConst: Boolean,
|
rightIsConst: Boolean,
|
||||||
subleftIsConst: Boolean,
|
subleftIsConst: Boolean,
|
||||||
subrightIsConst: Boolean): IExpression
|
subrightIsConst: Boolean): Expression
|
||||||
{
|
{
|
||||||
// @todo this implements only a small set of possible reorderings for now
|
// @todo this implements only a small set of possible reorderings for now
|
||||||
if(expr.operator==subExpr.operator) {
|
if(expr.operator==subExpr.operator) {
|
||||||
@ -539,13 +549,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): IStatement {
|
override fun visit(forLoop: ForLoop): Statement {
|
||||||
|
|
||||||
fun adjustRangeDt(rangeFrom: LiteralValue, targetDt: DataType, rangeTo: LiteralValue, stepLiteral: LiteralValue?, range: RangeExpr): RangeExpr {
|
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
||||||
val newFrom = rangeFrom.cast(targetDt)
|
val newFrom = rangeFrom.cast(targetDt)
|
||||||
val newTo = rangeTo.cast(targetDt)
|
val newTo = rangeTo.cast(targetDt)
|
||||||
if (newFrom != null && newTo != null) {
|
if (newFrom != null && newTo != null) {
|
||||||
val newStep: IExpression =
|
val newStep: Expression =
|
||||||
if (stepLiteral != null) (stepLiteral.cast(targetDt) ?: stepLiteral) else range.step
|
if (stepLiteral != null) (stepLiteral.cast(targetDt) ?: stepLiteral) else range.step
|
||||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
return RangeExpr(newFrom, newTo, newStep, range.position)
|
||||||
}
|
}
|
||||||
@ -555,13 +565,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||||
val resultStmt = super.visit(forLoop) as ForLoop
|
val resultStmt = super.visit(forLoop) as ForLoop
|
||||||
val iterableRange = resultStmt.iterable as? RangeExpr ?: return resultStmt
|
val iterableRange = resultStmt.iterable as? RangeExpr ?: return resultStmt
|
||||||
val rangeFrom = iterableRange.from as? LiteralValue
|
val rangeFrom = iterableRange.from as? NumericLiteralValue
|
||||||
val rangeTo = iterableRange.to as? LiteralValue
|
val rangeTo = iterableRange.to as? NumericLiteralValue
|
||||||
if(rangeFrom==null || rangeTo==null) return resultStmt
|
if(rangeFrom==null || rangeTo==null) return resultStmt
|
||||||
|
|
||||||
val loopvar = resultStmt.loopVar?.targetVarDecl(program.namespace)
|
val loopvar = resultStmt.loopVar?.targetVarDecl(program.namespace)
|
||||||
if(loopvar!=null) {
|
if(loopvar!=null) {
|
||||||
val stepLiteral = iterableRange.step as? LiteralValue
|
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
||||||
when(loopvar.datatype) {
|
when(loopvar.datatype) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(rangeFrom.type!= DataType.UBYTE) {
|
if(rangeFrom.type!= DataType.UBYTE) {
|
||||||
@ -593,39 +603,46 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
return resultStmt
|
return resultStmt
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(literalValue: LiteralValue): LiteralValue {
|
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
||||||
val litval = super.visit(literalValue)
|
val litval = super.visit(refLiteral)
|
||||||
if(litval.isString) {
|
if(litval is ReferenceLiteralValue) {
|
||||||
|
if (litval.isString) {
|
||||||
// intern the string; move it into the heap
|
// intern the string; move it into the heap
|
||||||
if(litval.strvalue!!.length !in 1..255)
|
if (litval.str!!.length !in 1..255)
|
||||||
addError(ExpressionError("string literal length must be between 1 and 255", litval.position))
|
addError(ExpressionError("string literal length must be between 1 and 255", litval.position))
|
||||||
else {
|
else {
|
||||||
litval.addToHeap(program.heap) // TODO: we don't know the actual string type yet, STR != STR_S etc...
|
litval.addToHeap(program.heap) // TODO: we don't know the actual string type yet, STR != STR_S etc...
|
||||||
}
|
}
|
||||||
} else if(litval.arrayvalue!=null) {
|
} else if (litval.isArray) {
|
||||||
// first, adjust the array datatype
|
// first, adjust the array datatype
|
||||||
val litval2 = adjustArrayValDatatype(litval)
|
val litval2 = adjustArrayValDatatype(litval)
|
||||||
litval2.addToHeap(program.heap)
|
litval2.addToHeap(program.heap)
|
||||||
return litval2
|
return litval2
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return litval
|
return litval
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustArrayValDatatype(litval: LiteralValue): LiteralValue {
|
private fun adjustArrayValDatatype(litval: ReferenceLiteralValue): ReferenceLiteralValue {
|
||||||
val array = litval.arrayvalue!!
|
if(litval.array==null) {
|
||||||
val typesInArray = array.mapNotNull { it.inferType(program) }.toSet()
|
if(litval.heapId!=null)
|
||||||
|
return litval // thing is already on the heap, assume it's the right type
|
||||||
|
throw FatalAstException("missing array value")
|
||||||
|
}
|
||||||
|
|
||||||
|
val typesInArray = litval.array.mapNotNull { it.inferType(program) }.toSet()
|
||||||
val arrayDt =
|
val arrayDt =
|
||||||
when {
|
when {
|
||||||
array.any { it is AddressOf } -> DataType.ARRAY_UW
|
litval.array.any { it is AddressOf } -> DataType.ARRAY_UW
|
||||||
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
|
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
|
||||||
DataType.WORD in typesInArray -> DataType.ARRAY_W
|
DataType.WORD in typesInArray -> DataType.ARRAY_W
|
||||||
else -> {
|
else -> {
|
||||||
val allElementsAreConstantOrAddressOf = array.fold(true) { c, expr-> c and (expr is LiteralValue || expr is AddressOf)}
|
val allElementsAreConstantOrAddressOf = litval.array.fold(true) { c, expr-> c and (expr is NumericLiteralValue|| expr is AddressOf)}
|
||||||
if(!allElementsAreConstantOrAddressOf) {
|
if(!allElementsAreConstantOrAddressOf) {
|
||||||
addError(ExpressionError("array literal can only consist of constant primitive numerical values or memory pointers", litval.position))
|
addError(ExpressionError("array literal can only consist of constant primitive numerical values or memory pointers", litval.position))
|
||||||
return litval
|
return litval
|
||||||
} else {
|
} else {
|
||||||
val integerArray = array.map { it.constValue(program)!!.asIntegerValue!! }
|
val integerArray = litval.array.map { it.constValue(program)!!.number.toInt() }
|
||||||
val maxValue = integerArray.max()!!
|
val maxValue = integerArray.max()!!
|
||||||
val minValue = integerArray.min()!!
|
val minValue = integerArray.min()!!
|
||||||
if (minValue >= 0) {
|
if (minValue >= 0) {
|
||||||
@ -646,70 +663,69 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(arrayDt!=litval.type) {
|
if(arrayDt!=litval.type) {
|
||||||
return LiteralValue(arrayDt, arrayvalue = litval.arrayvalue, position = litval.position)
|
return ReferenceLiteralValue(arrayDt, array = litval.array, position = litval.position)
|
||||||
}
|
}
|
||||||
return litval
|
return litval
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): IStatement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
super.visit(assignment)
|
super.visit(assignment)
|
||||||
val lv = assignment.value as? LiteralValue
|
val lv = assignment.value as? NumericLiteralValue
|
||||||
if(lv!=null) {
|
if(lv!=null) {
|
||||||
// see if we can promote/convert a literal value to the required datatype
|
// see if we can promote/convert a literal value to the required datatype
|
||||||
when(assignment.singleTarget?.inferType(program, assignment)) {
|
when(assignment.target.inferType(program, assignment)) {
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
||||||
if(lv.type== DataType.UBYTE)
|
if(lv.type== DataType.UBYTE)
|
||||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
||||||
else if(lv.type== DataType.BYTE && lv.bytevalue!!>=0)
|
else if(lv.type== DataType.BYTE && lv.number.toInt()>=0)
|
||||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
||||||
else if(lv.type== DataType.WORD && lv.wordvalue!!>=0)
|
else if(lv.type== DataType.WORD && lv.number.toInt()>=0)
|
||||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
||||||
else if(lv.type== DataType.FLOAT) {
|
else if(lv.type== DataType.FLOAT) {
|
||||||
val d = lv.floatvalue!!
|
val d = lv.number.toDouble()
|
||||||
if(floor(d)==d && d>=0 && d<=65535)
|
if(floor(d)==d && d>=0 && d<=65535)
|
||||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = floor(d).toInt(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UWORD, floor(d).toInt(), lv.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
|
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
|
||||||
if(lv.type== DataType.UWORD && lv.wordvalue!! <= 255)
|
if(lv.type== DataType.UWORD && lv.number.toInt() <= 255)
|
||||||
assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
|
||||||
else if(lv.type== DataType.BYTE && lv.bytevalue!! >=0)
|
else if(lv.type== DataType.BYTE && lv.number.toInt() >=0)
|
||||||
assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
|
||||||
else if(lv.type== DataType.FLOAT) {
|
else if(lv.type== DataType.FLOAT) {
|
||||||
val d = lv.floatvalue!!
|
val d = lv.number.toDouble()
|
||||||
if(floor(d)==d && d >=0 && d<=255)
|
if(floor(d)==d && d >=0 && d<=255)
|
||||||
assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.UBYTE, floor(d).toShort(), lv.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
|
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
|
||||||
if(lv.type== DataType.UWORD && lv.wordvalue!! <= 127)
|
if(lv.type== DataType.UWORD && lv.number.toInt() <= 127)
|
||||||
assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
|
||||||
else if(lv.type== DataType.UBYTE && lv.bytevalue!! <= 127)
|
else if(lv.type== DataType.UBYTE && lv.number.toInt() <= 127)
|
||||||
assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
|
||||||
else if(lv.type== DataType.FLOAT) {
|
else if(lv.type== DataType.FLOAT) {
|
||||||
val d = lv.floatvalue!!
|
val d = lv.number.toDouble()
|
||||||
if(floor(d)==d && d>=0 && d<=127)
|
if(floor(d)==d && d>=0 && d<=127)
|
||||||
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
|
// 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)
|
if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE)
|
||||||
assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.bytevalue!!.toInt(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
|
||||||
else if(lv.type== DataType.UWORD && lv.wordvalue!! <= 32767)
|
else if(lv.type== DataType.UWORD && lv.number.toInt() <= 32767)
|
||||||
assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.wordvalue, position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
|
||||||
else if(lv.type== DataType.FLOAT) {
|
else if(lv.type== DataType.FLOAT) {
|
||||||
val d = lv.floatvalue!!
|
val d = lv.number.toDouble()
|
||||||
if(floor(d)==d && d>=-32768 && d<=32767)
|
if(floor(d)==d && d>=-32768 && d<=32767)
|
||||||
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
|
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if(lv.isNumeric)
|
assignment.value = NumericLiteralValue(DataType.FLOAT, lv.number.toDouble(), lv.position)
|
||||||
assignment.value = LiteralValue(DataType.FLOAT, floatvalue = lv.asNumericValue?.toDouble(), position = lv.position)
|
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.statements.NopStatement
|
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
|
|
||||||
|
|
||||||
@ -31,14 +30,6 @@ internal fun Program.constantFold() {
|
|||||||
internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
||||||
val optimizer = StatementOptimizer(this, optimizeInlining)
|
val optimizer = StatementOptimizer(this, optimizeInlining)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
for(scope in optimizer.scopesToFlatten.reversed()) {
|
|
||||||
val namescope = scope.parent as INameScope
|
|
||||||
val idx = namescope.statements.indexOf(scope as IStatement)
|
|
||||||
if(idx>=0) {
|
|
||||||
namescope.statements[idx] = NopStatement(scope.position)
|
|
||||||
namescope.statements.addAll(idx, scope.statements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
||||||
|
|
||||||
return optimizer.optimizationsDone
|
return optimizer.optimizationsDone
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.IntegerDatatypes
|
import prog8.ast.base.IntegerDatatypes
|
||||||
@ -8,6 +8,7 @@ import prog8.ast.base.NumericDatatypes
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.Statement
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
|
|
||||||
@ -21,13 +22,13 @@ import kotlin.math.log2
|
|||||||
internal class SimplifyExpressions(private val program: Program) : IAstModifyingVisitor {
|
internal class SimplifyExpressions(private val program: Program) : IAstModifyingVisitor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): IStatement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
if (assignment.aug_op != null)
|
if (assignment.aug_op != null)
|
||||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
||||||
return super.visit(assignment)
|
return super.visit(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
override fun visit(memread: DirectMemoryRead): Expression {
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
||||||
val addrOf = memread.addressExpression as? AddressOf
|
val addrOf = memread.addressExpression as? AddressOf
|
||||||
if(addrOf!=null)
|
if(addrOf!=null)
|
||||||
@ -35,28 +36,53 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return super.visit(memread)
|
return super.visit(memread)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression): IExpression {
|
override fun visit(typecast: TypecastExpression): Expression {
|
||||||
// remove redundant typecasts
|
|
||||||
var tc = typecast
|
var tc = typecast
|
||||||
|
|
||||||
|
// try to statically convert a literal value into one of the desired type
|
||||||
|
val literal = tc.expression as? NumericLiteralValue
|
||||||
|
if(literal!=null) {
|
||||||
|
val newLiteral = literal.cast(tc.type)
|
||||||
|
if(newLiteral!=null && newLiteral!==literal) {
|
||||||
|
optimizationsDone++
|
||||||
|
return newLiteral
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove redundant typecasts
|
||||||
while(true) {
|
while(true) {
|
||||||
val expr = tc.expression
|
val expr = tc.expression
|
||||||
if(expr !is TypecastExpression || expr.type!=tc.type) {
|
if(expr !is TypecastExpression || expr.type!=tc.type) {
|
||||||
val assignment = typecast.parent as? Assignment
|
val assignment = typecast.parent as? Assignment
|
||||||
if(assignment!=null) {
|
if(assignment!=null) {
|
||||||
val targetDt = assignment.singleTarget?.inferType(program, assignment)
|
val targetDt = assignment.target.inferType(program, assignment)
|
||||||
if(tc.expression.inferType(program)==targetDt) {
|
if(tc.expression.inferType(program)==targetDt) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return tc.expression
|
return tc.expression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val subTc = tc.expression as? TypecastExpression
|
||||||
|
if(subTc!=null) {
|
||||||
|
// if the previous typecast was casting to a 'bigger' type, just ignore that one
|
||||||
|
// if the previous typecast was casting to a similar type, ignore that one
|
||||||
|
if(subTc.type largerThan tc.type || subTc.type equalsSize tc.type) {
|
||||||
|
subTc.type = tc.type
|
||||||
|
subTc.parent = tc.parent
|
||||||
|
optimizationsDone++
|
||||||
|
return subTc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return super.visit(tc)
|
return super.visit(tc)
|
||||||
}
|
}
|
||||||
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
tc = expr
|
tc = expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: PrefixExpression): IExpression {
|
override fun visit(expr: PrefixExpression): Expression {
|
||||||
if (expr.operator == "+") {
|
if (expr.operator == "+") {
|
||||||
// +X --> X
|
// +X --> X
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -103,12 +129,12 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return super.visit(expr)
|
return super.visit(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): IExpression {
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
val leftVal = expr.left.constValue(program)
|
val leftVal = expr.left.constValue(program)
|
||||||
val rightVal = expr.right.constValue(program)
|
val rightVal = expr.right.constValue(program)
|
||||||
val constTrue = LiteralValue.fromBoolean(true, expr.position)
|
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
||||||
val constFalse = LiteralValue.fromBoolean(false, expr.position)
|
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
||||||
|
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
@ -149,10 +175,10 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
// X + (-value) --> X - value
|
// X + (-value) --> X - value
|
||||||
if (expr.operator == "+" && rightVal != null) {
|
if (expr.operator == "+" && rightVal != null) {
|
||||||
val rv = rightVal.asNumericValue?.toDouble()
|
val rv = rightVal.number.toDouble()
|
||||||
if (rv != null && rv < 0.0) {
|
if (rv < 0.0) {
|
||||||
expr.operator = "-"
|
expr.operator = "-"
|
||||||
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
|
expr.right = NumericLiteralValue(rightVal.type, -rv, rightVal.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
@ -160,10 +186,10 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
// (-value) + X --> X - value
|
// (-value) + X --> X - value
|
||||||
if (expr.operator == "+" && leftVal != null) {
|
if (expr.operator == "+" && leftVal != null) {
|
||||||
val lv = leftVal.asNumericValue?.toDouble()
|
val lv = leftVal.number.toDouble()
|
||||||
if (lv != null && lv < 0.0) {
|
if (lv < 0.0) {
|
||||||
expr.operator = "-"
|
expr.operator = "-"
|
||||||
expr.right = LiteralValue.fromNumber(-lv, leftVal.type, leftVal.position)
|
expr.right = NumericLiteralValue(leftVal.type, -lv, leftVal.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
@ -179,10 +205,10 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
// X - (-value) --> X + value
|
// X - (-value) --> X + value
|
||||||
if (expr.operator == "-" && rightVal != null) {
|
if (expr.operator == "-" && rightVal != null) {
|
||||||
val rv = rightVal.asNumericValue?.toDouble()
|
val rv = rightVal.number.toDouble()
|
||||||
if (rv != null && rv < 0.0) {
|
if (rv < 0.0) {
|
||||||
expr.operator = "+"
|
expr.operator = "+"
|
||||||
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
|
expr.right = NumericLiteralValue(rightVal.type, -rv, rightVal.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
@ -200,7 +226,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val x = expr.right
|
val x = expr.right
|
||||||
val y = determineY(x, leftBinExpr)
|
val y = determineY(x, leftBinExpr)
|
||||||
if(y!=null) {
|
if(y!=null) {
|
||||||
val yPlus1 = BinaryExpression(y, "+", LiteralValue.fromNumber(1, leftDt!!, y.position), y.position)
|
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt!!, 1, y.position), y.position)
|
||||||
return BinaryExpression(x, "*", yPlus1, x.position)
|
return BinaryExpression(x, "*", yPlus1, x.position)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -209,7 +235,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val x = expr.right
|
val x = expr.right
|
||||||
val y = determineY(x, leftBinExpr)
|
val y = determineY(x, leftBinExpr)
|
||||||
if(y!=null) {
|
if(y!=null) {
|
||||||
val yMinus1 = BinaryExpression(y, "-", LiteralValue.fromNumber(1, leftDt!!, y.position), y.position)
|
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt!!, 1, y.position), y.position)
|
||||||
return BinaryExpression(x, "*", yMinus1, x.position)
|
return BinaryExpression(x, "*", yMinus1, x.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,7 +247,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val x = expr.left
|
val x = expr.left
|
||||||
val y = determineY(x, rightBinExpr)
|
val y = determineY(x, rightBinExpr)
|
||||||
if(y!=null) {
|
if(y!=null) {
|
||||||
val yPlus1 = BinaryExpression(y, "+", LiteralValue.optimalInteger(1, y.position), y.position)
|
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue.optimalInteger(1, y.position), y.position)
|
||||||
return BinaryExpression(x, "*", yPlus1, x.position)
|
return BinaryExpression(x, "*", yPlus1, x.position)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -230,7 +256,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val x = expr.left
|
val x = expr.left
|
||||||
val y = determineY(x, rightBinExpr)
|
val y = determineY(x, rightBinExpr)
|
||||||
if(y!=null) {
|
if(y!=null) {
|
||||||
val oneMinusY = BinaryExpression(LiteralValue.optimalInteger(1, y.position), "-", y, y.position)
|
val oneMinusY = BinaryExpression(NumericLiteralValue.optimalInteger(1, y.position), "-", y, y.position)
|
||||||
return BinaryExpression(x, "*", oneMinusY, x.position)
|
return BinaryExpression(x, "*", oneMinusY, x.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,7 +342,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
|
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||||
return when {
|
return when {
|
||||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||||
subBinExpr.right isSameAs x -> subBinExpr.left
|
subBinExpr.right isSameAs x -> subBinExpr.left
|
||||||
@ -325,58 +351,58 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustDatatypes(expr: BinaryExpression,
|
private fun adjustDatatypes(expr: BinaryExpression,
|
||||||
leftConstVal: LiteralValue?, leftDt: DataType,
|
leftConstVal: NumericLiteralValue?, leftDt: DataType,
|
||||||
rightConstVal: LiteralValue?, rightDt: DataType): Boolean {
|
rightConstVal: NumericLiteralValue?, rightDt: DataType): Boolean {
|
||||||
|
|
||||||
fun adjust(value: LiteralValue, targetDt: DataType): Pair<Boolean, LiteralValue>{
|
fun adjust(value: NumericLiteralValue, targetDt: DataType): Pair<Boolean, NumericLiteralValue>{
|
||||||
if(value.type==targetDt)
|
if(value.type==targetDt)
|
||||||
return Pair(false, value)
|
return Pair(false, value)
|
||||||
when(value.type) {
|
when(value.type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (targetDt == DataType.BYTE) {
|
if (targetDt == DataType.BYTE) {
|
||||||
if(value.bytevalue!! < 127)
|
if(value.number.toInt() < 127)
|
||||||
return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
||||||
}
|
}
|
||||||
else if (targetDt == DataType.UWORD || targetDt == DataType.WORD)
|
else if (targetDt == DataType.UWORD || targetDt == DataType.WORD)
|
||||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if (targetDt == DataType.UBYTE) {
|
if (targetDt == DataType.UBYTE) {
|
||||||
if(value.bytevalue!! >= 0)
|
if(value.number.toInt() >= 0)
|
||||||
return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
||||||
}
|
}
|
||||||
else if (targetDt == DataType.UWORD) {
|
else if (targetDt == DataType.UWORD) {
|
||||||
if(value.bytevalue!! >= 0)
|
if(value.number.toInt() >= 0)
|
||||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue.toInt(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), 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, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if (targetDt == DataType.UBYTE) {
|
if (targetDt == DataType.UBYTE) {
|
||||||
if(value.wordvalue!! <= 255)
|
if(value.number.toInt() <= 255)
|
||||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
||||||
}
|
}
|
||||||
else if (targetDt == DataType.BYTE) {
|
else if (targetDt == DataType.BYTE) {
|
||||||
if(value.wordvalue!! <= 127)
|
if(value.number.toInt() <= 127)
|
||||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
||||||
}
|
}
|
||||||
else if (targetDt == DataType.WORD) {
|
else if (targetDt == DataType.WORD) {
|
||||||
if(value.wordvalue!! <= 32767)
|
if(value.number.toInt() <= 32767)
|
||||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.wordvalue, position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if (targetDt == DataType.UBYTE) {
|
if (targetDt == DataType.UBYTE) {
|
||||||
if(value.wordvalue!! in 0..255)
|
if(value.number.toInt() in 0..255)
|
||||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
||||||
}
|
}
|
||||||
else if (targetDt == DataType.BYTE) {
|
else if (targetDt == DataType.BYTE) {
|
||||||
if(value.wordvalue!! in -128..127)
|
if(value.number.toInt() in -128..127)
|
||||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
||||||
}
|
}
|
||||||
else if (targetDt == DataType.UWORD) {
|
else if (targetDt == DataType.UWORD) {
|
||||||
if(value.wordvalue!! >= 0)
|
if(value.number.toInt() >= 0)
|
||||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
@ -385,7 +411,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(leftConstVal==null && rightConstVal!=null) {
|
if(leftConstVal==null && rightConstVal!=null) {
|
||||||
if(leftDt biggerThan rightDt) {
|
if(leftDt largerThan rightDt) {
|
||||||
val (adjusted, newValue) = adjust(rightConstVal, leftDt)
|
val (adjusted, newValue) = adjust(rightConstVal, leftDt)
|
||||||
if (adjusted) {
|
if (adjusted) {
|
||||||
expr.right = newValue
|
expr.right = newValue
|
||||||
@ -395,7 +421,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
} else if(leftConstVal!=null && rightConstVal==null) {
|
} else if(leftConstVal!=null && rightConstVal==null) {
|
||||||
if(rightDt biggerThan leftDt) {
|
if(rightDt largerThan leftDt) {
|
||||||
val (adjusted, newValue) = adjust(leftConstVal, rightDt)
|
val (adjusted, newValue) = adjust(leftConstVal, rightDt)
|
||||||
if (adjusted) {
|
if (adjusted) {
|
||||||
expr.left = newValue
|
expr.left = newValue
|
||||||
@ -409,9 +435,9 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?)
|
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
|
||||||
|
|
||||||
private fun reorderAssociative(expr: BinaryExpression, leftVal: LiteralValue?): ReorderedAssociativeBinaryExpr {
|
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
|
||||||
if(expr.operator in associativeOperators && leftVal!=null) {
|
if(expr.operator in associativeOperators && leftVal!=null) {
|
||||||
// swap left and right so that right is always the constant
|
// swap left and right so that right is always the constant
|
||||||
val tmp = expr.left
|
val tmp = expr.left
|
||||||
@ -423,15 +449,15 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
|
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
|
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): Expression {
|
||||||
if(pleftVal==null && prightVal==null)
|
if(pleftVal==null && prightVal==null)
|
||||||
return pexpr
|
return pexpr
|
||||||
|
|
||||||
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
||||||
if(rightVal!=null) {
|
if(rightVal!=null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: LiteralValue = rightVal
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
when(rightConst.asNumericValue?.toDouble()) {
|
when(rightConst.number.toDouble()) {
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
// left
|
// left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -444,14 +470,14 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeSub(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
|
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||||
if(leftVal==null && rightVal==null)
|
if(leftVal==null && rightVal==null)
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
if(rightVal!=null) {
|
if(rightVal!=null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: LiteralValue = rightVal
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
when(rightConst.asNumericValue?.toDouble()) {
|
when(rightConst.number.toDouble()) {
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
// left
|
// left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -461,7 +487,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
if(leftVal!=null) {
|
if(leftVal!=null) {
|
||||||
// left value is a constant, see if we can optimize
|
// left value is a constant, see if we can optimize
|
||||||
when(leftVal.asNumericValue?.toDouble()) {
|
when(leftVal.number.toDouble()) {
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
// -right
|
// -right
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -473,38 +499,38 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizePower(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
|
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||||
if(leftVal==null && rightVal==null)
|
if(leftVal==null && rightVal==null)
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
if(rightVal!=null) {
|
if(rightVal!=null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: LiteralValue = rightVal
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
when(rightConst.asNumericValue?.toDouble()) {
|
when(rightConst.number.toDouble()) {
|
||||||
-3.0 -> {
|
-3.0 -> {
|
||||||
// -1/(left*left*left)
|
// -1/(left*left*left)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
|
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||||
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
|
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
|
||||||
expr.position)
|
expr.position)
|
||||||
}
|
}
|
||||||
-2.0 -> {
|
-2.0 -> {
|
||||||
// -1/(left*left)
|
// -1/(left*left)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
|
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||||
BinaryExpression(expr.left, "*", expr.left, expr.position),
|
BinaryExpression(expr.left, "*", expr.left, expr.position),
|
||||||
expr.position)
|
expr.position)
|
||||||
}
|
}
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
// -1/left
|
// -1/left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
|
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||||
expr.left, expr.position)
|
expr.left, expr.position)
|
||||||
}
|
}
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
// 1
|
// 1
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue.fromNumber(1, rightConst.type, expr.position)
|
return NumericLiteralValue(rightConst.type, 1, expr.position)
|
||||||
}
|
}
|
||||||
0.5 -> {
|
0.5 -> {
|
||||||
// sqrt(left)
|
// sqrt(left)
|
||||||
@ -530,21 +556,21 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
if(leftVal!=null) {
|
if(leftVal!=null) {
|
||||||
// left value is a constant, see if we can optimize
|
// left value is a constant, see if we can optimize
|
||||||
when(leftVal.asNumericValue?.toDouble()) {
|
when(leftVal.number.toDouble()) {
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
// -1
|
// -1
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position)
|
return NumericLiteralValue(DataType.FLOAT, -1.0, expr.position)
|
||||||
}
|
}
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
// 0
|
// 0
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue.fromNumber(0, leftVal.type, expr.position)
|
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
||||||
}
|
}
|
||||||
1.0 -> {
|
1.0 -> {
|
||||||
//1
|
//1
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue.fromNumber(1, leftVal.type, expr.position)
|
return NumericLiteralValue(leftVal.type, 1, expr.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -553,22 +579,22 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeRemainder(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
|
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||||
if(leftVal==null && rightVal==null)
|
if(leftVal==null && rightVal==null)
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
// simplify assignments A = B <operator> C
|
// simplify assignments A = B <operator> C
|
||||||
|
|
||||||
val cv = rightVal?.asIntegerValue?.toDouble()
|
val cv = rightVal?.number?.toInt()?.toDouble()
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"%" -> {
|
"%" -> {
|
||||||
if (cv == 1.0) {
|
if (cv == 1.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue.fromNumber(0, expr.inferType(program)!!, expr.position)
|
return NumericLiteralValue(expr.inferType(program)!!, 0, expr.position)
|
||||||
} else if (cv == 2.0) {
|
} else if (cv == 2.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
expr.operator = "&"
|
expr.operator = "&"
|
||||||
expr.right = LiteralValue.optimalInteger(1, expr.position)
|
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,7 +603,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
|
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||||
if(leftVal==null && rightVal==null)
|
if(leftVal==null && rightVal==null)
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
@ -585,8 +611,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
if(rightVal!=null) {
|
if(rightVal!=null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: LiteralValue = rightVal
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
val cv = rightConst.asNumericValue?.toDouble()
|
val cv = rightConst.number.toDouble()
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
when(cv) {
|
when(cv) {
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
@ -608,7 +634,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
// divided by a power of two => shift right
|
// divided by a power of two => shift right
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
val numshifts = log2(cv).toInt()
|
val numshifts = log2(cv).toInt()
|
||||||
return BinaryExpression(expr.left, ">>", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
||||||
@ -616,32 +642,32 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
// divided by a negative power of two => negate, then shift right
|
// divided by a negative power of two => negate, then shift right
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
val numshifts = log2(-cv).toInt()
|
val numshifts = log2(-cv).toInt()
|
||||||
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leftDt == DataType.UBYTE) {
|
if (leftDt == DataType.UBYTE) {
|
||||||
if(abs(rightConst.asNumericValue!!.toDouble()) >= 256.0) {
|
if(abs(rightConst.number.toDouble()) >= 256.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue(DataType.UBYTE, 0, position = expr.position)
|
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (leftDt == DataType.UWORD) {
|
else if (leftDt == DataType.UWORD) {
|
||||||
if(abs(rightConst.asNumericValue!!.toDouble()) >= 65536.0) {
|
if(abs(rightConst.number.toDouble()) >= 65536.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue(DataType.UBYTE, 0, position = expr.position)
|
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftVal!=null) {
|
if(leftVal!=null) {
|
||||||
// left value is a constant, see if we can optimize
|
// left value is a constant, see if we can optimize
|
||||||
when(leftVal.asNumericValue?.toDouble()) {
|
when(leftVal.number.toDouble()) {
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
// 0
|
// 0
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue.fromNumber(0, leftVal.type, expr.position)
|
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -649,16 +675,16 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
|
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): Expression {
|
||||||
if(pleftVal==null && prightVal==null)
|
if(pleftVal==null && prightVal==null)
|
||||||
return pexpr
|
return pexpr
|
||||||
|
|
||||||
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
||||||
if(rightVal!=null) {
|
if(rightVal!=null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val leftValue: IExpression = expr.left
|
val leftValue: Expression = expr.left
|
||||||
val rightConst: LiteralValue = rightVal
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
when(val cv = rightConst.asNumericValue?.toDouble()) {
|
when(val cv = rightConst.number.toDouble()) {
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
// -left
|
// -left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -667,7 +693,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
0.0 -> {
|
0.0 -> {
|
||||||
// 0
|
// 0
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return LiteralValue.fromNumber(0, rightConst.type, expr.position)
|
return NumericLiteralValue(rightConst.type, 0, expr.position)
|
||||||
}
|
}
|
||||||
1.0 -> {
|
1.0 -> {
|
||||||
// left
|
// left
|
||||||
@ -679,7 +705,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
// times a power of two => shift left
|
// times a power of two => shift left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
val numshifts = log2(cv).toInt()
|
val numshifts = log2(cv).toInt()
|
||||||
return BinaryExpression(expr.left, "<<", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(expr.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
||||||
@ -687,7 +713,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
// times a negative power of two => negate, then shift left
|
// times a negative power of two => negate, then shift left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
val numshifts = log2(-cv).toInt()
|
val numshifts = log2(-cv).toInt()
|
||||||
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), "<<", LiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import prog8.ast.*
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
@ -15,11 +16,10 @@ import kotlin.math.floor
|
|||||||
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstModifyingVisitor {
|
internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstModifyingVisitor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
private set
|
private set
|
||||||
var scopesToFlatten = mutableListOf<INameScope>()
|
|
||||||
val nopStatements = mutableListOf<NopStatement>()
|
|
||||||
|
|
||||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||||
private val callgraph = CallGraph(program)
|
private val callgraph = CallGraph(program)
|
||||||
@ -34,9 +34,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
inlineSubroutines(callgraph)
|
inlineSubroutines(callgraph)
|
||||||
}
|
}
|
||||||
super.visit(program)
|
super.visit(program)
|
||||||
|
|
||||||
// at the end, remove the encountered NOP statements
|
|
||||||
this.nopStatements.forEach { it.definingScope().remove(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inlineSubroutines(callgraph: CallGraph) {
|
private fun inlineSubroutines(callgraph: CallGraph) {
|
||||||
@ -62,7 +59,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
val scope = caller.definingScope()
|
val scope = caller.definingScope()
|
||||||
if(sub.calledBy.count { it.definingScope()===scope } > 1)
|
if(sub.calledBy.count { it.definingScope()===scope } > 1)
|
||||||
return
|
return
|
||||||
if(caller !is IFunctionCall || caller !is IStatement || sub.statements.any { it is Subroutine })
|
if(caller !is IFunctionCall || caller !is Statement || sub.statements.any { it is Subroutine })
|
||||||
return
|
return
|
||||||
|
|
||||||
if(sub.parameters.isEmpty() && sub.returntypes.isEmpty()) {
|
if(sub.parameters.isEmpty() && sub.returntypes.isEmpty()) {
|
||||||
@ -81,7 +78,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
val returns = inlined.statements.withIndex().filter { iv -> iv.value is Return }.map { iv -> Pair(iv.index, iv.value as Return)}
|
val returns = inlined.statements.withIndex().filter { iv -> iv.value is Return }.map { iv -> Pair(iv.index, iv.value as Return)}
|
||||||
for(returnIdx in returns) {
|
for(returnIdx in returns) {
|
||||||
assert(returnIdx.second.values.isEmpty())
|
|
||||||
val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position)
|
val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position)
|
||||||
inlined.statements[returnIdx.first] = jump
|
inlined.statements[returnIdx.first] = jump
|
||||||
endLabelUsed = true
|
endLabelUsed = true
|
||||||
@ -140,32 +136,32 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block): IStatement {
|
override fun visit(block: Block): Statement {
|
||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars()) {
|
if (block.containsNoCodeNorVars()) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
printWarning("removing empty block '${block.name}'", block.position)
|
printWarning("removing empty block '${block.name}'", block.position)
|
||||||
return NopStatement(block.position)
|
return NopStatement.insteadOf(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
if (block !in callgraph.usedSymbols) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
printWarning("removing unused block '${block.name}'", block.position)
|
printWarning("removing unused block '${block.name}'", block.position)
|
||||||
return NopStatement(block.position) // remove unused block
|
return NopStatement.insteadOf(block) // remove unused block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(block)
|
return super.visit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): IStatement {
|
override fun visit(subroutine: Subroutine): Statement {
|
||||||
super.visit(subroutine)
|
super.visit(subroutine)
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||||
if(subroutine.asmAddress==null && !forceOutput) {
|
if(subroutine.asmAddress==null && !forceOutput) {
|
||||||
if(subroutine.containsNoCodeNorVars()) {
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
printWarning("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
printWarning("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(subroutine.position)
|
return NopStatement.insteadOf(subroutine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,39 +185,39 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
printWarning("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
printWarning("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(subroutine.position) // remove unused subroutine
|
return NopStatement.insteadOf(subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): IStatement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||||
if(decl.type!=VarDeclType.CONST)
|
if(decl.type == VarDeclType.VAR)
|
||||||
printWarning("removing unused variable '${decl.name}'", decl.position)
|
printWarning("removing unused variable ${decl.type} '${decl.name}'", decl.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(decl.position) // remove unused variable
|
return NopStatement.insteadOf(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
private fun deduplicateAssignments(statements: List<Statement>): MutableList<Int> {
|
||||||
// removes 'duplicate' assignments that assign the isSameAs target
|
// removes 'duplicate' assignments that assign the isSameAs target
|
||||||
val linesToRemove = mutableListOf<Int>()
|
val linesToRemove = mutableListOf<Int>()
|
||||||
var previousAssignmentLine: Int? = null
|
var previousAssignmentLine: Int? = null
|
||||||
for (i in 0 until statements.size) {
|
for (i in 0 until statements.size) {
|
||||||
val stmt = statements[i] as? Assignment
|
val stmt = statements[i] as? Assignment
|
||||||
if (stmt != null && stmt.value is LiteralValue) {
|
if (stmt != null && stmt.value is NumericLiteralValue) {
|
||||||
if (previousAssignmentLine == null) {
|
if (previousAssignmentLine == null) {
|
||||||
previousAssignmentLine = i
|
previousAssignmentLine = i
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
val prev = statements[previousAssignmentLine] as Assignment
|
val prev = statements[previousAssignmentLine] as Assignment
|
||||||
if (prev.targets.size == 1 && stmt.targets.size == 1 && prev.targets[0].isSameAs(stmt.targets[0], program)) {
|
if (prev.target.isSameAs(stmt.target, program)) {
|
||||||
// get rid of the previous assignment, if the target is not MEMORY
|
// get rid of the previous assignment, if the target is not MEMORY
|
||||||
if (prev.targets[0].isNotMemory(program.namespace))
|
if (prev.target.isNotMemory(program.namespace))
|
||||||
linesToRemove.add(previousAssignmentLine)
|
linesToRemove.add(previousAssignmentLine)
|
||||||
}
|
}
|
||||||
previousAssignmentLine = i
|
previousAssignmentLine = i
|
||||||
@ -232,20 +228,20 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
return linesToRemove
|
return linesToRemove
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
if (functionName in pureBuiltinFunctions) {
|
if (functionName in pureBuiltinFunctions) {
|
||||||
printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(functionCallStatement.position)
|
return NopStatement.insteadOf(functionCallStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
||||||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
||||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||||
if(functionCallStatement.arglist.single() is LiteralValue)
|
if(functionCallStatement.arglist.single() is NumericLiteralValue)
|
||||||
throw AstException("string argument should be on heap already")
|
throw AstException("string argument should be on heap already")
|
||||||
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
|
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
|
||||||
if(stringVar!=null) {
|
if(stringVar!=null) {
|
||||||
@ -254,7 +250,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
if(string.length==1) {
|
if(string.length==1) {
|
||||||
val petscii = Petscii.encodePetscii(string, true)[0]
|
val petscii = Petscii.encodePetscii(string, true)[0]
|
||||||
functionCallStatement.arglist.clear()
|
functionCallStatement.arglist.clear()
|
||||||
functionCallStatement.arglist.add(LiteralValue.optimalInteger(petscii, functionCallStatement.position))
|
functionCallStatement.arglist.add(NumericLiteralValue.optimalInteger(petscii.toInt(), functionCallStatement.position))
|
||||||
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return functionCallStatement
|
return functionCallStatement
|
||||||
@ -262,9 +258,9 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
val petscii = Petscii.encodePetscii(string, true)
|
val petscii = Petscii.encodePetscii(string, true)
|
||||||
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
||||||
mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCallStatement.position)), functionCallStatement.position))
|
mutableListOf(NumericLiteralValue.optimalInteger(petscii[0].toInt(), functionCallStatement.position)), functionCallStatement.position))
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
||||||
mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCallStatement.position)), functionCallStatement.position))
|
mutableListOf(NumericLiteralValue.optimalInteger(petscii[1].toInt(), functionCallStatement.position)), functionCallStatement.position))
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
@ -283,14 +279,14 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
if(first is ReturnFromIrq || first is Return) {
|
if(first is ReturnFromIrq || first is Return) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(functionCallStatement.position)
|
return NopStatement.insteadOf(functionCallStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(functionCallStatement)
|
return super.visit(functionCallStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): IExpression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
// if it calls a subroutine,
|
// if it calls a subroutine,
|
||||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
||||||
@ -301,8 +297,8 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
|
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
|
||||||
}
|
}
|
||||||
if(first is Return && first.values.size==1) {
|
if(first is Return && first.value!=null) {
|
||||||
val constval = first.values[0].constValue(program)
|
val constval = first.value?.constValue(program)
|
||||||
if(constval!=null)
|
if(constval!=null)
|
||||||
return constval
|
return constval
|
||||||
}
|
}
|
||||||
@ -310,12 +306,12 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
return super.visit(functionCall)
|
return super.visit(functionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(ifStatement: IfStatement): IStatement {
|
override fun visit(ifStatement: IfStatement): Statement {
|
||||||
super.visit(ifStatement)
|
super.visit(ifStatement)
|
||||||
|
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(ifStatement.position)
|
return NopStatement.insteadOf(ifStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
||||||
@ -344,18 +340,18 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
return ifStatement
|
return ifStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): IStatement {
|
override fun visit(forLoop: ForLoop): Statement {
|
||||||
super.visit(forLoop)
|
super.visit(forLoop)
|
||||||
if(forLoop.body.containsNoCodeNorVars()) {
|
if(forLoop.body.containsNoCodeNorVars()) {
|
||||||
// remove empty for loop
|
// remove empty for loop
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(forLoop.position)
|
return NopStatement.insteadOf(forLoop)
|
||||||
} else if(forLoop.body.statements.size==1) {
|
} else if(forLoop.body.statements.size==1) {
|
||||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||||
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
|
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
|
||||||
// remove empty for loop
|
// remove empty for loop
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(forLoop.position)
|
return NopStatement.insteadOf(forLoop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +361,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
if(range.size()==1) {
|
if(range.size()==1) {
|
||||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||||
// loopvar/reg = range value , follow by block
|
// loopvar/reg = range value , follow by block
|
||||||
val assignment = Assignment(listOf(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position)), null, range.from, forLoop.position)
|
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position)
|
||||||
forLoop.body.statements.add(0, assignment)
|
forLoop.body.statements.add(0, assignment)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return forLoop.body
|
return forLoop.body
|
||||||
@ -374,7 +370,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
return forLoop
|
return forLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop): IStatement {
|
override fun visit(whileLoop: WhileLoop): Statement {
|
||||||
super.visit(whileLoop)
|
super.visit(whileLoop)
|
||||||
val constvalue = whileLoop.condition.constValue(program)
|
val constvalue = whileLoop.condition.constValue(program)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
@ -394,13 +390,13 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
// always false -> ditch whole statement
|
// always false -> ditch whole statement
|
||||||
printWarning("condition is always false", whileLoop.position)
|
printWarning("condition is always false", whileLoop.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NopStatement(whileLoop.position)
|
NopStatement.insteadOf(whileLoop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return whileLoop
|
return whileLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop): IStatement {
|
override fun visit(repeatLoop: RepeatLoop): Statement {
|
||||||
super.visit(repeatLoop)
|
super.visit(repeatLoop)
|
||||||
val constvalue = repeatLoop.untilCondition.constValue(program)
|
val constvalue = repeatLoop.untilCondition.constValue(program)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
@ -430,12 +426,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
return repeatLoop
|
return repeatLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement): IStatement {
|
override fun visit(whenStatement: WhenStatement): Statement {
|
||||||
this.nopStatements.add(nopStatement)
|
|
||||||
return nopStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
|
||||||
val choices = whenStatement.choices.toList()
|
val choices = whenStatement.choices.toList()
|
||||||
for(choice in choices) {
|
for(choice in choices) {
|
||||||
if(choice.statements.containsNoCodeNorVars())
|
if(choice.statements.containsNoCodeNorVars())
|
||||||
@ -450,12 +441,12 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
{
|
{
|
||||||
var count=0
|
var count=0
|
||||||
|
|
||||||
override fun visit(breakStmt: Break): IStatement {
|
override fun visit(breakStmt: Break): Statement {
|
||||||
count++
|
count++
|
||||||
return super.visit(breakStmt)
|
return super.visit(breakStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(contStmt: Continue): IStatement {
|
override fun visit(contStmt: Continue): Statement {
|
||||||
count++
|
count++
|
||||||
return super.visit(contStmt)
|
return super.visit(contStmt)
|
||||||
}
|
}
|
||||||
@ -469,7 +460,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
return s.count > 0
|
return s.count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(jump: Jump): IStatement {
|
override fun visit(jump: Jump): Statement {
|
||||||
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
// if the first instruction in the subroutine is another jump, shortcut this one
|
||||||
@ -486,53 +477,51 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
if(label!=null) {
|
if(label!=null) {
|
||||||
if(scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) {
|
if(scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(jump.position)
|
return NopStatement.insteadOf(jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return jump
|
return jump
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): IStatement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
if(assignment.aug_op!=null)
|
if(assignment.aug_op!=null)
|
||||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
||||||
|
|
||||||
if(assignment.targets.size==1) {
|
if(assignment.target isSameAs assignment.value) {
|
||||||
val target=assignment.targets[0]
|
|
||||||
if(target isSameAs assignment.value) {
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
val targetDt = target.inferType(program, assignment)
|
val targetDt = assignment.target.inferType(program, assignment)
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
|
val cv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||||
if(cv==null) {
|
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) {
|
if (bexpr.left isSameAs bexpr.right && assignment.target isSameAs bexpr.left) {
|
||||||
bexpr.operator = "*"
|
bexpr.operator = "*"
|
||||||
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
|
bexpr.right = NumericLiteralValue.optimalInteger(2, assignment.value.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return assignment
|
return assignment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (target isSameAs bexpr.left) {
|
if (assignment.target isSameAs bexpr.left) {
|
||||||
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
||||||
// A = A <operator> B
|
// A = A <operator> B
|
||||||
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type
|
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
|
||||||
|
|
||||||
when (bexpr.operator) {
|
when (bexpr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if (cv == 0.0) {
|
if (cv == 0.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||||
if((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt!=VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||||
// replace by several INCs (a bit less when dealing with memory targets)
|
// replace by several INCs (a bit less when dealing with memory targets)
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(cv.toInt()) {
|
repeat(cv.toInt()) {
|
||||||
decs.statements.add(PostIncrDecr(target, "++", assignment.position))
|
decs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
||||||
}
|
}
|
||||||
return decs
|
return decs
|
||||||
}
|
}
|
||||||
@ -541,13 +530,13 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
"-" -> {
|
"-" -> {
|
||||||
if (cv == 0.0) {
|
if (cv == 0.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||||
if((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt!=VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||||
// replace by several DECs (a bit less when dealing with memory targets)
|
// replace by several DECs (a bit less when dealing with memory targets)
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(cv.toInt()) {
|
repeat(cv.toInt()) {
|
||||||
decs.statements.add(PostIncrDecr(target, "--", assignment.position))
|
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
||||||
}
|
}
|
||||||
return decs
|
return decs
|
||||||
}
|
}
|
||||||
@ -555,32 +544,32 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
"*" -> if (cv == 1.0) {
|
"*" -> if (cv == 1.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
"/" -> if (cv == 1.0) {
|
"/" -> if (cv == 1.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
"**" -> if (cv == 1.0) {
|
"**" -> if (cv == 1.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
"|" -> if (cv == 0.0) {
|
"|" -> if (cv == 0.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
"^" -> if (cv == 0.0) {
|
"^" -> if (cv == 0.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (cv == 0.0) {
|
if (cv == 0.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
||||||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
||||||
assignment.value = LiteralValue.optimalInteger(0, assignment.value.position)
|
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
||||||
assignment.value.linkParents(assignment)
|
assignment.value.linkParents(assignment)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
} else {
|
} else {
|
||||||
@ -598,11 +587,11 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
">>" -> {
|
">>" -> {
|
||||||
if (cv == 0.0) {
|
if (cv == 0.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
||||||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
||||||
assignment.value = LiteralValue.optimalInteger(0, assignment.value.position)
|
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
||||||
assignment.value.linkParents(assignment)
|
assignment.value.linkParents(assignment)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
} else {
|
} else {
|
||||||
@ -621,30 +610,25 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(assignment)
|
return super.visit(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope): IStatement {
|
override fun visit(scope: AnonymousScope): Statement {
|
||||||
val linesToRemove = deduplicateAssignments(scope.statements)
|
val linesToRemove = deduplicateAssignments(scope.statements)
|
||||||
if(linesToRemove.isNotEmpty()) {
|
if(linesToRemove.isNotEmpty()) {
|
||||||
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(scope.parent is INameScope) {
|
|
||||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(scope)
|
return super.visit(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(label: Label): IStatement {
|
override fun visit(label: Label): Statement {
|
||||||
// remove duplicate labels
|
// remove duplicate labels
|
||||||
val stmts = label.definingScope().statements
|
val stmts = label.definingScope().statements
|
||||||
val startIdx = stmts.indexOf(label)
|
val startIdx = stmts.indexOf(label)
|
||||||
if(startIdx<(stmts.size-1) && stmts[startIdx+1] == label)
|
if(startIdx<(stmts.size-1) && stmts[startIdx+1] == label)
|
||||||
return NopStatement(label.position)
|
return NopStatement.insteadOf(label)
|
||||||
|
|
||||||
return super.visit(label)
|
return super.visit(label)
|
||||||
}
|
}
|
||||||
@ -652,3 +636,39 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
|
||||||
|
private var scopesToFlatten = mutableListOf<INameScope>()
|
||||||
|
private val nopStatements = mutableListOf<NopStatement>()
|
||||||
|
|
||||||
|
override fun visit(program: Program) {
|
||||||
|
super.visit(program)
|
||||||
|
for(scope in scopesToFlatten.reversed()) {
|
||||||
|
val namescope = scope.parent as INameScope
|
||||||
|
val idx = namescope.statements.indexOf(scope as Statement)
|
||||||
|
if(idx>=0) {
|
||||||
|
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
||||||
|
nop.parent = namescope as Node
|
||||||
|
namescope.statements[idx] = nop
|
||||||
|
namescope.statements.addAll(idx, scope.statements)
|
||||||
|
scope.statements.forEach { it.parent = namescope }
|
||||||
|
visit(nop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nopStatements.forEach {
|
||||||
|
it.definingScope().remove(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(scope: AnonymousScope) {
|
||||||
|
if(scope.parent is INameScope) {
|
||||||
|
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.visit(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(nopStatement: NopStatement) {
|
||||||
|
nopStatements.add(nopStatement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package prog8.parser
|
package prog8.parser
|
||||||
|
|
||||||
import org.antlr.v4.runtime.*
|
import org.antlr.v4.runtime.*
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.toAst
|
import prog8.ast.antlr.toAst
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package prog8.vm
|
package prog8.vm
|
||||||
|
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.LiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.ReferenceLiteralValue
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
@ -9,11 +10,12 @@ import kotlin.math.pow
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rather than a literal value (LiteralValue) that occurs in the parsed source code,
|
* Rather than a literal value (NumericLiteralValue) that occurs in the parsed source code,
|
||||||
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
|
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
|
||||||
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
|
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
|
||||||
*/
|
*/
|
||||||
open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null, val array: Array<Number>?=null, val heapId: Int?=null) {
|
open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null,
|
||||||
|
val array: Array<Number>?=null, val heapId: Int?=null) {
|
||||||
|
|
||||||
val byteval: Short?
|
val byteval: Short?
|
||||||
val wordval: Int?
|
val wordval: Int?
|
||||||
@ -21,16 +23,19 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
val asBoolean: Boolean
|
val asBoolean: Boolean
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(literalValue: LiteralValue, heap: HeapValues): RuntimeValue {
|
fun fromLv(literalValue: NumericLiteralValue): RuntimeValue {
|
||||||
|
return RuntimeValue(literalValue.type, num = literalValue.number)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromLv(literalValue: ReferenceLiteralValue, heap: HeapValues): RuntimeValue {
|
||||||
return when(literalValue.type) {
|
return when(literalValue.type) {
|
||||||
in NumericDatatypes -> RuntimeValue(literalValue.type, num = literalValue.asNumericValue!!)
|
in StringDatatypes -> fromHeapId(literalValue.heapId!!, heap)
|
||||||
in StringDatatypes -> from(literalValue.heapId!!, heap)
|
in ArrayDatatypes -> fromHeapId(literalValue.heapId!!, heap)
|
||||||
in ArrayDatatypes -> from(literalValue.heapId!!, heap)
|
else -> throw IllegalArgumentException("weird source value $literalValue")
|
||||||
else -> TODO("type")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun from(heapId: Int, heap: HeapValues): RuntimeValue {
|
fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue {
|
||||||
val value = heap.get(heapId)
|
val value = heap.get(heapId)
|
||||||
return when {
|
return when {
|
||||||
value.type in StringDatatypes ->
|
value.type in StringDatatypes ->
|
||||||
@ -40,11 +45,18 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
RuntimeValue(value.type, array = value.doubleArray!!.toList().toTypedArray(), heapId = heapId)
|
RuntimeValue(value.type, array = value.doubleArray!!.toList().toTypedArray(), heapId = heapId)
|
||||||
} else {
|
} else {
|
||||||
val array = value.array!!
|
val array = value.array!!
|
||||||
if (array.any { it.addressOf != null })
|
val resultArray = mutableListOf<Number>()
|
||||||
TODO("addressof values")
|
for(elt in array.withIndex()){
|
||||||
RuntimeValue(value.type, array = array.map { it.integer!! }.toTypedArray(), heapId = heapId)
|
if(elt.value.integer!=null)
|
||||||
|
resultArray.add(elt.value.integer!!)
|
||||||
|
else {
|
||||||
|
TODO("ADDRESSOF ${elt.value}")
|
||||||
}
|
}
|
||||||
else -> TODO("weird type on heap")
|
}
|
||||||
|
RuntimeValue(value.type, array = resultArray.toTypedArray(), heapId = heapId)
|
||||||
|
//RuntimeValue(value.type, array = array.map { it.integer!! }.toTypedArray(), heapId = heapId)
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("weird value type on heap $value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,27 +65,37 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
init {
|
init {
|
||||||
when(type) {
|
when(type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
byteval = (num!!.toInt() and 255).toShort()
|
val inum = num!!.toInt()
|
||||||
|
if(inum !in 0 .. 255)
|
||||||
|
throw IllegalArgumentException("invalid value for ubyte: $inum")
|
||||||
|
byteval = inum.toShort()
|
||||||
wordval = null
|
wordval = null
|
||||||
floatval = null
|
floatval = null
|
||||||
asBoolean = byteval != 0.toShort()
|
asBoolean = byteval != 0.toShort()
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
val v = num!!.toInt() and 255
|
val inum = num!!.toInt()
|
||||||
byteval = (if(v<128) v else v-256).toShort()
|
if(inum !in -128 .. 127)
|
||||||
|
throw IllegalArgumentException("invalid value for byte: $inum")
|
||||||
|
byteval = inum.toShort()
|
||||||
wordval = null
|
wordval = null
|
||||||
floatval = null
|
floatval = null
|
||||||
asBoolean = byteval != 0.toShort()
|
asBoolean = byteval != 0.toShort()
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
wordval = num!!.toInt() and 65535
|
val inum = num!!.toInt()
|
||||||
|
if(inum !in 0 .. 65535)
|
||||||
|
throw IllegalArgumentException("invalid value for uword: $inum")
|
||||||
|
wordval = inum
|
||||||
byteval = null
|
byteval = null
|
||||||
floatval = null
|
floatval = null
|
||||||
asBoolean = wordval != 0
|
asBoolean = wordval != 0
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
val v = num!!.toInt() and 65535
|
val inum = num!!.toInt()
|
||||||
wordval = if(v<32768) v else v - 65536
|
if(inum !in -32768 .. 32767)
|
||||||
|
throw IllegalArgumentException("invalid value for word: $inum")
|
||||||
|
wordval = inum
|
||||||
byteval = null
|
byteval = null
|
||||||
floatval = null
|
floatval = null
|
||||||
asBoolean = wordval != 0
|
asBoolean = wordval != 0
|
||||||
@ -95,19 +117,6 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asLiteralValue(): LiteralValue {
|
|
||||||
return when(type) {
|
|
||||||
in ByteDatatypes -> LiteralValue(type, byteval, position = Position("", 0, 0, 0))
|
|
||||||
in WordDatatypes -> LiteralValue(type, wordvalue = wordval, position = Position("", 0, 0, 0))
|
|
||||||
DataType.FLOAT -> LiteralValue(type, floatvalue = floatval, position = Position("", 0, 0, 0))
|
|
||||||
in StringDatatypes -> LiteralValue(type, strvalue = str, position = Position("", 0, 0, 0))
|
|
||||||
in ArrayDatatypes -> LiteralValue(type,
|
|
||||||
arrayvalue = array?.map { LiteralValue.optimalNumeric(it, Position("", 0, 0, 0)) }?.toTypedArray(),
|
|
||||||
position = Position("", 0, 0, 0))
|
|
||||||
else -> TODO("strange type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
DataType.UBYTE -> "ub:%02x".format(byteval)
|
DataType.UBYTE -> "ub:%02x".format(byteval)
|
||||||
@ -179,20 +188,44 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
if(leftDt== DataType.UBYTE)
|
if(leftDt== DataType.UBYTE)
|
||||||
RuntimeValue(DataType.UBYTE, (number xor 255) + 1)
|
RuntimeValue(DataType.UBYTE, (number xor 255) + 1)
|
||||||
else
|
else
|
||||||
RuntimeValue(DataType.UBYTE, (number xor 65535) + 1)
|
RuntimeValue(DataType.UWORD, (number xor 65535) + 1)
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
val v=result.toInt() and 255
|
||||||
|
if(v<128)
|
||||||
|
RuntimeValue(DataType.BYTE, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.BYTE, v-256)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val v=result.toInt() and 65535
|
||||||
|
if(v<32768)
|
||||||
|
RuntimeValue(DataType.WORD, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.WORD, v-65536)
|
||||||
}
|
}
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, result.toInt())
|
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, result.toInt())
|
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
|
||||||
else -> throw ArithmeticException("$op on non-numeric type")
|
else -> throw ArithmeticException("$op on non-numeric type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return when(leftDt) {
|
return when(leftDt) {
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, result.toInt())
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, result.toInt() and 255)
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, result.toInt())
|
DataType.BYTE -> {
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, result.toInt())
|
val v = result.toInt() and 255
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, result.toInt())
|
if(v<128)
|
||||||
|
RuntimeValue(DataType.BYTE, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.BYTE, v-256)
|
||||||
|
}
|
||||||
|
DataType.UWORD -> RuntimeValue(DataType.UWORD, result.toInt() and 65535)
|
||||||
|
DataType.WORD -> {
|
||||||
|
val v = result.toInt() and 65535
|
||||||
|
if(v<32768)
|
||||||
|
RuntimeValue(DataType.WORD, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.WORD, v-65536)
|
||||||
|
}
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
|
||||||
else -> throw ArithmeticException("$op on non-numeric type")
|
else -> throw ArithmeticException("$op on non-numeric type")
|
||||||
}
|
}
|
||||||
@ -268,10 +301,22 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
fun shl(): RuntimeValue {
|
fun shl(): RuntimeValue {
|
||||||
val v = integerValue()
|
val v = integerValue()
|
||||||
return when (type) {
|
return when (type) {
|
||||||
DataType.UBYTE,
|
DataType.UBYTE -> RuntimeValue(type, (v shl 1) and 255)
|
||||||
DataType.BYTE,
|
DataType.UWORD -> RuntimeValue(type, (v shl 1) and 65535)
|
||||||
DataType.UWORD,
|
DataType.BYTE -> {
|
||||||
DataType.WORD -> RuntimeValue(type, v shl 1)
|
val value = v shl 1
|
||||||
|
if(value<128)
|
||||||
|
RuntimeValue(type, value)
|
||||||
|
else
|
||||||
|
RuntimeValue(type, value-256)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val value = v shl 1
|
||||||
|
if(value<32768)
|
||||||
|
RuntimeValue(type, value)
|
||||||
|
else
|
||||||
|
RuntimeValue(type, value-65536)
|
||||||
|
}
|
||||||
else -> throw ArithmeticException("invalid type for shl: $type")
|
else -> throw ArithmeticException("invalid type for shl: $type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,16 +454,32 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
|
|
||||||
fun inv(): RuntimeValue {
|
fun inv(): RuntimeValue {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
in ByteDatatypes -> RuntimeValue(type, byteval!!.toInt().inv())
|
DataType.UBYTE -> RuntimeValue(type, byteval!!.toInt().inv() and 255)
|
||||||
in WordDatatypes -> RuntimeValue(type, wordval!!.inv())
|
DataType.UWORD -> RuntimeValue(type, wordval!!.inv() and 65535)
|
||||||
|
DataType.BYTE -> RuntimeValue(type, byteval!!.toInt().inv())
|
||||||
|
DataType.WORD -> RuntimeValue(type, wordval!!.inv())
|
||||||
else -> throw ArithmeticException("inv can only work on byte/word")
|
else -> throw ArithmeticException("inv can only work on byte/word")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inc(): RuntimeValue {
|
fun inc(): RuntimeValue {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
in ByteDatatypes -> RuntimeValue(type, byteval!! + 1)
|
DataType.UBYTE -> RuntimeValue(type, (byteval!! + 1) and 255)
|
||||||
in WordDatatypes -> RuntimeValue(type, wordval!! + 1)
|
DataType.UWORD -> RuntimeValue(type, (wordval!! + 1) and 65535)
|
||||||
|
DataType.BYTE -> {
|
||||||
|
val newval = byteval!! + 1
|
||||||
|
if(newval == 128)
|
||||||
|
RuntimeValue(type, -128)
|
||||||
|
else
|
||||||
|
RuntimeValue(type, newval)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val newval = wordval!! + 1
|
||||||
|
if(newval == 32768)
|
||||||
|
RuntimeValue(type, -32768)
|
||||||
|
else
|
||||||
|
RuntimeValue(type, newval)
|
||||||
|
}
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! + 1)
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! + 1)
|
||||||
else -> throw ArithmeticException("inc can only work on numeric types")
|
else -> throw ArithmeticException("inc can only work on numeric types")
|
||||||
}
|
}
|
||||||
@ -426,8 +487,22 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
|
|
||||||
fun dec(): RuntimeValue {
|
fun dec(): RuntimeValue {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
in ByteDatatypes -> RuntimeValue(type, byteval!! - 1)
|
DataType.UBYTE -> RuntimeValue(type, (byteval!! - 1) and 255)
|
||||||
in WordDatatypes -> RuntimeValue(type, wordval!! - 1)
|
DataType.UWORD -> RuntimeValue(type, (wordval!! - 1) and 65535)
|
||||||
|
DataType.BYTE -> {
|
||||||
|
val newval = byteval!! - 1
|
||||||
|
if(newval == -129)
|
||||||
|
RuntimeValue(type, 127)
|
||||||
|
else
|
||||||
|
RuntimeValue(type, newval)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val newval = wordval!! - 1
|
||||||
|
if(newval == -32769)
|
||||||
|
RuntimeValue(type, 32767)
|
||||||
|
else
|
||||||
|
RuntimeValue(type, newval)
|
||||||
|
}
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! - 1)
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! - 1)
|
||||||
else -> throw ArithmeticException("dec can only work on numeric types")
|
else -> throw ArithmeticException("dec can only work on numeric types")
|
||||||
}
|
}
|
||||||
@ -446,9 +521,21 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (targetType) {
|
when (targetType) {
|
||||||
DataType.UBYTE -> this
|
DataType.UBYTE -> this
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, byteval)
|
DataType.BYTE -> {
|
||||||
|
val nval=byteval!!.toInt()
|
||||||
|
if(nval<128)
|
||||||
|
RuntimeValue(DataType.BYTE, nval)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.BYTE, nval-256)
|
||||||
|
}
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue())
|
DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue())
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, numericValue())
|
DataType.WORD -> {
|
||||||
|
val nval = numericValue().toInt()
|
||||||
|
if(nval<32768)
|
||||||
|
RuntimeValue(DataType.WORD, nval)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.WORD, nval-65536)
|
||||||
|
}
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
||||||
}
|
}
|
||||||
@ -456,8 +543,8 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when (targetType) {
|
when (targetType) {
|
||||||
DataType.BYTE -> this
|
DataType.BYTE -> this
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue())
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue())
|
DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue() and 65535)
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, integerValue())
|
DataType.WORD -> RuntimeValue(DataType.WORD, integerValue())
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
||||||
@ -465,18 +552,36 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (targetType) {
|
when (targetType) {
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, integerValue())
|
DataType.BYTE -> {
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue())
|
val v=integerValue()
|
||||||
|
if(v<128)
|
||||||
|
RuntimeValue(DataType.BYTE, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.BYTE, v-256)
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
|
||||||
DataType.UWORD -> this
|
DataType.UWORD -> this
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, integerValue())
|
DataType.WORD -> {
|
||||||
|
val v=integerValue()
|
||||||
|
if(v<32768)
|
||||||
|
RuntimeValue(DataType.WORD, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.WORD, v-65536)
|
||||||
|
}
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
when (targetType) {
|
when (targetType) {
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, integerValue())
|
DataType.BYTE -> {
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue())
|
val v = integerValue() and 255
|
||||||
|
if(v<128)
|
||||||
|
RuntimeValue(DataType.BYTE, v)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.BYTE, v-256)
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 65535)
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue())
|
DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue())
|
||||||
DataType.WORD -> this
|
DataType.WORD -> this
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
package prog8.vm.astvm
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.LiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.vm.RuntimeValue
|
import prog8.vm.RuntimeValue
|
||||||
import prog8.vm.RuntimeValueRange
|
import prog8.vm.RuntimeValueRange
|
||||||
import prog8.compiler.target.c64.Petscii
|
|
||||||
import java.awt.EventQueue
|
import java.awt.EventQueue
|
||||||
|
import java.io.CharConversionException
|
||||||
|
import java.util.*
|
||||||
import kotlin.NoSuchElementException
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.concurrent.fixedRateTimer
|
import kotlin.concurrent.fixedRateTimer
|
||||||
import kotlin.math.min
|
import kotlin.math.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
class VmExecutionException(msg: String?) : Exception(msg)
|
class VmExecutionException(msg: String?) : Exception(msg)
|
||||||
@ -28,31 +33,31 @@ class StatusFlags {
|
|||||||
var negative: Boolean = false
|
var negative: Boolean = false
|
||||||
var irqd: Boolean = false
|
var irqd: Boolean = false
|
||||||
|
|
||||||
private fun setFlags(value: LiteralValue?) {
|
private fun setFlags(value: NumericLiteralValue?) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
when (value.type) {
|
when (value.type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
val v = value.bytevalue!!.toInt()
|
val v = value.number.toInt()
|
||||||
negative = v > 127
|
negative = v > 127
|
||||||
zero = v == 0
|
zero = v == 0
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
val v = value.bytevalue!!.toInt()
|
val v = value.number.toInt()
|
||||||
negative = v < 0
|
negative = v < 0
|
||||||
zero = v == 0
|
zero = v == 0
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
val v = value.wordvalue!!
|
val v = value.number.toInt()
|
||||||
negative = v > 32767
|
negative = v > 32767
|
||||||
zero = v == 0
|
zero = v == 0
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
val v = value.wordvalue!!
|
val v = value.number.toInt()
|
||||||
negative = v < 0
|
negative = v < 0
|
||||||
zero = v == 0
|
zero = v == 0
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
val flt = value.floatvalue!!
|
val flt = value.number.toDouble()
|
||||||
negative = flt < 0.0
|
negative = flt < 0.0
|
||||||
zero = flt == 0.0
|
zero = flt == 0.0
|
||||||
}
|
}
|
||||||
@ -94,14 +99,12 @@ class RuntimeVariables {
|
|||||||
|
|
||||||
fun get(scope: INameScope, name: String): RuntimeValue {
|
fun get(scope: INameScope, name: String): RuntimeValue {
|
||||||
val where = vars.getValue(scope)
|
val where = vars.getValue(scope)
|
||||||
val value = where[name] ?: throw NoSuchElementException("no such runtime variable: ${scope.name}.$name")
|
return where[name] ?: throw NoSuchElementException("no such runtime variable: ${scope.name}.$name")
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMemoryAddress(scope: INameScope, name: String): Int {
|
fun getMemoryAddress(scope: INameScope, name: String): Int {
|
||||||
val where = memvars.getValue(scope)
|
val where = memvars.getValue(scope)
|
||||||
val address = where[name] ?: throw NoSuchElementException("no such runtime memory-variable: ${scope.name}.$name")
|
return where[name] ?: throw NoSuchElementException("no such runtime memory-variable: ${scope.name}.$name")
|
||||||
return address
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun swap(a1: VarDecl, a2: VarDecl) = swap(a1.definingScope(), a1.name, a2.definingScope(), a2.name)
|
fun swap(a1: VarDecl, a2: VarDecl) = swap(a1.definingScope(), a1.name, a2.definingScope(), a2.name)
|
||||||
@ -128,9 +131,18 @@ class AstVm(val program: Program) {
|
|||||||
val bootTime = System.currentTimeMillis()
|
val bootTime = System.currentTimeMillis()
|
||||||
var rtcOffset = bootTime
|
var rtcOffset = bootTime
|
||||||
|
|
||||||
|
private val rnd = Random(0)
|
||||||
|
private val statusFlagsSave = Stack<StatusFlags>()
|
||||||
|
private val registerXsave = Stack<RuntimeValue>()
|
||||||
|
private val registerYsave = Stack<RuntimeValue>()
|
||||||
|
private val registerAsave = Stack<RuntimeValue>()
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// observe the jiffyclock
|
// observe the jiffyclock and screen matrix
|
||||||
mem.observe(0xa0, 0xa1, 0xa2)
|
mem.observe(0xa0, 0xa1, 0xa2)
|
||||||
|
for(i in 1024..2023)
|
||||||
|
mem.observe(i)
|
||||||
|
|
||||||
dialog.requestFocusInWindow()
|
dialog.requestFocusInWindow()
|
||||||
|
|
||||||
@ -159,6 +171,11 @@ class AstVm(val program: Program) {
|
|||||||
val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo
|
val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo
|
||||||
rtcOffset = bootTime - (jiffies*1000/60)
|
rtcOffset = bootTime - (jiffies*1000/60)
|
||||||
}
|
}
|
||||||
|
if(address in 1024..2023) {
|
||||||
|
// write to the screen matrix
|
||||||
|
val scraddr = address-1024
|
||||||
|
dialog.canvas.setChar(scraddr % 40, scraddr / 40, value, 1)
|
||||||
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +230,7 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dialog.canvas.printText("\n<program ended>", true)
|
||||||
println("PROGRAM EXITED!")
|
println("PROGRAM EXITED!")
|
||||||
dialog.title = "PROGRAM EXITED"
|
dialog.title = "PROGRAM EXITED"
|
||||||
} catch (tx: VmTerminationException) {
|
} catch (tx: VmTerminationException) {
|
||||||
@ -228,7 +246,11 @@ class AstVm(val program: Program) {
|
|||||||
if(statusflags.irqd)
|
if(statusflags.irqd)
|
||||||
return // interrupt is disabled
|
return // interrupt is disabled
|
||||||
|
|
||||||
val jiffies = min((timeStamp-rtcOffset)*60/1000, 24*3600*60-1)
|
var jiffies = (timeStamp-rtcOffset)*60/1000
|
||||||
|
if(jiffies>24*3600*60-1) {
|
||||||
|
jiffies = 0
|
||||||
|
rtcOffset = timeStamp
|
||||||
|
}
|
||||||
// update the C-64 60hz jiffy clock in the ZP addresses:
|
// update the C-64 60hz jiffy clock in the ZP addresses:
|
||||||
mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort())
|
mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort())
|
||||||
mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort())
|
mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort())
|
||||||
@ -236,17 +258,19 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val runtimeVariables = RuntimeVariables()
|
private val runtimeVariables = RuntimeVariables()
|
||||||
private val functions = BuiltinFunctions()
|
private val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, ::performBuiltinFunction, ::executeSubroutine)
|
||||||
private val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine)
|
|
||||||
|
|
||||||
class LoopControlBreak : Exception()
|
class LoopControlBreak : Exception()
|
||||||
class LoopControlContinue : Exception()
|
class LoopControlContinue : Exception()
|
||||||
class LoopControlReturn(val returnvalues: List<RuntimeValue>) : Exception()
|
class LoopControlReturn(val returnvalue: RuntimeValue?) : Exception()
|
||||||
class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception()
|
class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception()
|
||||||
|
|
||||||
|
|
||||||
internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValue>, startlabel: Label?=null): List<RuntimeValue> {
|
internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValue>, startAtLabel: Label?=null): RuntimeValue? {
|
||||||
assert(!sub.isAsmSubroutine)
|
if(sub.isAsmSubroutine) {
|
||||||
|
return performSyscall(sub, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
if (sub.statements.isEmpty())
|
if (sub.statements.isEmpty())
|
||||||
throw VmTerminationException("scope contains no statements: $sub")
|
throw VmTerminationException("scope contains no statements: $sub")
|
||||||
if (arguments.size != sub.parameters.size)
|
if (arguments.size != sub.parameters.size)
|
||||||
@ -259,10 +283,10 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val statements = sub.statements.iterator()
|
val statements = sub.statements.iterator()
|
||||||
if(startlabel!=null) {
|
if(startAtLabel!=null) {
|
||||||
do {
|
do {
|
||||||
val stmt = statements.next()
|
val stmt = statements.next()
|
||||||
} while(stmt!==startlabel)
|
} while(stmt!==startAtLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -277,7 +301,7 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (r: LoopControlReturn) {
|
} catch (r: LoopControlReturn) {
|
||||||
return r.returnvalues
|
return r.returnvalue
|
||||||
}
|
}
|
||||||
throw VmTerminationException("instruction pointer overflow, is a return missing? $sub")
|
throw VmTerminationException("instruction pointer overflow, is a return missing? $sub")
|
||||||
}
|
}
|
||||||
@ -289,7 +313,7 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun executeStatement(sub: INameScope, stmt: IStatement) {
|
private fun executeStatement(sub: INameScope, stmt: Statement) {
|
||||||
instructionCounter++
|
instructionCounter++
|
||||||
if (instructionCounter % 200 == 0)
|
if (instructionCounter % 200 == 0)
|
||||||
Thread.sleep(1)
|
Thread.sleep(1)
|
||||||
@ -324,7 +348,7 @@ class AstVm(val program: Program) {
|
|||||||
executeSwap(stmt)
|
executeSwap(stmt)
|
||||||
} else {
|
} else {
|
||||||
val args = evaluate(stmt.arglist)
|
val args = evaluate(stmt.arglist)
|
||||||
functions.performBuiltinFunction(target.name, args, statusflags)
|
performBuiltinFunction(target.name, args, statusflags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -332,23 +356,29 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Return -> throw LoopControlReturn(stmt.values.map { evaluate(it, evalCtx) })
|
is Return -> {
|
||||||
|
val value =
|
||||||
|
if(stmt.value==null)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
evaluate(stmt.value!!, evalCtx)
|
||||||
|
throw LoopControlReturn(value)
|
||||||
|
}
|
||||||
is Continue -> throw LoopControlContinue()
|
is Continue -> throw LoopControlContinue()
|
||||||
is Break -> throw LoopControlBreak()
|
is Break -> throw LoopControlBreak()
|
||||||
is Assignment -> {
|
is Assignment -> {
|
||||||
if (stmt.aug_op != null)
|
if (stmt.aug_op != null)
|
||||||
throw VmExecutionException("augmented assignment should have been converted into regular one $stmt")
|
throw VmExecutionException("augmented assignment should have been converted into regular one $stmt")
|
||||||
val target = stmt.singleTarget
|
|
||||||
if (target != null) {
|
|
||||||
val value = evaluate(stmt.value, evalCtx)
|
val value = evaluate(stmt.value, evalCtx)
|
||||||
performAssignment(target, value, stmt, evalCtx)
|
performAssignment(stmt.target, value, stmt, evalCtx)
|
||||||
} else TODO("assign multitarget $stmt")
|
|
||||||
}
|
}
|
||||||
is PostIncrDecr -> {
|
is PostIncrDecr -> {
|
||||||
when {
|
when {
|
||||||
stmt.target.identifier != null -> {
|
stmt.target.identifier != null -> {
|
||||||
val ident = stmt.definingScope().lookup(stmt.target.identifier!!.nameInSource, stmt) as VarDecl
|
val ident = stmt.definingScope().lookup(stmt.target.identifier!!.nameInSource, stmt) as VarDecl
|
||||||
val identScope = ident.definingScope()
|
val identScope = ident.definingScope()
|
||||||
|
when(ident.type){
|
||||||
|
VarDeclType.VAR -> {
|
||||||
var value = runtimeVariables.get(identScope, ident.name)
|
var value = runtimeVariables.get(identScope, ident.name)
|
||||||
value = when {
|
value = when {
|
||||||
stmt.operator == "++" -> value.add(RuntimeValue(value.type, 1))
|
stmt.operator == "++" -> value.add(RuntimeValue(value.type, 1))
|
||||||
@ -357,12 +387,50 @@ class AstVm(val program: Program) {
|
|||||||
}
|
}
|
||||||
runtimeVariables.set(identScope, ident.name, value)
|
runtimeVariables.set(identScope, ident.name, value)
|
||||||
}
|
}
|
||||||
|
VarDeclType.MEMORY -> {
|
||||||
|
val addr=ident.value!!.constValue(program)!!.number.toInt()
|
||||||
|
val newval = when {
|
||||||
|
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
|
||||||
|
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
|
||||||
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
|
}
|
||||||
|
mem.setUByte(addr,newval.toShort())
|
||||||
|
}
|
||||||
|
VarDeclType.CONST -> throw VmExecutionException("can't be const")
|
||||||
|
}
|
||||||
|
}
|
||||||
stmt.target.memoryAddress != null -> {
|
stmt.target.memoryAddress != null -> {
|
||||||
TODO("postincrdecr memory $stmt")
|
val addr = evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx).integerValue()
|
||||||
|
val newval = when {
|
||||||
|
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
|
||||||
|
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
|
||||||
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
|
}
|
||||||
|
mem.setUByte(addr,newval.toShort())
|
||||||
}
|
}
|
||||||
stmt.target.arrayindexed != null -> {
|
stmt.target.arrayindexed != null -> {
|
||||||
TODO("postincrdecr array $stmt")
|
val arrayvar = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!
|
||||||
|
val arrayvalue = runtimeVariables.get(arrayvar.definingScope(), arrayvar.name)
|
||||||
|
val elementType = stmt.target.arrayindexed!!.inferType(program)!!
|
||||||
|
val index = evaluate(stmt.target.arrayindexed!!.arrayspec.index, evalCtx).integerValue()
|
||||||
|
var value = RuntimeValue(elementType, arrayvalue.array!![index].toInt())
|
||||||
|
value = when {
|
||||||
|
stmt.operator == "++" -> value.inc()
|
||||||
|
stmt.operator == "--" -> value.dec()
|
||||||
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
}
|
}
|
||||||
|
arrayvalue.array[index] = value.numericValue()
|
||||||
|
}
|
||||||
|
stmt.target.register != null -> {
|
||||||
|
var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name)
|
||||||
|
value = when {
|
||||||
|
stmt.operator == "++" -> value.add(RuntimeValue(value.type, 1))
|
||||||
|
stmt.operator == "--" -> value.sub(RuntimeValue(value.type, 1))
|
||||||
|
else -> throw VmExecutionException("strange postincdec operator $stmt")
|
||||||
|
}
|
||||||
|
runtimeVariables.set(program.namespace, stmt.target.register!!.name, value)
|
||||||
|
}
|
||||||
|
else -> throw VmExecutionException("empty postincrdecr? $stmt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Jump -> throw LoopControlJump(stmt.identifier, stmt.address, stmt.generatedLabel)
|
is Jump -> throw LoopControlJump(stmt.identifier, stmt.address, stmt.generatedLabel)
|
||||||
@ -370,7 +438,7 @@ class AstVm(val program: Program) {
|
|||||||
if (sub is Subroutine) {
|
if (sub is Subroutine) {
|
||||||
val args = sub.parameters.map { runtimeVariables.get(sub, it.name) }
|
val args = sub.parameters.map { runtimeVariables.get(sub, it.name) }
|
||||||
performSyscall(sub, args)
|
performSyscall(sub, args)
|
||||||
throw LoopControlReturn(emptyList())
|
throw LoopControlReturn(null)
|
||||||
}
|
}
|
||||||
throw VmExecutionException("can't execute inline assembly in $sub")
|
throw VmExecutionException("can't execute inline assembly in $sub")
|
||||||
}
|
}
|
||||||
@ -445,13 +513,13 @@ class AstVm(val program: Program) {
|
|||||||
is WhenStatement -> {
|
is WhenStatement -> {
|
||||||
val condition=evaluate(stmt.condition, evalCtx)
|
val condition=evaluate(stmt.condition, evalCtx)
|
||||||
for(choice in stmt.choices) {
|
for(choice in stmt.choices) {
|
||||||
if(choice.value==null) {
|
if(choice.values==null) {
|
||||||
// the 'else' choice
|
// the 'else' choice
|
||||||
executeAnonymousScope(choice.statements)
|
executeAnonymousScope(choice.statements)
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
val value = choice.value.constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
|
val value = choice.values.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
|
||||||
val rtval = RuntimeValue.from(value, evalCtx.program.heap)
|
val rtval = RuntimeValue.fromLv(value)
|
||||||
if(condition==rtval) {
|
if(condition==rtval) {
|
||||||
executeAnonymousScope(choice.statements)
|
executeAnonymousScope(choice.statements)
|
||||||
break
|
break
|
||||||
@ -476,7 +544,7 @@ class AstVm(val program: Program) {
|
|||||||
performAssignment(target2, value1, swap, evalCtx)
|
performAssignment(target2, value1, swap, evalCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: IStatement, evalCtx: EvalContext) {
|
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: Statement, evalCtx: EvalContext) {
|
||||||
when {
|
when {
|
||||||
target.identifier != null -> {
|
target.identifier != null -> {
|
||||||
val decl = contextStmt.definingScope().lookup(target.identifier.nameInSource, contextStmt) as? VarDecl
|
val decl = contextStmt.definingScope().lookup(target.identifier.nameInSource, contextStmt) as? VarDecl
|
||||||
@ -491,7 +559,7 @@ class AstVm(val program: Program) {
|
|||||||
DataType.FLOAT -> mem.setFloat(address, value.floatval!!)
|
DataType.FLOAT -> mem.setFloat(address, value.floatval!!)
|
||||||
DataType.STR -> mem.setString(address, value.str!!)
|
DataType.STR -> mem.setString(address, value.str!!)
|
||||||
DataType.STR_S -> mem.setScreencodeString(address, value.str!!)
|
DataType.STR_S -> mem.setScreencodeString(address, value.str!!)
|
||||||
else -> TODO("set memvar $decl")
|
else -> throw VmExecutionException("weird memaddress type $decl")
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
runtimeVariables.set(decl.definingScope(), decl.name, value)
|
runtimeVariables.set(decl.definingScope(), decl.name, value)
|
||||||
@ -501,6 +569,8 @@ class AstVm(val program: Program) {
|
|||||||
evalCtx.mem.setUByte(address, value.byteval!!)
|
evalCtx.mem.setUByte(address, value.byteval!!)
|
||||||
}
|
}
|
||||||
target.arrayindexed != null -> {
|
target.arrayindexed != null -> {
|
||||||
|
val vardecl = target.arrayindexed.identifier.targetVarDecl(program.namespace)!!
|
||||||
|
if(vardecl.type==VarDeclType.VAR) {
|
||||||
val array = evaluate(target.arrayindexed.identifier, evalCtx)
|
val array = evaluate(target.arrayindexed.identifier, evalCtx)
|
||||||
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx)
|
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx)
|
||||||
when (array.type) {
|
when (array.type) {
|
||||||
@ -543,6 +613,20 @@ class AstVm(val program: Program) {
|
|||||||
runtimeVariables.set(identScope, ident.name, RuntimeValue(array.type, str = newstr, heapId = array.heapId))
|
runtimeVariables.set(identScope, ident.name, RuntimeValue(array.type, str = newstr, heapId = array.heapId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
val address = (vardecl.value as NumericLiteralValue).number.toInt()
|
||||||
|
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx).integerValue()
|
||||||
|
val elementType = target.arrayindexed.inferType(program)!!
|
||||||
|
when(elementType) {
|
||||||
|
DataType.UBYTE -> mem.setUByte(address+index, value.byteval!!)
|
||||||
|
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
|
||||||
|
DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!)
|
||||||
|
DataType.WORD -> mem.setSWord(address+index*2, value.wordval!!)
|
||||||
|
DataType.FLOAT -> mem.setFloat(address+index* MachineDefinition.Mflpt5.MemorySize, value.floatval!!)
|
||||||
|
else -> throw VmExecutionException("strange array elt type $elementType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
target.register != null -> {
|
target.register != null -> {
|
||||||
runtimeVariables.set(program.namespace, target.register.name, value)
|
runtimeVariables.set(program.namespace, target.register.name, value)
|
||||||
}
|
}
|
||||||
@ -557,56 +641,56 @@ class AstVm(val program: Program) {
|
|||||||
executeAnonymousScope(stmt.body)
|
executeAnonymousScope(stmt.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluate(args: List<IExpression>) = args.map { evaluate(it, evalCtx) }
|
private fun evaluate(args: List<Expression>) = args.map { evaluate(it, evalCtx) }
|
||||||
|
|
||||||
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>) {
|
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): RuntimeValue? {
|
||||||
assert(sub.isAsmSubroutine)
|
var result: RuntimeValue? = null
|
||||||
when (sub.scopedname) {
|
when (sub.scopedname) {
|
||||||
"c64scr.print" -> {
|
"c64scr.print" -> {
|
||||||
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
|
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
|
||||||
if (args[0].wordval != null) {
|
if (args[0].wordval != null) {
|
||||||
val str = program.heap.get(args[0].wordval!!).str!!
|
val str = program.heap.get(args[0].wordval!!).str!!
|
||||||
dialog.canvas.printText(str, 1, true)
|
dialog.canvas.printText(str, true)
|
||||||
} else
|
} else
|
||||||
dialog.canvas.printText(args[0].str!!, 1, true)
|
dialog.canvas.printText(args[0].str!!, true)
|
||||||
}
|
}
|
||||||
"c64scr.print_ub" -> {
|
"c64scr.print_ub" -> {
|
||||||
dialog.canvas.printText(args[0].byteval!!.toString(), 1, true)
|
dialog.canvas.printText(args[0].byteval!!.toString(), true)
|
||||||
}
|
}
|
||||||
"c64scr.print_ub0" -> {
|
"c64scr.print_ub0" -> {
|
||||||
dialog.canvas.printText("%03d".format(args[0].byteval!!), 1, true)
|
dialog.canvas.printText("%03d".format(args[0].byteval!!), true)
|
||||||
}
|
}
|
||||||
"c64scr.print_b" -> {
|
"c64scr.print_b" -> {
|
||||||
dialog.canvas.printText(args[0].byteval!!.toString(), 1, true)
|
dialog.canvas.printText(args[0].byteval!!.toString(), true)
|
||||||
}
|
}
|
||||||
"c64scr.print_uw" -> {
|
"c64scr.print_uw" -> {
|
||||||
dialog.canvas.printText(args[0].wordval!!.toString(), 1, true)
|
dialog.canvas.printText(args[0].wordval!!.toString(), true)
|
||||||
}
|
}
|
||||||
"c64scr.print_uw0" -> {
|
"c64scr.print_uw0" -> {
|
||||||
dialog.canvas.printText("%05d".format(args[0].wordval!!), 1, true)
|
dialog.canvas.printText("%05d".format(args[0].wordval!!), true)
|
||||||
}
|
}
|
||||||
"c64scr.print_w" -> {
|
"c64scr.print_w" -> {
|
||||||
dialog.canvas.printText(args[0].wordval!!.toString(), 1, true)
|
dialog.canvas.printText(args[0].wordval!!.toString(), true)
|
||||||
}
|
}
|
||||||
"c64scr.print_ubhex" -> {
|
"c64scr.print_ubhex" -> {
|
||||||
val prefix = if (args[0].asBoolean) "$" else ""
|
val prefix = if (args[0].asBoolean) "$" else ""
|
||||||
val number = args[1].byteval!!
|
val number = args[1].byteval!!
|
||||||
dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", 1, true)
|
dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", true)
|
||||||
}
|
}
|
||||||
"c64scr.print_uwhex" -> {
|
"c64scr.print_uwhex" -> {
|
||||||
val prefix = if (args[0].asBoolean) "$" else ""
|
val prefix = if (args[0].asBoolean) "$" else ""
|
||||||
val number = args[1].wordval!!
|
val number = args[1].wordval!!
|
||||||
dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", 1, true)
|
dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", true)
|
||||||
}
|
}
|
||||||
"c64scr.print_uwbin" -> {
|
"c64scr.print_uwbin" -> {
|
||||||
val prefix = if (args[0].asBoolean) "%" else ""
|
val prefix = if (args[0].asBoolean) "%" else ""
|
||||||
val number = args[1].wordval!!
|
val number = args[1].wordval!!
|
||||||
dialog.canvas.printText("$prefix${number.toString(2).padStart(16, '0')}", 1, true)
|
dialog.canvas.printText("$prefix${number.toString(2).padStart(16, '0')}", true)
|
||||||
}
|
}
|
||||||
"c64scr.print_ubbin" -> {
|
"c64scr.print_ubbin" -> {
|
||||||
val prefix = if (args[0].asBoolean) "%" else ""
|
val prefix = if (args[0].asBoolean) "%" else ""
|
||||||
val number = args[1].byteval!!
|
val number = args[1].byteval!!
|
||||||
dialog.canvas.printText("$prefix${number.toString(2).padStart(8, '0')}", 1, true)
|
dialog.canvas.printText("$prefix${number.toString(2).padStart(8, '0')}", true)
|
||||||
}
|
}
|
||||||
"c64scr.clear_screenchars" -> {
|
"c64scr.clear_screenchars" -> {
|
||||||
dialog.canvas.clearScreen(6)
|
dialog.canvas.clearScreen(6)
|
||||||
@ -620,14 +704,252 @@ class AstVm(val program: Program) {
|
|||||||
"c64scr.plot" -> {
|
"c64scr.plot" -> {
|
||||||
dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue())
|
dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue())
|
||||||
}
|
}
|
||||||
"c64.CHROUT" -> {
|
"c64scr.input_chars" -> {
|
||||||
dialog.canvas.printChar(args[0].byteval!!)
|
val input=mutableListOf<Char>()
|
||||||
|
for(i in 0 until 80) {
|
||||||
|
while(dialog.keyboardBuffer.isEmpty()) {
|
||||||
|
Thread.sleep(10)
|
||||||
|
}
|
||||||
|
val char=dialog.keyboardBuffer.pop()
|
||||||
|
if(char=='\n')
|
||||||
|
break
|
||||||
|
else {
|
||||||
|
input.add(char)
|
||||||
|
val printChar = try {
|
||||||
|
Petscii.encodePetscii("" + char, true).first()
|
||||||
|
} catch (cv: CharConversionException) {
|
||||||
|
0x3f.toShort()
|
||||||
|
}
|
||||||
|
dialog.canvas.printPetscii(printChar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val inputStr = input.joinToString("")
|
||||||
|
val heapId = args[0].wordval!!
|
||||||
|
val origStr = program.heap.get(heapId).str!!
|
||||||
|
val paddedStr=inputStr.padEnd(origStr.length+1, '\u0000').substring(0, origStr.length)
|
||||||
|
program.heap.update(heapId, paddedStr)
|
||||||
|
result = RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000'))
|
||||||
}
|
}
|
||||||
"c64flt.print_f" -> {
|
"c64flt.print_f" -> {
|
||||||
dialog.canvas.printText(args[0].floatval.toString(), 1, true)
|
dialog.canvas.printText(args[0].floatval.toString(), true)
|
||||||
|
}
|
||||||
|
"c64.CHROUT" -> {
|
||||||
|
dialog.canvas.printPetscii(args[0].byteval!!)
|
||||||
|
}
|
||||||
|
"c64.CLEARSCR" -> {
|
||||||
|
dialog.canvas.clearScreen(6)
|
||||||
|
}
|
||||||
|
"c64.CHRIN" -> {
|
||||||
|
while(dialog.keyboardBuffer.isEmpty()) {
|
||||||
|
Thread.sleep(10)
|
||||||
|
}
|
||||||
|
val char=dialog.keyboardBuffer.pop()
|
||||||
|
result = RuntimeValue(DataType.UBYTE, char.toShort())
|
||||||
|
}
|
||||||
|
"c64utils.str2uword" -> {
|
||||||
|
val heapId = args[0].wordval!!
|
||||||
|
val argString = program.heap.get(heapId).str!!
|
||||||
|
val numericpart = argString.takeWhile { it.isDigit() }
|
||||||
|
result = RuntimeValue(DataType.UWORD, numericpart.toInt() and 65535)
|
||||||
}
|
}
|
||||||
else -> TODO("syscall ${sub.scopedname} $sub")
|
else -> TODO("syscall ${sub.scopedname} $sub")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun performBuiltinFunction(name: String, args: List<RuntimeValue>, statusflags: StatusFlags): RuntimeValue? {
|
||||||
|
return when (name) {
|
||||||
|
"rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255)
|
||||||
|
"rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535)
|
||||||
|
"rndf" -> RuntimeValue(DataType.FLOAT, rnd.nextDouble())
|
||||||
|
"lsb" -> RuntimeValue(DataType.UBYTE, args[0].integerValue() and 255)
|
||||||
|
"msb" -> RuntimeValue(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255)
|
||||||
|
"sin" -> RuntimeValue(DataType.FLOAT, sin(args[0].numericValue().toDouble()))
|
||||||
|
"sin8" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.BYTE, (127.0 * sin(rad)).toShort())
|
||||||
|
}
|
||||||
|
"sin8u" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort())
|
||||||
|
}
|
||||||
|
"sin16" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.BYTE, (32767.0 * sin(rad)).toShort())
|
||||||
|
}
|
||||||
|
"sin16u" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * sin(rad)).toShort())
|
||||||
|
}
|
||||||
|
"cos" -> RuntimeValue(DataType.FLOAT, cos(args[0].numericValue().toDouble()))
|
||||||
|
"cos8" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.BYTE, (127.0 * cos(rad)).toShort())
|
||||||
|
}
|
||||||
|
"cos8u" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort())
|
||||||
|
}
|
||||||
|
"cos16" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.BYTE, (32767.0 * cos(rad)).toShort())
|
||||||
|
}
|
||||||
|
"cos16u" -> {
|
||||||
|
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
||||||
|
RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * cos(rad)).toShort())
|
||||||
|
}
|
||||||
|
"tan" -> RuntimeValue(DataType.FLOAT, tan(args[0].numericValue().toDouble()))
|
||||||
|
"atan" -> RuntimeValue(DataType.FLOAT, atan(args[0].numericValue().toDouble()))
|
||||||
|
"ln" -> RuntimeValue(DataType.FLOAT, ln(args[0].numericValue().toDouble()))
|
||||||
|
"log2" -> RuntimeValue(DataType.FLOAT, log2(args[0].numericValue().toDouble()))
|
||||||
|
"sqrt" -> RuntimeValue(DataType.FLOAT, sqrt(args[0].numericValue().toDouble()))
|
||||||
|
"sqrt16" -> RuntimeValue(DataType.UBYTE, sqrt(args[0].wordval!!.toDouble()).toInt())
|
||||||
|
"rad" -> RuntimeValue(DataType.FLOAT, Math.toRadians(args[0].numericValue().toDouble()))
|
||||||
|
"deg" -> RuntimeValue(DataType.FLOAT, Math.toDegrees(args[0].numericValue().toDouble()))
|
||||||
|
"round" -> RuntimeValue(DataType.FLOAT, round(args[0].numericValue().toDouble()))
|
||||||
|
"floor" -> RuntimeValue(DataType.FLOAT, floor(args[0].numericValue().toDouble()))
|
||||||
|
"ceil" -> RuntimeValue(DataType.FLOAT, ceil(args[0].numericValue().toDouble()))
|
||||||
|
"rol" -> {
|
||||||
|
val (result, newCarry) = args[0].rol(statusflags.carry)
|
||||||
|
statusflags.carry = newCarry
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
"rol2" -> args[0].rol2()
|
||||||
|
"ror" -> {
|
||||||
|
val (result, newCarry) = args[0].ror(statusflags.carry)
|
||||||
|
statusflags.carry = newCarry
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
"ror2" -> args[0].ror2()
|
||||||
|
"lsl" -> args[0].shl()
|
||||||
|
"lsr" -> args[0].shr()
|
||||||
|
"abs" -> {
|
||||||
|
when (args[0].type) {
|
||||||
|
DataType.UBYTE -> args[0]
|
||||||
|
DataType.BYTE -> RuntimeValue(DataType.UBYTE, abs(args[0].numericValue().toDouble()))
|
||||||
|
DataType.UWORD -> args[0]
|
||||||
|
DataType.WORD -> RuntimeValue(DataType.UWORD, abs(args[0].numericValue().toDouble()))
|
||||||
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, abs(args[0].numericValue().toDouble()))
|
||||||
|
else -> throw VmExecutionException("strange abs type ${args[0]}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"max" -> {
|
||||||
|
val numbers = args.single().array!!.map { it.toDouble() }
|
||||||
|
RuntimeValue(ArrayElementTypes.getValue(args[0].type), numbers.max())
|
||||||
|
}
|
||||||
|
"min" -> {
|
||||||
|
val numbers = args.single().array!!.map { it.toDouble() }
|
||||||
|
RuntimeValue(ArrayElementTypes.getValue(args[0].type), numbers.min())
|
||||||
|
}
|
||||||
|
"avg" -> {
|
||||||
|
val numbers = args.single().array!!.map { it.toDouble() }
|
||||||
|
RuntimeValue(DataType.FLOAT, numbers.average())
|
||||||
|
}
|
||||||
|
"sum" -> {
|
||||||
|
val sum = args.single().array!!.map { it.toDouble() }.sum()
|
||||||
|
when (args[0].type) {
|
||||||
|
DataType.ARRAY_UB -> RuntimeValue(DataType.UWORD, sum)
|
||||||
|
DataType.ARRAY_B -> RuntimeValue(DataType.WORD, sum)
|
||||||
|
DataType.ARRAY_UW -> RuntimeValue(DataType.UWORD, sum)
|
||||||
|
DataType.ARRAY_W -> RuntimeValue(DataType.WORD, sum)
|
||||||
|
DataType.ARRAY_F -> RuntimeValue(DataType.FLOAT, sum)
|
||||||
|
else -> throw VmExecutionException("weird sum type ${args[0]}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"any" -> {
|
||||||
|
val numbers = args.single().array!!.map { it.toDouble() }
|
||||||
|
RuntimeValue(DataType.UBYTE, if (numbers.any { it != 0.0 }) 1 else 0)
|
||||||
|
}
|
||||||
|
"all" -> {
|
||||||
|
val numbers = args.single().array!!.map { it.toDouble() }
|
||||||
|
RuntimeValue(DataType.UBYTE, if (numbers.all { it != 0.0 }) 1 else 0)
|
||||||
|
}
|
||||||
|
"swap" ->
|
||||||
|
throw VmExecutionException("swap() cannot be implemented as a function")
|
||||||
|
"strlen" -> {
|
||||||
|
val zeroIndex = args[0].str!!.indexOf(0.toChar())
|
||||||
|
if (zeroIndex >= 0)
|
||||||
|
RuntimeValue(DataType.UBYTE, zeroIndex)
|
||||||
|
else
|
||||||
|
RuntimeValue(DataType.UBYTE, args[0].str!!.length)
|
||||||
|
}
|
||||||
|
"memset" -> {
|
||||||
|
val target = args[0].array!!
|
||||||
|
val amount = args[1].integerValue()
|
||||||
|
val value = args[2].integerValue()
|
||||||
|
for (i in 0 until amount) {
|
||||||
|
target[i] = value
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"memsetw" -> {
|
||||||
|
val target = args[0].array!!
|
||||||
|
val amount = args[1].integerValue()
|
||||||
|
val value = args[2].integerValue()
|
||||||
|
for (i in 0 until amount step 2) {
|
||||||
|
target[i * 2] = value and 255
|
||||||
|
target[i * 2 + 1] = value ushr 8
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"memcopy" -> {
|
||||||
|
val source = args[0].array!!
|
||||||
|
val dest = args[1].array!!
|
||||||
|
val amount = args[2].integerValue()
|
||||||
|
for(i in 0 until amount) {
|
||||||
|
dest[i] = source[i]
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"mkword" -> {
|
||||||
|
val result = (args[1].integerValue() shl 8) or args[0].integerValue()
|
||||||
|
RuntimeValue(DataType.UWORD, result)
|
||||||
|
}
|
||||||
|
"set_carry" -> {
|
||||||
|
statusflags.carry=true
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"clear_carry" -> {
|
||||||
|
statusflags.carry=false
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"set_irqd" -> {
|
||||||
|
statusflags.irqd=true
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"clear_irqd" -> {
|
||||||
|
statusflags.irqd=false
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"read_flags" -> {
|
||||||
|
val carry = if(statusflags.carry) 1 else 0
|
||||||
|
val zero = if(statusflags.zero) 2 else 0
|
||||||
|
val irqd = if(statusflags.irqd) 4 else 0
|
||||||
|
val negative = if(statusflags.negative) 128 else 0
|
||||||
|
RuntimeValue(DataType.UBYTE, carry or zero or irqd or negative)
|
||||||
|
}
|
||||||
|
"rsave" -> {
|
||||||
|
statusFlagsSave.push(statusflags)
|
||||||
|
registerAsave.push(runtimeVariables.get(program.namespace, Register.A.name))
|
||||||
|
registerXsave.push(runtimeVariables.get(program.namespace, Register.X.name))
|
||||||
|
registerYsave.push(runtimeVariables.get(program.namespace, Register.Y.name))
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"rrestore" -> {
|
||||||
|
val flags = statusFlagsSave.pop()
|
||||||
|
statusflags.carry = flags.carry
|
||||||
|
statusflags.negative = flags.negative
|
||||||
|
statusflags.zero = flags.zero
|
||||||
|
statusflags.irqd = flags.irqd
|
||||||
|
runtimeVariables.set(program.namespace, Register.A.name, registerAsave.pop())
|
||||||
|
runtimeVariables.set(program.namespace, Register.X.name, registerXsave.pop())
|
||||||
|
runtimeVariables.set(program.namespace, Register.Y.name, registerYsave.pop())
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else -> TODO("builtin function $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,204 +0,0 @@
|
|||||||
package prog8.vm.astvm
|
|
||||||
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import java.lang.Math.toDegrees
|
|
||||||
import java.lang.Math.toRadians
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.math.*
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFunctions {
|
|
||||||
|
|
||||||
private val rnd = Random(0)
|
|
||||||
private val statusFlagsSave = Stack<StatusFlags>()
|
|
||||||
|
|
||||||
|
|
||||||
fun performBuiltinFunction(name: String, args: List<RuntimeValue>, statusflags: StatusFlags): RuntimeValue? {
|
|
||||||
return when (name) {
|
|
||||||
"rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255)
|
|
||||||
"rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535)
|
|
||||||
"rndf" -> RuntimeValue(DataType.FLOAT, rnd.nextDouble())
|
|
||||||
"lsb" -> RuntimeValue(DataType.UBYTE, args[0].integerValue() and 255)
|
|
||||||
"msb" -> RuntimeValue(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255)
|
|
||||||
"sin" -> RuntimeValue(DataType.FLOAT, sin(args[0].numericValue().toDouble()))
|
|
||||||
"sin8" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.BYTE, (127.0 * sin(rad)).toShort())
|
|
||||||
}
|
|
||||||
"sin8u" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort())
|
|
||||||
}
|
|
||||||
"sin16" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.BYTE, (32767.0 * sin(rad)).toShort())
|
|
||||||
}
|
|
||||||
"sin16u" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * sin(rad)).toShort())
|
|
||||||
}
|
|
||||||
"cos" -> RuntimeValue(DataType.FLOAT, cos(args[0].numericValue().toDouble()))
|
|
||||||
"cos8" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.BYTE, (127.0 * cos(rad)).toShort())
|
|
||||||
}
|
|
||||||
"cos8u" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort())
|
|
||||||
}
|
|
||||||
"cos16" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.BYTE, (32767.0 * cos(rad)).toShort())
|
|
||||||
}
|
|
||||||
"cos16u" -> {
|
|
||||||
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
|
|
||||||
RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * cos(rad)).toShort())
|
|
||||||
}
|
|
||||||
"tan" -> RuntimeValue(DataType.FLOAT, tan(args[0].numericValue().toDouble()))
|
|
||||||
"atan" -> RuntimeValue(DataType.FLOAT, atan(args[0].numericValue().toDouble()))
|
|
||||||
"ln" -> RuntimeValue(DataType.FLOAT, ln(args[0].numericValue().toDouble()))
|
|
||||||
"log2" -> RuntimeValue(DataType.FLOAT, log2(args[0].numericValue().toDouble()))
|
|
||||||
"sqrt" -> RuntimeValue(DataType.FLOAT, sqrt(args[0].numericValue().toDouble()))
|
|
||||||
"sqrt16" -> RuntimeValue(DataType.UBYTE, sqrt(args[0].wordval!!.toDouble()).toInt())
|
|
||||||
"rad" -> RuntimeValue(DataType.FLOAT, toRadians(args[0].numericValue().toDouble()))
|
|
||||||
"deg" -> RuntimeValue(DataType.FLOAT, toDegrees(args[0].numericValue().toDouble()))
|
|
||||||
"round" -> RuntimeValue(DataType.FLOAT, round(args[0].numericValue().toDouble()))
|
|
||||||
"floor" -> RuntimeValue(DataType.FLOAT, floor(args[0].numericValue().toDouble()))
|
|
||||||
"ceil" -> RuntimeValue(DataType.FLOAT, ceil(args[0].numericValue().toDouble()))
|
|
||||||
"rol" -> {
|
|
||||||
val (result, newCarry) = args[0].rol(statusflags.carry)
|
|
||||||
statusflags.carry = newCarry
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
"rol2" -> args[0].rol2()
|
|
||||||
"ror" -> {
|
|
||||||
val (result, newCarry) = args[0].ror(statusflags.carry)
|
|
||||||
statusflags.carry = newCarry
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
"ror2" -> args[0].ror2()
|
|
||||||
"lsl" -> args[0].shl()
|
|
||||||
"lsr" -> args[0].shr()
|
|
||||||
"abs" -> {
|
|
||||||
when (args[0].type) {
|
|
||||||
DataType.UBYTE -> args[0]
|
|
||||||
DataType.BYTE -> RuntimeValue(DataType.UBYTE, abs(args[0].numericValue().toDouble()))
|
|
||||||
DataType.UWORD -> args[0]
|
|
||||||
DataType.WORD -> RuntimeValue(DataType.UWORD, abs(args[0].numericValue().toDouble()))
|
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, abs(args[0].numericValue().toDouble()))
|
|
||||||
else -> TODO("strange abs type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"max" -> {
|
|
||||||
val numbers = args.map { it.numericValue().toDouble() }
|
|
||||||
RuntimeValue(args[0].type, numbers.max())
|
|
||||||
}
|
|
||||||
"min" -> {
|
|
||||||
val numbers = args.map { it.numericValue().toDouble() }
|
|
||||||
RuntimeValue(args[0].type, numbers.min())
|
|
||||||
}
|
|
||||||
"avg" -> {
|
|
||||||
val numbers = args.map { it.numericValue().toDouble() }
|
|
||||||
RuntimeValue(DataType.FLOAT, numbers.average())
|
|
||||||
}
|
|
||||||
"sum" -> {
|
|
||||||
val sum = args.map { it.numericValue().toDouble() }.sum()
|
|
||||||
when (args[0].type) {
|
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UWORD, sum)
|
|
||||||
DataType.BYTE -> RuntimeValue(DataType.WORD, sum)
|
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, sum)
|
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, sum)
|
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, sum)
|
|
||||||
else -> TODO("weird sum type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"any" -> {
|
|
||||||
val numbers = args.map { it.numericValue().toDouble() }
|
|
||||||
RuntimeValue(DataType.UBYTE, if (numbers.any { it != 0.0 }) 1 else 0)
|
|
||||||
}
|
|
||||||
"all" -> {
|
|
||||||
val numbers = args.map { it.numericValue().toDouble() }
|
|
||||||
RuntimeValue(DataType.UBYTE, if (numbers.all { it != 0.0 }) 1 else 0)
|
|
||||||
}
|
|
||||||
"swap" ->
|
|
||||||
throw VmExecutionException("swap() cannot be implemented as a function")
|
|
||||||
"strlen" -> {
|
|
||||||
val zeroIndex = args[0].str!!.indexOf(0.toChar())
|
|
||||||
if (zeroIndex >= 0)
|
|
||||||
RuntimeValue(DataType.UBYTE, zeroIndex)
|
|
||||||
else
|
|
||||||
RuntimeValue(DataType.UBYTE, args[0].str!!.length)
|
|
||||||
}
|
|
||||||
"memset" -> {
|
|
||||||
val target = args[0].array!!
|
|
||||||
val amount = args[1].integerValue()
|
|
||||||
val value = args[2].integerValue()
|
|
||||||
for (i in 0 until amount) {
|
|
||||||
target[i] = value
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"memsetw" -> {
|
|
||||||
val target = args[0].array!!
|
|
||||||
val amount = args[1].integerValue()
|
|
||||||
val value = args[2].integerValue()
|
|
||||||
for (i in 0 until amount step 2) {
|
|
||||||
target[i * 2] = value and 255
|
|
||||||
target[i * 2 + 1] = value ushr 8
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"memcopy" -> {
|
|
||||||
val source = args[0].array!!
|
|
||||||
val dest = args[1].array!!
|
|
||||||
val amount = args[2].integerValue()
|
|
||||||
for(i in 0 until amount) {
|
|
||||||
dest[i] = source[i]
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"mkword" -> {
|
|
||||||
val result = (args[1].integerValue() shl 8) or args[0].integerValue()
|
|
||||||
RuntimeValue(DataType.UWORD, result)
|
|
||||||
}
|
|
||||||
"set_carry" -> {
|
|
||||||
statusflags.carry=true
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"clear_carry" -> {
|
|
||||||
statusflags.carry=false
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"set_irqd" -> {
|
|
||||||
statusflags.irqd=true
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"clear_irqd" -> {
|
|
||||||
statusflags.irqd=false
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"read_flags" -> {
|
|
||||||
val carry = if(statusflags.carry) 1 else 0
|
|
||||||
val zero = if(statusflags.zero) 2 else 0
|
|
||||||
val irqd = if(statusflags.irqd) 4 else 0
|
|
||||||
val negative = if(statusflags.negative) 128 else 0
|
|
||||||
RuntimeValue(DataType.UBYTE, carry or zero or irqd or negative)
|
|
||||||
}
|
|
||||||
"rsave" -> {
|
|
||||||
statusFlagsSave.push(statusflags)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
"rrestore" -> {
|
|
||||||
val flags = statusFlagsSave.pop()
|
|
||||||
statusflags.carry = flags.carry
|
|
||||||
statusflags.negative = flags.negative
|
|
||||||
statusflags.zero = flags.zero
|
|
||||||
statusflags.irqd = flags.irqd
|
|
||||||
null
|
|
||||||
}
|
|
||||||
else -> TODO("builtin function $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package prog8.vm.astvm
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class CallStack {
|
|
||||||
|
|
||||||
private val stack = Stack<Pair<INameScope, Int>>()
|
|
||||||
|
|
||||||
fun pop(): Pair<INameScope, Int> {
|
|
||||||
return stack.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(scope: INameScope, index: Int) {
|
|
||||||
stack.push(Pair(scope, index))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
package prog8.vm.astvm
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ArrayElementTypes
|
import prog8.ast.base.ArrayElementTypes
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||||
@ -13,18 +14,27 @@ import prog8.vm.RuntimeValue
|
|||||||
import prog8.vm.RuntimeValueRange
|
import prog8.vm.RuntimeValueRange
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags,
|
|
||||||
val runtimeVars: RuntimeVariables, val functions: BuiltinFunctions,
|
|
||||||
val executeSubroutine: (sub: Subroutine, args: List<RuntimeValue>, startlabel: Label?) -> List<RuntimeValue>)
|
|
||||||
|
|
||||||
fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
typealias BuiltinfunctionCaller = (name: String, args: List<RuntimeValue>, flags: StatusFlags) -> RuntimeValue?
|
||||||
|
typealias SubroutineCaller = (sub: Subroutine, args: List<RuntimeValue>, startAtLabel: Label?) -> RuntimeValue?
|
||||||
|
|
||||||
|
|
||||||
|
class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags,
|
||||||
|
val runtimeVars: RuntimeVariables,
|
||||||
|
val performBuiltinFunction: BuiltinfunctionCaller,
|
||||||
|
val executeSubroutine: SubroutineCaller)
|
||||||
|
|
||||||
|
fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
|
||||||
val constval = expr.constValue(ctx.program)
|
val constval = expr.constValue(ctx.program)
|
||||||
if(constval!=null)
|
if(constval!=null)
|
||||||
return RuntimeValue.from(constval, ctx.program.heap)
|
return RuntimeValue.fromLv(constval)
|
||||||
|
|
||||||
when(expr) {
|
when(expr) {
|
||||||
is LiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
return RuntimeValue.from(expr, ctx.program.heap)
|
return RuntimeValue.fromLv(expr)
|
||||||
|
}
|
||||||
|
is ReferenceLiteralValue -> {
|
||||||
|
return RuntimeValue.fromLv(expr, ctx.program.heap)
|
||||||
}
|
}
|
||||||
is PrefixExpression -> {
|
is PrefixExpression -> {
|
||||||
return when(expr.operator) {
|
return when(expr.operator) {
|
||||||
@ -81,8 +91,14 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
|||||||
}
|
}
|
||||||
is AddressOf -> {
|
is AddressOf -> {
|
||||||
// we support: address of heap var -> the heap id
|
// we support: address of heap var -> the heap id
|
||||||
|
return try {
|
||||||
val heapId = expr.identifier.heapId(ctx.program.namespace)
|
val heapId = expr.identifier.heapId(ctx.program.namespace)
|
||||||
return RuntimeValue(DataType.UWORD, heapId)
|
RuntimeValue(DataType.UWORD, heapId)
|
||||||
|
} catch( f: FatalAstException) {
|
||||||
|
// fallback: use the hash of the name, so we have at least *a* value...
|
||||||
|
val address = expr.identifier.hashCode() and 65535
|
||||||
|
RuntimeValue(DataType.UWORD, address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
val address = evaluate(expr.addressExpression, ctx).wordval!!
|
val address = evaluate(expr.addressExpression, ctx).wordval!!
|
||||||
@ -93,9 +109,10 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
|||||||
val scope = expr.definingScope()
|
val scope = expr.definingScope()
|
||||||
val variable = scope.lookup(expr.nameInSource, expr)
|
val variable = scope.lookup(expr.nameInSource, expr)
|
||||||
if(variable is VarDecl) {
|
if(variable is VarDecl) {
|
||||||
if(variable.type==VarDeclType.VAR)
|
when {
|
||||||
return ctx.runtimeVars.get(variable.definingScope(), variable.name)
|
variable.type==VarDeclType.VAR -> return ctx.runtimeVars.get(variable.definingScope(), variable.name)
|
||||||
else {
|
variable.datatype==DataType.STRUCT -> throw VmExecutionException("cannot process structs by-value. at ${expr.position}")
|
||||||
|
else -> {
|
||||||
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
||||||
return when(variable.datatype) {
|
return when(variable.datatype) {
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
||||||
@ -108,6 +125,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
|||||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
else -> throw VmExecutionException("unexpected datatype $variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
throw VmExecutionException("weird identifier reference $variable")
|
throw VmExecutionException("weird identifier reference $variable")
|
||||||
}
|
}
|
||||||
@ -116,13 +134,12 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
|||||||
val args = expr.arglist.map { evaluate(it, ctx) }
|
val args = expr.arglist.map { evaluate(it, ctx) }
|
||||||
return when(sub) {
|
return when(sub) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
val results = ctx.executeSubroutine(sub, args, null)
|
val result = ctx.executeSubroutine(sub, args, null)
|
||||||
if(results.size!=1)
|
?: throw VmExecutionException("expected a result from functioncall $expr")
|
||||||
throw VmExecutionException("expected 1 result from functioncall $expr")
|
result
|
||||||
results[0]
|
|
||||||
}
|
}
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
val result = ctx.functions.performBuiltinFunction(sub.name, args, ctx.statusflags)
|
val result = ctx.performBuiltinFunction(sub.name, args, ctx.statusflags)
|
||||||
?: throw VmExecutionException("expected 1 result from functioncall $expr")
|
?: throw VmExecutionException("expected 1 result from functioncall $expr")
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.vm.astvm
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.compiler.target.c64.Mflpt5
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setFloat(address: Int, value: Double) {
|
fun setFloat(address: Int, value: Double) {
|
||||||
val mflpt5 = Mflpt5.fromNumber(value)
|
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(value)
|
||||||
setUByte(address, mflpt5.b0)
|
setUByte(address, mflpt5.b0)
|
||||||
setUByte(address+1, mflpt5.b1)
|
setUByte(address+1, mflpt5.b1)
|
||||||
setUByte(address+2, mflpt5.b2)
|
setUByte(address+2, mflpt5.b2)
|
||||||
@ -89,7 +89,7 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getFloat(address: Int): Double {
|
fun getFloat(address: Int): Double {
|
||||||
return Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2),
|
return MachineDefinition.Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2),
|
||||||
getUByte(address + 3), getUByte(address + 4)).toDouble()
|
getUByte(address + 3), getUByte(address + 4)).toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package prog8.vm.astvm
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.compiler.target.c64.Charset
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
import prog8.compiler.target.c64.Colors
|
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import java.awt.*
|
import java.awt.*
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import java.awt.event.KeyListener
|
import java.awt.event.KeyListener
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
import java.util.*
|
||||||
import javax.swing.JFrame
|
import javax.swing.JFrame
|
||||||
import javax.swing.JPanel
|
import javax.swing.JPanel
|
||||||
import javax.swing.Timer
|
import javax.swing.Timer
|
||||||
@ -18,6 +18,7 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
private val g2d = image.graphics as Graphics2D
|
private val g2d = image.graphics as Graphics2D
|
||||||
private var cursorX: Int=0
|
private var cursorX: Int=0
|
||||||
private var cursorY: Int=0
|
private var cursorY: Int=0
|
||||||
|
val keyboardBuffer: Deque<Char> = LinkedList()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val size = Dimension(image.width * SCALING, image.height * SCALING)
|
val size = Dimension(image.width * SCALING, image.height * SCALING)
|
||||||
@ -30,14 +31,14 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
addKeyListener(this)
|
addKeyListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun keyTyped(p0: KeyEvent?) {}
|
override fun keyTyped(p0: KeyEvent) {
|
||||||
|
keyboardBuffer.add(p0.keyChar)
|
||||||
|
}
|
||||||
|
|
||||||
override fun keyPressed(p0: KeyEvent?) {
|
override fun keyPressed(p0: KeyEvent) {
|
||||||
println("pressed: $p0.k")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun keyReleased(p0: KeyEvent?) {
|
override fun keyReleased(p0: KeyEvent?) {
|
||||||
println("released: $p0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun paint(graphics: Graphics?) {
|
override fun paint(graphics: Graphics?) {
|
||||||
@ -49,61 +50,82 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearScreen(color: Short) {
|
fun clearScreen(color: Short) {
|
||||||
g2d.background = Colors.palette[color % Colors.palette.size]
|
g2d.background = MachineDefinition.colorPalette[color % MachineDefinition.colorPalette.size]
|
||||||
g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT)
|
g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT)
|
||||||
cursorX = 0
|
cursorX = 0
|
||||||
cursorY = 0
|
cursorY = 0
|
||||||
}
|
}
|
||||||
fun setPixel(x: Int, y: Int, color: Short) {
|
fun setPixel(x: Int, y: Int, color: Short) {
|
||||||
image.setRGB(x, y, Colors.palette[color % Colors.palette.size].rgb)
|
image.setRGB(x, y, MachineDefinition.colorPalette[color % MachineDefinition.colorPalette.size].rgb)
|
||||||
}
|
}
|
||||||
fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) {
|
fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) {
|
||||||
g2d.color = Colors.palette[color % Colors.palette.size]
|
g2d.color = MachineDefinition.colorPalette[color % MachineDefinition.colorPalette.size]
|
||||||
g2d.drawLine(x1, y1, x2, y2)
|
g2d.drawLine(x1, y1, x2, y2)
|
||||||
}
|
}
|
||||||
fun printText(text: String, color: Short, lowercase: Boolean) {
|
|
||||||
|
fun printText(text: String, lowercase: Boolean, inverseVideo: Boolean=false) {
|
||||||
val t2 = text.substringBefore(0.toChar())
|
val t2 = text.substringBefore(0.toChar())
|
||||||
val lines = t2.split('\n')
|
val lines = t2.split('\n')
|
||||||
for(line in lines.withIndex()) {
|
for(line in lines.withIndex()) {
|
||||||
printTextSingleLine(line.value, color, lowercase)
|
val petscii = Petscii.encodePetscii(line.value, lowercase)
|
||||||
|
petscii.forEach { printPetscii(it, inverseVideo) }
|
||||||
if(line.index<lines.size-1) {
|
if(line.index<lines.size-1) {
|
||||||
cursorX=0
|
printPetscii(13) // newline
|
||||||
cursorY++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun printTextSingleLine(text: String, color: Short, lowercase: Boolean) {
|
|
||||||
for(clearx in cursorX until cursorX+text.length) {
|
|
||||||
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
|
||||||
}
|
|
||||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
|
||||||
setChar(cursorX, cursorY, sc, color)
|
|
||||||
cursorX++
|
|
||||||
if(cursorX>=(SCREENWIDTH /8)) {
|
|
||||||
cursorY++
|
|
||||||
cursorX=0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printChar(char: Short) {
|
fun printPetscii(char: Short, inverseVideo: Boolean=false) {
|
||||||
if(char==13.toShort() || char==141.toShort()) {
|
if(char==13.toShort() || char==141.toShort()) {
|
||||||
cursorX=0
|
cursorX=0
|
||||||
cursorY++
|
cursorY++
|
||||||
} else {
|
} else {
|
||||||
setChar(cursorX, cursorY, char, 1)
|
setPetscii(cursorX, cursorY, char, 1, inverseVideo)
|
||||||
cursorX++
|
cursorX++
|
||||||
if (cursorX >= (SCREENWIDTH / 8)) {
|
if (cursorX >= (SCREENWIDTH / 8)) {
|
||||||
cursorY++
|
cursorY++
|
||||||
cursorX = 0
|
cursorX = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while(cursorY>=(SCREENHEIGHT/8)) {
|
||||||
|
// scroll the screen up because the cursor went past the last line
|
||||||
|
Thread.sleep(10)
|
||||||
|
val screen = image.copy()
|
||||||
|
val graphics = image.graphics as Graphics2D
|
||||||
|
graphics.drawImage(screen, 0, -8, null)
|
||||||
|
val color = graphics.color
|
||||||
|
graphics.color = MachineDefinition.colorPalette[6]
|
||||||
|
graphics.fillRect(0, 24*8, SCREENWIDTH, 25*8)
|
||||||
|
graphics.color=color
|
||||||
|
cursorY--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setChar(x: Int, y: Int, screenCode: Short, color: Short) {
|
fun writeTextAt(x: Int, y: Int, text: String, color: Short, lowercase: Boolean, inverseVideo: Boolean=false) {
|
||||||
|
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
|
||||||
|
var xx=x
|
||||||
|
for(clearx in xx until xx+text.length) {
|
||||||
|
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
||||||
|
}
|
||||||
|
for(sc in Petscii.encodePetscii(text, lowercase)) {
|
||||||
|
if(sc==0.toShort())
|
||||||
|
break
|
||||||
|
setPetscii(xx++, y, sc, colorIdx, inverseVideo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPetscii(x: Int, y: Int, petscii: Short, color: Short, inverseVideo: Boolean) {
|
||||||
g2d.clearRect(8*x, 8*y, 8, 8)
|
g2d.clearRect(8*x, 8*y, 8, 8)
|
||||||
val colorIdx = (color % Colors.palette.size).toShort()
|
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
|
||||||
val coloredImage = Charset.getColoredChar(screenCode, colorIdx)
|
val screencode = Petscii.petscii2scr(petscii, inverseVideo)
|
||||||
|
val coloredImage = MachineDefinition.Charset.getColoredChar(screencode, colorIdx)
|
||||||
|
g2d.drawImage(coloredImage, 8*x, 8*y , null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChar(x: Int, y: Int, screencode: Short, color: Short) {
|
||||||
|
g2d.clearRect(8*x, 8*y, 8, 8)
|
||||||
|
val colorIdx = (color % MachineDefinition.colorPalette.size).toShort()
|
||||||
|
val coloredImage = MachineDefinition.Charset.getColoredChar(screencode, colorIdx)
|
||||||
g2d.drawImage(coloredImage, 8*x, 8*y , null)
|
g2d.drawImage(coloredImage, 8*x, 8*y , null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,20 +138,6 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
return Pair(cursorX, cursorY)
|
return Pair(cursorX, cursorY)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeText(x: Int, y: Int, text: String, color: Short, lowercase: Boolean) {
|
|
||||||
val colorIdx = (color % Colors.palette.size).toShort()
|
|
||||||
var xx=x
|
|
||||||
for(clearx in xx until xx+text.length) {
|
|
||||||
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
|
||||||
}
|
|
||||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
|
||||||
if(sc==0.toShort())
|
|
||||||
break
|
|
||||||
setChar(xx++, y, sc, colorIdx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SCREENWIDTH = 320
|
const val SCREENWIDTH = 320
|
||||||
const val SCREENHEIGHT = 200
|
const val SCREENHEIGHT = 200
|
||||||
@ -140,29 +148,30 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
|||||||
|
|
||||||
class ScreenDialog(title: String) : JFrame(title) {
|
class ScreenDialog(title: String) : JFrame(title) {
|
||||||
val canvas = BitmapScreenPanel()
|
val canvas = BitmapScreenPanel()
|
||||||
|
val keyboardBuffer = canvas.keyboardBuffer
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val borderWidth = 16
|
val borderWidth = 16
|
||||||
layout = GridBagLayout()
|
layout = GridBagLayout()
|
||||||
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
|
defaultCloseOperation = EXIT_ON_CLOSE
|
||||||
isResizable = false
|
isResizable = false
|
||||||
|
|
||||||
// the borders (top, left, right, bottom)
|
// the borders (top, left, right, bottom)
|
||||||
val borderTop = JPanel().apply {
|
val borderTop = JPanel().apply {
|
||||||
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||||
background = Colors.palette[14]
|
background = MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
val borderBottom = JPanel().apply {
|
val borderBottom = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||||
background = Colors.palette[14]
|
background = MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
val borderLeft = JPanel().apply {
|
val borderLeft = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||||
background = Colors.palette[14]
|
background = MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
val borderRight = JPanel().apply {
|
val borderRight = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||||
background = Colors.palette[14]
|
background = MachineDefinition.colorPalette[14]
|
||||||
}
|
}
|
||||||
var c = GridBagConstraints()
|
var c = GridBagConstraints()
|
||||||
c.gridx=0; c.gridy=1; c.gridwidth=3
|
c.gridx=0; c.gridy=1; c.gridwidth=3
|
||||||
@ -189,3 +198,12 @@ class ScreenDialog(title: String) : JFrame(title) {
|
|||||||
repaintTimer.start()
|
repaintTimer.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun BufferedImage.copy(): BufferedImage {
|
||||||
|
val bcopy = BufferedImage(this.width, this.height, this.type)
|
||||||
|
val g = bcopy.graphics
|
||||||
|
g.drawImage(this, 0, 0, null)
|
||||||
|
g.dispose()
|
||||||
|
return bcopy
|
||||||
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
package prog8.vm.astvm
|
package prog8.vm.astvm
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.expressions.LiteralValue
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.base.Register
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.ReferenceLiteralValue
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
|
import prog8.ast.statements.Statement
|
||||||
|
import prog8.ast.statements.StructDecl
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.ast.statements.ZeropageWish
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.vm.RuntimeValue
|
import prog8.vm.RuntimeValue
|
||||||
|
|
||||||
@ -17,9 +24,12 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
|||||||
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
|
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
|
||||||
|
|
||||||
val globalpos = Position("<<global>>", 0, 0, 0)
|
val globalpos = Position("<<global>>", 0, 0, 0)
|
||||||
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.A.name, LiteralValue.optimalInteger(0, globalpos), isArray = false, autoGenerated = true, position = globalpos)
|
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.A.name, null,
|
||||||
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.X.name, LiteralValue.optimalInteger(255, globalpos), isArray = false, autoGenerated = true, position = globalpos)
|
NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
|
||||||
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.Y.name, LiteralValue.optimalInteger(0, globalpos), isArray = false, autoGenerated = true, position = globalpos)
|
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.X.name, null,
|
||||||
|
NumericLiteralValue.optimalInteger(255, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
|
||||||
|
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.Y.name, null,
|
||||||
|
NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
|
||||||
vdA.linkParents(program.namespace)
|
vdA.linkParents(program.namespace)
|
||||||
vdX.linkParents(program.namespace)
|
vdX.linkParents(program.namespace)
|
||||||
vdY.linkParents(program.namespace)
|
vdY.linkParents(program.namespace)
|
||||||
@ -30,24 +40,34 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
|||||||
super.visit(program)
|
super.visit(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): IStatement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
when(decl.type) {
|
// if the decl is part of a struct, just skip it
|
||||||
// we can assume the value in the vardecl already has been converted into a constant LiteralValue here.
|
if(decl.parent !is StructDecl) {
|
||||||
|
when (decl.type) {
|
||||||
VarDeclType.VAR -> {
|
VarDeclType.VAR -> {
|
||||||
val value = RuntimeValue.from(decl.value as LiteralValue, heap)
|
if(decl.datatype!=DataType.STRUCT) {
|
||||||
|
val numericLv = decl.value as? NumericLiteralValue
|
||||||
|
val value = if(numericLv!=null) {
|
||||||
|
RuntimeValue.fromLv(numericLv)
|
||||||
|
} else {
|
||||||
|
val referenceLv = decl.value as ReferenceLiteralValue
|
||||||
|
RuntimeValue.fromLv(referenceLv, heap)
|
||||||
|
}
|
||||||
runtimeVariables.define(decl.definingScope(), decl.name, value)
|
runtimeVariables.define(decl.definingScope(), decl.name, value)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as LiteralValue).asIntegerValue!!)
|
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as NumericLiteralValue).number.toInt())
|
||||||
}
|
}
|
||||||
VarDeclType.CONST -> {
|
VarDeclType.CONST -> {
|
||||||
// consts should have been const-folded away
|
// consts should have been const-folded away
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// override fun accept(assignment: Assignment): IStatement {
|
// override fun accept(assignment: Assignment): Statement {
|
||||||
// if(assignment is VariableInitializationAssignment) {
|
// if(assignment is VariableInitializationAssignment) {
|
||||||
// println("INIT VAR $assignment")
|
// println("INIT VAR $assignment")
|
||||||
// }
|
// }
|
||||||
|
@ -4,10 +4,13 @@ import prog8.ast.antlr.unescape
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.AddressOf
|
import prog8.ast.expressions.AddressOf
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.IntegerOrAddressOf
|
import prog8.compiler.IntegerOrAddressOf
|
||||||
import prog8.compiler.intermediate.*
|
import prog8.compiler.intermediate.Instruction
|
||||||
|
import prog8.compiler.intermediate.LabelInstr
|
||||||
|
import prog8.compiler.intermediate.Opcode
|
||||||
|
import prog8.compiler.intermediate.opcodesWithVarArgument
|
||||||
|
import prog8.vm.RuntimeValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@ -226,18 +229,18 @@ class Program (val name: String,
|
|||||||
if(valueStr[0] !='"' && ':' !in valueStr)
|
if(valueStr[0] !='"' && ':' !in valueStr)
|
||||||
throw VmExecutionException("missing value type character")
|
throw VmExecutionException("missing value type character")
|
||||||
val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) {
|
val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) {
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).toShort(16))
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).toShort(16))
|
DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).toInt(16))
|
DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).toInt(16))
|
DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).toDouble())
|
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).substringBefore(' ').toDouble())// TODO process ZP and struct info?
|
||||||
in StringDatatypes -> {
|
in StringDatatypes -> {
|
||||||
if(valueStr.startsWith('"') && valueStr.endsWith('"'))
|
if(valueStr.startsWith('"') && valueStr.endsWith('"'))
|
||||||
throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap")
|
throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap")
|
||||||
else if(!valueStr.startsWith("heap:"))
|
else if(!valueStr.startsWith("heap:"))
|
||||||
throw VmExecutionException("invalid string value, should be a heap reference")
|
throw VmExecutionException("invalid string value, should be a heap reference")
|
||||||
else {
|
else {
|
||||||
val heapId = valueStr.substring(5).toInt()
|
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
|
||||||
RuntimeValue(type, heapId = heapId)
|
RuntimeValue(type, heapId = heapId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,7 +248,7 @@ class Program (val name: String,
|
|||||||
if(!valueStr.startsWith("heap:"))
|
if(!valueStr.startsWith("heap:"))
|
||||||
throw VmExecutionException("invalid array value, should be a heap reference")
|
throw VmExecutionException("invalid array value, should be a heap reference")
|
||||||
else {
|
else {
|
||||||
val heapId = valueStr.substring(5).toInt()
|
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
|
||||||
RuntimeValue(type, heapId = heapId)
|
RuntimeValue(type, heapId = heapId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,22 @@
|
|||||||
package prog8.vm.stackvm
|
package prog8.vm.stackvm
|
||||||
|
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.IterableDatatypes
|
|
||||||
import prog8.ast.base.NumericDatatypes
|
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.base.initvarsSubName
|
|
||||||
import prog8.vm.astvm.BitmapScreenPanel
|
|
||||||
import prog8.vm.astvm.Memory
|
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.IntegerOrAddressOf
|
import prog8.compiler.IntegerOrAddressOf
|
||||||
import prog8.compiler.intermediate.Instruction
|
import prog8.compiler.intermediate.Instruction
|
||||||
import prog8.compiler.intermediate.Opcode
|
import prog8.compiler.intermediate.Opcode
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
|
import prog8.vm.RuntimeValue
|
||||||
|
import prog8.vm.astvm.BitmapScreenPanel
|
||||||
|
import prog8.vm.astvm.Memory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
|
|
||||||
enum class Syscall(val callNr: Short) {
|
internal enum class Syscall(val callNr: Short) {
|
||||||
VM_WRITE_MEMCHR(10), // print a single char from the memory address popped from stack
|
VM_WRITE_MEMCHR(10), // print a single char from the memory address popped from stack
|
||||||
VM_WRITE_MEMSTR(11), // print a 0-terminated petscii string from the memory address popped from stack
|
VM_WRITE_MEMSTR(11), // print a 0-terminated petscii string from the memory address popped from stack
|
||||||
VM_WRITE_NUM(12), // pop from the evaluation stack and print it as a number
|
VM_WRITE_NUM(12), // pop from the evaluation stack and print it as a number
|
||||||
@ -107,10 +103,9 @@ enum class Syscall(val callNr: Short) {
|
|||||||
SYSASM_c64flt_print_f(214),
|
SYSASM_c64flt_print_f(214),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val syscallNames = enumValues<Syscall>().map { it.name }.toSet()
|
||||||
|
|
||||||
val syscallNames = enumValues<Syscall>().map { it.name }.toSet()
|
internal val syscallsForStackVm = setOf(
|
||||||
|
|
||||||
val syscallsForStackVm = setOf(
|
|
||||||
Syscall.VM_WRITE_MEMCHR,
|
Syscall.VM_WRITE_MEMCHR,
|
||||||
Syscall.VM_WRITE_MEMSTR,
|
Syscall.VM_WRITE_MEMSTR,
|
||||||
Syscall.VM_WRITE_NUM,
|
Syscall.VM_WRITE_NUM,
|
||||||
@ -123,13 +118,13 @@ val syscallsForStackVm = setOf(
|
|||||||
Syscall.VM_GFX_LINE
|
Syscall.VM_GFX_LINE
|
||||||
)
|
)
|
||||||
|
|
||||||
class VmExecutionException(msg: String?) : Exception(msg)
|
internal class VmExecutionException(msg: String?) : Exception(msg)
|
||||||
|
|
||||||
class VmTerminationException(msg: String?) : Exception(msg)
|
internal class VmTerminationException(msg: String?) : Exception(msg)
|
||||||
|
|
||||||
class VmBreakpointException : Exception("breakpoint")
|
internal class VmBreakpointException : Exception("breakpoint")
|
||||||
|
|
||||||
class MyStack<T> : Stack<T>() {
|
internal class MyStack<T> : Stack<T>() {
|
||||||
fun peek(amount: Int) : List<T> {
|
fun peek(amount: Int) : List<T> {
|
||||||
return this.toList().subList(max(0, size-amount), size)
|
return this.toList().subList(max(0, size-amount), size)
|
||||||
}
|
}
|
||||||
@ -141,7 +136,6 @@ class MyStack<T> : Stack<T>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StackVm(private var traceOutputFile: String?) {
|
class StackVm(private var traceOutputFile: String?) {
|
||||||
val mem = Memory(::memread, ::memwrite)
|
val mem = Memory(::memread, ::memwrite)
|
||||||
var P_carry: Boolean = false
|
var P_carry: Boolean = false
|
||||||
@ -156,9 +150,9 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
private set
|
private set
|
||||||
var memoryPointers = mutableMapOf<String, Pair<Int, DataType>>() // all named pointers
|
var memoryPointers = mutableMapOf<String, Pair<Int, DataType>>() // all named pointers
|
||||||
private set
|
private set
|
||||||
var evalstack = MyStack<RuntimeValue>()
|
internal var evalstack = MyStack<RuntimeValue>()
|
||||||
private set
|
private set
|
||||||
var callstack = MyStack<Int>()
|
internal var callstack = MyStack<Int>()
|
||||||
private set
|
private set
|
||||||
private var program = listOf<Instruction>()
|
private var program = listOf<Instruction>()
|
||||||
private var labels = emptyMap<String, Int>()
|
private var labels = emptyMap<String, Int>()
|
||||||
@ -204,7 +198,7 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
throw VmExecutionException("program contains variable(s) for the reserved registers A/X/Y")
|
throw VmExecutionException("program contains variable(s) for the reserved registers A/X/Y")
|
||||||
// define the 'registers'
|
// define the 'registers'
|
||||||
variables["A"] = RuntimeValue(DataType.UBYTE, 0)
|
variables["A"] = RuntimeValue(DataType.UBYTE, 0)
|
||||||
variables["X"] = RuntimeValue(DataType.UBYTE, 0)
|
variables["X"] = RuntimeValue(DataType.UBYTE, 255)
|
||||||
variables["Y"] = RuntimeValue(DataType.UBYTE, 0)
|
variables["Y"] = RuntimeValue(DataType.UBYTE, 0)
|
||||||
|
|
||||||
initMemory(program.memory)
|
initMemory(program.memory)
|
||||||
@ -1865,8 +1859,8 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
}
|
}
|
||||||
Opcode.RRESTORE -> {
|
Opcode.RRESTORE -> {
|
||||||
variables["A"] = evalstack.pop()
|
variables["A"] = evalstack.pop()
|
||||||
variables["X"] = evalstack.pop()
|
|
||||||
variables["Y"] = evalstack.pop()
|
variables["Y"] = evalstack.pop()
|
||||||
|
variables["X"] = evalstack.pop()
|
||||||
P_carry = evalstack.pop().asBoolean
|
P_carry = evalstack.pop().asBoolean
|
||||||
P_irqd = evalstack.pop().asBoolean
|
P_irqd = evalstack.pop().asBoolean
|
||||||
}
|
}
|
||||||
@ -1875,10 +1869,17 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins")
|
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins")
|
||||||
Opcode.INCLUDE_FILE -> throw VmExecutionException("stackVm doesn't support including a file $ins")
|
Opcode.INCLUDE_FILE -> throw VmExecutionException("stackVm doesn't support including a file $ins")
|
||||||
Opcode.PUSH_ADDR_HEAPVAR -> {
|
Opcode.PUSH_ADDR_HEAPVAR -> {
|
||||||
val heapId = variables.getValue(ins.callLabel!!).heapId!!
|
val variable = variables.getValue(ins.callLabel!!)
|
||||||
if(heapId<0)
|
if(variable.heapId!=null) {
|
||||||
|
val heapId = variable.heapId
|
||||||
|
if (heapId < 0)
|
||||||
throw VmExecutionException("expected variable on heap")
|
throw VmExecutionException("expected variable on heap")
|
||||||
evalstack.push(RuntimeValue(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator)
|
evalstack.push(RuntimeValue(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator)
|
||||||
|
} else {
|
||||||
|
// hack: return hash of the name, so we have at least *a* value...
|
||||||
|
val addr = ins.callLabel.hashCode() and 65535
|
||||||
|
evalstack.push(RuntimeValue(DataType.UWORD, addr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Opcode.CAST_UB_TO_B -> typecast(DataType.UBYTE, DataType.BYTE)
|
Opcode.CAST_UB_TO_B -> typecast(DataType.UBYTE, DataType.BYTE)
|
||||||
Opcode.CAST_W_TO_B -> typecast(DataType.WORD, DataType.BYTE)
|
Opcode.CAST_W_TO_B -> typecast(DataType.WORD, DataType.BYTE)
|
||||||
@ -1928,7 +1929,7 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
}
|
}
|
||||||
"c64.CHROUT" -> {
|
"c64.CHROUT" -> {
|
||||||
val sc=variables.getValue("A").integerValue()
|
val sc=variables.getValue("A").integerValue()
|
||||||
canvas?.printChar(sc.toShort())
|
canvas?.printPetscii(sc.toShort())
|
||||||
callstack.pop()
|
callstack.pop()
|
||||||
}
|
}
|
||||||
"c64.GETIN" -> {
|
"c64.GETIN" -> {
|
||||||
@ -2029,7 +2030,7 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
val color = evalstack.pop()
|
val color = evalstack.pop()
|
||||||
val (cy, cx) = evalstack.pop2()
|
val (cy, cx) = evalstack.pop2()
|
||||||
val text = heap.get(textPtr)
|
val text = heap.get(textPtr)
|
||||||
canvas?.writeText(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue().toShort(), true)
|
canvas?.writeTextAt(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue().toShort(), true)
|
||||||
}
|
}
|
||||||
Syscall.FUNC_RND -> evalstack.push(RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255))
|
Syscall.FUNC_RND -> evalstack.push(RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255))
|
||||||
Syscall.FUNC_RNDW -> evalstack.push(RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535))
|
Syscall.FUNC_RNDW -> evalstack.push(RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535))
|
||||||
@ -2311,54 +2312,54 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
Syscall.SYSASM_c64scr_print -> {
|
Syscall.SYSASM_c64scr_print -> {
|
||||||
val straddr = variables.getValue("A").integerValue() + 256*variables.getValue("Y").integerValue()
|
val straddr = variables.getValue("A").integerValue() + 256*variables.getValue("Y").integerValue()
|
||||||
val str = heap.get(straddr).str!!
|
val str = heap.get(straddr).str!!
|
||||||
canvas?.printText(str, 1, true)
|
canvas?.printText(str, true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_ub -> {
|
Syscall.SYSASM_c64scr_print_ub -> {
|
||||||
val num = variables.getValue("A").integerValue()
|
val num = variables.getValue("A").integerValue()
|
||||||
canvas?.printText(num.toString(), 1, true)
|
canvas?.printText(num.toString(), true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_ub0 -> {
|
Syscall.SYSASM_c64scr_print_ub0 -> {
|
||||||
val num = variables.getValue("A").integerValue()
|
val num = variables.getValue("A").integerValue()
|
||||||
canvas?.printText("%03d".format(num), 1, true)
|
canvas?.printText("%03d".format(num), true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_b -> {
|
Syscall.SYSASM_c64scr_print_b -> {
|
||||||
val num = variables.getValue("A").integerValue()
|
val num = variables.getValue("A").integerValue()
|
||||||
if(num<=127)
|
if(num<=127)
|
||||||
canvas?.printText(num.toString(), 1, true)
|
canvas?.printText(num.toString(), true)
|
||||||
else
|
else
|
||||||
canvas?.printText("-${256-num}", 1, true)
|
canvas?.printText("-${256-num}", true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_uw -> {
|
Syscall.SYSASM_c64scr_print_uw -> {
|
||||||
val lo = variables.getValue("A").integerValue()
|
val lo = variables.getValue("A").integerValue()
|
||||||
val hi = variables.getValue("Y").integerValue()
|
val hi = variables.getValue("Y").integerValue()
|
||||||
val number = lo+256*hi
|
val number = lo+256*hi
|
||||||
canvas?.printText(number.toString(), 1, true)
|
canvas?.printText(number.toString(), true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_uw0 -> {
|
Syscall.SYSASM_c64scr_print_uw0 -> {
|
||||||
val lo = variables.getValue("A").integerValue()
|
val lo = variables.getValue("A").integerValue()
|
||||||
val hi = variables.getValue("Y").integerValue()
|
val hi = variables.getValue("Y").integerValue()
|
||||||
val number = lo+256*hi
|
val number = lo+256*hi
|
||||||
canvas?.printText("%05d".format(number), 1, true)
|
canvas?.printText("%05d".format(number), true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_uwhex -> {
|
Syscall.SYSASM_c64scr_print_uwhex -> {
|
||||||
val prefix = if(this.P_carry) "$" else ""
|
val prefix = if(this.P_carry) "$" else ""
|
||||||
val lo = variables.getValue("A").integerValue()
|
val lo = variables.getValue("A").integerValue()
|
||||||
val hi = variables.getValue("Y").integerValue()
|
val hi = variables.getValue("Y").integerValue()
|
||||||
val number = lo+256*hi
|
val number = lo+256*hi
|
||||||
canvas?.printText("$prefix${number.toString(16).padStart(4, '0')}", 1, true)
|
canvas?.printText("$prefix${number.toString(16).padStart(4, '0')}", true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_print_w -> {
|
Syscall.SYSASM_c64scr_print_w -> {
|
||||||
val lo = variables.getValue("A").integerValue()
|
val lo = variables.getValue("A").integerValue()
|
||||||
val hi = variables.getValue("Y").integerValue()
|
val hi = variables.getValue("Y").integerValue()
|
||||||
val number = lo+256*hi
|
val number = lo+256*hi
|
||||||
if(number<=32767)
|
if(number<=32767)
|
||||||
canvas?.printText(number.toString(), 1, true)
|
canvas?.printText(number.toString(), true)
|
||||||
else
|
else
|
||||||
canvas?.printText("-${65536-number}", 1, true)
|
canvas?.printText("-${65536-number}", true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64flt_print_f -> {
|
Syscall.SYSASM_c64flt_print_f -> {
|
||||||
val number = variables.getValue("c64flt.print_f.value").numericValue()
|
val number = variables.getValue("c64flt.print_f.value").numericValue()
|
||||||
canvas?.printText(number.toString(), 1, true)
|
canvas?.printText(number.toString(), true)
|
||||||
}
|
}
|
||||||
Syscall.SYSASM_c64scr_setcc -> {
|
Syscall.SYSASM_c64scr_setcc -> {
|
||||||
val x = variables.getValue("c64scr.setcc.column").integerValue()
|
val x = variables.getValue("c64scr.setcc.column").integerValue()
|
||||||
@ -2412,12 +2413,12 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
irqStoredCarry = P_carry
|
irqStoredCarry = P_carry
|
||||||
irqStoredTraceOutputFile = traceOutputFile
|
irqStoredTraceOutputFile = traceOutputFile
|
||||||
|
|
||||||
if(irqStartInstructionPtr>=0)
|
currentInstructionPtr = if(irqStartInstructionPtr>=0)
|
||||||
currentInstructionPtr = irqStartInstructionPtr
|
irqStartInstructionPtr
|
||||||
else {
|
else {
|
||||||
if(program.last().opcode!=Opcode.RETURN)
|
if(program.last().opcode!=Opcode.RETURN)
|
||||||
throw VmExecutionException("last instruction in program should be RETURN for irq handler")
|
throw VmExecutionException("last instruction in program should be RETURN for irq handler")
|
||||||
currentInstructionPtr = program.size-1
|
program.size-1
|
||||||
}
|
}
|
||||||
callstack = MyStack()
|
callstack = MyStack()
|
||||||
evalstack = MyStack()
|
evalstack = MyStack()
|
||||||
|
@ -3,24 +3,32 @@ package prog8tests
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.expressions.LiteralValue
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import kotlin.test.*
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.ReferenceLiteralValue
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertNotEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
private fun sameValueAndType(lv1: LiteralValue, lv2: LiteralValue): Boolean {
|
private fun sameValueAndType(lv1: NumericLiteralValue, lv2: NumericLiteralValue): Boolean {
|
||||||
return lv1.type==lv2.type && lv1==lv2
|
return lv1.type==lv2.type && lv1==lv2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sameValueAndType(rv1: ReferenceLiteralValue, rv2: ReferenceLiteralValue): Boolean {
|
||||||
|
return rv1.type==rv2.type && rv1==rv2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestParserLiteralValue {
|
class TestParserNumericLiteralValue {
|
||||||
|
|
||||||
private val dummyPos = Position("test", 0, 0, 0)
|
private val dummyPos = Position("test", 0, 0, 0)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testIdentity() {
|
fun testIdentity() {
|
||||||
val v = LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)
|
val v = NumericLiteralValue(DataType.UWORD, 12345, dummyPos)
|
||||||
assertEquals(v, v)
|
assertEquals(v, v)
|
||||||
assertFalse(v != v)
|
assertFalse(v != v)
|
||||||
assertTrue(v <= v)
|
assertTrue(v <= v)
|
||||||
@ -28,104 +36,109 @@ class TestParserLiteralValue {
|
|||||||
assertFalse(v < v)
|
assertFalse(v < v)
|
||||||
assertFalse(v > v)
|
assertFalse(v > v)
|
||||||
|
|
||||||
assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)))
|
assertTrue(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12345, dummyPos)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEqualsAndNotEquals() {
|
fun testEqualsAndNotEquals() {
|
||||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 100, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.UWORD, 254, dummyPos), NumericLiteralValue(DataType.UBYTE, 254, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12345, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12345.0, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.FLOAT, 22239.0, dummyPos), NumericLiteralValue(DataType.UWORD, 22239, dummyPos))
|
||||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos))
|
assertEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos))
|
||||||
|
|
||||||
assertTrue(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
|
assertTrue(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 100, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 254, dummyPos), NumericLiteralValue(DataType.UBYTE, 254, dummyPos)))
|
||||||
assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)))
|
assertTrue(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12345, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12345.0, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 100, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 22239.0, dummyPos), NumericLiteralValue(DataType.UWORD, 22239, dummyPos)))
|
||||||
assertTrue(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos)))
|
assertTrue(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos)))
|
||||||
|
|
||||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 101, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 101, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 101.0, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.UWORD, 245, dummyPos), NumericLiteralValue(DataType.UBYTE, 246, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12346, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12346.0, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UBYTE, 9, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UWORD, 9, dummyPos))
|
||||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos))
|
assertNotEquals(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.0, dummyPos))
|
||||||
|
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UBYTE, 101, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.UWORD, 101, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100, dummyPos), NumericLiteralValue(DataType.FLOAT, 101.0, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 245, dummyPos), NumericLiteralValue(DataType.UBYTE, 246, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.UWORD, 12346, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345, dummyPos), NumericLiteralValue(DataType.FLOAT, 12346.0, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UBYTE, 9, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UWORD, 9, dummyPos)))
|
||||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos)))
|
assertFalse(sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.0, 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)
|
@Test
|
||||||
val lvOneR = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
|
fun testEqualsRef() {
|
||||||
val lvTwoR = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
|
assertTrue(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos)))
|
||||||
val lvThreeR = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
|
assertFalse(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "bye", position = dummyPos)))
|
||||||
val lvFour= LiteralValue(DataType.UBYTE, 4, position = dummyPos)
|
|
||||||
val lv1 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
|
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
|
||||||
val lv2 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
|
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
||||||
val lv3 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
|
val lvThree = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
|
||||||
|
val lvOneR = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
|
||||||
|
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
||||||
|
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
|
||||||
|
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
|
||||||
|
val lv1 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
|
||||||
|
val lv2 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
|
||||||
|
val lv3 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
|
||||||
assertEquals(lv1, lv2)
|
assertEquals(lv1, lv2)
|
||||||
assertNotEquals(lv1, lv3)
|
assertNotEquals(lv1, lv3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGreaterThan(){
|
fun testGreaterThan(){
|
||||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 99, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) > NumericLiteralValue(DataType.UBYTE, 99, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) > NumericLiteralValue(DataType.UWORD, 253, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) > NumericLiteralValue(DataType.FLOAT, 99.9, dummyPos))
|
||||||
|
|
||||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) >= NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) >= NumericLiteralValue(DataType.UWORD, 254, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
|
||||||
|
|
||||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) > NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) > NumericLiteralValue(DataType.UWORD, 254, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) > NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
|
||||||
|
|
||||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 101, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) >= NumericLiteralValue(DataType.UBYTE, 101, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) >= NumericLiteralValue(DataType.UWORD, 255, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteralValue(DataType.FLOAT, 100.1, dummyPos))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLessThan() {
|
fun testLessThan() {
|
||||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 101, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) < NumericLiteralValue(DataType.UBYTE, 101, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) < NumericLiteralValue(DataType.UWORD, 255, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) < NumericLiteralValue(DataType.FLOAT, 100.1, dummyPos))
|
||||||
|
|
||||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) <= NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.UWORD, 254, dummyPos) <= NumericLiteralValue(DataType.UWORD, 254, dummyPos))
|
||||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
assertTrue(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
|
||||||
|
|
||||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) < NumericLiteralValue(DataType.UBYTE, 100, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) < NumericLiteralValue(DataType.UWORD, 254, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) < NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos))
|
||||||
|
|
||||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 99, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UBYTE, 100, dummyPos) <= NumericLiteralValue(DataType.UBYTE, 99, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.UWORD, 254, dummyPos) <= NumericLiteralValue(DataType.UWORD, 253, dummyPos))
|
||||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
|
assertFalse(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteralValue(DataType.FLOAT, 99.9, dummyPos))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,39 +17,27 @@ class TestRuntimeValue {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testValueRanges() {
|
fun testValueRanges() {
|
||||||
assertEquals(100, RuntimeValue(DataType.UBYTE, 100).integerValue())
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 0).integerValue())
|
||||||
assertEquals(100, RuntimeValue(DataType.BYTE, 100).integerValue())
|
assertEquals(255, RuntimeValue(DataType.UBYTE, 255).integerValue())
|
||||||
assertEquals(10000, RuntimeValue(DataType.UWORD, 10000).integerValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UBYTE, -1)}
|
||||||
assertEquals(10000, RuntimeValue(DataType.WORD, 10000).integerValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UBYTE, 256)}
|
||||||
assertEquals(100.11, RuntimeValue(DataType.FLOAT, 100.11).numericValue())
|
|
||||||
|
|
||||||
assertEquals(200, RuntimeValue(DataType.UBYTE, 200).integerValue())
|
assertEquals(0, RuntimeValue(DataType.BYTE, 0).integerValue())
|
||||||
assertEquals(-56, RuntimeValue(DataType.BYTE, 200).integerValue())
|
assertEquals(-128, RuntimeValue(DataType.BYTE, -128).integerValue())
|
||||||
assertEquals(50000, RuntimeValue(DataType.UWORD, 50000).integerValue())
|
assertEquals(127, RuntimeValue(DataType.BYTE, 127).integerValue())
|
||||||
assertEquals(-15536, RuntimeValue(DataType.WORD, 50000).integerValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.BYTE, -129)}
|
||||||
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.BYTE, 128)}
|
||||||
|
|
||||||
assertEquals(44, RuntimeValue(DataType.UBYTE, 300).integerValue())
|
assertEquals(0, RuntimeValue(DataType.UWORD, 0).integerValue())
|
||||||
assertEquals(44, RuntimeValue(DataType.BYTE, 300).integerValue())
|
assertEquals(65535, RuntimeValue(DataType.UWORD, 65535).integerValue())
|
||||||
assertEquals(144, RuntimeValue(DataType.UBYTE, 400).integerValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UWORD, -1)}
|
||||||
assertEquals(-112, RuntimeValue(DataType.BYTE, 400).integerValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UWORD, 65536)}
|
||||||
assertEquals(34463, RuntimeValue(DataType.UWORD, 99999).integerValue())
|
|
||||||
assertEquals(-31073, RuntimeValue(DataType.WORD, 99999).integerValue())
|
|
||||||
|
|
||||||
assertEquals(156, RuntimeValue(DataType.UBYTE, -100).integerValue())
|
assertEquals(0, RuntimeValue(DataType.WORD, 0).integerValue())
|
||||||
assertEquals(-100, RuntimeValue(DataType.BYTE, -100).integerValue())
|
assertEquals(-32768, RuntimeValue(DataType.WORD, -32768).integerValue())
|
||||||
assertEquals(55536, RuntimeValue(DataType.UWORD, -10000).integerValue())
|
assertEquals(32767, RuntimeValue(DataType.WORD, 32767).integerValue())
|
||||||
assertEquals(-10000, RuntimeValue(DataType.WORD, -10000).integerValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.WORD, -32769)}
|
||||||
assertEquals(-100.11, RuntimeValue(DataType.FLOAT, -100.11).numericValue())
|
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.WORD, 32768)}
|
||||||
|
|
||||||
assertEquals(56, RuntimeValue(DataType.UBYTE, -200).integerValue())
|
|
||||||
assertEquals(56, RuntimeValue(DataType.BYTE, -200).integerValue())
|
|
||||||
assertEquals(45536, RuntimeValue(DataType.UWORD, -20000).integerValue())
|
|
||||||
assertEquals(-20000, RuntimeValue(DataType.WORD, -20000).integerValue())
|
|
||||||
|
|
||||||
assertEquals(212, RuntimeValue(DataType.UBYTE, -300).integerValue())
|
|
||||||
assertEquals(-44, RuntimeValue(DataType.BYTE, -300).integerValue())
|
|
||||||
assertEquals(42184, RuntimeValue(DataType.UWORD, -88888).integerValue())
|
|
||||||
assertEquals(-23352, RuntimeValue(DataType.WORD, -88888).integerValue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -60,10 +48,6 @@ class TestRuntimeValue {
|
|||||||
assertFalse(RuntimeValue(DataType.WORD, 0).asBoolean)
|
assertFalse(RuntimeValue(DataType.WORD, 0).asBoolean)
|
||||||
assertFalse(RuntimeValue(DataType.UWORD, 0).asBoolean)
|
assertFalse(RuntimeValue(DataType.UWORD, 0).asBoolean)
|
||||||
assertFalse(RuntimeValue(DataType.FLOAT, 0.0).asBoolean)
|
assertFalse(RuntimeValue(DataType.FLOAT, 0.0).asBoolean)
|
||||||
assertFalse(RuntimeValue(DataType.BYTE, 256).asBoolean)
|
|
||||||
assertFalse(RuntimeValue(DataType.UBYTE, 256).asBoolean)
|
|
||||||
assertFalse(RuntimeValue(DataType.WORD, 65536).asBoolean)
|
|
||||||
assertFalse(RuntimeValue(DataType.UWORD, 65536).asBoolean)
|
|
||||||
|
|
||||||
assertTrue(RuntimeValue(DataType.BYTE, 42).asBoolean)
|
assertTrue(RuntimeValue(DataType.BYTE, 42).asBoolean)
|
||||||
assertTrue(RuntimeValue(DataType.UBYTE, 42).asBoolean)
|
assertTrue(RuntimeValue(DataType.UBYTE, 42).asBoolean)
|
||||||
@ -71,9 +55,7 @@ class TestRuntimeValue {
|
|||||||
assertTrue(RuntimeValue(DataType.UWORD, 42).asBoolean)
|
assertTrue(RuntimeValue(DataType.UWORD, 42).asBoolean)
|
||||||
assertTrue(RuntimeValue(DataType.FLOAT, 42.0).asBoolean)
|
assertTrue(RuntimeValue(DataType.FLOAT, 42.0).asBoolean)
|
||||||
assertTrue(RuntimeValue(DataType.BYTE, -42).asBoolean)
|
assertTrue(RuntimeValue(DataType.BYTE, -42).asBoolean)
|
||||||
assertTrue(RuntimeValue(DataType.UBYTE, -42).asBoolean)
|
|
||||||
assertTrue(RuntimeValue(DataType.WORD, -42).asBoolean)
|
assertTrue(RuntimeValue(DataType.WORD, -42).asBoolean)
|
||||||
assertTrue(RuntimeValue(DataType.UWORD, -42).asBoolean)
|
|
||||||
assertTrue(RuntimeValue(DataType.FLOAT, -42.0).asBoolean)
|
assertTrue(RuntimeValue(DataType.FLOAT, -42.0).asBoolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,4 +227,155 @@ class TestRuntimeValue {
|
|||||||
}
|
}
|
||||||
val result = RuntimeValue(DataType.FLOAT, 233.333).add(RuntimeValue(DataType.FLOAT, 1.234))
|
val result = RuntimeValue(DataType.FLOAT, 233.333).add(RuntimeValue(DataType.FLOAT, 1.234))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun arithmetictestUbyte() {
|
||||||
|
assertEquals(255, RuntimeValue(DataType.UBYTE, 200).add(RuntimeValue(DataType.UBYTE, 55)).integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 200).add(RuntimeValue(DataType.UBYTE, 56)).integerValue())
|
||||||
|
assertEquals(1, RuntimeValue(DataType.UBYTE, 200).add(RuntimeValue(DataType.UBYTE, 57)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(1, RuntimeValue(DataType.UBYTE, 2).sub(RuntimeValue(DataType.UBYTE, 1)).integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 2).sub(RuntimeValue(DataType.UBYTE, 2)).integerValue())
|
||||||
|
assertEquals(255, RuntimeValue(DataType.UBYTE, 2).sub(RuntimeValue(DataType.UBYTE, 3)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(255, RuntimeValue(DataType.UBYTE, 254).inc().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 255).inc().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 1).dec().integerValue())
|
||||||
|
assertEquals(255, RuntimeValue(DataType.UBYTE, 0).dec().integerValue())
|
||||||
|
|
||||||
|
assertEquals(255, RuntimeValue(DataType.UBYTE, 0).inv().integerValue())
|
||||||
|
assertEquals(0b00110011, RuntimeValue(DataType.UBYTE, 0b11001100).inv().integerValue())
|
||||||
|
// assertEquals(0, RuntimeValue(DataType.UBYTE, 0).neg().integerValue())
|
||||||
|
// assertEquals(0, RuntimeValue(DataType.UBYTE, 0).neg().integerValue())
|
||||||
|
assertEquals(1, RuntimeValue(DataType.UBYTE, 0).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 1).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 111).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UBYTE, 255).not().integerValue())
|
||||||
|
|
||||||
|
assertEquals(200, RuntimeValue(DataType.UBYTE, 20).mul(RuntimeValue(DataType.UBYTE, 10)).integerValue())
|
||||||
|
assertEquals(144, RuntimeValue(DataType.UBYTE, 20).mul(RuntimeValue(DataType.UBYTE, 20)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(25, RuntimeValue(DataType.UBYTE, 5).pow(RuntimeValue(DataType.UBYTE, 2)).integerValue())
|
||||||
|
assertEquals(125, RuntimeValue(DataType.UBYTE, 5).pow(RuntimeValue(DataType.UBYTE, 3)).integerValue())
|
||||||
|
assertEquals(113, RuntimeValue(DataType.UBYTE, 5).pow(RuntimeValue(DataType.UBYTE, 4)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(100, RuntimeValue(DataType.UBYTE, 50).shl().integerValue())
|
||||||
|
assertEquals(200, RuntimeValue(DataType.UBYTE, 100).shl().integerValue())
|
||||||
|
assertEquals(144, RuntimeValue(DataType.UBYTE, 200).shl().integerValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun arithmetictestUWord() {
|
||||||
|
assertEquals(65535, RuntimeValue(DataType.UWORD, 60000).add(RuntimeValue(DataType.UWORD, 5535)).integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 60000).add(RuntimeValue(DataType.UWORD, 5536)).integerValue())
|
||||||
|
assertEquals(1, RuntimeValue(DataType.UWORD, 60000).add(RuntimeValue(DataType.UWORD, 5537)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(1, RuntimeValue(DataType.UWORD, 2).sub(RuntimeValue(DataType.UWORD, 1)).integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 2).sub(RuntimeValue(DataType.UWORD, 2)).integerValue())
|
||||||
|
assertEquals(65535, RuntimeValue(DataType.UWORD, 2).sub(RuntimeValue(DataType.UWORD, 3)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(65535, RuntimeValue(DataType.UWORD, 65534).inc().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 65535).inc().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 1).dec().integerValue())
|
||||||
|
assertEquals(65535, RuntimeValue(DataType.UWORD, 0).dec().integerValue())
|
||||||
|
|
||||||
|
assertEquals(65535, RuntimeValue(DataType.UWORD, 0).inv().integerValue())
|
||||||
|
assertEquals(0b0011001101010101, RuntimeValue(DataType.UWORD, 0b1100110010101010).inv().integerValue())
|
||||||
|
// assertEquals(0, RuntimeValue(DataType.UWORD, 0).neg().integerValue())
|
||||||
|
// assertEquals(0, RuntimeValue(DataType.UWORD, 0).neg().integerValue())
|
||||||
|
assertEquals(1, RuntimeValue(DataType.UWORD, 0).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 1).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 11111).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.UWORD, 65535).not().integerValue())
|
||||||
|
|
||||||
|
assertEquals(2000, RuntimeValue(DataType.UWORD, 200).mul(RuntimeValue(DataType.UWORD, 10)).integerValue())
|
||||||
|
assertEquals(40000, RuntimeValue(DataType.UWORD, 200).mul(RuntimeValue(DataType.UWORD, 200)).integerValue())
|
||||||
|
assertEquals(14464, RuntimeValue(DataType.UWORD, 200).mul(RuntimeValue(DataType.UWORD, 400)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(15625, RuntimeValue(DataType.UWORD, 5).pow(RuntimeValue(DataType.UWORD, 6)).integerValue())
|
||||||
|
assertEquals(12589, RuntimeValue(DataType.UWORD, 5).pow(RuntimeValue(DataType.UWORD, 7)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(10000, RuntimeValue(DataType.UWORD, 5000).shl().integerValue())
|
||||||
|
assertEquals(60000, RuntimeValue(DataType.UWORD, 30000).shl().integerValue())
|
||||||
|
assertEquals(14464, RuntimeValue(DataType.UWORD, 40000).shl().integerValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun arithmetictestByte() {
|
||||||
|
assertEquals(127, RuntimeValue(DataType.BYTE, 100).add(RuntimeValue(DataType.BYTE, 27)).integerValue())
|
||||||
|
assertEquals(-128, RuntimeValue(DataType.BYTE, 100).add(RuntimeValue(DataType.BYTE, 28)).integerValue())
|
||||||
|
assertEquals(-127, RuntimeValue(DataType.BYTE, 100).add(RuntimeValue(DataType.BYTE, 29)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(1, RuntimeValue(DataType.BYTE, 2).sub(RuntimeValue(DataType.BYTE, 1)).integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.BYTE, 2).sub(RuntimeValue(DataType.BYTE, 2)).integerValue())
|
||||||
|
assertEquals(-1, RuntimeValue(DataType.BYTE, 2).sub(RuntimeValue(DataType.BYTE, 3)).integerValue())
|
||||||
|
assertEquals(-128, RuntimeValue(DataType.BYTE, -100).sub(RuntimeValue(DataType.BYTE, 28)).integerValue())
|
||||||
|
assertEquals(127, RuntimeValue(DataType.BYTE, -100).sub(RuntimeValue(DataType.BYTE, 29)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(127, RuntimeValue(DataType.BYTE, 126).inc().integerValue())
|
||||||
|
assertEquals(-128, RuntimeValue(DataType.BYTE, 127).inc().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.BYTE, 1).dec().integerValue())
|
||||||
|
assertEquals(-1, RuntimeValue(DataType.BYTE, 0).dec().integerValue())
|
||||||
|
assertEquals(-128, RuntimeValue(DataType.BYTE, -127).dec().integerValue())
|
||||||
|
assertEquals(127, RuntimeValue(DataType.BYTE, -128).dec().integerValue())
|
||||||
|
|
||||||
|
assertEquals(-1, RuntimeValue(DataType.BYTE, 0).inv().integerValue())
|
||||||
|
assertEquals(-103, RuntimeValue(DataType.BYTE, 0b01100110).inv().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.BYTE, 0).neg().integerValue())
|
||||||
|
assertEquals(-2, RuntimeValue(DataType.BYTE, 2).neg().integerValue())
|
||||||
|
assertEquals(1, RuntimeValue(DataType.BYTE, 0).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.BYTE, 1).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.BYTE, 111).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.BYTE, -33).not().integerValue())
|
||||||
|
|
||||||
|
assertEquals(100, RuntimeValue(DataType.BYTE, 10).mul(RuntimeValue(DataType.BYTE, 10)).integerValue())
|
||||||
|
assertEquals(-56, RuntimeValue(DataType.BYTE, 20).mul(RuntimeValue(DataType.BYTE, 10)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(25, RuntimeValue(DataType.BYTE, 5).pow(RuntimeValue(DataType.BYTE, 2)).integerValue())
|
||||||
|
assertEquals(125, RuntimeValue(DataType.BYTE, 5).pow(RuntimeValue(DataType.BYTE, 3)).integerValue())
|
||||||
|
assertEquals(113, RuntimeValue(DataType.BYTE, 5).pow(RuntimeValue(DataType.BYTE, 4)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(100, RuntimeValue(DataType.BYTE, 50).shl().integerValue())
|
||||||
|
assertEquals(-56, RuntimeValue(DataType.BYTE, 100).shl().integerValue())
|
||||||
|
assertEquals(-2, RuntimeValue(DataType.BYTE, -1).shl().integerValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun arithmetictestWorrd() {
|
||||||
|
assertEquals(32767, RuntimeValue(DataType.WORD, 32700).add(RuntimeValue(DataType.WORD, 67)).integerValue())
|
||||||
|
assertEquals(-32768, RuntimeValue(DataType.WORD, 32700).add(RuntimeValue(DataType.WORD, 68)).integerValue())
|
||||||
|
assertEquals(-32767, RuntimeValue(DataType.WORD, 32700).add(RuntimeValue(DataType.WORD, 69)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(1, RuntimeValue(DataType.WORD, 2).sub(RuntimeValue(DataType.WORD, 1)).integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.WORD, 2).sub(RuntimeValue(DataType.WORD, 2)).integerValue())
|
||||||
|
assertEquals(-1, RuntimeValue(DataType.WORD, 2).sub(RuntimeValue(DataType.WORD, 3)).integerValue())
|
||||||
|
assertEquals(-32768, RuntimeValue(DataType.WORD, -32700).sub(RuntimeValue(DataType.WORD, 68)).integerValue())
|
||||||
|
assertEquals(32767, RuntimeValue(DataType.WORD, -32700).sub(RuntimeValue(DataType.WORD, 69)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(32767, RuntimeValue(DataType.WORD, 32766).inc().integerValue())
|
||||||
|
assertEquals(-32768, RuntimeValue(DataType.WORD, 32767).inc().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.WORD, 1).dec().integerValue())
|
||||||
|
assertEquals(-1, RuntimeValue(DataType.WORD, 0).dec().integerValue())
|
||||||
|
assertEquals(-32768, RuntimeValue(DataType.WORD, -32767).dec().integerValue())
|
||||||
|
assertEquals(32767, RuntimeValue(DataType.WORD, -32768).dec().integerValue())
|
||||||
|
|
||||||
|
assertEquals(-1, RuntimeValue(DataType.WORD, 0).inv().integerValue())
|
||||||
|
assertEquals(-103, RuntimeValue(DataType.WORD, 0b01100110).inv().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.WORD, 0).neg().integerValue())
|
||||||
|
assertEquals(-2, RuntimeValue(DataType.WORD, 2).neg().integerValue())
|
||||||
|
assertEquals(1, RuntimeValue(DataType.WORD, 0).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.WORD, 1).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.WORD, 111).not().integerValue())
|
||||||
|
assertEquals(0, RuntimeValue(DataType.WORD, -33).not().integerValue())
|
||||||
|
|
||||||
|
assertEquals(10000, RuntimeValue(DataType.WORD, 100).mul(RuntimeValue(DataType.WORD, 100)).integerValue())
|
||||||
|
assertEquals(-25536, RuntimeValue(DataType.WORD, 200).mul(RuntimeValue(DataType.WORD, 200)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(15625, RuntimeValue(DataType.WORD, 5).pow(RuntimeValue(DataType.WORD, 6)).integerValue())
|
||||||
|
assertEquals(-6487, RuntimeValue(DataType.WORD, 9).pow(RuntimeValue(DataType.WORD, 5)).integerValue())
|
||||||
|
|
||||||
|
assertEquals(18000, RuntimeValue(DataType.WORD, 9000).shl().integerValue())
|
||||||
|
assertEquals(-25536, RuntimeValue(DataType.WORD, 20000).shl().integerValue())
|
||||||
|
assertEquals(-2, RuntimeValue(DataType.WORD, -1).shl().integerValue())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ import prog8.ast.base.ByteDatatypes
|
|||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.IterableDatatypes
|
import prog8.ast.base.IterableDatatypes
|
||||||
import prog8.ast.base.WordDatatypes
|
import prog8.ast.base.WordDatatypes
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.intermediate.Instruction
|
import prog8.compiler.intermediate.Instruction
|
||||||
import prog8.compiler.intermediate.Opcode
|
import prog8.compiler.intermediate.Opcode
|
||||||
|
import prog8.vm.RuntimeValue
|
||||||
import prog8.vm.stackvm.*
|
import prog8.vm.stackvm.*
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -413,10 +413,10 @@ class TestStackVmOpcodes {
|
|||||||
testUnaryOperator(RuntimeValue(DataType.UBYTE, 123), Opcode.INV_BYTE, RuntimeValue(DataType.UBYTE, 0x84))
|
testUnaryOperator(RuntimeValue(DataType.UBYTE, 123), Opcode.INV_BYTE, RuntimeValue(DataType.UBYTE, 0x84))
|
||||||
testUnaryOperator(RuntimeValue(DataType.UWORD, 4044), Opcode.INV_WORD, RuntimeValue(DataType.UWORD, 0xf033))
|
testUnaryOperator(RuntimeValue(DataType.UWORD, 4044), Opcode.INV_WORD, RuntimeValue(DataType.UWORD, 0xf033))
|
||||||
assertFailsWith<VmExecutionException> {
|
assertFailsWith<VmExecutionException> {
|
||||||
testUnaryOperator(RuntimeValue(DataType.BYTE, 123), Opcode.INV_BYTE, RuntimeValue(DataType.BYTE, 0x84))
|
testUnaryOperator(RuntimeValue(DataType.BYTE, 123), Opcode.INV_BYTE, RuntimeValue(DataType.BYTE, -124))
|
||||||
}
|
}
|
||||||
assertFailsWith<VmExecutionException> {
|
assertFailsWith<VmExecutionException> {
|
||||||
testUnaryOperator(RuntimeValue(DataType.WORD, 4044), Opcode.INV_WORD, RuntimeValue(DataType.WORD, 0xf033))
|
testUnaryOperator(RuntimeValue(DataType.WORD, 4044), Opcode.INV_WORD, RuntimeValue(DataType.WORD, -4043))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,15 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.LiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.vm.RuntimeValue
|
import prog8.ast.expressions.ReferenceLiteralValue
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.c64.*
|
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.Mflpt5
|
||||||
|
import prog8.compiler.target.c64.Petscii
|
||||||
|
import prog8.vm.RuntimeValue
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -336,8 +341,8 @@ class TestPetscii {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLiteralValueComparisons() {
|
fun testLiteralValueComparisons() {
|
||||||
val ten = LiteralValue(DataType.UWORD, wordvalue = 10, position = Position("", 0, 0, 0))
|
val ten = NumericLiteralValue(DataType.UWORD, 10, Position("", 0, 0, 0))
|
||||||
val nine = LiteralValue(DataType.UBYTE, bytevalue = 9, position = Position("", 0, 0, 0))
|
val nine = NumericLiteralValue(DataType.UBYTE, 9, Position("", 0, 0, 0))
|
||||||
assertEquals(ten, ten)
|
assertEquals(ten, ten)
|
||||||
assertNotEquals(ten, nine)
|
assertNotEquals(ten, nine)
|
||||||
assertFalse(ten != ten)
|
assertFalse(ten != ten)
|
||||||
@ -353,17 +358,11 @@ class TestPetscii {
|
|||||||
assertTrue(ten <= ten)
|
assertTrue(ten <= ten)
|
||||||
assertFalse(ten < ten)
|
assertFalse(ten < ten)
|
||||||
|
|
||||||
val abc = LiteralValue(DataType.STR, strvalue = "abc", position = Position("", 0, 0, 0))
|
val abc = ReferenceLiteralValue(DataType.STR, str = "abc", position = Position("", 0, 0, 0))
|
||||||
val abd = LiteralValue(DataType.STR, strvalue = "abd", position = Position("", 0, 0, 0))
|
val abd = ReferenceLiteralValue(DataType.STR, str = "abd", position = Position("", 0, 0, 0))
|
||||||
assertEquals(abc, abc)
|
assertEquals(abc, abc)
|
||||||
assertTrue(abc!=abd)
|
assertTrue(abc!=abd)
|
||||||
assertFalse(abc!=abc)
|
assertFalse(abc!=abc)
|
||||||
assertTrue(abc < abd)
|
|
||||||
assertTrue(abc <= abd)
|
|
||||||
assertFalse(abd <= abc)
|
|
||||||
assertTrue(abd >= abc)
|
|
||||||
assertTrue(abd > abc)
|
|
||||||
assertFalse(abc > abd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.7 (py3)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.7 (py3)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
Writing and building a program
|
Writing and building a program
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
.. _building_compiler:
|
||||||
|
|
||||||
First, getting a working compiler
|
First, getting a working compiler
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
@ -11,20 +13,38 @@ Then you can choose a few ways to get a compiler:
|
|||||||
|
|
||||||
**Download a precompiled version from github:**
|
**Download a precompiled version from github:**
|
||||||
|
|
||||||
#. download a recent "prog8compiler.jar" from `the releases on Github <https://github.com/irmen/prog8/releases>`_
|
#. download a recent "fat-jar" (called something like "prog8compiler-all.jar") from `the releases on Github <https://github.com/irmen/prog8/releases>`_
|
||||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
#. run the compiler with "java -jar prog8compiler-all.jar" to see how you can use it.
|
||||||
|
|
||||||
**Using the shell scripts:**
|
**using the Gradle build system to make it yourself:**
|
||||||
|
|
||||||
#. run the "create_compiler_jar.sh" shell script and have a little patience while everything is built
|
The Gradle build system is used to build the compiler.
|
||||||
#. it will output "prog8compiler.jar" file which contains everything.
|
The most interesting gradle commands to run are probably:
|
||||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
|
||||||
|
|
||||||
**using the Gradle build system directly:**
|
``./gradlew check``
|
||||||
|
Builds the compiler code and runs all available checks and unit-tests.
|
||||||
|
``./gradlew installDist``
|
||||||
|
Builds the compiler and installs it with scripts to run it, in the directory
|
||||||
|
``./compiler/build/install/p8compile``
|
||||||
|
``./gradlew installShadowDist``
|
||||||
|
Creates a 'fat-jar' that contains the compiler and all dependencies, in a single
|
||||||
|
executable .jar file, and includes few start scripts to run it.
|
||||||
|
The output can be found in ``.compiler/build/install/compiler-shadow/``
|
||||||
|
``./gradlew shadowDistZip``
|
||||||
|
Creates a zipfile with the above in it, for easy distribution.
|
||||||
|
This file can be found in ``./compiler/build/distributions/``
|
||||||
|
|
||||||
#. run the command "./gradlew installDist" and have a little patience while everything is built
|
For normal use, the ``installDist`` target should suffice and ater succesful completion
|
||||||
#. it will create the commands and required libraries in the "./compiler/build/install/p8compile/" directory
|
of that build task, you can start the compiler with:
|
||||||
#. run the compiler with the "./compiler/build/install/p8compile/bin/p8compile" command to see how you can use it.
|
|
||||||
|
``./compiler/build/install/p8compile/bin/p8compile <options> <sourcefile>``
|
||||||
|
|
||||||
|
(You should probably make an alias...)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Development and testing is done on Linux, but the compiler should run on most
|
||||||
|
operating systems. If you do have trouble building or running
|
||||||
|
the compiler on another operating system, please let me know!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -54,14 +74,10 @@ The compiler will link everything together into one output program at the end.
|
|||||||
If you start the compiler without arguments, it will print a short usage text.
|
If you start the compiler without arguments, it will print a short usage text.
|
||||||
For normal use the compiler is invoked with the command:
|
For normal use the compiler is invoked with the command:
|
||||||
|
|
||||||
``$ java -jar prog8compiler.jar sourcefile.p8`` if you're using the packaged release version, or
|
``$ java -jar prog8compiler.jar sourcefile.p8``
|
||||||
|
|
||||||
``$ ./p8compile.sh sourcefile.p8`` if you're running an unpackaged development version.
|
Other options are also available, see the introduction page about how
|
||||||
|
to build and run the compiler.
|
||||||
If you tell it to save the intermediate code for the prog8 virtual machine, you can
|
|
||||||
actually run this code with the command:
|
|
||||||
|
|
||||||
``$ ./p8vm.sh sourcefile.vm.txt``
|
|
||||||
|
|
||||||
|
|
||||||
By default, assembly code is generated and written to ``sourcefile.asm``.
|
By default, assembly code is generated and written to ``sourcefile.asm``.
|
||||||
@ -131,3 +147,24 @@ or::
|
|||||||
|
|
||||||
$ ./p8compile.sh -emu examples/rasterbars.p8
|
$ ./p8compile.sh -emu examples/rasterbars.p8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Virtual Machine
|
||||||
|
---------------
|
||||||
|
|
||||||
|
You may have noticed the ``-avm`` and ``-vm`` command line options for the compiler:
|
||||||
|
|
||||||
|
-avm
|
||||||
|
Launches the "AST virtual machine" that directly executes the parsed program.
|
||||||
|
No compilation steps will be performed.
|
||||||
|
Allows for very fast testing and debugging before actually compiling programs
|
||||||
|
to machine code.
|
||||||
|
It simulates a bare minimum of features from the target platform, so most stuff
|
||||||
|
that calls ROM routines or writes into hardware registers won't work. But basic
|
||||||
|
system routines are emulated.
|
||||||
|
|
||||||
|
-vm <vm bytecode file>
|
||||||
|
Launches the "intermediate code VM"
|
||||||
|
it interprets the intermediate code that the compiler can write when using the ``-writevm``
|
||||||
|
option. This is the code that will be fed to the assembly code generator,
|
||||||
|
so you'll skip that last step.
|
||||||
|
@ -37,8 +37,8 @@ This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/license
|
|||||||
:alt: Fully playable tetris clone
|
:alt: Fully playable tetris clone
|
||||||
|
|
||||||
|
|
||||||
Code example
|
Code examples
|
||||||
------------
|
-------------
|
||||||
|
|
||||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
@ -89,13 +89,47 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when compiled an ran on a C-64 you'll get:
|
when compiled an ran on a C-64 you get this:
|
||||||
|
|
||||||
.. image:: _static/primes_example.png
|
.. image:: _static/primes_example.png
|
||||||
:align: center
|
:align: center
|
||||||
:alt: result when run on C-64
|
:alt: result when run on C-64
|
||||||
|
|
||||||
|
|
||||||
|
The following programs shows a use of the high level ``struct`` type::
|
||||||
|
|
||||||
|
%import c64utils
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
~ main {
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
ubyte red
|
||||||
|
ubyte green
|
||||||
|
ubyte blue
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
|
||||||
|
Color purple = {255, 0, 255}
|
||||||
|
Color other
|
||||||
|
other = purple
|
||||||
|
other.red /= 2
|
||||||
|
other.green = 10 + other.green / 2
|
||||||
|
other.blue = 99
|
||||||
|
|
||||||
|
c64scr.print_ub(other.red)
|
||||||
|
c64.CHROUT(',')
|
||||||
|
c64scr.print_ub(other.green)
|
||||||
|
c64.CHROUT(',')
|
||||||
|
c64scr.print_ub(other.blue)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when compiled and ran, it prints ``127,10,99`` on the screen.
|
||||||
|
|
||||||
|
|
||||||
Design principles and features
|
Design principles and features
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@ -143,37 +177,14 @@ Finally: a **C-64 emulator** (or a real C-64 ofcourse) to run the programs on. T
|
|||||||
of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
**Building the compiler itself:**
|
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
||||||
|
|
||||||
(re)building the compiler itself requires a recent Kotlin SDK.
|
(re)building the compiler itself requires a recent Kotlin SDK.
|
||||||
The compiler is developed using the `IntelliJ IDEA <https://www.jetbrains.com/idea/>`_
|
The compiler is developed using the `IntelliJ IDEA <https://www.jetbrains.com/idea/>`_
|
||||||
IDE from Jetbrains, with the Kotlin plugin (free community edition of this IDE is available).
|
IDE from Jetbrains, with the Kotlin plugin (free community edition of this IDE is available).
|
||||||
But a bare Kotlin SDK installation should work just as well.
|
But a bare Kotlin SDK installation should work just as well.
|
||||||
|
|
||||||
A shell script (``create_compiler_jar.sh``) is provided to build and package the compiler from the command line.
|
Instructions on how to obtain a working compiler are in :ref:`building_compiler`.
|
||||||
If you have the 'fat-jar' you can run it with ``java -jar prog8compiler.jar``.
|
|
||||||
You can also use the Gradle build system to build the compiler (it will take care of
|
|
||||||
downloading all required libraries for you) by typing ``gradle build`` for instance.
|
|
||||||
The output of this gradle build will appear in the "./compiler/build/install/p8compile/" directory.
|
|
||||||
The most interesting gradle commands to run are probably:
|
|
||||||
|
|
||||||
``./gradlew check``
|
|
||||||
Builds the compiler code and runs all available checks and unit-tests.
|
|
||||||
``./gradlew installShadowDist``
|
|
||||||
Creates a 'fat-jar' that contains the compiler and all dependencies,
|
|
||||||
and a few start scripts to run it.
|
|
||||||
The output can be found in ``.compiler/build/install/compiler-shadow/``
|
|
||||||
and you can launch the compiler with the script
|
|
||||||
``./compiler/build/install/compiler-shadow/bin/p8compile``.
|
|
||||||
``./gradlew shadowDistZip``
|
|
||||||
Creates a zipfile with the above in it, for easy distribution.
|
|
||||||
This file can be found in ``./compiler/build/distributions/``
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Development and testing is done on Linux, but the compiler should run on most
|
|
||||||
operating systems. If you do have trouble building or running
|
|
||||||
the compiler on another operating system, please let me know!
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@ -237,6 +237,7 @@ Arrays
|
|||||||
^^^^^^
|
^^^^^^
|
||||||
Array types are also supported. They can be made of bytes, words or floats::
|
Array types are also supported. They can be made of bytes, words or floats::
|
||||||
|
|
||||||
|
byte[10] array ; array of 10 bytes, initially set to 0
|
||||||
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
|
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
|
||||||
byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
|
byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
|
||||||
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
||||||
@ -266,6 +267,13 @@ Strings in your source code files will be encoded (translated from ASCII/UTF-8)
|
|||||||
PETSCII is the default choice. If you need screencodes (also called 'poke' codes) instead,
|
PETSCII is the default choice. If you need screencodes (also called 'poke' codes) instead,
|
||||||
you have to use the ``str_s`` variants of the string type identifier.
|
you have to use the ``str_s`` variants of the string type identifier.
|
||||||
|
|
||||||
|
You can concatenate two string literals using '+' (not very useful though) or repeat
|
||||||
|
a string literal a given number of times using '*'::
|
||||||
|
|
||||||
|
str string1 = "first part" + "second part"
|
||||||
|
str string2 = "hello!" * 10
|
||||||
|
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
It's probably best that you don't change strings after they're created.
|
It's probably best that you don't change strings after they're created.
|
||||||
This is because if your program exits and is restarted (without loading it again),
|
This is because if your program exits and is restarted (without loading it again),
|
||||||
@ -273,6 +281,39 @@ you have to use the ``str_s`` variants of the string type identifier.
|
|||||||
The same is true for arrays by the way.
|
The same is true for arrays by the way.
|
||||||
|
|
||||||
|
|
||||||
|
Structs
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
A struct is a group of one or more other variables.
|
||||||
|
This allows you to reuse the definition and manipulate it as a whole.
|
||||||
|
Individual variables in the struct are accessed as you would expect, just
|
||||||
|
use a scoped name to refer to them: ``structvariable.membername``.
|
||||||
|
|
||||||
|
Structs are a bit limited in Prog8: you can only use numerical variables
|
||||||
|
as member of a struct, so strings and arrays and other structs can not be part of a struct.
|
||||||
|
Also, it is not possible to use a struct itself inside an array.
|
||||||
|
Structs are mainly syntactic sugar for repeated groups of vardecls
|
||||||
|
and assignments that belong together. However,
|
||||||
|
*they are layed out in sequence in memory as the members are defined*
|
||||||
|
which may be usefulif you want to pass pointers around.
|
||||||
|
|
||||||
|
To create a variable of a struct type you need to define the struct itself,
|
||||||
|
and then create a variable with it::
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
ubyte red
|
||||||
|
ubyte green
|
||||||
|
ubyte blue
|
||||||
|
}
|
||||||
|
|
||||||
|
Color rgb = {255,122,0} ; note the curly braces here instead of brackets
|
||||||
|
Color another ; the init value is optional, like arrays
|
||||||
|
|
||||||
|
another = rgb ; assign all of the values of rgb to another
|
||||||
|
another.blue = 255 ; set a single member
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Special types: const and memory-mapped
|
Special types: const and memory-mapped
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -321,10 +362,17 @@ Initial values across multiple runs of the program
|
|||||||
When declaring values with an initial value, this value will be set into the variable each time
|
When declaring values with an initial value, this value will be set into the variable each time
|
||||||
the program reaches the declaration again. This can be in loops, multiple subroutine calls,
|
the program reaches the declaration again. This can be in loops, multiple subroutine calls,
|
||||||
or even multiple invocations of the entire program. If you omit an initial value, it will
|
or even multiple invocations of the entire program. If you omit an initial value, it will
|
||||||
be set to zero only for the first run of the program. A second run will utilize the last value
|
be set to zero *but only for the first run of the program*. A second run will utilize the last value
|
||||||
where it left off (but your code will be a bit smaller because no initialization instructions
|
where it left off (but your code will be a bit smaller because no initialization instructions
|
||||||
are generated)
|
are generated)
|
||||||
|
|
||||||
|
This only works for simple types, *and not for string variables and arrays*.
|
||||||
|
It is assumed these are left unchanged by the program; they are not re-initialized on
|
||||||
|
a second run.
|
||||||
|
If you do modify them in-place, you should take care yourself that they work as
|
||||||
|
expected when the program is restarted.
|
||||||
|
(This is an optimization choice to avoid having to store two copies of every string and array)
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
variables that get allocated in zero-page will *not* have a zero starting value when you omit
|
variables that get allocated in zero-page will *not* have a zero starting value when you omit
|
||||||
the variable's initialization. They'll be whatever the last value in that zero page
|
the variable's initialization. They'll be whatever the last value in that zero page
|
||||||
@ -334,12 +382,6 @@ are generated)
|
|||||||
this behavior may change in a future version so that subsequent runs always
|
this behavior may change in a future version so that subsequent runs always
|
||||||
use the same initial values
|
use the same initial values
|
||||||
|
|
||||||
This only works for simple types, *and not for string variables and arrays*.
|
|
||||||
It is assumed these are left unchanged by the program.
|
|
||||||
If you do modify them in-place, you should take care yourself that they work as
|
|
||||||
expected when the program is restarted.
|
|
||||||
(This is an optimization choice to avoid having to store two copies of every string and array)
|
|
||||||
|
|
||||||
|
|
||||||
Loops
|
Loops
|
||||||
-----
|
-----
|
||||||
@ -416,15 +458,26 @@ So ``if_cc goto target`` will directly translate into the single CPU instruction
|
|||||||
Maybe in the future this will be a separate nested scope, but for now, that is
|
Maybe in the future this will be a separate nested scope, but for now, that is
|
||||||
only possible when defining a subroutine.
|
only possible when defining a subroutine.
|
||||||
|
|
||||||
when statement (jumptable)
|
when statement ('jump table')
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. attention::
|
Instead of writing a bunch of sequential if-elseif statements, it is more readable to
|
||||||
TODO: docs for this this must still be written.
|
use a ``when`` statement. (It will also result in greatly improved assembly code generation)
|
||||||
TODO: the code generator for this is not yet working.
|
Use a ``when`` statement if you have a set of fixed choices that each should result in a certain
|
||||||
|
action. It is possible to combine several choices to result in the same action::
|
||||||
|
|
||||||
Use a ``when`` statement if you have a set of choices that each should result in a certain
|
when value {
|
||||||
action. It's more readable (and results in faster code) than using a lot of if / else statements.
|
4 -> c64scr.print("four")
|
||||||
|
5 -> c64scr.print("five")
|
||||||
|
10,20,30 -> {
|
||||||
|
c64scr.print("ten or twenty or thirty")
|
||||||
|
}
|
||||||
|
else -> c64scr.print("don't know")
|
||||||
|
}
|
||||||
|
|
||||||
|
The when-*value* can be any expression but the choice values have to evaluate to
|
||||||
|
compile-time constant integers (bytes or words). They also have to be the same
|
||||||
|
datatype as the when-value, otherwise no efficient comparison can be done.
|
||||||
|
|
||||||
|
|
||||||
Assignments
|
Assignments
|
||||||
|
@ -215,7 +215,7 @@ Variable declarations
|
|||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||||
for them. You must give them an initial value as well. That value can be a simple literal value,
|
for them. You can give them an initial value as well. That value can be a simple literal value,
|
||||||
or an expression. You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
or an expression. You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
||||||
when selecting variables to be put into zeropage.
|
when selecting variables to be put into zeropage.
|
||||||
The syntax is::
|
The syntax is::
|
||||||
@ -231,10 +231,11 @@ Various examples::
|
|||||||
str name = "my name is Irmen"
|
str name = "my name is Irmen"
|
||||||
uword address = &counter
|
uword address = &counter
|
||||||
byte[] values = [11, 22, 33, 44, 55]
|
byte[] values = [11, 22, 33, 44, 55]
|
||||||
|
byte[5] values ; array of 5 bytes, initially set to zero
|
||||||
byte[5] values = 255 ; initialize with five 255 bytes
|
byte[5] values = 255 ; initialize with five 255 bytes
|
||||||
|
|
||||||
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
||||||
|
Color rgb = {1,255,0} ; a struct variable with initial values
|
||||||
|
|
||||||
|
|
||||||
Data types
|
Data types
|
||||||
@ -358,6 +359,26 @@ Syntax is familiar with brackets: ``arrayvar[x]`` ::
|
|||||||
string[4] ; the fifth character (=byte) in the string
|
string[4] ; the fifth character (=byte) in the string
|
||||||
|
|
||||||
|
|
||||||
|
Struct
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
A *struct* has to be defined to specify what its member variables are.
|
||||||
|
There are one or more members::
|
||||||
|
|
||||||
|
struct <structname> {
|
||||||
|
<vardecl>
|
||||||
|
[ <vardecl> ...]
|
||||||
|
}
|
||||||
|
|
||||||
|
You can only use numerical variables as member of a struct, so strings and arrays
|
||||||
|
and other structs can not be part of a struct. Vice versa, a struct can not occur in an array.
|
||||||
|
|
||||||
|
After defining a struct you can use the name of the struct as a data type to declare variables with.
|
||||||
|
|
||||||
|
Struct variables can be assigned a struct literal value (also in their declaration as initial value)::
|
||||||
|
|
||||||
|
Color rgb = {255, 100, 0} ; curly braces instead of brackets
|
||||||
|
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
@ -444,11 +465,12 @@ Normal subroutines can only return zero or one return values.
|
|||||||
However, the special ``asmsub`` routines (implemented in assembly code or referencing
|
However, the special ``asmsub`` routines (implemented in assembly code or referencing
|
||||||
a routine in kernel ROM) can return more than one return values, for instance a status
|
a routine in kernel ROM) can return more than one return values, for instance a status
|
||||||
in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
||||||
Only for these kind of subroutines it is possible to write a multi value assignment to
|
It is not possible to process the results of a call to these kind of routines
|
||||||
store the resulting values::
|
directly from the language, because only single value assignments are possible.
|
||||||
|
You can still call the subroutine and not store the results.
|
||||||
var1, var2, var3 = asmsubroutine()
|
But if you want to do something with the values it returns, you'll have to write
|
||||||
|
a small block of custom inline assembly that does the call and stores the values
|
||||||
|
appropriately. Don't forget to save/restore the registers if required.
|
||||||
|
|
||||||
|
|
||||||
Subroutine definitions
|
Subroutine definitions
|
||||||
@ -610,28 +632,28 @@ The XX corresponds to one of the eigth branching instructions so the possibiliti
|
|||||||
``if_cs``, ``if_cc``, ``if_eq``, ``if_ne``, ``if_pl``, ``if_mi``, ``if_vs`` and ``if_vc``.
|
``if_cs``, ``if_cc``, ``if_eq``, ``if_ne``, ``if_pl``, ``if_mi``, ``if_vs`` and ``if_vc``.
|
||||||
It can also be one of the four aliases that are easier to read: ``if_z``, ``if_nz``, ``if_pos`` and ``if_neg``.
|
It can also be one of the four aliases that are easier to read: ``if_z``, ``if_nz``, ``if_pos`` and ``if_neg``.
|
||||||
|
|
||||||
when statement (jumptable)
|
when statement ('jump table')
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
.. attention::
|
The structure of a when statement is like this::
|
||||||
TODO: docs for this this must still be written.
|
|
||||||
TODO: the code generator for this is not yet working.
|
|
||||||
|
|
||||||
The condition value can only be an integer datatype.
|
when <expression> {
|
||||||
The choice values must be constant integer values.
|
<value(s)> -> <statement(s)>
|
||||||
|
<value(s)> -> <statement(s)>
|
||||||
code example::
|
...
|
||||||
|
[ else -> <statement(s)> ]
|
||||||
when 4+A+Y {
|
|
||||||
10 -> {
|
|
||||||
c64scr.print("ten")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
The when-*value* can be any expression but the choice values have to evaluate to
|
||||||
|
compile-time constant integers (bytes or words).
|
||||||
|
The else part is optional.
|
||||||
|
Choices can result in a single statement or a block of multiple statements in which
|
||||||
|
case you have to use { } to enclose them::
|
||||||
|
|
||||||
|
when value {
|
||||||
|
4 -> c64scr.print("four")
|
||||||
5 -> c64scr.print("five")
|
5 -> c64scr.print("five")
|
||||||
30 -> c64scr.print("thirty")
|
10,20,30 -> {
|
||||||
99 -> c64scr.print("nn")
|
c64scr.print("ten or twenty or thirty")
|
||||||
55 -> {
|
|
||||||
; will be optimized away
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
c64scr.print("!??!\n")
|
|
||||||
}
|
}
|
||||||
|
else -> c64scr.print("don't know")
|
||||||
}
|
}
|
||||||
|
@ -52,25 +52,6 @@ Allocate a fixed word in ZP that is the TOS so we can operate on TOS directly
|
|||||||
without having to to index into the stack?
|
without having to to index into the stack?
|
||||||
|
|
||||||
|
|
||||||
structs?
|
|
||||||
^^^^^^^^
|
|
||||||
|
|
||||||
A user defined struct type would be nice to group a bunch
|
|
||||||
of values together (and use it multiple times). Something like::
|
|
||||||
|
|
||||||
struct Point {
|
|
||||||
ubyte color
|
|
||||||
word[] vec = [0,0,0]
|
|
||||||
}
|
|
||||||
|
|
||||||
Point p1
|
|
||||||
Point p2
|
|
||||||
Point p3
|
|
||||||
|
|
||||||
p1.color = 3
|
|
||||||
p1.vec[2] = 2
|
|
||||||
|
|
||||||
|
|
||||||
Misc
|
Misc
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,8 +3,6 @@
|
|||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
; The classic number guessing game.
|
; The classic number guessing game.
|
||||||
; This version uses mostly high level subroutine calls and loops.
|
|
||||||
; It's more readable than the low-level version, but produces a slightly larger program.
|
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
@ -32,7 +30,8 @@
|
|||||||
ubyte guess = lsb(c64utils.str2uword(input))
|
ubyte guess = lsb(c64utils.str2uword(input))
|
||||||
|
|
||||||
if guess==secretnumber {
|
if guess==secretnumber {
|
||||||
return ending(true)
|
ending(true)
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
c64scr.print("\n\nThat is too ")
|
c64scr.print("\n\nThat is too ")
|
||||||
if guess<secretnumber
|
if guess<secretnumber
|
||||||
@ -42,7 +41,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ending(false)
|
ending(false)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
sub ending(ubyte success) {
|
sub ending(ubyte success) {
|
||||||
|
@ -43,8 +43,6 @@
|
|||||||
while multiple < len(sieve) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
sieve[lsb(multiple)] = true
|
||||||
multiple += candidate_prime
|
multiple += candidate_prime
|
||||||
; c64scr.print_uw(multiple) ; TODO
|
|
||||||
; c4.CHROUT('\n') ; TODO
|
|
||||||
}
|
}
|
||||||
return candidate_prime
|
return candidate_prime
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
c64.SCROLY &= %11101111 ; blank the screen
|
c64.SCROLY &= %11101111 ; blank the screen
|
||||||
c64utils.set_rasterirq_excl(40)
|
c64utils.set_rasterirq_excl(40) ; register exclusive raster irq handler
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
|
; enjoy the moving bars :)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -20,22 +21,20 @@
|
|||||||
const ubyte barheight = 4
|
const ubyte barheight = 4
|
||||||
ubyte[] colors = [6,2,4,5,15,7,1,13,3,12,8,11,9]
|
ubyte[] colors = [6,2,4,5,15,7,1,13,3,12,8,11,9]
|
||||||
ubyte color = 0
|
ubyte color = 0
|
||||||
ubyte ypos = 0
|
ubyte yanim = 0
|
||||||
|
|
||||||
sub irq() {
|
sub irq() {
|
||||||
Y++ ; slight timing delay to avoid rasterline transition issues
|
|
||||||
ubyte rasterpos = c64.RASTER
|
|
||||||
|
|
||||||
if color!=len(colors) {
|
if color!=len(colors) {
|
||||||
c64.EXTCOL = colors[color]
|
c64.EXTCOL = colors[color]
|
||||||
|
c64.RASTER += barheight ; next raster Irq for next color
|
||||||
color++
|
color++
|
||||||
c64.RASTER = rasterpos+barheight
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ypos += 2
|
|
||||||
c64.EXTCOL = 0
|
c64.EXTCOL = 0
|
||||||
color = 0
|
color = 0
|
||||||
c64.RASTER = sin8u(ypos)/2+40
|
yanim += 2
|
||||||
|
c64.RASTER = sin8u(yanim)/2+30 ; new start of raster Irq
|
||||||
}
|
}
|
||||||
|
c64.SCROLY &= $7f ; set high bit of the raster pos to zero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,8 @@
|
|||||||
|
|
||||||
|
|
||||||
~ irq {
|
~ irq {
|
||||||
sub irq() {
|
|
||||||
|
sub irq() {
|
||||||
c64.EXTCOL--
|
c64.EXTCOL--
|
||||||
|
|
||||||
; float up & wobble horizontally
|
; float up & wobble horizontally
|
||||||
@ -67,6 +68,6 @@ sub irq() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c64.EXTCOL++
|
c64.EXTCOL++
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
31
examples/structs.p8
Normal file
31
examples/structs.p8
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
%import c64utils
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
~ main {
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
ubyte red
|
||||||
|
ubyte green
|
||||||
|
ubyte blue
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
|
||||||
|
Color purple = {255, 0, 255}
|
||||||
|
|
||||||
|
Color other
|
||||||
|
|
||||||
|
other = purple
|
||||||
|
|
||||||
|
other.red /= 2
|
||||||
|
other.green = 10 + other.green / 2
|
||||||
|
other.blue = 99
|
||||||
|
|
||||||
|
c64scr.print_ub(other.red)
|
||||||
|
c64.CHROUT(',')
|
||||||
|
c64scr.print_ub(other.green)
|
||||||
|
c64.CHROUT(',')
|
||||||
|
c64scr.print_ub(other.blue)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
}
|
||||||
|
}
|
@ -5,20 +5,24 @@
|
|||||||
const uword width = 40
|
const uword width = 40
|
||||||
const uword height = 25
|
const uword height = 25
|
||||||
|
|
||||||
sub start() {
|
struct Ball {
|
||||||
|
|
||||||
uword anglex
|
uword anglex
|
||||||
uword angley
|
uword angley
|
||||||
ubyte color
|
ubyte color
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
|
||||||
|
Ball ball
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
ubyte x = msb(sin8u(msb(anglex)) as uword * width)
|
ubyte x = msb(sin8u(msb(ball.anglex)) as uword * width)
|
||||||
ubyte y = msb(cos8u(msb(angley)) as uword * height)
|
ubyte y = msb(cos8u(msb(ball.angley)) as uword * height)
|
||||||
c64scr.setcc(x, y, 81, color)
|
c64scr.setcc(x, y, 81, ball.color)
|
||||||
|
|
||||||
anglex+=800
|
ball.anglex+=800
|
||||||
angley+=947
|
ball.angley+=947
|
||||||
color++
|
ball.color++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,33 +75,31 @@ waitkey:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub keypress(ubyte key) {
|
sub move_left() {
|
||||||
if key==157 or key==',' {
|
|
||||||
; move left
|
|
||||||
drawBlock(xpos, ypos, 32)
|
drawBlock(xpos, ypos, 32)
|
||||||
if blocklogic.noCollision(xpos-1, ypos) {
|
if blocklogic.noCollision(xpos-1, ypos) {
|
||||||
xpos--
|
xpos--
|
||||||
}
|
}
|
||||||
drawBlock(xpos, ypos, 160)
|
drawBlock(xpos, ypos, 160)
|
||||||
}
|
}
|
||||||
else if key==29 or key=='/' {
|
|
||||||
; move right
|
sub move_right() {
|
||||||
drawBlock(xpos, ypos, 32)
|
drawBlock(xpos, ypos, 32)
|
||||||
if blocklogic.noCollision(xpos+1, ypos) {
|
if blocklogic.noCollision(xpos+1, ypos) {
|
||||||
xpos++
|
xpos++
|
||||||
}
|
}
|
||||||
drawBlock(xpos, ypos, 160)
|
drawBlock(xpos, ypos, 160)
|
||||||
}
|
}
|
||||||
else if key==17 or key=='.' {
|
|
||||||
; move down faster
|
sub move_down_faster() {
|
||||||
drawBlock(xpos, ypos, 32)
|
drawBlock(xpos, ypos, 32)
|
||||||
if blocklogic.noCollision(xpos, ypos+1) {
|
if blocklogic.noCollision(xpos, ypos+1) {
|
||||||
ypos++
|
ypos++
|
||||||
}
|
}
|
||||||
drawBlock(xpos, ypos, 160)
|
drawBlock(xpos, ypos, 160)
|
||||||
}
|
}
|
||||||
else if key==145 or key==' ' {
|
|
||||||
; drop down immediately
|
sub drop_down_immediately() {
|
||||||
drawBlock(xpos, ypos, 32)
|
drawBlock(xpos, ypos, 32)
|
||||||
ubyte dropypos
|
ubyte dropypos
|
||||||
for dropypos in ypos+1 to boardOffsetY+boardHeight-1 {
|
for dropypos in ypos+1 to boardOffsetY+boardHeight-1 {
|
||||||
@ -120,7 +118,15 @@ waitkey:
|
|||||||
drawScore()
|
drawScore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if key=='z' { ; no joystick equivalent (there is only 1 fire button)
|
|
||||||
|
sub keypress(ubyte key) {
|
||||||
|
when key {
|
||||||
|
157, ',' -> move_left()
|
||||||
|
29, '/' -> move_right()
|
||||||
|
17, '.' -> move_down_faster()
|
||||||
|
145, ' ' -> drop_down_immediately()
|
||||||
|
'z' -> {
|
||||||
|
; no joystick equivalent (there is only 1 fire button)
|
||||||
; rotate counter clockwise
|
; rotate counter clockwise
|
||||||
drawBlock(xpos, ypos, 32)
|
drawBlock(xpos, ypos, 32)
|
||||||
if blocklogic.canRotateCCW(xpos, ypos) {
|
if blocklogic.canRotateCCW(xpos, ypos) {
|
||||||
@ -139,7 +145,7 @@ waitkey:
|
|||||||
}
|
}
|
||||||
drawBlock(xpos, ypos, 160)
|
drawBlock(xpos, ypos, 160)
|
||||||
}
|
}
|
||||||
else if key=='x' {
|
'x' -> {
|
||||||
; rotate clockwise
|
; rotate clockwise
|
||||||
drawBlock(xpos, ypos, 32)
|
drawBlock(xpos, ypos, 32)
|
||||||
if blocklogic.canRotateCW(xpos, ypos) {
|
if blocklogic.canRotateCW(xpos, ypos) {
|
||||||
@ -158,7 +164,7 @@ waitkey:
|
|||||||
}
|
}
|
||||||
drawBlock(xpos, ypos, 160)
|
drawBlock(xpos, ypos, 160)
|
||||||
}
|
}
|
||||||
else if key=='c' {
|
'c' -> {
|
||||||
; hold
|
; hold
|
||||||
if holdingAllowed {
|
if holdingAllowed {
|
||||||
sound.swapping()
|
sound.swapping()
|
||||||
@ -177,6 +183,7 @@ waitkey:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub checkForLines() {
|
sub checkForLines() {
|
||||||
; check if line(s) are full -> flash/clear line(s) + add score + move rest down
|
; check if line(s) are full -> flash/clear line(s) + add score + move rest down
|
||||||
|
105
examples/test.p8
105
examples/test.p8
@ -1,56 +1,75 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
A=10
|
|
||||||
Y=22
|
|
||||||
uword uw = A*Y
|
|
||||||
|
|
||||||
str teststring = "hello"
|
word w1
|
||||||
c64scr.print(&teststring)
|
word w2
|
||||||
|
word w3
|
||||||
|
word w4
|
||||||
|
word w5
|
||||||
|
word w6
|
||||||
|
word w7
|
||||||
|
word w8
|
||||||
|
word w9
|
||||||
|
word w10
|
||||||
|
word w11
|
||||||
|
word w12
|
||||||
|
word w13
|
||||||
|
word w14
|
||||||
|
word w15
|
||||||
|
word w16
|
||||||
|
word w17
|
||||||
|
word w18
|
||||||
|
word w19
|
||||||
|
word @zp w20
|
||||||
|
word @zp w21
|
||||||
|
word w22
|
||||||
|
word w23
|
||||||
|
word w24
|
||||||
|
word w25
|
||||||
|
word w26
|
||||||
|
word w27
|
||||||
|
word w28
|
||||||
|
word w29
|
||||||
|
word w30
|
||||||
|
|
||||||
when uw {
|
ubyte[] blaat = 10 to 20
|
||||||
12345 -> {
|
|
||||||
A=44
|
for ubyte c in 'a' to 'z' {
|
||||||
}
|
c64.CHROUT(blaat[c])
|
||||||
12346 -> {
|
|
||||||
A=44
|
|
||||||
}
|
|
||||||
12347 -> {
|
|
||||||
A=44
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
A=0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
when 4+A+Y {
|
word q
|
||||||
10 -> {
|
q=w26
|
||||||
c64scr.print("ten")
|
q+=w27
|
||||||
}
|
q+=w28
|
||||||
5 -> c64scr.print("five")
|
q+=w29
|
||||||
30 -> c64scr.print("thirty")
|
q+=w30
|
||||||
31 -> c64scr.print("thirty1")
|
q+=w10
|
||||||
32 -> c64scr.print("thirty2")
|
q+=w11
|
||||||
33 -> c64scr.print("thirty3")
|
q+=w12
|
||||||
99 -> c64scr.print("nn")
|
q+=w13
|
||||||
55 -> {
|
q+=w14
|
||||||
; should be optimized away
|
q+=w15
|
||||||
}
|
q+=w16
|
||||||
56 -> {
|
q+=w17
|
||||||
; should be optimized away
|
q+=w18
|
||||||
}
|
q+=w19
|
||||||
57243 -> {
|
q+=w20
|
||||||
; should be optimized away
|
q+=w21
|
||||||
}
|
q+=w22
|
||||||
else -> {
|
q+=w23
|
||||||
c64scr.print("!??!\n")
|
q+=w24
|
||||||
c64scr.print("!??!!??!\n")
|
q+=w25
|
||||||
c64scr.print("!??!!??!!?!\n")
|
q+=w26
|
||||||
}
|
q+=w27
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# compile using regular Kotlin sdk command line tool
|
|
||||||
|
|
||||||
echo "Compiling the parser..."
|
|
||||||
java -jar ./parser/antlr/lib/antlr-4.7.2-complete.jar -o ./parser/src/prog8/parser -Xexact-output-dir -no-listener -no-visitor ./parser/antlr/prog8.g4
|
|
||||||
|
|
||||||
|
|
||||||
PARSER_CLASSES=./out/production/parser
|
|
||||||
COMPILER_JAR=prog8compiler.jar
|
|
||||||
ANTLR_RUNTIME=./parser/antlr/lib/antlr-runtime-4.7.2.jar
|
|
||||||
|
|
||||||
mkdir -p ${PARSER_CLASSES}
|
|
||||||
javac -d ${PARSER_CLASSES} -cp ${ANTLR_RUNTIME} ./parser/src/prog8/parser/prog8Lexer.java ./parser/src/prog8/parser/prog8Parser.java
|
|
||||||
|
|
||||||
echo "Compiling the compiler itself..."
|
|
||||||
JAVA_OPTS="-Xmx3G -Xms300M" kotlinc -verbose -include-runtime -d ${COMPILER_JAR} -jvm-target 1.8 -cp ${ANTLR_RUNTIME}:${PARSER_CLASSES} ./compiler/src/prog8
|
|
||||||
|
|
||||||
echo "Finalizing the compiler jar file..."
|
|
||||||
# add the antlr parser classes
|
|
||||||
jar ufe ${COMPILER_JAR} prog8.CompilerMainKt -C ${PARSER_CLASSES} prog8
|
|
||||||
|
|
||||||
# add the resources
|
|
||||||
jar uf ${COMPILER_JAR} -C ./compiler/res .
|
|
||||||
|
|
||||||
# add the antlr runtime classes
|
|
||||||
rm -rf antlr_runtime_extraction
|
|
||||||
mkdir antlr_runtime_extraction
|
|
||||||
(cd antlr_runtime_extraction; jar xf ../${ANTLR_RUNTIME})
|
|
||||||
jar uf ${COMPILER_JAR} -C antlr_runtime_extraction org
|
|
||||||
rm -rf antlr_runtime_extraction
|
|
||||||
|
|
||||||
echo "Done!"
|
|
@ -1,9 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
set PROG8CLASSPATH=./compiler/build/classes/kotlin/main;./compiler/build/resources/main;./parser/build/classes/java/main
|
|
||||||
set KOTLINPATH=%USERPROFILE%/.IdeaIC2019.1/config/plugins/Kotlin
|
|
||||||
set LIBJARS=%KOTLINPATH%/lib/kotlin-stdlib.jar;%KOTLINPATH%/lib/kotlin-reflect.jar;./parser/antlr/lib/antlr-runtime-4.7.2.jar
|
|
||||||
|
|
||||||
java -cp %PROG8CLASSPATH%;%LIBJARS% prog8.CompilerMainKt %*
|
|
||||||
|
|
||||||
@REM if you have created a .jar file using the 'create_compiler_jar' script, you can simply do: java -jar prog8compiler.jar
|
|
@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
PROG8CLASSPATH=./compiler/build/classes/kotlin/main:./compiler/build/resources/main:./parser/build/classes/java/main
|
|
||||||
KOTLINPATH=${HOME}/.IntelliJIdea2019.1/config/plugins/Kotlin
|
|
||||||
LIBJARS=${KOTLINPATH}/lib/kotlin-stdlib.jar:${KOTLINPATH}/lib/kotlin-reflect.jar:./parser/antlr/lib/antlr-runtime-4.7.2.jar
|
|
||||||
|
|
||||||
java -cp ${PROG8CLASSPATH}:${LIBJARS} prog8.CompilerMainKt $*
|
|
||||||
|
|
||||||
# if you have created a .jar file using the 'create_compiler_jar' script, you can simply do: java -jar prog8compiler.jar
|
|
9
p8vm.cmd
9
p8vm.cmd
@ -1,9 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
set PROG8CLASSPATH=./compiler/build/classes/kotlin/main;./compiler/build/resources/main
|
|
||||||
set KOTLINPATH=%USERPROFILE%/.IdeaIC2019.1/config/plugins/Kotlin
|
|
||||||
set LIBJARS=%KOTLINPATH%/lib/kotlin-stdlib.jar;%KOTLINPATH%/lib/kotlin-reflect.jar
|
|
||||||
|
|
||||||
java -cp %PROG8CLASSPATH%;%LIBJARS% prog8.vm.stackvm.MainKt %*
|
|
||||||
|
|
||||||
@REM if you have created a .jar file using the 'create_compiler_jar' script, you can simply do: java -jar prog8compiler.jar -vm
|
|
9
p8vm.sh
9
p8vm.sh
@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
PROG8CLASSPATH=./compiler/build/classes/kotlin/main:./compiler/build/resources/main
|
|
||||||
KOTLINPATH=${HOME}/.IntelliJIdea2019.1/config/plugins/Kotlin
|
|
||||||
LIBJARS=${KOTLINPATH}/lib/kotlin-stdlib.jar:${KOTLINPATH}/lib/kotlin-reflect.jar
|
|
||||||
|
|
||||||
java -cp ${PROG8CLASSPATH}:${LIBJARS} prog8.vm.stackvm.MainKt $*
|
|
||||||
|
|
||||||
# if you have created a .jar file using the 'create_compiler_jar' script, you can simply do: java -jar prog8compiler.jar -vm
|
|
@ -73,9 +73,12 @@ block: '~' identifier integerliteral? statement_block EOL ;
|
|||||||
statement :
|
statement :
|
||||||
directive
|
directive
|
||||||
| varinitializer
|
| varinitializer
|
||||||
|
| structvarinitializer
|
||||||
| vardecl
|
| vardecl
|
||||||
|
| structvardecl
|
||||||
| constdecl
|
| constdecl
|
||||||
| memoryvardecl
|
| memoryvardecl
|
||||||
|
| structdecl
|
||||||
| assignment
|
| assignment
|
||||||
| augassignment
|
| augassignment
|
||||||
| unconditionaljump
|
| unconditionaljump
|
||||||
@ -109,21 +112,25 @@ directive :
|
|||||||
|
|
||||||
directivearg : stringliteral | identifier | integerliteral ;
|
directivearg : stringliteral | identifier | integerliteral ;
|
||||||
|
|
||||||
vardecl: datatype ZEROPAGE? (arrayindex | ARRAYSIG) ? identifier ;
|
vardecl: datatype ZEROPAGE? (arrayindex | ARRAYSIG) ? varname=identifier ;
|
||||||
|
|
||||||
|
structvardecl: structname=identifier varname=identifier ;
|
||||||
|
|
||||||
varinitializer : vardecl '=' expression ;
|
varinitializer : vardecl '=' expression ;
|
||||||
|
|
||||||
|
structvarinitializer : structvardecl '=' expression ;
|
||||||
|
|
||||||
constdecl: 'const' varinitializer ;
|
constdecl: 'const' varinitializer ;
|
||||||
|
|
||||||
memoryvardecl: ADDRESS_OF varinitializer;
|
memoryvardecl: ADDRESS_OF varinitializer;
|
||||||
|
|
||||||
|
structdecl: 'struct' identifier '{' EOL vardecl ( EOL vardecl)* EOL? '}' EOL;
|
||||||
|
|
||||||
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'str_s' ;
|
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'str_s' ;
|
||||||
|
|
||||||
arrayindex: '[' expression ']' ;
|
arrayindex: '[' expression ']' ;
|
||||||
|
|
||||||
assignment : assign_targets '=' expression ;
|
assignment : assign_target '=' expression ;
|
||||||
|
|
||||||
assign_targets : assign_target (',' assign_target)* ;
|
|
||||||
|
|
||||||
augassignment :
|
augassignment :
|
||||||
assign_target operator=('+=' | '-=' | '/=' | '*=' | '**=' | '&=' | '|=' | '^=' | '%=' | '<<=' | '>>=' ) expression
|
assign_target operator=('+=' | '-=' | '/=' | '*=' | '**=' | '&=' | '|=' | '^=' | '%=' | '<<=' | '>>=' ) expression
|
||||||
@ -185,7 +192,7 @@ expression_list :
|
|||||||
expression (',' EOL? expression)* // you can split the expression list over several lines
|
expression (',' EOL? expression)* // you can split the expression list over several lines
|
||||||
;
|
;
|
||||||
|
|
||||||
returnstmt : 'return' expression_list? ;
|
returnstmt : 'return' expression? ;
|
||||||
|
|
||||||
breakstmt : 'break';
|
breakstmt : 'break';
|
||||||
|
|
||||||
@ -207,7 +214,9 @@ wordsuffix : '.w' ;
|
|||||||
|
|
||||||
booleanliteral : 'true' | 'false' ;
|
booleanliteral : 'true' | 'false' ;
|
||||||
|
|
||||||
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the array list over several lines
|
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines
|
||||||
|
|
||||||
|
structliteral : '{' EOL? expression (',' EOL? expression)* EOL? '}' ; // you can split the values over several lines
|
||||||
|
|
||||||
stringliteral : STRING ;
|
stringliteral : STRING ;
|
||||||
|
|
||||||
@ -215,6 +224,7 @@ charliteral : SINGLECHAR ;
|
|||||||
|
|
||||||
floatliteral : FLOAT_NUMBER ;
|
floatliteral : FLOAT_NUMBER ;
|
||||||
|
|
||||||
|
|
||||||
literalvalue :
|
literalvalue :
|
||||||
integerliteral
|
integerliteral
|
||||||
| booleanliteral
|
| booleanliteral
|
||||||
@ -222,6 +232,7 @@ literalvalue :
|
|||||||
| stringliteral
|
| stringliteral
|
||||||
| charliteral
|
| charliteral
|
||||||
| floatliteral
|
| floatliteral
|
||||||
|
| structliteral
|
||||||
;
|
;
|
||||||
|
|
||||||
inlineasm : '%asm' INLINEASMBLOCK;
|
inlineasm : '%asm' INLINEASMBLOCK;
|
||||||
@ -282,4 +293,5 @@ repeatloop: 'repeat' (statement | statement_block) EOL? 'until' expression ;
|
|||||||
|
|
||||||
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;
|
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;
|
||||||
|
|
||||||
when_choice: (expression | 'else' ) '->' (statement | statement_block ) ;
|
when_choice: (expression_list | 'else' ) '->' (statement | statement_block ) ;
|
||||||
|
|
||||||
|
@ -1,512 +0,0 @@
|
|||||||
// Generated from prog8.g4 by ANTLR 4.7.2
|
|
||||||
|
|
||||||
package prog8.parser;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.Lexer;
|
|
||||||
import org.antlr.v4.runtime.CharStream;
|
|
||||||
import org.antlr.v4.runtime.Token;
|
|
||||||
import org.antlr.v4.runtime.TokenStream;
|
|
||||||
import org.antlr.v4.runtime.*;
|
|
||||||
import org.antlr.v4.runtime.atn.*;
|
|
||||||
import org.antlr.v4.runtime.dfa.DFA;
|
|
||||||
import org.antlr.v4.runtime.misc.*;
|
|
||||||
|
|
||||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
|
||||||
public class prog8Lexer extends Lexer {
|
|
||||||
static { RuntimeMetaData.checkVersion("4.7.2", RuntimeMetaData.VERSION); }
|
|
||||||
|
|
||||||
protected static final DFA[] _decisionToDFA;
|
|
||||||
protected static final PredictionContextCache _sharedContextCache =
|
|
||||||
new PredictionContextCache();
|
|
||||||
public static final int
|
|
||||||
T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9,
|
|
||||||
T__9=10, T__10=11, T__11=12, T__12=13, T__13=14, T__14=15, T__15=16, T__16=17,
|
|
||||||
T__17=18, T__18=19, T__19=20, T__20=21, T__21=22, T__22=23, T__23=24,
|
|
||||||
T__24=25, T__25=26, T__26=27, T__27=28, T__28=29, T__29=30, T__30=31,
|
|
||||||
T__31=32, T__32=33, T__33=34, T__34=35, T__35=36, T__36=37, T__37=38,
|
|
||||||
T__38=39, T__39=40, T__40=41, T__41=42, T__42=43, T__43=44, T__44=45,
|
|
||||||
T__45=46, T__46=47, T__47=48, T__48=49, T__49=50, T__50=51, T__51=52,
|
|
||||||
T__52=53, T__53=54, T__54=55, T__55=56, T__56=57, T__57=58, T__58=59,
|
|
||||||
T__59=60, T__60=61, T__61=62, T__62=63, T__63=64, T__64=65, T__65=66,
|
|
||||||
T__66=67, T__67=68, T__68=69, T__69=70, T__70=71, T__71=72, T__72=73,
|
|
||||||
T__73=74, T__74=75, T__75=76, T__76=77, T__77=78, T__78=79, T__79=80,
|
|
||||||
T__80=81, T__81=82, T__82=83, T__83=84, T__84=85, T__85=86, T__86=87,
|
|
||||||
T__87=88, T__88=89, T__89=90, T__90=91, T__91=92, T__92=93, T__93=94,
|
|
||||||
T__94=95, T__95=96, T__96=97, T__97=98, T__98=99, T__99=100, T__100=101,
|
|
||||||
T__101=102, T__102=103, T__103=104, T__104=105, T__105=106, T__106=107,
|
|
||||||
T__107=108, T__108=109, LINECOMMENT=110, COMMENT=111, WS=112, EOL=113,
|
|
||||||
NAME=114, DEC_INTEGER=115, HEX_INTEGER=116, BIN_INTEGER=117, ADDRESS_OF=118,
|
|
||||||
FLOAT_NUMBER=119, STRING=120, INLINEASMBLOCK=121, SINGLECHAR=122, ZEROPAGE=123,
|
|
||||||
ARRAYSIG=124;
|
|
||||||
public static String[] channelNames = {
|
|
||||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
|
||||||
};
|
|
||||||
|
|
||||||
public static String[] modeNames = {
|
|
||||||
"DEFAULT_MODE"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static String[] makeRuleNames() {
|
|
||||||
return new String[] {
|
|
||||||
"T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8",
|
|
||||||
"T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16",
|
|
||||||
"T__17", "T__18", "T__19", "T__20", "T__21", "T__22", "T__23", "T__24",
|
|
||||||
"T__25", "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", "T__32",
|
|
||||||
"T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40",
|
|
||||||
"T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48",
|
|
||||||
"T__49", "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", "T__56",
|
|
||||||
"T__57", "T__58", "T__59", "T__60", "T__61", "T__62", "T__63", "T__64",
|
|
||||||
"T__65", "T__66", "T__67", "T__68", "T__69", "T__70", "T__71", "T__72",
|
|
||||||
"T__73", "T__74", "T__75", "T__76", "T__77", "T__78", "T__79", "T__80",
|
|
||||||
"T__81", "T__82", "T__83", "T__84", "T__85", "T__86", "T__87", "T__88",
|
|
||||||
"T__89", "T__90", "T__91", "T__92", "T__93", "T__94", "T__95", "T__96",
|
|
||||||
"T__97", "T__98", "T__99", "T__100", "T__101", "T__102", "T__103", "T__104",
|
|
||||||
"T__105", "T__106", "T__107", "T__108", "LINECOMMENT", "COMMENT", "WS",
|
|
||||||
"EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF",
|
|
||||||
"FLOAT_NUMBER", "FNUMBER", "STRING_ESCAPE_SEQ", "STRING", "INLINEASMBLOCK",
|
|
||||||
"SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public static final String[] ruleNames = makeRuleNames();
|
|
||||||
|
|
||||||
private static String[] makeLiteralNames() {
|
|
||||||
return new String[] {
|
|
||||||
null, "'~'", "':'", "'goto'", "'%output'", "'%launcher'", "'%zeropage'",
|
|
||||||
"'%zpreserved'", "'%address'", "'%import'", "'%breakpoint'", "'%asminclude'",
|
|
||||||
"'%asmbinary'", "'%option'", "','", "'='", "'const'", "'ubyte'", "'byte'",
|
|
||||||
"'uword'", "'word'", "'float'", "'str'", "'str_s'", "'['", "']'", "'+='",
|
|
||||||
"'-='", "'/='", "'*='", "'**='", "'&='", "'|='", "'^='", "'%='", "'<<='",
|
|
||||||
"'>>='", "'++'", "'--'", "'+'", "'-'", "'**'", "'*'", "'/'", "'%'", "'<<'",
|
|
||||||
"'>>'", "'<'", "'>'", "'<='", "'>='", "'=='", "'!='", "'^'", "'|'", "'to'",
|
|
||||||
"'step'", "'and'", "'or'", "'xor'", "'not'", "'('", "')'", "'as'", "'@'",
|
|
||||||
"'return'", "'break'", "'continue'", "'.'", "'A'", "'X'", "'Y'", "'AX'",
|
|
||||||
"'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'", "'Pv'", "'.w'", "'true'", "'false'",
|
|
||||||
"'%asm'", "'sub'", "'->'", "'{'", "'}'", "'asmsub'", "'stack'", "'clobbers'",
|
|
||||||
"'if'", "'else'", "'if_cs'", "'if_cc'", "'if_eq'", "'if_z'", "'if_ne'",
|
|
||||||
"'if_nz'", "'if_pl'", "'if_pos'", "'if_mi'", "'if_neg'", "'if_vs'", "'if_vc'",
|
|
||||||
"'for'", "'in'", "'while'", "'repeat'", "'until'", "'when'", null, null,
|
|
||||||
null, null, null, null, null, null, "'&'", null, null, null, null, "'@zp'",
|
|
||||||
"'[]'"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
private static final String[] _LITERAL_NAMES = makeLiteralNames();
|
|
||||||
private static String[] makeSymbolicNames() {
|
|
||||||
return new String[] {
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
|
||||||
null, null, "LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER",
|
|
||||||
"HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF", "FLOAT_NUMBER", "STRING",
|
|
||||||
"INLINEASMBLOCK", "SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
|
|
||||||
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #VOCABULARY} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static final String[] tokenNames;
|
|
||||||
static {
|
|
||||||
tokenNames = new String[_SYMBOLIC_NAMES.length];
|
|
||||||
for (int i = 0; i < tokenNames.length; i++) {
|
|
||||||
tokenNames[i] = VOCABULARY.getLiteralName(i);
|
|
||||||
if (tokenNames[i] == null) {
|
|
||||||
tokenNames[i] = VOCABULARY.getSymbolicName(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenNames[i] == null) {
|
|
||||||
tokenNames[i] = "<INVALID>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public String[] getTokenNames() {
|
|
||||||
return tokenNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
|
|
||||||
public Vocabulary getVocabulary() {
|
|
||||||
return VOCABULARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public prog8Lexer(CharStream input) {
|
|
||||||
super(input);
|
|
||||||
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getGrammarFileName() { return "prog8.g4"; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getRuleNames() { return ruleNames; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSerializedATN() { return _serializedATN; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getChannelNames() { return channelNames; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getModeNames() { return modeNames; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ATN getATN() { return _ATN; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
|
|
||||||
switch (ruleIndex) {
|
|
||||||
case 121:
|
|
||||||
STRING_action((RuleContext)_localctx, actionIndex);
|
|
||||||
break;
|
|
||||||
case 122:
|
|
||||||
INLINEASMBLOCK_action((RuleContext)_localctx, actionIndex);
|
|
||||||
break;
|
|
||||||
case 123:
|
|
||||||
SINGLECHAR_action((RuleContext)_localctx, actionIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void STRING_action(RuleContext _localctx, int actionIndex) {
|
|
||||||
switch (actionIndex) {
|
|
||||||
case 0:
|
|
||||||
|
|
||||||
// get rid of the enclosing quotes
|
|
||||||
String s = getText();
|
|
||||||
setText(s.substring(1, s.length() - 1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void INLINEASMBLOCK_action(RuleContext _localctx, int actionIndex) {
|
|
||||||
switch (actionIndex) {
|
|
||||||
case 1:
|
|
||||||
|
|
||||||
// get rid of the enclosing double braces
|
|
||||||
String s = getText();
|
|
||||||
setText(s.substring(2, s.length() - 2));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void SINGLECHAR_action(RuleContext _localctx, int actionIndex) {
|
|
||||||
switch (actionIndex) {
|
|
||||||
case 2:
|
|
||||||
|
|
||||||
// get rid of the enclosing quotes
|
|
||||||
String s = getText();
|
|
||||||
setText(s.substring(1, s.length() - 1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String _serializedATN =
|
|
||||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2~\u0365\b\1\4\2\t"+
|
|
||||||
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
|
|
||||||
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
|
|
||||||
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
|
|
||||||
"\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+
|
|
||||||
"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+
|
|
||||||
",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+
|
|
||||||
"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+
|
|
||||||
"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+
|
|
||||||
"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
|
|
||||||
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
|
|
||||||
"`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+
|
|
||||||
"k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4"+
|
|
||||||
"w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\3\2\3\2\3\3"+
|
|
||||||
"\3\3\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3"+
|
|
||||||
"\6\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b"+
|
|
||||||
"\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3"+
|
|
||||||
"\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3"+
|
|
||||||
"\13\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f"+
|
|
||||||
"\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16"+
|
|
||||||
"\3\16\3\16\3\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21"+
|
|
||||||
"\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\24\3\24"+
|
|
||||||
"\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26"+
|
|
||||||
"\3\26\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\32"+
|
|
||||||
"\3\32\3\33\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36\3\36\3\36\3\37"+
|
|
||||||
"\3\37\3\37\3\37\3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3$\3%"+
|
|
||||||
"\3%\3%\3%\3&\3&\3&\3\'\3\'\3\'\3(\3(\3)\3)\3*\3*\3*\3+\3+\3,\3,\3-\3-"+
|
|
||||||
"\3.\3.\3.\3/\3/\3/\3\60\3\60\3\61\3\61\3\62\3\62\3\62\3\63\3\63\3\63\3"+
|
|
||||||
"\64\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\67\3\67\38\38\38\39\39\39\39"+
|
|
||||||
"\39\3:\3:\3:\3:\3;\3;\3;\3<\3<\3<\3<\3=\3=\3=\3=\3>\3>\3?\3?\3@\3@\3@"+
|
|
||||||
"\3A\3A\3B\3B\3B\3B\3B\3B\3B\3C\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3D\3D"+
|
|
||||||
"\3D\3E\3E\3F\3F\3G\3G\3H\3H\3I\3I\3I\3J\3J\3J\3K\3K\3K\3L\3L\3L\3M\3M"+
|
|
||||||
"\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3R\3S\3S"+
|
|
||||||
"\3S\3S\3S\3T\3T\3T\3T\3U\3U\3U\3V\3V\3W\3W\3X\3X\3X\3X\3X\3X\3X\3Y\3Y"+
|
|
||||||
"\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3\\\3\\\3\\\3\\\3\\\3"+
|
|
||||||
"]\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3`\3`\3`\3`\3`\3"+
|
|
||||||
"a\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3d\3"+
|
|
||||||
"d\3d\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3g\3g\3h\3h\3"+
|
|
||||||
"h\3h\3h\3h\3i\3i\3i\3i\3j\3j\3j\3k\3k\3k\3k\3k\3k\3l\3l\3l\3l\3l\3l\3"+
|
|
||||||
"l\3m\3m\3m\3m\3m\3m\3n\3n\3n\3n\3n\3o\3o\7o\u02eb\no\fo\16o\u02ee\13o"+
|
|
||||||
"\3o\3o\3o\3o\3p\3p\7p\u02f6\np\fp\16p\u02f9\13p\3p\3p\3q\3q\3q\3q\3r\6"+
|
|
||||||
"r\u0302\nr\rr\16r\u0303\3s\3s\7s\u0308\ns\fs\16s\u030b\13s\3t\3t\3t\6"+
|
|
||||||
"t\u0310\nt\rt\16t\u0311\5t\u0314\nt\3u\3u\6u\u0318\nu\ru\16u\u0319\3v"+
|
|
||||||
"\3v\6v\u031e\nv\rv\16v\u031f\3w\3w\3x\3x\3x\5x\u0327\nx\3x\5x\u032a\n"+
|
|
||||||
"x\3y\6y\u032d\ny\ry\16y\u032e\3y\3y\6y\u0333\ny\ry\16y\u0334\5y\u0337"+
|
|
||||||
"\ny\3z\3z\3z\3z\5z\u033d\nz\3{\3{\3{\7{\u0342\n{\f{\16{\u0345\13{\3{\3"+
|
|
||||||
"{\3{\3|\3|\3|\3|\6|\u034e\n|\r|\16|\u034f\3|\3|\3|\3|\3|\3}\3}\3}\5}\u035a"+
|
|
||||||
"\n}\3}\3}\3}\3~\3~\3~\3~\3\177\3\177\3\177\3\u034f\2\u0080\3\3\5\4\7\5"+
|
|
||||||
"\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23"+
|
|
||||||
"%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G"+
|
|
||||||
"%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{"+
|
|
||||||
"?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091"+
|
|
||||||
"J\u0093K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5"+
|
|
||||||
"T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9"+
|
|
||||||
"^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cd"+
|
|
||||||
"h\u00cfi\u00d1j\u00d3k\u00d5l\u00d7m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1"+
|
|
||||||
"r\u00e3s\u00e5t\u00e7u\u00e9v\u00ebw\u00edx\u00efy\u00f1\2\u00f3\2\u00f5"+
|
|
||||||
"z\u00f7{\u00f9|\u00fb}\u00fd~\3\2\n\4\2\f\f\17\17\4\2\13\13\"\"\5\2C\\"+
|
|
||||||
"aac|\6\2\62;C\\aac|\5\2\62;CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u0374"+
|
|
||||||
"\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2"+
|
|
||||||
"\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2"+
|
|
||||||
"\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2"+
|
|
||||||
"\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2"+
|
|
||||||
"\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3"+
|
|
||||||
"\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2"+
|
|
||||||
"\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2"+
|
|
||||||
"U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3"+
|
|
||||||
"\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2"+
|
|
||||||
"\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2"+
|
|
||||||
"{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085"+
|
|
||||||
"\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2"+
|
|
||||||
"\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097"+
|
|
||||||
"\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2"+
|
|
||||||
"\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9"+
|
|
||||||
"\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2"+
|
|
||||||
"\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb"+
|
|
||||||
"\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2"+
|
|
||||||
"\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd"+
|
|
||||||
"\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2"+
|
|
||||||
"\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df"+
|
|
||||||
"\3\2\2\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2"+
|
|
||||||
"\2\2\u00e9\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f5"+
|
|
||||||
"\3\2\2\2\2\u00f7\3\2\2\2\2\u00f9\3\2\2\2\2\u00fb\3\2\2\2\2\u00fd\3\2\2"+
|
|
||||||
"\2\3\u00ff\3\2\2\2\5\u0101\3\2\2\2\7\u0103\3\2\2\2\t\u0108\3\2\2\2\13"+
|
|
||||||
"\u0110\3\2\2\2\r\u011a\3\2\2\2\17\u0124\3\2\2\2\21\u0130\3\2\2\2\23\u0139"+
|
|
||||||
"\3\2\2\2\25\u0141\3\2\2\2\27\u014d\3\2\2\2\31\u0159\3\2\2\2\33\u0164\3"+
|
|
||||||
"\2\2\2\35\u016c\3\2\2\2\37\u016e\3\2\2\2!\u0170\3\2\2\2#\u0176\3\2\2\2"+
|
|
||||||
"%\u017c\3\2\2\2\'\u0181\3\2\2\2)\u0187\3\2\2\2+\u018c\3\2\2\2-\u0192\3"+
|
|
||||||
"\2\2\2/\u0196\3\2\2\2\61\u019c\3\2\2\2\63\u019e\3\2\2\2\65\u01a0\3\2\2"+
|
|
||||||
"\2\67\u01a3\3\2\2\29\u01a6\3\2\2\2;\u01a9\3\2\2\2=\u01ac\3\2\2\2?\u01b0"+
|
|
||||||
"\3\2\2\2A\u01b3\3\2\2\2C\u01b6\3\2\2\2E\u01b9\3\2\2\2G\u01bc\3\2\2\2I"+
|
|
||||||
"\u01c0\3\2\2\2K\u01c4\3\2\2\2M\u01c7\3\2\2\2O\u01ca\3\2\2\2Q\u01cc\3\2"+
|
|
||||||
"\2\2S\u01ce\3\2\2\2U\u01d1\3\2\2\2W\u01d3\3\2\2\2Y\u01d5\3\2\2\2[\u01d7"+
|
|
||||||
"\3\2\2\2]\u01da\3\2\2\2_\u01dd\3\2\2\2a\u01df\3\2\2\2c\u01e1\3\2\2\2e"+
|
|
||||||
"\u01e4\3\2\2\2g\u01e7\3\2\2\2i\u01ea\3\2\2\2k\u01ed\3\2\2\2m\u01ef\3\2"+
|
|
||||||
"\2\2o\u01f1\3\2\2\2q\u01f4\3\2\2\2s\u01f9\3\2\2\2u\u01fd\3\2\2\2w\u0200"+
|
|
||||||
"\3\2\2\2y\u0204\3\2\2\2{\u0208\3\2\2\2}\u020a\3\2\2\2\177\u020c\3\2\2"+
|
|
||||||
"\2\u0081\u020f\3\2\2\2\u0083\u0211\3\2\2\2\u0085\u0218\3\2\2\2\u0087\u021e"+
|
|
||||||
"\3\2\2\2\u0089\u0227\3\2\2\2\u008b\u0229\3\2\2\2\u008d\u022b\3\2\2\2\u008f"+
|
|
||||||
"\u022d\3\2\2\2\u0091\u022f\3\2\2\2\u0093\u0232\3\2\2\2\u0095\u0235\3\2"+
|
|
||||||
"\2\2\u0097\u0238\3\2\2\2\u0099\u023b\3\2\2\2\u009b\u023e\3\2\2\2\u009d"+
|
|
||||||
"\u0241\3\2\2\2\u009f\u0244\3\2\2\2\u00a1\u0247\3\2\2\2\u00a3\u024c\3\2"+
|
|
||||||
"\2\2\u00a5\u0252\3\2\2\2\u00a7\u0257\3\2\2\2\u00a9\u025b\3\2\2\2\u00ab"+
|
|
||||||
"\u025e\3\2\2\2\u00ad\u0260\3\2\2\2\u00af\u0262\3\2\2\2\u00b1\u0269\3\2"+
|
|
||||||
"\2\2\u00b3\u026f\3\2\2\2\u00b5\u0278\3\2\2\2\u00b7\u027b\3\2\2\2\u00b9"+
|
|
||||||
"\u0280\3\2\2\2\u00bb\u0286\3\2\2\2\u00bd\u028c\3\2\2\2\u00bf\u0292\3\2"+
|
|
||||||
"\2\2\u00c1\u0297\3\2\2\2\u00c3\u029d\3\2\2\2\u00c5\u02a3\3\2\2\2\u00c7"+
|
|
||||||
"\u02a9\3\2\2\2\u00c9\u02b0\3\2\2\2\u00cb\u02b6\3\2\2\2\u00cd\u02bd\3\2"+
|
|
||||||
"\2\2\u00cf\u02c3\3\2\2\2\u00d1\u02c9\3\2\2\2\u00d3\u02cd\3\2\2\2\u00d5"+
|
|
||||||
"\u02d0\3\2\2\2\u00d7\u02d6\3\2\2\2\u00d9\u02dd\3\2\2\2\u00db\u02e3\3\2"+
|
|
||||||
"\2\2\u00dd\u02e8\3\2\2\2\u00df\u02f3\3\2\2\2\u00e1\u02fc\3\2\2\2\u00e3"+
|
|
||||||
"\u0301\3\2\2\2\u00e5\u0305\3\2\2\2\u00e7\u0313\3\2\2\2\u00e9\u0315\3\2"+
|
|
||||||
"\2\2\u00eb\u031b\3\2\2\2\u00ed\u0321\3\2\2\2\u00ef\u0323\3\2\2\2\u00f1"+
|
|
||||||
"\u032c\3\2\2\2\u00f3\u033c\3\2\2\2\u00f5\u033e\3\2\2\2\u00f7\u0349\3\2"+
|
|
||||||
"\2\2\u00f9\u0356\3\2\2\2\u00fb\u035e\3\2\2\2\u00fd\u0362\3\2\2\2\u00ff"+
|
|
||||||
"\u0100\7\u0080\2\2\u0100\4\3\2\2\2\u0101\u0102\7<\2\2\u0102\6\3\2\2\2"+
|
|
||||||
"\u0103\u0104\7i\2\2\u0104\u0105\7q\2\2\u0105\u0106\7v\2\2\u0106\u0107"+
|
|
||||||
"\7q\2\2\u0107\b\3\2\2\2\u0108\u0109\7\'\2\2\u0109\u010a\7q\2\2\u010a\u010b"+
|
|
||||||
"\7w\2\2\u010b\u010c\7v\2\2\u010c\u010d\7r\2\2\u010d\u010e\7w\2\2\u010e"+
|
|
||||||
"\u010f\7v\2\2\u010f\n\3\2\2\2\u0110\u0111\7\'\2\2\u0111\u0112\7n\2\2\u0112"+
|
|
||||||
"\u0113\7c\2\2\u0113\u0114\7w\2\2\u0114\u0115\7p\2\2\u0115\u0116\7e\2\2"+
|
|
||||||
"\u0116\u0117\7j\2\2\u0117\u0118\7g\2\2\u0118\u0119\7t\2\2\u0119\f\3\2"+
|
|
||||||
"\2\2\u011a\u011b\7\'\2\2\u011b\u011c\7|\2\2\u011c\u011d\7g\2\2\u011d\u011e"+
|
|
||||||
"\7t\2\2\u011e\u011f\7q\2\2\u011f\u0120\7r\2\2\u0120\u0121\7c\2\2\u0121"+
|
|
||||||
"\u0122\7i\2\2\u0122\u0123\7g\2\2\u0123\16\3\2\2\2\u0124\u0125\7\'\2\2"+
|
|
||||||
"\u0125\u0126\7|\2\2\u0126\u0127\7r\2\2\u0127\u0128\7t\2\2\u0128\u0129"+
|
|
||||||
"\7g\2\2\u0129\u012a\7u\2\2\u012a\u012b\7g\2\2\u012b\u012c\7t\2\2\u012c"+
|
|
||||||
"\u012d\7x\2\2\u012d\u012e\7g\2\2\u012e\u012f\7f\2\2\u012f\20\3\2\2\2\u0130"+
|
|
||||||
"\u0131\7\'\2\2\u0131\u0132\7c\2\2\u0132\u0133\7f\2\2\u0133\u0134\7f\2"+
|
|
||||||
"\2\u0134\u0135\7t\2\2\u0135\u0136\7g\2\2\u0136\u0137\7u\2\2\u0137\u0138"+
|
|
||||||
"\7u\2\2\u0138\22\3\2\2\2\u0139\u013a\7\'\2\2\u013a\u013b\7k\2\2\u013b"+
|
|
||||||
"\u013c\7o\2\2\u013c\u013d\7r\2\2\u013d\u013e\7q\2\2\u013e\u013f\7t\2\2"+
|
|
||||||
"\u013f\u0140\7v\2\2\u0140\24\3\2\2\2\u0141\u0142\7\'\2\2\u0142\u0143\7"+
|
|
||||||
"d\2\2\u0143\u0144\7t\2\2\u0144\u0145\7g\2\2\u0145\u0146\7c\2\2\u0146\u0147"+
|
|
||||||
"\7m\2\2\u0147\u0148\7r\2\2\u0148\u0149\7q\2\2\u0149\u014a\7k\2\2\u014a"+
|
|
||||||
"\u014b\7p\2\2\u014b\u014c\7v\2\2\u014c\26\3\2\2\2\u014d\u014e\7\'\2\2"+
|
|
||||||
"\u014e\u014f\7c\2\2\u014f\u0150\7u\2\2\u0150\u0151\7o\2\2\u0151\u0152"+
|
|
||||||
"\7k\2\2\u0152\u0153\7p\2\2\u0153\u0154\7e\2\2\u0154\u0155\7n\2\2\u0155"+
|
|
||||||
"\u0156\7w\2\2\u0156\u0157\7f\2\2\u0157\u0158\7g\2\2\u0158\30\3\2\2\2\u0159"+
|
|
||||||
"\u015a\7\'\2\2\u015a\u015b\7c\2\2\u015b\u015c\7u\2\2\u015c\u015d\7o\2"+
|
|
||||||
"\2\u015d\u015e\7d\2\2\u015e\u015f\7k\2\2\u015f\u0160\7p\2\2\u0160\u0161"+
|
|
||||||
"\7c\2\2\u0161\u0162\7t\2\2\u0162\u0163\7{\2\2\u0163\32\3\2\2\2\u0164\u0165"+
|
|
||||||
"\7\'\2\2\u0165\u0166\7q\2\2\u0166\u0167\7r\2\2\u0167\u0168\7v\2\2\u0168"+
|
|
||||||
"\u0169\7k\2\2\u0169\u016a\7q\2\2\u016a\u016b\7p\2\2\u016b\34\3\2\2\2\u016c"+
|
|
||||||
"\u016d\7.\2\2\u016d\36\3\2\2\2\u016e\u016f\7?\2\2\u016f \3\2\2\2\u0170"+
|
|
||||||
"\u0171\7e\2\2\u0171\u0172\7q\2\2\u0172\u0173\7p\2\2\u0173\u0174\7u\2\2"+
|
|
||||||
"\u0174\u0175\7v\2\2\u0175\"\3\2\2\2\u0176\u0177\7w\2\2\u0177\u0178\7d"+
|
|
||||||
"\2\2\u0178\u0179\7{\2\2\u0179\u017a\7v\2\2\u017a\u017b\7g\2\2\u017b$\3"+
|
|
||||||
"\2\2\2\u017c\u017d\7d\2\2\u017d\u017e\7{\2\2\u017e\u017f\7v\2\2\u017f"+
|
|
||||||
"\u0180\7g\2\2\u0180&\3\2\2\2\u0181\u0182\7w\2\2\u0182\u0183\7y\2\2\u0183"+
|
|
||||||
"\u0184\7q\2\2\u0184\u0185\7t\2\2\u0185\u0186\7f\2\2\u0186(\3\2\2\2\u0187"+
|
|
||||||
"\u0188\7y\2\2\u0188\u0189\7q\2\2\u0189\u018a\7t\2\2\u018a\u018b\7f\2\2"+
|
|
||||||
"\u018b*\3\2\2\2\u018c\u018d\7h\2\2\u018d\u018e\7n\2\2\u018e\u018f\7q\2"+
|
|
||||||
"\2\u018f\u0190\7c\2\2\u0190\u0191\7v\2\2\u0191,\3\2\2\2\u0192\u0193\7"+
|
|
||||||
"u\2\2\u0193\u0194\7v\2\2\u0194\u0195\7t\2\2\u0195.\3\2\2\2\u0196\u0197"+
|
|
||||||
"\7u\2\2\u0197\u0198\7v\2\2\u0198\u0199\7t\2\2\u0199\u019a\7a\2\2\u019a"+
|
|
||||||
"\u019b\7u\2\2\u019b\60\3\2\2\2\u019c\u019d\7]\2\2\u019d\62\3\2\2\2\u019e"+
|
|
||||||
"\u019f\7_\2\2\u019f\64\3\2\2\2\u01a0\u01a1\7-\2\2\u01a1\u01a2\7?\2\2\u01a2"+
|
|
||||||
"\66\3\2\2\2\u01a3\u01a4\7/\2\2\u01a4\u01a5\7?\2\2\u01a58\3\2\2\2\u01a6"+
|
|
||||||
"\u01a7\7\61\2\2\u01a7\u01a8\7?\2\2\u01a8:\3\2\2\2\u01a9\u01aa\7,\2\2\u01aa"+
|
|
||||||
"\u01ab\7?\2\2\u01ab<\3\2\2\2\u01ac\u01ad\7,\2\2\u01ad\u01ae\7,\2\2\u01ae"+
|
|
||||||
"\u01af\7?\2\2\u01af>\3\2\2\2\u01b0\u01b1\7(\2\2\u01b1\u01b2\7?\2\2\u01b2"+
|
|
||||||
"@\3\2\2\2\u01b3\u01b4\7~\2\2\u01b4\u01b5\7?\2\2\u01b5B\3\2\2\2\u01b6\u01b7"+
|
|
||||||
"\7`\2\2\u01b7\u01b8\7?\2\2\u01b8D\3\2\2\2\u01b9\u01ba\7\'\2\2\u01ba\u01bb"+
|
|
||||||
"\7?\2\2\u01bbF\3\2\2\2\u01bc\u01bd\7>\2\2\u01bd\u01be\7>\2\2\u01be\u01bf"+
|
|
||||||
"\7?\2\2\u01bfH\3\2\2\2\u01c0\u01c1\7@\2\2\u01c1\u01c2\7@\2\2\u01c2\u01c3"+
|
|
||||||
"\7?\2\2\u01c3J\3\2\2\2\u01c4\u01c5\7-\2\2\u01c5\u01c6\7-\2\2\u01c6L\3"+
|
|
||||||
"\2\2\2\u01c7\u01c8\7/\2\2\u01c8\u01c9\7/\2\2\u01c9N\3\2\2\2\u01ca\u01cb"+
|
|
||||||
"\7-\2\2\u01cbP\3\2\2\2\u01cc\u01cd\7/\2\2\u01cdR\3\2\2\2\u01ce\u01cf\7"+
|
|
||||||
",\2\2\u01cf\u01d0\7,\2\2\u01d0T\3\2\2\2\u01d1\u01d2\7,\2\2\u01d2V\3\2"+
|
|
||||||
"\2\2\u01d3\u01d4\7\61\2\2\u01d4X\3\2\2\2\u01d5\u01d6\7\'\2\2\u01d6Z\3"+
|
|
||||||
"\2\2\2\u01d7\u01d8\7>\2\2\u01d8\u01d9\7>\2\2\u01d9\\\3\2\2\2\u01da\u01db"+
|
|
||||||
"\7@\2\2\u01db\u01dc\7@\2\2\u01dc^\3\2\2\2\u01dd\u01de\7>\2\2\u01de`\3"+
|
|
||||||
"\2\2\2\u01df\u01e0\7@\2\2\u01e0b\3\2\2\2\u01e1\u01e2\7>\2\2\u01e2\u01e3"+
|
|
||||||
"\7?\2\2\u01e3d\3\2\2\2\u01e4\u01e5\7@\2\2\u01e5\u01e6\7?\2\2\u01e6f\3"+
|
|
||||||
"\2\2\2\u01e7\u01e8\7?\2\2\u01e8\u01e9\7?\2\2\u01e9h\3\2\2\2\u01ea\u01eb"+
|
|
||||||
"\7#\2\2\u01eb\u01ec\7?\2\2\u01ecj\3\2\2\2\u01ed\u01ee\7`\2\2\u01eel\3"+
|
|
||||||
"\2\2\2\u01ef\u01f0\7~\2\2\u01f0n\3\2\2\2\u01f1\u01f2\7v\2\2\u01f2\u01f3"+
|
|
||||||
"\7q\2\2\u01f3p\3\2\2\2\u01f4\u01f5\7u\2\2\u01f5\u01f6\7v\2\2\u01f6\u01f7"+
|
|
||||||
"\7g\2\2\u01f7\u01f8\7r\2\2\u01f8r\3\2\2\2\u01f9\u01fa\7c\2\2\u01fa\u01fb"+
|
|
||||||
"\7p\2\2\u01fb\u01fc\7f\2\2\u01fct\3\2\2\2\u01fd\u01fe\7q\2\2\u01fe\u01ff"+
|
|
||||||
"\7t\2\2\u01ffv\3\2\2\2\u0200\u0201\7z\2\2\u0201\u0202\7q\2\2\u0202\u0203"+
|
|
||||||
"\7t\2\2\u0203x\3\2\2\2\u0204\u0205\7p\2\2\u0205\u0206\7q\2\2\u0206\u0207"+
|
|
||||||
"\7v\2\2\u0207z\3\2\2\2\u0208\u0209\7*\2\2\u0209|\3\2\2\2\u020a\u020b\7"+
|
|
||||||
"+\2\2\u020b~\3\2\2\2\u020c\u020d\7c\2\2\u020d\u020e\7u\2\2\u020e\u0080"+
|
|
||||||
"\3\2\2\2\u020f\u0210\7B\2\2\u0210\u0082\3\2\2\2\u0211\u0212\7t\2\2\u0212"+
|
|
||||||
"\u0213\7g\2\2\u0213\u0214\7v\2\2\u0214\u0215\7w\2\2\u0215\u0216\7t\2\2"+
|
|
||||||
"\u0216\u0217\7p\2\2\u0217\u0084\3\2\2\2\u0218\u0219\7d\2\2\u0219\u021a"+
|
|
||||||
"\7t\2\2\u021a\u021b\7g\2\2\u021b\u021c\7c\2\2\u021c\u021d\7m\2\2\u021d"+
|
|
||||||
"\u0086\3\2\2\2\u021e\u021f\7e\2\2\u021f\u0220\7q\2\2\u0220\u0221\7p\2"+
|
|
||||||
"\2\u0221\u0222\7v\2\2\u0222\u0223\7k\2\2\u0223\u0224\7p\2\2\u0224\u0225"+
|
|
||||||
"\7w\2\2\u0225\u0226\7g\2\2\u0226\u0088\3\2\2\2\u0227\u0228\7\60\2\2\u0228"+
|
|
||||||
"\u008a\3\2\2\2\u0229\u022a\7C\2\2\u022a\u008c\3\2\2\2\u022b\u022c\7Z\2"+
|
|
||||||
"\2\u022c\u008e\3\2\2\2\u022d\u022e\7[\2\2\u022e\u0090\3\2\2\2\u022f\u0230"+
|
|
||||||
"\7C\2\2\u0230\u0231\7Z\2\2\u0231\u0092\3\2\2\2\u0232\u0233\7C\2\2\u0233"+
|
|
||||||
"\u0234\7[\2\2\u0234\u0094\3\2\2\2\u0235\u0236\7Z\2\2\u0236\u0237\7[\2"+
|
|
||||||
"\2\u0237\u0096\3\2\2\2\u0238\u0239\7R\2\2\u0239\u023a\7e\2\2\u023a\u0098"+
|
|
||||||
"\3\2\2\2\u023b\u023c\7R\2\2\u023c\u023d\7|\2\2\u023d\u009a\3\2\2\2\u023e"+
|
|
||||||
"\u023f\7R\2\2\u023f\u0240\7p\2\2\u0240\u009c\3\2\2\2\u0241\u0242\7R\2"+
|
|
||||||
"\2\u0242\u0243\7x\2\2\u0243\u009e\3\2\2\2\u0244\u0245\7\60\2\2\u0245\u0246"+
|
|
||||||
"\7y\2\2\u0246\u00a0\3\2\2\2\u0247\u0248\7v\2\2\u0248\u0249\7t\2\2\u0249"+
|
|
||||||
"\u024a\7w\2\2\u024a\u024b\7g\2\2\u024b\u00a2\3\2\2\2\u024c\u024d\7h\2"+
|
|
||||||
"\2\u024d\u024e\7c\2\2\u024e\u024f\7n\2\2\u024f\u0250\7u\2\2\u0250\u0251"+
|
|
||||||
"\7g\2\2\u0251\u00a4\3\2\2\2\u0252\u0253\7\'\2\2\u0253\u0254\7c\2\2\u0254"+
|
|
||||||
"\u0255\7u\2\2\u0255\u0256\7o\2\2\u0256\u00a6\3\2\2\2\u0257\u0258\7u\2"+
|
|
||||||
"\2\u0258\u0259\7w\2\2\u0259\u025a\7d\2\2\u025a\u00a8\3\2\2\2\u025b\u025c"+
|
|
||||||
"\7/\2\2\u025c\u025d\7@\2\2\u025d\u00aa\3\2\2\2\u025e\u025f\7}\2\2\u025f"+
|
|
||||||
"\u00ac\3\2\2\2\u0260\u0261\7\177\2\2\u0261\u00ae\3\2\2\2\u0262\u0263\7"+
|
|
||||||
"c\2\2\u0263\u0264\7u\2\2\u0264\u0265\7o\2\2\u0265\u0266\7u\2\2\u0266\u0267"+
|
|
||||||
"\7w\2\2\u0267\u0268\7d\2\2\u0268\u00b0\3\2\2\2\u0269\u026a\7u\2\2\u026a"+
|
|
||||||
"\u026b\7v\2\2\u026b\u026c\7c\2\2\u026c\u026d\7e\2\2\u026d\u026e\7m\2\2"+
|
|
||||||
"\u026e\u00b2\3\2\2\2\u026f\u0270\7e\2\2\u0270\u0271\7n\2\2\u0271\u0272"+
|
|
||||||
"\7q\2\2\u0272\u0273\7d\2\2\u0273\u0274\7d\2\2\u0274\u0275\7g\2\2\u0275"+
|
|
||||||
"\u0276\7t\2\2\u0276\u0277\7u\2\2\u0277\u00b4\3\2\2\2\u0278\u0279\7k\2"+
|
|
||||||
"\2\u0279\u027a\7h\2\2\u027a\u00b6\3\2\2\2\u027b\u027c\7g\2\2\u027c\u027d"+
|
|
||||||
"\7n\2\2\u027d\u027e\7u\2\2\u027e\u027f\7g\2\2\u027f\u00b8\3\2\2\2\u0280"+
|
|
||||||
"\u0281\7k\2\2\u0281\u0282\7h\2\2\u0282\u0283\7a\2\2\u0283\u0284\7e\2\2"+
|
|
||||||
"\u0284\u0285\7u\2\2\u0285\u00ba\3\2\2\2\u0286\u0287\7k\2\2\u0287\u0288"+
|
|
||||||
"\7h\2\2\u0288\u0289\7a\2\2\u0289\u028a\7e\2\2\u028a\u028b\7e\2\2\u028b"+
|
|
||||||
"\u00bc\3\2\2\2\u028c\u028d\7k\2\2\u028d\u028e\7h\2\2\u028e\u028f\7a\2"+
|
|
||||||
"\2\u028f\u0290\7g\2\2\u0290\u0291\7s\2\2\u0291\u00be\3\2\2\2\u0292\u0293"+
|
|
||||||
"\7k\2\2\u0293\u0294\7h\2\2\u0294\u0295\7a\2\2\u0295\u0296\7|\2\2\u0296"+
|
|
||||||
"\u00c0\3\2\2\2\u0297\u0298\7k\2\2\u0298\u0299\7h\2\2\u0299\u029a\7a\2"+
|
|
||||||
"\2\u029a\u029b\7p\2\2\u029b\u029c\7g\2\2\u029c\u00c2\3\2\2\2\u029d\u029e"+
|
|
||||||
"\7k\2\2\u029e\u029f\7h\2\2\u029f\u02a0\7a\2\2\u02a0\u02a1\7p\2\2\u02a1"+
|
|
||||||
"\u02a2\7|\2\2\u02a2\u00c4\3\2\2\2\u02a3\u02a4\7k\2\2\u02a4\u02a5\7h\2"+
|
|
||||||
"\2\u02a5\u02a6\7a\2\2\u02a6\u02a7\7r\2\2\u02a7\u02a8\7n\2\2\u02a8\u00c6"+
|
|
||||||
"\3\2\2\2\u02a9\u02aa\7k\2\2\u02aa\u02ab\7h\2\2\u02ab\u02ac\7a\2\2\u02ac"+
|
|
||||||
"\u02ad\7r\2\2\u02ad\u02ae\7q\2\2\u02ae\u02af\7u\2\2\u02af\u00c8\3\2\2"+
|
|
||||||
"\2\u02b0\u02b1\7k\2\2\u02b1\u02b2\7h\2\2\u02b2\u02b3\7a\2\2\u02b3\u02b4"+
|
|
||||||
"\7o\2\2\u02b4\u02b5\7k\2\2\u02b5\u00ca\3\2\2\2\u02b6\u02b7\7k\2\2\u02b7"+
|
|
||||||
"\u02b8\7h\2\2\u02b8\u02b9\7a\2\2\u02b9\u02ba\7p\2\2\u02ba\u02bb\7g\2\2"+
|
|
||||||
"\u02bb\u02bc\7i\2\2\u02bc\u00cc\3\2\2\2\u02bd\u02be\7k\2\2\u02be\u02bf"+
|
|
||||||
"\7h\2\2\u02bf\u02c0\7a\2\2\u02c0\u02c1\7x\2\2\u02c1\u02c2\7u\2\2\u02c2"+
|
|
||||||
"\u00ce\3\2\2\2\u02c3\u02c4\7k\2\2\u02c4\u02c5\7h\2\2\u02c5\u02c6\7a\2"+
|
|
||||||
"\2\u02c6\u02c7\7x\2\2\u02c7\u02c8\7e\2\2\u02c8\u00d0\3\2\2\2\u02c9\u02ca"+
|
|
||||||
"\7h\2\2\u02ca\u02cb\7q\2\2\u02cb\u02cc\7t\2\2\u02cc\u00d2\3\2\2\2\u02cd"+
|
|
||||||
"\u02ce\7k\2\2\u02ce\u02cf\7p\2\2\u02cf\u00d4\3\2\2\2\u02d0\u02d1\7y\2"+
|
|
||||||
"\2\u02d1\u02d2\7j\2\2\u02d2\u02d3\7k\2\2\u02d3\u02d4\7n\2\2\u02d4\u02d5"+
|
|
||||||
"\7g\2\2\u02d5\u00d6\3\2\2\2\u02d6\u02d7\7t\2\2\u02d7\u02d8\7g\2\2\u02d8"+
|
|
||||||
"\u02d9\7r\2\2\u02d9\u02da\7g\2\2\u02da\u02db\7c\2\2\u02db\u02dc\7v\2\2"+
|
|
||||||
"\u02dc\u00d8\3\2\2\2\u02dd\u02de\7w\2\2\u02de\u02df\7p\2\2\u02df\u02e0"+
|
|
||||||
"\7v\2\2\u02e0\u02e1\7k\2\2\u02e1\u02e2\7n\2\2\u02e2\u00da\3\2\2\2\u02e3"+
|
|
||||||
"\u02e4\7y\2\2\u02e4\u02e5\7j\2\2\u02e5\u02e6\7g\2\2\u02e6\u02e7\7p\2\2"+
|
|
||||||
"\u02e7\u00dc\3\2\2\2\u02e8\u02ec\t\2\2\2\u02e9\u02eb\t\3\2\2\u02ea\u02e9"+
|
|
||||||
"\3\2\2\2\u02eb\u02ee\3\2\2\2\u02ec\u02ea\3\2\2\2\u02ec\u02ed\3\2\2\2\u02ed"+
|
|
||||||
"\u02ef\3\2\2\2\u02ee\u02ec\3\2\2\2\u02ef\u02f0\5\u00dfp\2\u02f0\u02f1"+
|
|
||||||
"\3\2\2\2\u02f1\u02f2\bo\2\2\u02f2\u00de\3\2\2\2\u02f3\u02f7\7=\2\2\u02f4"+
|
|
||||||
"\u02f6\n\2\2\2\u02f5\u02f4\3\2\2\2\u02f6\u02f9\3\2\2\2\u02f7\u02f5\3\2"+
|
|
||||||
"\2\2\u02f7\u02f8\3\2\2\2\u02f8\u02fa\3\2\2\2\u02f9\u02f7\3\2\2\2\u02fa"+
|
|
||||||
"\u02fb\bp\2\2\u02fb\u00e0\3\2\2\2\u02fc\u02fd\t\3\2\2\u02fd\u02fe\3\2"+
|
|
||||||
"\2\2\u02fe\u02ff\bq\3\2\u02ff\u00e2\3\2\2\2\u0300\u0302\t\2\2\2\u0301"+
|
|
||||||
"\u0300\3\2\2\2\u0302\u0303\3\2\2\2\u0303\u0301\3\2\2\2\u0303\u0304\3\2"+
|
|
||||||
"\2\2\u0304\u00e4\3\2\2\2\u0305\u0309\t\4\2\2\u0306\u0308\t\5\2\2\u0307"+
|
|
||||||
"\u0306\3\2\2\2\u0308\u030b\3\2\2\2\u0309\u0307\3\2\2\2\u0309\u030a\3\2"+
|
|
||||||
"\2\2\u030a\u00e6\3\2\2\2\u030b\u0309\3\2\2\2\u030c\u0314\4\62;\2\u030d"+
|
|
||||||
"\u030f\4\63;\2\u030e\u0310\4\62;\2\u030f\u030e\3\2\2\2\u0310\u0311\3\2"+
|
|
||||||
"\2\2\u0311\u030f\3\2\2\2\u0311\u0312\3\2\2\2\u0312\u0314\3\2\2\2\u0313"+
|
|
||||||
"\u030c\3\2\2\2\u0313\u030d\3\2\2\2\u0314\u00e8\3\2\2\2\u0315\u0317\7&"+
|
|
||||||
"\2\2\u0316\u0318\t\6\2\2\u0317\u0316\3\2\2\2\u0318\u0319\3\2\2\2\u0319"+
|
|
||||||
"\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u00ea\3\2\2\2\u031b\u031d\7\'"+
|
|
||||||
"\2\2\u031c\u031e\4\62\63\2\u031d\u031c\3\2\2\2\u031e\u031f\3\2\2\2\u031f"+
|
|
||||||
"\u031d\3\2\2\2\u031f\u0320\3\2\2\2\u0320\u00ec\3\2\2\2\u0321\u0322\7("+
|
|
||||||
"\2\2\u0322\u00ee\3\2\2\2\u0323\u0329\5\u00f1y\2\u0324\u0326\t\7\2\2\u0325"+
|
|
||||||
"\u0327\t\b\2\2\u0326\u0325\3\2\2\2\u0326\u0327\3\2\2\2\u0327\u0328\3\2"+
|
|
||||||
"\2\2\u0328\u032a\5\u00f1y\2\u0329\u0324\3\2\2\2\u0329\u032a\3\2\2\2\u032a"+
|
|
||||||
"\u00f0\3\2\2\2\u032b\u032d\4\62;\2\u032c\u032b\3\2\2\2\u032d\u032e\3\2"+
|
|
||||||
"\2\2\u032e\u032c\3\2\2\2\u032e\u032f\3\2\2\2\u032f\u0336\3\2\2\2\u0330"+
|
|
||||||
"\u0332\7\60\2\2\u0331\u0333\4\62;\2\u0332\u0331\3\2\2\2\u0333\u0334\3"+
|
|
||||||
"\2\2\2\u0334\u0332\3\2\2\2\u0334\u0335\3\2\2\2\u0335\u0337\3\2\2\2\u0336"+
|
|
||||||
"\u0330\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u00f2\3\2\2\2\u0338\u0339\7^"+
|
|
||||||
"\2\2\u0339\u033d\13\2\2\2\u033a\u033b\7^\2\2\u033b\u033d\5\u00e3r\2\u033c"+
|
|
||||||
"\u0338\3\2\2\2\u033c\u033a\3\2\2\2\u033d\u00f4\3\2\2\2\u033e\u0343\7$"+
|
|
||||||
"\2\2\u033f\u0342\5\u00f3z\2\u0340\u0342\n\t\2\2\u0341\u033f\3\2\2\2\u0341"+
|
|
||||||
"\u0340\3\2\2\2\u0342\u0345\3\2\2\2\u0343\u0341\3\2\2\2\u0343\u0344\3\2"+
|
|
||||||
"\2\2\u0344\u0346\3\2\2\2\u0345\u0343\3\2\2\2\u0346\u0347\7$\2\2\u0347"+
|
|
||||||
"\u0348\b{\4\2\u0348\u00f6\3\2\2\2\u0349\u034a\7}\2\2\u034a\u034b\7}\2"+
|
|
||||||
"\2\u034b\u034d\3\2\2\2\u034c\u034e\13\2\2\2\u034d\u034c\3\2\2\2\u034e"+
|
|
||||||
"\u034f\3\2\2\2\u034f\u0350\3\2\2\2\u034f\u034d\3\2\2\2\u0350\u0351\3\2"+
|
|
||||||
"\2\2\u0351\u0352\7\177\2\2\u0352\u0353\7\177\2\2\u0353\u0354\3\2\2\2\u0354"+
|
|
||||||
"\u0355\b|\5\2\u0355\u00f8\3\2\2\2\u0356\u0359\7)\2\2\u0357\u035a\5\u00f3"+
|
|
||||||
"z\2\u0358\u035a\n\t\2\2\u0359\u0357\3\2\2\2\u0359\u0358\3\2\2\2\u035a"+
|
|
||||||
"\u035b\3\2\2\2\u035b\u035c\7)\2\2\u035c\u035d\b}\6\2\u035d\u00fa\3\2\2"+
|
|
||||||
"\2\u035e\u035f\7B\2\2\u035f\u0360\7|\2\2\u0360\u0361\7r\2\2\u0361\u00fc"+
|
|
||||||
"\3\2\2\2\u0362\u0363\7]\2\2\u0363\u0364\7_\2\2\u0364\u00fe\3\2\2\2\26"+
|
|
||||||
"\2\u02ec\u02f7\u0303\u0309\u0311\u0313\u0317\u0319\u031f\u0326\u0329\u032e"+
|
|
||||||
"\u0334\u0336\u033c\u0341\u0343\u034f\u0359\7\2\3\2\b\2\2\3{\2\3|\3\3}"+
|
|
||||||
"\4";
|
|
||||||
public static final ATN _ATN =
|
|
||||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
|
||||||
static {
|
|
||||||
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
|
|
||||||
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
|
|
||||||
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list
|
rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list
|
||||||
rm -r build
|
rm -rf build out
|
||||||
|
|
Reference in New Issue
Block a user