generated from rnentjes/kotlin-server-web-undertow
Refactor: Restructure project package hierarchy and add initial implementation for assembler instructions, shell commands, and exception handling.
This commit is contained in:
1
.idea/.name
generated
1
.idea/.name
generated
@@ -1 +0,0 @@
|
|||||||
template
|
|
||||||
8
.idea/artifacts/mtmc_web_js_0_1_0_SNAPSHOT.xml
generated
Normal file
8
.idea/artifacts/mtmc_web_js_0_1_0_SNAPSHOT.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="mtmc-web-js-0.1.0-SNAPSHOT">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="mtmc-web-js-0.1.0-SNAPSHOT.jar">
|
||||||
|
<element id="module-output" name="mtmc-web.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/mtmc_web_jvm_0_1_0_SNAPSHOT.xml
generated
Normal file
8
.idea/artifacts/mtmc_web_jvm_0_1_0_SNAPSHOT.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="mtmc-web-jvm-0.1.0-SNAPSHOT">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="mtmc-web-jvm-0.1.0-SNAPSHOT.jar">
|
||||||
|
<element id="module-output" name="mtmc-web.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -3,7 +3,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
<component name="accountSettings">
|
<component name="accountSettings">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@file:OptIn(ExperimentalDistributionDsl::class)
|
@file:OptIn(ExperimentalDistributionDsl::class)
|
||||||
|
|
||||||
|
import org.codehaus.groovy.tools.shell.util.Logger.io
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl
|
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@@ -50,6 +51,9 @@ kotlin {
|
|||||||
implementation("nl.astraeus:simple-jdbc-stats:1.6.1") {
|
implementation("nl.astraeus:simple-jdbc-stats:1.6.1") {
|
||||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implementation("io.pebbletemplates:pebble:3.2.3")
|
||||||
|
implementation("com.google.code.gson:gson:2.12.1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jvmTest by getting {
|
val jvmTest by getting {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
||||||
}
|
}
|
||||||
val REPO_NAME = "dummy so the gitea template compiles, please remove"
|
|
||||||
rootProject.name = "mtmc-web"
|
rootProject.name = "mtmc-web"
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package nl.astraeus.tmpl
|
package mtmc
|
||||||
|
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import nl.astraeus.komp.HtmlBuilder
|
import nl.astraeus.komp.HtmlBuilder
|
||||||
import nl.astraeus.komp.Komponent
|
import nl.astraeus.komp.Komponent
|
||||||
|
|
||||||
|
|
||||||
class HelloKomponent : Komponent() {
|
class HelloKomponent : Komponent() {
|
||||||
override fun HtmlBuilder.render() {
|
override fun HtmlBuilder.render() {
|
||||||
div {
|
div {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package nl.astraeus.tmpl
|
package mtmc
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariConfig
|
import com.zaxxer.hikari.HikariConfig
|
||||||
import io.undertow.Undertow
|
import io.undertow.Undertow
|
||||||
@@ -8,8 +8,8 @@ import io.undertow.server.handlers.encoding.ContentEncodingRepository
|
|||||||
import io.undertow.server.handlers.encoding.EncodingHandler
|
import io.undertow.server.handlers.encoding.EncodingHandler
|
||||||
import io.undertow.server.handlers.encoding.GzipEncodingProvider
|
import io.undertow.server.handlers.encoding.GzipEncodingProvider
|
||||||
import nl.astraeus.logger.Logger
|
import nl.astraeus.logger.Logger
|
||||||
import nl.astraeus.tmpl.db.Database
|
import mtmc.db.Database
|
||||||
import nl.astraeus.tmpl.web.RequestHandler
|
import mtmc.web.RequestHandler
|
||||||
|
|
||||||
val log = Logger()
|
val log = Logger()
|
||||||
|
|
||||||
8
src/jvmMain/kotlin/mtmc/Placeholders.kt
Normal file
8
src/jvmMain/kotlin/mtmc/Placeholders.kt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package mtmc
|
||||||
|
|
||||||
|
val SERVER_PORT = 4001
|
||||||
|
val JDBC_PORT = 4002
|
||||||
|
|
||||||
|
val pageTitle = "mtmc-web"
|
||||||
|
val itemUrl = "mtmc-web"
|
||||||
|
val repoName = "mtmc-web"
|
||||||
19
src/jvmMain/kotlin/mtmc/asm/ASMElement.kt
Normal file
19
src/jvmMain/kotlin/mtmc/asm/ASMElement.kt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package mtmc.asm
|
||||||
|
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
|
||||||
|
abstract class ASMElement(
|
||||||
|
val labels: List<MTMCToken>,
|
||||||
|
@JvmField var lineNumber: Int
|
||||||
|
) : HasLocation {
|
||||||
|
@JvmField
|
||||||
|
var errors: MutableList<ASMError> = ArrayList()
|
||||||
|
override var location: Int = 0
|
||||||
|
override var sizeInBytes: Int = 0
|
||||||
|
|
||||||
|
fun addError(token: MTMCToken, error: String) {
|
||||||
|
errors.add(ASMError(token, error))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun addError(integerValueRequired: String)
|
||||||
|
}
|
||||||
13
src/jvmMain/kotlin/mtmc/asm/ASMError.kt
Normal file
13
src/jvmMain/kotlin/mtmc/asm/ASMError.kt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package mtmc.asm
|
||||||
|
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
data class ASMError(
|
||||||
|
@JvmField val token: MTMCToken,
|
||||||
|
@JvmField val error: String
|
||||||
|
) {
|
||||||
|
fun formattedErrorMessage(): String {
|
||||||
|
return "Line " + token.line + ": " + error
|
||||||
|
}
|
||||||
|
}
|
||||||
798
src/jvmMain/kotlin/mtmc/asm/Assembler.kt
Normal file
798
src/jvmMain/kotlin/mtmc/asm/Assembler.kt
Normal file
@@ -0,0 +1,798 @@
|
|||||||
|
package mtmc.asm
|
||||||
|
|
||||||
|
import mtmc.asm.data.Data
|
||||||
|
import mtmc.asm.graphics.Graphic
|
||||||
|
import mtmc.asm.instructions.ALUInstruction
|
||||||
|
import mtmc.asm.instructions.ALUOp.Companion.isALUOp
|
||||||
|
import mtmc.asm.instructions.ErrorInstruction
|
||||||
|
import mtmc.asm.instructions.Instruction
|
||||||
|
import mtmc.asm.instructions.InstructionType
|
||||||
|
import mtmc.asm.instructions.InstructionType.Companion.fromString
|
||||||
|
import mtmc.asm.instructions.InstructionType.InstructionClass
|
||||||
|
import mtmc.asm.instructions.JumpInstruction
|
||||||
|
import mtmc.asm.instructions.JumpRegisterInstruction
|
||||||
|
import mtmc.asm.instructions.LoadStoreInstruction
|
||||||
|
import mtmc.asm.instructions.LoadStoreRegisterInstruction
|
||||||
|
import mtmc.asm.instructions.MetaInstruction
|
||||||
|
import mtmc.asm.instructions.MiscInstruction
|
||||||
|
import mtmc.asm.instructions.StackInstruction
|
||||||
|
import mtmc.asm.instructions.TestInstruction
|
||||||
|
import mtmc.emulator.DebugInfo
|
||||||
|
import mtmc.emulator.DebugInfo.GlobalInfo
|
||||||
|
import mtmc.emulator.DebugInfo.LocalInfo
|
||||||
|
import mtmc.emulator.MonTanaMiniComputer
|
||||||
|
import mtmc.emulator.Register.Companion.isReadable
|
||||||
|
import mtmc.emulator.Register.Companion.isWriteable
|
||||||
|
import mtmc.os.SysCall
|
||||||
|
import mtmc.os.exec.Executable
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.tokenizer.MTMCTokenizer
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.util.*
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
|
class Assembler {
|
||||||
|
var instructions: MutableList<Instruction> = ArrayList<Instruction>()
|
||||||
|
var instructionsSizeInBytes: Int = 0
|
||||||
|
var data: MutableList<Data> = ArrayList<Data>()
|
||||||
|
var graphics: MutableList<Graphic> = ArrayList<Graphic>()
|
||||||
|
var dataSize: Int = 0
|
||||||
|
var labels: HashMap<String?, HasLocation?>
|
||||||
|
|
||||||
|
private var mode: ASMMode = ASMMode.TEXT
|
||||||
|
var tokenizer: MTMCTokenizer? = null
|
||||||
|
private var lastLabels: MutableList<MTMCToken> = mutableListOf()
|
||||||
|
private var srcName = "disk/file.asm"
|
||||||
|
private var debugStrings: MutableList<String> = mutableListOf()
|
||||||
|
|
||||||
|
init {
|
||||||
|
labels = HashMap<String?, HasLocation?>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assemble(asm: String): AssemblyResult {
|
||||||
|
return assemble(null, asm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assemble(file: String?, asm: String): AssemblyResult {
|
||||||
|
tokenizer = MTMCTokenizer(asm, "#")
|
||||||
|
debugStrings = ArrayList<String>()
|
||||||
|
parseAssembly()
|
||||||
|
resolveLocations()
|
||||||
|
val errors = collectErrors()
|
||||||
|
var debugInfo: DebugInfo? = null
|
||||||
|
var code: ByteArray = ByteArray(0)
|
||||||
|
var data: ByteArray = ByteArray(0)
|
||||||
|
var graphics: Array<ByteArray> = arrayOf()
|
||||||
|
if (errors.isEmpty()) {
|
||||||
|
code = genCode()
|
||||||
|
data = genData()
|
||||||
|
graphics = genGraphics()
|
||||||
|
debugInfo = genDebugInfo(file, asm, code)
|
||||||
|
}
|
||||||
|
return AssemblyResult(code, data, graphics, debugInfo, errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genDebugInfo(
|
||||||
|
assemblyFile: String?,
|
||||||
|
assemblySource: String,
|
||||||
|
code: ByteArray
|
||||||
|
): DebugInfo {
|
||||||
|
val assemblyLineNumbers = IntArray(code.size)
|
||||||
|
|
||||||
|
var originalFile: String = ""
|
||||||
|
val originalLineNumbers = IntArray(code.size)
|
||||||
|
|
||||||
|
val globals: MutableList<GlobalInfo> = ArrayList<GlobalInfo>()
|
||||||
|
val locals = Array<Array<LocalInfo?>>(code.size) { arrayOf() }
|
||||||
|
|
||||||
|
var location = 0
|
||||||
|
var originalLineNumber = 0
|
||||||
|
val currentLocals: MutableMap<String?, LocalInfo?> = TreeMap<String?, LocalInfo?>()
|
||||||
|
for (instruction in instructions) {
|
||||||
|
val asmLineNumber = instruction.lineNumber
|
||||||
|
if (instruction is MetaInstruction) {
|
||||||
|
if (instruction.isFileDirective) {
|
||||||
|
originalFile = instruction.getOriginalFilePath()!!
|
||||||
|
} else if (instruction.isLineDirective) {
|
||||||
|
originalLineNumber = instruction.originalLineNumber
|
||||||
|
} else if (instruction.isGlobalDirective) {
|
||||||
|
globals.add(
|
||||||
|
GlobalInfo(
|
||||||
|
instruction.getGlobalName(),
|
||||||
|
instruction.getGlobalLocation(),
|
||||||
|
instruction.getGlobalType()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (instruction.isLocalDirective) {
|
||||||
|
currentLocals.put(
|
||||||
|
instruction.getLocalName(),
|
||||||
|
LocalInfo(
|
||||||
|
instruction.getLocalName(),
|
||||||
|
instruction.getLocalOffset(),
|
||||||
|
instruction.getLocalType()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (instruction.isEndLocalDirective) {
|
||||||
|
currentLocals.remove(instruction.getLocalName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (location < instruction.location + instruction.sizeInBytes) {
|
||||||
|
assemblyLineNumbers[location] = asmLineNumber
|
||||||
|
originalLineNumbers[location] = originalLineNumber
|
||||||
|
locals[location] = currentLocals.values.toTypedArray<LocalInfo?>()
|
||||||
|
location++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val debugInfo = DebugInfo(
|
||||||
|
debugStrings,
|
||||||
|
assemblyFile,
|
||||||
|
assemblySource,
|
||||||
|
assemblyLineNumbers,
|
||||||
|
originalFile,
|
||||||
|
originalLineNumbers,
|
||||||
|
globals.toTypedArray<GlobalInfo>(),
|
||||||
|
locals
|
||||||
|
)
|
||||||
|
|
||||||
|
return debugInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assembleExecutable(srcName: String, asm: String): Executable {
|
||||||
|
this.srcName = "disk/" + srcName //TODO: The prefix should be replaced with FileSystem access
|
||||||
|
|
||||||
|
val result = assemble(srcName, asm)
|
||||||
|
if (!result.errors.isEmpty()) {
|
||||||
|
throw RuntimeException(
|
||||||
|
"Errors:\n" + result.errors
|
||||||
|
.stream()
|
||||||
|
.map<String> { e: ASMError? -> " - " + e!!.formattedErrorMessage() }
|
||||||
|
.collect(Collectors.joining("\n")))
|
||||||
|
}
|
||||||
|
|
||||||
|
return Executable(
|
||||||
|
Executable.Format.Orc1,
|
||||||
|
result.code,
|
||||||
|
result.data,
|
||||||
|
result.graphics,
|
||||||
|
srcName,
|
||||||
|
result.debugInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseAssembly() {
|
||||||
|
while (tokenizer!!.more()) {
|
||||||
|
parseLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genData(): ByteArray {
|
||||||
|
val dataBytes = ByteArray(dataSize)
|
||||||
|
for (dataElt in data) {
|
||||||
|
dataElt.genData(dataBytes, this)
|
||||||
|
}
|
||||||
|
return dataBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genCode(): ByteArray {
|
||||||
|
val code = ByteArray(this.instructionsSizeInBytes)
|
||||||
|
for (instruction in instructions) {
|
||||||
|
instruction.genCode(code, this)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genOriginalLineNumbers(length: Int): IntArray {
|
||||||
|
val asmLineNumbers = IntArray(length)
|
||||||
|
var currentLineNumber = 0
|
||||||
|
var location = 0
|
||||||
|
for (instruction in instructions) {
|
||||||
|
if (instruction is MetaInstruction && instruction.isLineDirective) {
|
||||||
|
currentLineNumber = instruction.originalLineNumber
|
||||||
|
}
|
||||||
|
while (location < instruction.location + instruction.sizeInBytes) {
|
||||||
|
asmLineNumbers[location] = currentLineNumber
|
||||||
|
location++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return asmLineNumbers
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genGraphics(): Array<ByteArray> {
|
||||||
|
val graphics = Array<ByteArray>(this.graphics.size) { index ->
|
||||||
|
graphics[index].imageData
|
||||||
|
}
|
||||||
|
|
||||||
|
return graphics
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectErrors(): MutableList<ASMError> {
|
||||||
|
val errors: MutableList<ASMError> = ArrayList<ASMError>()
|
||||||
|
for (instruction in instructions) {
|
||||||
|
errors.addAll(instruction.errors)
|
||||||
|
}
|
||||||
|
for (data in this.data) {
|
||||||
|
errors.addAll(data.errors)
|
||||||
|
}
|
||||||
|
for (graphic in this.graphics) {
|
||||||
|
errors.addAll(graphic.errors)
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveLocations() {
|
||||||
|
// assign locations
|
||||||
|
var offset = 0
|
||||||
|
for (instruction in instructions) {
|
||||||
|
instruction.location = offset
|
||||||
|
offset += instruction.sizeInBytes
|
||||||
|
this.instructionsSizeInBytes += instruction.sizeInBytes
|
||||||
|
}
|
||||||
|
for (data in data) {
|
||||||
|
data.location = offset
|
||||||
|
offset += data.sizeInBytes
|
||||||
|
dataSize += data.sizeInBytes
|
||||||
|
}
|
||||||
|
for (instruction in instructions) {
|
||||||
|
instruction.validateLabel(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun parseLine() {
|
||||||
|
val tokens = this.tokensForLine
|
||||||
|
|
||||||
|
if (parseMetaDirective(tokens)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val newMode = parseModeFlag(tokens)
|
||||||
|
if (newMode != null) {
|
||||||
|
mode = newMode
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// label only lines are ok and propagate to the next instruction
|
||||||
|
val labelTokens: MutableList<MTMCToken> = maybeGetLabels(tokens)
|
||||||
|
val labels = ArrayList<MTMCToken>(lastLabels)
|
||||||
|
labels.addAll(labelTokens)
|
||||||
|
lastLabels = labels
|
||||||
|
if (tokens.isEmpty()) {
|
||||||
|
for (labelToken in labelTokens) {
|
||||||
|
val labelData = Data(labelTokens, labelToken.line)
|
||||||
|
if (hasLabel(labelToken.stringValue)) {
|
||||||
|
labelData.addError(tokens.poll(), "Label already defined: " + labelToken.stringValue)
|
||||||
|
} else {
|
||||||
|
this.labels.put(labelToken.labelValue(), labelData)
|
||||||
|
}
|
||||||
|
data.add(labelData)
|
||||||
|
}
|
||||||
|
} else if (mode == ASMMode.TEXT) {
|
||||||
|
parseInstruction(tokens, labels)
|
||||||
|
} else {
|
||||||
|
parseData(tokens, labels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseMetaDirective(tokens: LinkedList<MTMCToken>): Boolean {
|
||||||
|
if (tokens.size >= 2 && tokens.get(0).type == MTMCToken.TokenType.AT) {
|
||||||
|
tokens.removeFirst()
|
||||||
|
val metaInstruction = MetaInstruction(tokens.removeFirst())
|
||||||
|
if (metaInstruction.isFileDirective) {
|
||||||
|
val path = requireString(tokens, metaInstruction)
|
||||||
|
metaInstruction.setOriginalFilePath(path)
|
||||||
|
} else if (metaInstruction.isGlobalDirective) {
|
||||||
|
val name = requireString(tokens, metaInstruction)
|
||||||
|
val location = requireIntegerToken(tokens, metaInstruction, Int.Companion.MAX_VALUE)
|
||||||
|
val type = requireString(tokens, metaInstruction)
|
||||||
|
metaInstruction.setGlobalInfo(name, location, type)
|
||||||
|
} else if (metaInstruction.isLocalDirective) {
|
||||||
|
val name = requireString(tokens, metaInstruction)
|
||||||
|
val offset = requireIntegerToken(tokens, metaInstruction, Int.Companion.MAX_VALUE)
|
||||||
|
val type = requireString(tokens, metaInstruction)
|
||||||
|
metaInstruction.setLocalInfo(name, offset, type)
|
||||||
|
} else if (metaInstruction.isEndLocalDirective) {
|
||||||
|
val name = requireString(tokens, metaInstruction)
|
||||||
|
metaInstruction.setEndLocalInfo(name)
|
||||||
|
} else if (metaInstruction.isLineDirective) {
|
||||||
|
val lineNumber = requireIntegerToken(tokens, metaInstruction, Int.Companion.MAX_VALUE)
|
||||||
|
metaInstruction.setOriginalLineNumber(lineNumber)
|
||||||
|
} else {
|
||||||
|
metaInstruction.addError("Unknown meta directive")
|
||||||
|
}
|
||||||
|
instructions.add(metaInstruction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseModeFlag(tokens: LinkedList<MTMCToken>): ASMMode? {
|
||||||
|
if (tokens.size >= 2 && tokens.get(0).type == MTMCToken.TokenType.DOT && tokens.get(1).type == MTMCToken.TokenType.IDENTIFIER && tokens.get(
|
||||||
|
0
|
||||||
|
).end == tokens.get(1).start && (tokens.get(1).stringValue == "data" ||
|
||||||
|
tokens.get(1).stringValue == "text")
|
||||||
|
) {
|
||||||
|
return ASMMode.valueOf(tokens.get(1).stringValue.uppercase(Locale.getDefault()))
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseData(tokens: LinkedList<MTMCToken>, labelTokens: MutableList<MTMCToken>) {
|
||||||
|
lastLabels = mutableListOf()
|
||||||
|
var dataToken = tokens.poll()
|
||||||
|
var dataElt = Data(labelTokens, if (dataToken == null) 0 else dataToken.line)
|
||||||
|
if (dataToken != null) {
|
||||||
|
if (dataToken.type == MTMCToken.TokenType.STRING) {
|
||||||
|
val stringBytes = dataToken.stringValue.toByteArray(StandardCharsets.US_ASCII)
|
||||||
|
val nullTerminated = ByteArray(stringBytes.size + 1)
|
||||||
|
System.arraycopy(stringBytes, 0, nullTerminated, 0, stringBytes.size)
|
||||||
|
nullTerminated[stringBytes.size] = '\u0000'.code.toByte()
|
||||||
|
dataElt.setValue(dataToken, nullTerminated)
|
||||||
|
} else if (isInteger(dataToken)) {
|
||||||
|
val integerValue = dataToken.intValue()
|
||||||
|
if (integerValue > Short.Companion.MAX_VALUE || integerValue < Short.Companion.MIN_VALUE) {
|
||||||
|
dataElt.addError(dataToken, "Number is too large")
|
||||||
|
}
|
||||||
|
dataElt.setValue(
|
||||||
|
dataToken,
|
||||||
|
byteArrayOf((integerValue ushr 8).toByte(), integerValue.toByte())
|
||||||
|
)
|
||||||
|
} else if (dataToken.type == MTMCToken.TokenType.DOT) {
|
||||||
|
dataToken = tokens.poll()
|
||||||
|
dataElt = Data(labelTokens, dataToken.line)
|
||||||
|
if (dataToken.stringValue == "int") {
|
||||||
|
val intToken = requireIntegerToken(tokens, dataElt, MonTanaMiniComputer.MEMORY_SIZE)
|
||||||
|
if (intToken != null) {
|
||||||
|
dataElt.setValue(intToken, ByteArray(intToken.intValue() * 2))
|
||||||
|
}
|
||||||
|
} else if (dataToken.stringValue == "byte") {
|
||||||
|
val intToken = requireIntegerToken(tokens, dataElt, MonTanaMiniComputer.MEMORY_SIZE)
|
||||||
|
if (intToken != null) {
|
||||||
|
dataElt.setValue(intToken, ByteArray(intToken.intValue()))
|
||||||
|
}
|
||||||
|
} else if (dataToken.stringValue == "image") {
|
||||||
|
val stringToken = requireString(tokens, dataElt)
|
||||||
|
if (stringToken != null) {
|
||||||
|
loadGraphic(labelTokens, dataElt, stringToken)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataElt.addError(dataToken, "only data types are .int, .byte, and .image")
|
||||||
|
}
|
||||||
|
} else if (dataToken.type == MTMCToken.TokenType.MINUS) {
|
||||||
|
val nextToken = tokens.poll() // get next
|
||||||
|
if (nextToken == null || (nextToken.type != MTMCToken.TokenType.INTEGER && nextToken.type != MTMCToken.TokenType.HEX && nextToken.type != MTMCToken.TokenType.BINARY)) {
|
||||||
|
dataElt.addError(dataToken, "Number is too negative")
|
||||||
|
} else {
|
||||||
|
val integerValue = -1 * nextToken.intValue()
|
||||||
|
if (integerValue < Short.Companion.MIN_VALUE) {
|
||||||
|
dataElt.addError(dataToken, "Number is too negative")
|
||||||
|
}
|
||||||
|
val joinToken = MTMCToken.join(dataToken, nextToken, MTMCToken.TokenType.INTEGER)
|
||||||
|
dataElt.setValue(
|
||||||
|
joinToken,
|
||||||
|
byteArrayOf((integerValue ushr 8).toByte(), integerValue.toByte())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataElt.addError(dataToken, "Unknown token type: " + dataToken.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (labelToken in labelTokens) {
|
||||||
|
if (hasLabel(labelToken.stringValue)) {
|
||||||
|
dataElt.addError(tokens.poll(), "Label already defined: " + labelToken.stringValue)
|
||||||
|
} else {
|
||||||
|
labels.put(labelToken.labelValue(), dataElt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.add(dataElt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadGraphic(
|
||||||
|
labelTokens: MutableList<MTMCToken>,
|
||||||
|
data: Data,
|
||||||
|
token: MTMCToken
|
||||||
|
): Graphic {
|
||||||
|
val graphic = Graphic(labelTokens, token.line)
|
||||||
|
val filename = token.stringValue
|
||||||
|
val file = File(File(this.srcName).getParent(), filename)
|
||||||
|
val index = graphics.size
|
||||||
|
|
||||||
|
data.setValue(token, byteArrayOf(((index shr 8) and 0xFF).toByte(), (index and 0xFF).toByte()))
|
||||||
|
graphic.setImage(file.getPath())
|
||||||
|
graphics.add(graphic)
|
||||||
|
|
||||||
|
return graphic
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseInstruction(tokens: LinkedList<MTMCToken>, labelTokens: MutableList<MTMCToken>) {
|
||||||
|
var tokens = tokens
|
||||||
|
var instructionToken = tokens.peekFirst()
|
||||||
|
if (instructionToken == null) return
|
||||||
|
|
||||||
|
lastLabels = mutableListOf()
|
||||||
|
if (instructionToken.type != MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
instructions.add(ErrorInstruction(labelTokens, instructionToken, "Invalid Token"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens = transformSyntheticInstructions(tokens)
|
||||||
|
|
||||||
|
instructionToken = tokens.poll()
|
||||||
|
val type = fromString(instructionToken.stringValue)
|
||||||
|
|
||||||
|
if (type == null) {
|
||||||
|
instructions.add(
|
||||||
|
ErrorInstruction(
|
||||||
|
labelTokens,
|
||||||
|
instructionToken,
|
||||||
|
"Unknown instruction token type: " + instructionToken.stringValue
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val instruction: Instruction?
|
||||||
|
if (type.instructionClass == InstructionClass.MISC) {
|
||||||
|
val miscInst = MiscInstruction(type, labelTokens, instructionToken)
|
||||||
|
if (type == InstructionType.SYS) {
|
||||||
|
val sysCallType = requireSysCall(tokens, miscInst)
|
||||||
|
miscInst.setSyscallType(sysCallType)
|
||||||
|
} else if (type == InstructionType.MOV) {
|
||||||
|
val toRegister = requireWriteableRegister(tokens, miscInst)
|
||||||
|
miscInst.setTo(toRegister)
|
||||||
|
val fromRegister = requireReadableRegister(tokens, miscInst)
|
||||||
|
miscInst.setFrom(fromRegister)
|
||||||
|
} else if (type == InstructionType.INC || type == InstructionType.DEC) {
|
||||||
|
val toRegister = requireWriteableRegister(tokens, miscInst)
|
||||||
|
miscInst.setTo(toRegister)
|
||||||
|
if (!tokens.isEmpty()) {
|
||||||
|
val value = requireIntegerToken(tokens, miscInst, 15)
|
||||||
|
miscInst.setValue(value)
|
||||||
|
}
|
||||||
|
} else if (type == InstructionType.SETI) {
|
||||||
|
val toRegister = requireWriteableRegister(tokens, miscInst)
|
||||||
|
miscInst.setTo(toRegister)
|
||||||
|
val value = requireIntegerToken(tokens, miscInst, 15)
|
||||||
|
miscInst.setValue(value)
|
||||||
|
} else if (type == InstructionType.MCP) {
|
||||||
|
val fromRegister = requireWriteableRegister(tokens, miscInst)
|
||||||
|
miscInst.setFrom(fromRegister)
|
||||||
|
val toRegister = requireWriteableRegister(tokens, miscInst)
|
||||||
|
miscInst.setTo(toRegister)
|
||||||
|
val value = requireIntegerToken(tokens, miscInst, Short.Companion.MAX_VALUE.toInt())
|
||||||
|
miscInst.setValue(value)
|
||||||
|
} else if (type == InstructionType.DEBUG) {
|
||||||
|
val debugString = requireString(tokens, miscInst)
|
||||||
|
// create a dummy int token representing the offset of the debug string in the debug info
|
||||||
|
val debugStringIndex = debugStrings!!.size
|
||||||
|
debugStrings!!.add(debugString.stringValue)
|
||||||
|
val value = MTMCToken(0, 0, 0, 0, debugStringIndex.toString(), MTMCToken.TokenType.INTEGER)
|
||||||
|
miscInst.setValue(value)
|
||||||
|
}
|
||||||
|
instruction = miscInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.ALU) {
|
||||||
|
val aluInst = ALUInstruction(type, labelTokens, instructionToken)
|
||||||
|
if (aluInst.isBinaryOp) {
|
||||||
|
val toRegister = requireWriteableRegister(tokens, aluInst)
|
||||||
|
aluInst.setTo(toRegister)
|
||||||
|
val fromRegister = requireReadableRegister(tokens, aluInst)
|
||||||
|
aluInst.setFrom(fromRegister)
|
||||||
|
} else if (aluInst.isImmediateOp()) {
|
||||||
|
val immediateOp = requireALUOp(tokens, aluInst)
|
||||||
|
// TODO - validate is max or lower op
|
||||||
|
aluInst.setImmediateOp(immediateOp)
|
||||||
|
|
||||||
|
val toRegister = requireWriteableRegister(tokens, aluInst)
|
||||||
|
aluInst.setTo(toRegister)
|
||||||
|
|
||||||
|
val value = requireIntegerToken(tokens, aluInst, Short.Companion.MAX_VALUE.toInt())
|
||||||
|
aluInst.setImmediateValue(value)
|
||||||
|
} else {
|
||||||
|
val toRegister = requireWriteableRegister(tokens, aluInst)
|
||||||
|
aluInst.setTo(toRegister)
|
||||||
|
}
|
||||||
|
instruction = aluInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.STACK) {
|
||||||
|
val stackInst = StackInstruction(type, labelTokens, instructionToken)
|
||||||
|
if (type == InstructionType.PUSH) {
|
||||||
|
val targetRegister = requireReadableRegister(tokens, stackInst)
|
||||||
|
stackInst.setTarget(targetRegister)
|
||||||
|
} else if (type == InstructionType.POP) {
|
||||||
|
val toRegister = requireWriteableRegister(tokens, stackInst)
|
||||||
|
stackInst.setTarget(toRegister)
|
||||||
|
} else if (type == InstructionType.SOP) {
|
||||||
|
val aluOp = requireAluOp(tokens, stackInst)
|
||||||
|
stackInst.setALUOp(aluOp)
|
||||||
|
} else if (type == InstructionType.PUSHI) {
|
||||||
|
val value = requireIntegerToken(tokens, stackInst, Short.Companion.MAX_VALUE.toInt())
|
||||||
|
stackInst.setValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a stack register specified, consume it
|
||||||
|
if (!tokens.isEmpty() && tokens.peekFirst().type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
val stackReg = requireReadableRegister(tokens, stackInst)
|
||||||
|
stackInst.setStackRegister(stackReg)
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction = stackInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.TEST) {
|
||||||
|
val testInst = TestInstruction(type, labelTokens, instructionToken)
|
||||||
|
testInst.setFirst(requireReadableRegister(tokens, testInst))
|
||||||
|
if (testInst.isImmediate) {
|
||||||
|
testInst.setImmediateValue(requireIntegerToken(tokens, testInst, 15))
|
||||||
|
} else {
|
||||||
|
testInst.setSecond(requireReadableRegister(tokens, testInst))
|
||||||
|
}
|
||||||
|
instruction = testInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.LOAD_STORE) {
|
||||||
|
val loadInst = LoadStoreInstruction(type, labelTokens, instructionToken)
|
||||||
|
|
||||||
|
val targetReg = requireReadableRegister(tokens, loadInst)
|
||||||
|
loadInst.setTargetToken(targetReg)
|
||||||
|
|
||||||
|
if (loadInst.isOffset) {
|
||||||
|
val offsetReg = requireReadableRegister(tokens, loadInst)
|
||||||
|
loadInst.setOffsetToken(offsetReg)
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = requireIntegerOrLabelReferenceToken(tokens, loadInst)
|
||||||
|
loadInst.setValue(value)
|
||||||
|
|
||||||
|
instruction = loadInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.LOAD_STORE_REGISTER) {
|
||||||
|
val loadInst = LoadStoreRegisterInstruction(type, labelTokens, instructionToken)
|
||||||
|
|
||||||
|
val targetReg = requireWriteableRegister(tokens, loadInst)
|
||||||
|
loadInst.setTargetToken(targetReg)
|
||||||
|
|
||||||
|
val pointerReg = requireWriteableRegister(tokens, loadInst)
|
||||||
|
loadInst.setPointerToken(pointerReg)
|
||||||
|
|
||||||
|
// if there is an offset register specified, consume it
|
||||||
|
if (!tokens.isEmpty() && tokens.peekFirst().type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
val offsetReg = requireReadableRegister(tokens, loadInst)
|
||||||
|
loadInst.setOffsetToken(offsetReg)
|
||||||
|
}
|
||||||
|
instruction = loadInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.JUMP_REGISTER) {
|
||||||
|
val jumpInst = JumpRegisterInstruction(type, labelTokens, instructionToken)
|
||||||
|
val register = requireReadableRegister(tokens, jumpInst)
|
||||||
|
jumpInst.setRegister(register)
|
||||||
|
instruction = jumpInst
|
||||||
|
} else if (type.instructionClass == InstructionClass.JUMP) {
|
||||||
|
val jumpInst = JumpInstruction(type, labelTokens, instructionToken)
|
||||||
|
val labelValue: MTMCToken? = maybeGetLabelReference(tokens)
|
||||||
|
val valueToken: MTMCToken?
|
||||||
|
if (labelValue != null) {
|
||||||
|
valueToken = labelValue
|
||||||
|
} else {
|
||||||
|
valueToken = requireIntegerToken(tokens, jumpInst, MonTanaMiniComputer.MEMORY_SIZE)
|
||||||
|
}
|
||||||
|
jumpInst.setAddressToken(valueToken)
|
||||||
|
instruction = jumpInst
|
||||||
|
} else {
|
||||||
|
instruction = ErrorInstruction(
|
||||||
|
listOf(),
|
||||||
|
instructionToken,
|
||||||
|
"Unexpected Token"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (labelToken in labelTokens) {
|
||||||
|
if (hasLabel(labelToken.stringValue)) {
|
||||||
|
instruction.addError(tokens.poll(), "Label already defined: " + labelToken.stringValue)
|
||||||
|
} else {
|
||||||
|
labels.put(labelToken.labelValue(), instruction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add error if any tokens are left on the line
|
||||||
|
if (!tokens.isEmpty()) {
|
||||||
|
instruction.addError(tokens.poll(), "Unexpected Token")
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.add(instruction)
|
||||||
|
}
|
||||||
|
|
||||||
|
//===================================================
|
||||||
|
// tokenization helper functions
|
||||||
|
//===================================================
|
||||||
|
private fun requireSysCall(tokens: LinkedList<MTMCToken>, inst: Instruction): MTMCToken {
|
||||||
|
val sysCallType = tokens.poll()
|
||||||
|
if (sysCallType == null) {
|
||||||
|
inst.addError("Syscall required")
|
||||||
|
} else if (sysCallType.type != MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
inst.addError(sysCallType, "Syscall required")
|
||||||
|
} else if (!SysCall.isSysCall(sysCallType.stringValue)) {
|
||||||
|
inst.addError(sysCallType, "Unknown syscall : " + sysCallType.stringValue)
|
||||||
|
}
|
||||||
|
return sysCallType!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireAluOp(tokens: LinkedList<MTMCToken>, inst: Instruction): MTMCToken {
|
||||||
|
val sysCallType = tokens.poll()
|
||||||
|
if (sysCallType == null) {
|
||||||
|
inst.addError("Syscall required")
|
||||||
|
} else if (sysCallType.type != MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
inst.addError(sysCallType, "Syscall required")
|
||||||
|
} else if (!isALUOp(sysCallType.stringValue)) {
|
||||||
|
inst.addError(sysCallType, "Unknown alu operation : " + sysCallType.stringValue)
|
||||||
|
}
|
||||||
|
return sysCallType!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireWriteableRegister(
|
||||||
|
tokens: LinkedList<MTMCToken>,
|
||||||
|
instruction: Instruction
|
||||||
|
): MTMCToken {
|
||||||
|
val nextToken = tokens.poll()
|
||||||
|
if (nextToken == null) {
|
||||||
|
instruction.addError("Register required")
|
||||||
|
} else if (nextToken.type != MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
instruction.addError(nextToken, "Invalid Register : " + nextToken.stringValue)
|
||||||
|
} else if (!isWriteable(nextToken.stringValue)) {
|
||||||
|
instruction.addError(nextToken, "Register not writeable : " + nextToken.stringValue)
|
||||||
|
}
|
||||||
|
return nextToken!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireReadableRegister(
|
||||||
|
tokens: LinkedList<MTMCToken>,
|
||||||
|
instruction: Instruction
|
||||||
|
): MTMCToken {
|
||||||
|
val nextToken = tokens.poll()
|
||||||
|
if (nextToken == null) {
|
||||||
|
instruction.addError("Register required")
|
||||||
|
} else if (nextToken.type != MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
instruction.addError(nextToken, "Invalid Register : " + nextToken.stringValue)
|
||||||
|
} else if (!isReadable(nextToken.stringValue)) {
|
||||||
|
instruction.addError(nextToken, "Register not readable : " + nextToken.stringValue)
|
||||||
|
}
|
||||||
|
return nextToken!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireALUOp(tokens: LinkedList<MTMCToken>, instruction: Instruction): MTMCToken {
|
||||||
|
val nextToken = tokens.poll()
|
||||||
|
if (nextToken == null || nextToken.type != MTMCToken.TokenType.IDENTIFIER || !isALUOp(nextToken.stringValue)) {
|
||||||
|
instruction.addError("ALU operation required")
|
||||||
|
}
|
||||||
|
return nextToken!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireString(tokens: LinkedList<MTMCToken>, instruction: ASMElement): MTMCToken {
|
||||||
|
val nextToken = tokens.poll()
|
||||||
|
if (nextToken == null || nextToken.type != MTMCToken.TokenType.STRING) {
|
||||||
|
instruction.addError("String required")
|
||||||
|
}
|
||||||
|
return nextToken!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireIntegerToken(
|
||||||
|
tokens: LinkedList<MTMCToken>,
|
||||||
|
inst: ASMElement,
|
||||||
|
max: Int
|
||||||
|
): MTMCToken {
|
||||||
|
val token = tokens.poll()
|
||||||
|
if (token == null) {
|
||||||
|
inst.addError("Integer value required")
|
||||||
|
} else if (isInteger(token)) {
|
||||||
|
val integerValue = token.intValue()
|
||||||
|
if (integerValue < 0 || max < integerValue) {
|
||||||
|
inst.addError(token, "Integer value out of range: 0-" + max)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inst.addError(token, "Integer value expected")
|
||||||
|
}
|
||||||
|
return token!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireToken(
|
||||||
|
tokens: LinkedList<MTMCToken>,
|
||||||
|
type: MTMCToken.TokenType,
|
||||||
|
inst: ASMElement
|
||||||
|
): MTMCToken? {
|
||||||
|
val token = tokens.poll()
|
||||||
|
if (token == null || token.type != type) {
|
||||||
|
inst.addError(token, "Token " + type.name + " required")
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requireIntegerOrLabelReferenceToken(
|
||||||
|
tokens: LinkedList<MTMCToken>,
|
||||||
|
inst: LoadStoreInstruction
|
||||||
|
): MTMCToken {
|
||||||
|
val token = tokens.poll()
|
||||||
|
if (token == null) {
|
||||||
|
inst.addError("Integer or label value required")
|
||||||
|
} else if (!isInteger(token) && token.type != MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
inst.addError(token, "Integer or label value expected")
|
||||||
|
}
|
||||||
|
return token!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val tokensForLine: LinkedList<MTMCToken>
|
||||||
|
get() {
|
||||||
|
val tokens = LinkedList<MTMCToken>()
|
||||||
|
if (tokenizer!!.more()) {
|
||||||
|
val first = tokenizer!!.consume()
|
||||||
|
tokens.add(first)
|
||||||
|
while (tokenizer!!.more() &&
|
||||||
|
first.line == tokenizer!!.currentToken().line
|
||||||
|
) {
|
||||||
|
tokens.add(tokenizer!!.consume())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasLabel(label: String?): Boolean {
|
||||||
|
return labels.containsKey(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolveLabel(label: String?): Int {
|
||||||
|
return labels.get(label)!!.location
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum class ASMMode {
|
||||||
|
DATA,
|
||||||
|
TEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun transformSyntheticInstructions(tokens: LinkedList<MTMCToken>): LinkedList<MTMCToken> {
|
||||||
|
if (!tokens.isEmpty()) {
|
||||||
|
val first = tokens.peekFirst()
|
||||||
|
if (first.type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
val stringVal = first.stringValue
|
||||||
|
if (stringVal.endsWith("i")) {
|
||||||
|
val op = stringVal.substring(0, stringVal.length - 1)
|
||||||
|
if (isALUOp(op)) {
|
||||||
|
val syntheticImmediate = tokens.removeFirst()
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal(op))
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal("imm"))
|
||||||
|
}
|
||||||
|
} else if (stringVal.startsWith("s")) {
|
||||||
|
val op = stringVal.substring(1, stringVal.length)
|
||||||
|
if (isALUOp(op)) {
|
||||||
|
val syntheticImmediate = tokens.removeFirst()
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal(op))
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal("sop"))
|
||||||
|
}
|
||||||
|
} else if (stringVal == "la") {
|
||||||
|
val syntheticImmediate = tokens.removeFirst()
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal("li"))
|
||||||
|
} else if (stringVal == "ret") {
|
||||||
|
val syntheticImmediate = tokens.removeFirst()
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal("ra"))
|
||||||
|
tokens.addFirst(syntheticImmediate.cloneWithVal("jr"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun maybeGetLabels(tokens: LinkedList<MTMCToken>): MutableList<MTMCToken> {
|
||||||
|
val labels = LinkedList<MTMCToken>()
|
||||||
|
while (!tokens.isEmpty() && tokens.getFirst().type == MTMCToken.TokenType.LABEL) {
|
||||||
|
val label = tokens.poll()
|
||||||
|
labels.add(label!!)
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun maybeGetLabelReference(tokens: LinkedList<MTMCToken>): MTMCToken? {
|
||||||
|
var label: MTMCToken? = null
|
||||||
|
if (tokens.getFirst().type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
label = tokens.poll()
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isInteger(token: MTMCToken?): Boolean {
|
||||||
|
return token != null && (token.type == MTMCToken.TokenType.INTEGER || token.type == MTMCToken.TokenType.HEX || token.type == MTMCToken.TokenType.BINARY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/jvmMain/kotlin/mtmc/asm/AssemblyResult.kt
Normal file
20
src/jvmMain/kotlin/mtmc/asm/AssemblyResult.kt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package mtmc.asm
|
||||||
|
|
||||||
|
import mtmc.emulator.DebugInfo
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
data class AssemblyResult(
|
||||||
|
@JvmField val code: ByteArray,
|
||||||
|
val data: ByteArray,
|
||||||
|
val graphics: Array<ByteArray>,
|
||||||
|
@JvmField val debugInfo: DebugInfo?,
|
||||||
|
@JvmField val errors: MutableList<ASMError>
|
||||||
|
) {
|
||||||
|
fun printErrors(): String {
|
||||||
|
val builder = StringBuilder("Errors:\n")
|
||||||
|
for (error in errors) {
|
||||||
|
builder.append(" Line " + error.token.line + ": " + error.error).append('\n')
|
||||||
|
}
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/jvmMain/kotlin/mtmc/asm/HasLocation.kt
Normal file
6
src/jvmMain/kotlin/mtmc/asm/HasLocation.kt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package mtmc.asm
|
||||||
|
|
||||||
|
interface HasLocation {
|
||||||
|
var location: Int
|
||||||
|
var sizeInBytes: Int
|
||||||
|
}
|
||||||
37
src/jvmMain/kotlin/mtmc/asm/data/Data.kt
Normal file
37
src/jvmMain/kotlin/mtmc/asm/data/Data.kt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package mtmc.asm.data
|
||||||
|
|
||||||
|
import mtmc.asm.ASMElement
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
|
||||||
|
class Data(labels: MutableList<MTMCToken>, lineNumber: Int) : ASMElement(labels, lineNumber) {
|
||||||
|
var valueToken: MTMCToken? = null
|
||||||
|
private var value: ByteArray = ByteArray(0)
|
||||||
|
|
||||||
|
override var sizeInBytes: Int
|
||||||
|
get() = value.size
|
||||||
|
set(value) {}
|
||||||
|
|
||||||
|
fun genData(dataBytes: ByteArray, assembler: Assembler) {
|
||||||
|
val offset = location - assembler.instructionsSizeInBytes
|
||||||
|
for (i in 0..<sizeInBytes) {
|
||||||
|
val dataByte = value[i]
|
||||||
|
dataBytes[offset + i] = dataByte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(src: MTMCToken, value: ByteArray) {
|
||||||
|
this.valueToken = src
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Data{" +
|
||||||
|
"value=" + value.contentToString() +
|
||||||
|
'}'
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addError(err: String) {
|
||||||
|
addError(labels.last(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/jvmMain/kotlin/mtmc/asm/graphics/Graphic.kt
Normal file
51
src/jvmMain/kotlin/mtmc/asm/graphics/Graphic.kt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package mtmc.asm.graphics
|
||||||
|
|
||||||
|
import mtmc.asm.ASMElement
|
||||||
|
import mtmc.emulator.MTMCDisplay.Companion.convertImage
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.io.IOException
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jbanes
|
||||||
|
*/
|
||||||
|
class Graphic(labels: MutableList<MTMCToken>, lineNumber: Int) : ASMElement(labels, lineNumber) {
|
||||||
|
private var filename: String? = null
|
||||||
|
var imageData: ByteArray = ByteArray(0)
|
||||||
|
|
||||||
|
fun setImage(filename: String) {
|
||||||
|
try {
|
||||||
|
var image = ImageIO.read(File(filename))
|
||||||
|
val buffer = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
if (image.getWidth() > 1024 || image.getHeight() > 1024) {
|
||||||
|
addError(filename + " is too large. Maximum image size is 1024x1024")
|
||||||
|
}
|
||||||
|
|
||||||
|
image = convertImage(image)
|
||||||
|
|
||||||
|
ImageIO.write(image, "png", buffer)
|
||||||
|
|
||||||
|
this.filename = filename
|
||||||
|
this.imageData = buffer.toByteArray()
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
addError("$filename not found")
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
addError(e.message ?: "error in Graphic") // TODO: Verify these messages are meaningful
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addError(err: String) {
|
||||||
|
addError(labels.last(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var sizeInBytes: Int
|
||||||
|
get() = 2
|
||||||
|
set(value) {}
|
||||||
|
}
|
||||||
89
src/jvmMain/kotlin/mtmc/asm/instructions/ALUInstruction.kt
Normal file
89
src/jvmMain/kotlin/mtmc/asm/instructions/ALUInstruction.kt
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.emulator.Register.Companion.toInteger
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class ALUInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var toToken: MTMCToken? = null
|
||||||
|
private var fromToken: MTMCToken? = null
|
||||||
|
private var immediateOp: MTMCToken? = null
|
||||||
|
private var value: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setTo(to: MTMCToken) {
|
||||||
|
this.toToken = to
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFrom(from: MTMCToken?) {
|
||||||
|
this.fromToken = from
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setImmediateValue(value: MTMCToken) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setImmediateOp(immediateOp: MTMCToken) {
|
||||||
|
this.immediateOp = immediateOp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isImmediateOp(): Boolean {
|
||||||
|
return this.type == InstructionType.IMM
|
||||||
|
}
|
||||||
|
|
||||||
|
val isBinaryOp: Boolean
|
||||||
|
get() = !ALUOp.valueOf(instructionToken.stringValue.uppercase(Locale.getDefault())).isUnary
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
val opCode = ALUOp.toInteger(instructionToken.stringValue)
|
||||||
|
val to = toInteger(toToken!!.stringValue)
|
||||||
|
var from = 0
|
||||||
|
if (fromToken != null) {
|
||||||
|
from = toInteger(fromToken!!.stringValue)
|
||||||
|
}
|
||||||
|
output[location] = (16 or opCode).toByte()
|
||||||
|
if (this.isBinaryOp) {
|
||||||
|
output[location + 1] = (to shl 4 or from).toByte()
|
||||||
|
} else if (isImmediateOp()) {
|
||||||
|
val immediateValue = value!!.intValue()
|
||||||
|
val immediateOpValue = ALUOp.toInteger(immediateOp!!.stringValue)
|
||||||
|
output[location + 1] = (to shl 4 or immediateOpValue).toByte()
|
||||||
|
output[location + 2] = (immediateValue ushr 8).toByte()
|
||||||
|
output[location + 3] = immediateValue.toByte()
|
||||||
|
} else { // unary op
|
||||||
|
output[location + 1] = (to shl 4).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 1) {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
val opCode = BinaryUtils.getBits(12, 4, instruction)
|
||||||
|
val op = ALUOp.fromInt(opCode)
|
||||||
|
val aluOp = ALUOp.valueOf(op.uppercase(Locale.getDefault()))
|
||||||
|
if (aluOp == ALUOp.IMM) {
|
||||||
|
builder.append(op).append(" ")
|
||||||
|
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")
|
||||||
|
builder.append(ALUOp.fromInt(BinaryUtils.getBits(4, 4, instruction))).append(" ")
|
||||||
|
} else if (aluOp.isUnary) {
|
||||||
|
builder.append(op).append(" ")
|
||||||
|
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")
|
||||||
|
} else {
|
||||||
|
builder.append(op).append(" ")
|
||||||
|
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")
|
||||||
|
builder.append(fromInteger(BinaryUtils.getBits(4, 4, instruction).toInt())).append(" ")
|
||||||
|
}
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/jvmMain/kotlin/mtmc/asm/instructions/ALUOp.kt
Normal file
44
src/jvmMain/kotlin/mtmc/asm/instructions/ALUOp.kt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
enum class ALUOp(val opCode: Int, val isUnary: Boolean) {
|
||||||
|
ADD(0x0000, false),
|
||||||
|
SUB(0x0001, false),
|
||||||
|
MUL(0x0002, false),
|
||||||
|
DIV(0x0003, false),
|
||||||
|
MOD(0x0004, false),
|
||||||
|
AND(0x0005, false),
|
||||||
|
OR(0x0006, false),
|
||||||
|
XOR(0x0007, false),
|
||||||
|
SHL(0x0008, false),
|
||||||
|
SHR(0x0009, false),
|
||||||
|
MIN(0x000A, false),
|
||||||
|
MAX(0x000B, false),
|
||||||
|
NOT(0x000C, true),
|
||||||
|
LNOT(0x000D, true),
|
||||||
|
NEG(0x000E, true),
|
||||||
|
IMM(0x000F, true);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun toInteger(instruction: String): Int {
|
||||||
|
return valueOf(instruction.uppercase(Locale.getDefault())).opCode
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun fromInt(opCode: Short): String {
|
||||||
|
return entries[opCode.toInt()].name.lowercase(Locale.getDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isALUOp(op: String): Boolean {
|
||||||
|
try {
|
||||||
|
val aluOp = valueOf(op.uppercase(Locale.getDefault()))
|
||||||
|
return true
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/jvmMain/kotlin/mtmc/asm/instructions/ErrorInstruction.kt
Normal file
23
src/jvmMain/kotlin/mtmc/asm/instructions/ErrorInstruction.kt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
|
||||||
|
class ErrorInstruction(
|
||||||
|
labels: List<MTMCToken>,
|
||||||
|
instruction: MTMCToken,
|
||||||
|
error: String
|
||||||
|
) : Instruction(
|
||||||
|
InstructionType.ERROR,
|
||||||
|
labels,
|
||||||
|
instruction
|
||||||
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
addError(instruction, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/jvmMain/kotlin/mtmc/asm/instructions/Instruction.kt
Normal file
72
src/jvmMain/kotlin/mtmc/asm/instructions/Instruction.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.ASMElement
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.asm.instructions.ALUInstruction.Companion.disassemble
|
||||||
|
import mtmc.emulator.MonTanaMiniComputer.Companion.isDoubleWordInstruction
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
|
||||||
|
abstract class Instruction(
|
||||||
|
@JvmField val type: InstructionType,
|
||||||
|
labels: List<MTMCToken>,
|
||||||
|
@JvmField val instructionToken: MTMCToken
|
||||||
|
) : ASMElement(labels, instructionToken.line) {
|
||||||
|
open fun validateLabel(assembler: Assembler) {
|
||||||
|
// default does nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addError(error: String) {
|
||||||
|
addError(instructionToken, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun genCode(output: ByteArray, assembler: Assembler)
|
||||||
|
|
||||||
|
override var sizeInBytes: Int = type.sizeInBytes
|
||||||
|
get() = type.sizeInBytes
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun isInstruction(cmd: String): Boolean {
|
||||||
|
return InstructionType.fromString(cmd) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disassemble(instruction: Short, previousInstruction: Short): String {
|
||||||
|
if (isDoubleWordInstruction(previousInstruction)) {
|
||||||
|
return instruction.toInt().toString()
|
||||||
|
}
|
||||||
|
val misc = MiscInstruction.disassemble(instruction)
|
||||||
|
if (misc != null) {
|
||||||
|
return misc
|
||||||
|
}
|
||||||
|
val aluOp = disassemble(instruction)
|
||||||
|
if (aluOp != null) {
|
||||||
|
return aluOp
|
||||||
|
}
|
||||||
|
val stack = StackInstruction.disassemble(instruction)
|
||||||
|
if (stack != null) {
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
val test = TestInstruction.disassemble(instruction)
|
||||||
|
if (test != null) {
|
||||||
|
return test
|
||||||
|
}
|
||||||
|
val lsr = LoadStoreRegisterInstruction.disassemble(instruction)
|
||||||
|
if (lsr != null) {
|
||||||
|
return lsr
|
||||||
|
}
|
||||||
|
val ls = LoadStoreInstruction.disassemble(instruction)
|
||||||
|
if (ls != null) {
|
||||||
|
return ls
|
||||||
|
}
|
||||||
|
val jumpReg = JumpInstruction.disassemble(instruction)
|
||||||
|
if (jumpReg != null) {
|
||||||
|
return jumpReg
|
||||||
|
}
|
||||||
|
val jump = JumpInstruction.disassemble(instruction)
|
||||||
|
if (jump != null) {
|
||||||
|
return jump
|
||||||
|
}
|
||||||
|
return "<unknown>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
src/jvmMain/kotlin/mtmc/asm/instructions/InstructionType.kt
Normal file
98
src/jvmMain/kotlin/mtmc/asm/instructions/InstructionType.kt
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
enum class InstructionType @JvmOverloads constructor(
|
||||||
|
@JvmField val instructionClass: InstructionClass?,
|
||||||
|
val sizeInBytes: Int = 2
|
||||||
|
) {
|
||||||
|
SYS(InstructionClass.MISC),
|
||||||
|
MOV(InstructionClass.MISC),
|
||||||
|
INC(InstructionClass.MISC),
|
||||||
|
DEC(InstructionClass.MISC),
|
||||||
|
SETI(InstructionClass.MISC),
|
||||||
|
NOP(InstructionClass.MISC),
|
||||||
|
MCP(InstructionClass.MISC, 4),
|
||||||
|
DEBUG(InstructionClass.MISC),
|
||||||
|
ADD(InstructionClass.ALU),
|
||||||
|
SUB(InstructionClass.ALU),
|
||||||
|
MUL(InstructionClass.ALU),
|
||||||
|
DIV(InstructionClass.ALU),
|
||||||
|
MOD(InstructionClass.ALU),
|
||||||
|
AND(InstructionClass.ALU),
|
||||||
|
OR(InstructionClass.ALU),
|
||||||
|
XOR(InstructionClass.ALU),
|
||||||
|
SHL(InstructionClass.ALU),
|
||||||
|
SHR(InstructionClass.ALU),
|
||||||
|
MIN(InstructionClass.ALU),
|
||||||
|
MAX(InstructionClass.ALU),
|
||||||
|
NOT(InstructionClass.ALU),
|
||||||
|
LNOT(InstructionClass.ALU),
|
||||||
|
NEG(InstructionClass.ALU),
|
||||||
|
IMM(InstructionClass.ALU, 4),
|
||||||
|
PUSH(InstructionClass.STACK),
|
||||||
|
POP(InstructionClass.STACK),
|
||||||
|
DUP(InstructionClass.STACK),
|
||||||
|
SWAP(InstructionClass.STACK),
|
||||||
|
DROP(InstructionClass.STACK),
|
||||||
|
OVER(InstructionClass.STACK),
|
||||||
|
ROT(InstructionClass.STACK),
|
||||||
|
SOP(InstructionClass.STACK),
|
||||||
|
PUSHI(InstructionClass.STACK, 4),
|
||||||
|
EQ(InstructionClass.TEST),
|
||||||
|
NEQ(InstructionClass.TEST),
|
||||||
|
GT(InstructionClass.TEST),
|
||||||
|
GTE(InstructionClass.TEST),
|
||||||
|
LT(InstructionClass.TEST),
|
||||||
|
LTE(InstructionClass.TEST),
|
||||||
|
EQI(InstructionClass.TEST),
|
||||||
|
NEQI(InstructionClass.TEST),
|
||||||
|
GTI(InstructionClass.TEST),
|
||||||
|
GTEI(InstructionClass.TEST),
|
||||||
|
LTI(InstructionClass.TEST),
|
||||||
|
LTEI(InstructionClass.TEST),
|
||||||
|
LWR(InstructionClass.LOAD_STORE_REGISTER),
|
||||||
|
LBR(InstructionClass.LOAD_STORE_REGISTER),
|
||||||
|
SWR(InstructionClass.LOAD_STORE_REGISTER),
|
||||||
|
SBR(InstructionClass.LOAD_STORE_REGISTER),
|
||||||
|
LW(InstructionClass.LOAD_STORE, 4),
|
||||||
|
LWO(InstructionClass.LOAD_STORE, 4),
|
||||||
|
LI(InstructionClass.LOAD_STORE, 4),
|
||||||
|
LB(InstructionClass.LOAD_STORE, 4),
|
||||||
|
LBO(InstructionClass.LOAD_STORE, 4),
|
||||||
|
SW(InstructionClass.LOAD_STORE, 4),
|
||||||
|
SWO(InstructionClass.LOAD_STORE, 4),
|
||||||
|
SB(InstructionClass.LOAD_STORE, 4),
|
||||||
|
SBO(InstructionClass.LOAD_STORE, 4),
|
||||||
|
JR(InstructionClass.JUMP_REGISTER),
|
||||||
|
J(InstructionClass.JUMP),
|
||||||
|
JZ(InstructionClass.JUMP),
|
||||||
|
JNZ(InstructionClass.JUMP),
|
||||||
|
JAL(InstructionClass.JUMP),
|
||||||
|
META(InstructionClass.MISC),
|
||||||
|
ERROR(InstructionClass.MISC),
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
enum class InstructionClass {
|
||||||
|
MISC,
|
||||||
|
ALU,
|
||||||
|
STACK,
|
||||||
|
TEST,
|
||||||
|
LOAD_STORE_REGISTER,
|
||||||
|
LOAD_STORE,
|
||||||
|
JUMP_REGISTER,
|
||||||
|
JUMP
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun fromString(string: String): InstructionType? {
|
||||||
|
try {
|
||||||
|
return valueOf(string.uppercase(Locale.getDefault()))
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/jvmMain/kotlin/mtmc/asm/instructions/JumpInstruction.kt
Normal file
69
src/jvmMain/kotlin/mtmc/asm/instructions/JumpInstruction.kt
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class JumpInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var addressToken: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setAddressToken(addressToken: MTMCToken) {
|
||||||
|
this.addressToken = addressToken
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validateLabel(assembler: Assembler) {
|
||||||
|
if (addressToken!!.type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
if (!assembler.hasLabel(addressToken!!.stringValue)) {
|
||||||
|
addError("Unresolved label: " + addressToken!!.stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveTargetAddress(assembler: Assembler): Int? {
|
||||||
|
if (addressToken!!.type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
return assembler.resolveLabel(addressToken!!.stringValue)
|
||||||
|
} else {
|
||||||
|
return addressToken!!.intValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
var opcode = 0
|
||||||
|
when (type) {
|
||||||
|
InstructionType.J -> opcode = 12
|
||||||
|
InstructionType.JZ -> opcode = 13
|
||||||
|
InstructionType.JNZ -> opcode = 14
|
||||||
|
InstructionType.JAL -> opcode = 15
|
||||||
|
else -> println("Invalid instruction type")
|
||||||
|
}
|
||||||
|
val address = resolveTargetAddress(assembler)!!
|
||||||
|
output[location] = (opcode shl 4 or (address ushr 8)).toByte()
|
||||||
|
output[location + 1] = address.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 2, instruction).toInt() == 3) {
|
||||||
|
val jumpType = BinaryUtils.getBits(14, 2, instruction)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
if (jumpType.toInt() == 0) {
|
||||||
|
sb.append("j")
|
||||||
|
} else if (jumpType.toInt() == 1) {
|
||||||
|
sb.append("jz")
|
||||||
|
} else if (jumpType.toInt() == 2) {
|
||||||
|
sb.append("jnz")
|
||||||
|
} else if (jumpType.toInt() == 3) {
|
||||||
|
sb.append("jal")
|
||||||
|
}
|
||||||
|
val target = BinaryUtils.getBits(12, 12, instruction)
|
||||||
|
sb.append(" ").append(target.toInt())
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.emulator.Register.Companion.toInteger
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class JumpRegisterInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var register: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setRegister(register: MTMCToken) {
|
||||||
|
this.register = register
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
val opcode = 9
|
||||||
|
output[location] = (opcode shl 4).toByte()
|
||||||
|
val reg = toInteger(register!!.stringValue)
|
||||||
|
output[location + 1] = reg.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 5, instruction).toInt() == 9) {
|
||||||
|
val reg = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val sb = StringBuilder("jr")
|
||||||
|
sb.append(fromInteger(reg.toInt()))
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
src/jvmMain/kotlin/mtmc/asm/instructions/LoadStoreInstruction.kt
Normal file
115
src/jvmMain/kotlin/mtmc/asm/instructions/LoadStoreInstruction.kt
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.emulator.Register.Companion.toInteger
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class LoadStoreInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var targetToken: MTMCToken? = null
|
||||||
|
private var offsetToken: MTMCToken? = null
|
||||||
|
private var value: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setTargetToken(targetToken: MTMCToken) {
|
||||||
|
this.targetToken = targetToken
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOffsetToken(offsetToken: MTMCToken) {
|
||||||
|
this.offsetToken = offsetToken
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: MTMCToken) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
val isOffset: Boolean
|
||||||
|
get() = type.name.endsWith("O")
|
||||||
|
|
||||||
|
public override fun validateLabel(assembler: Assembler) {
|
||||||
|
if (value!!.type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
if (!assembler.hasLabel(value!!.stringValue)) {
|
||||||
|
addError("Unresolved label: " + value!!.stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveValue(assembler: Assembler): Int? {
|
||||||
|
if (value!!.type == MTMCToken.TokenType.IDENTIFIER) {
|
||||||
|
return assembler.resolveLabel(value!!.stringValue)
|
||||||
|
} else {
|
||||||
|
return value!!.intValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
val upperByte = when (type) {
|
||||||
|
InstructionType.LW -> 128
|
||||||
|
InstructionType.LWO -> 129
|
||||||
|
InstructionType.LB -> 130
|
||||||
|
InstructionType.LBO -> 131
|
||||||
|
InstructionType.SW -> 132
|
||||||
|
InstructionType.SWO -> 133
|
||||||
|
InstructionType.SB -> 134
|
||||||
|
InstructionType.SBO -> 135
|
||||||
|
InstructionType.LI -> 143
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val target = toInteger(targetToken!!.stringValue)
|
||||||
|
output[location] = upperByte.toByte()
|
||||||
|
|
||||||
|
if (this.isOffset) {
|
||||||
|
val offsetReg = toInteger(offsetToken!!.stringValue)
|
||||||
|
output[location + 1] = (target shl 4 or offsetReg).toByte()
|
||||||
|
} else {
|
||||||
|
output[location + 1] = (target shl 4).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val numericValue = resolveValue(assembler)!!
|
||||||
|
output[location + 2] = (numericValue ushr 8).toByte()
|
||||||
|
output[location + 3] = numericValue.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 8) {
|
||||||
|
val topNibble = BinaryUtils.getBits(12, 4, instruction)
|
||||||
|
val sb = StringBuilder()
|
||||||
|
if (topNibble.toInt() == 15) {
|
||||||
|
sb.append("li ")
|
||||||
|
} else if (topNibble.toInt() == 0) {
|
||||||
|
sb.append("lw ")
|
||||||
|
} else if (topNibble.toInt() == 1) {
|
||||||
|
sb.append("lwo ")
|
||||||
|
} else if (topNibble.toInt() == 2) {
|
||||||
|
sb.append("lb ")
|
||||||
|
} else if (topNibble.toInt() == 3) {
|
||||||
|
sb.append("lbo ")
|
||||||
|
} else if (topNibble.toInt() == 4) {
|
||||||
|
sb.append("sw ")
|
||||||
|
} else if (topNibble.toInt() == 5) {
|
||||||
|
sb.append("swo ")
|
||||||
|
} else if (topNibble.toInt() == 6) {
|
||||||
|
sb.append("sb ")
|
||||||
|
} else if (topNibble.toInt() == 7) {
|
||||||
|
sb.append("sbo ")
|
||||||
|
}
|
||||||
|
val target = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val reg = fromInteger(target.toInt())
|
||||||
|
sb.append(reg)
|
||||||
|
if (topNibble.toInt() == 1 || topNibble.toInt() == 3 || topNibble.toInt() == 5 || topNibble.toInt() == 7) {
|
||||||
|
val offset = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val offsetReg = fromInteger(offset.toInt())
|
||||||
|
sb.append(" ").append(offsetReg)
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.emulator.Register
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.emulator.Register.Companion.toInteger
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class LoadStoreRegisterInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var targetToken: MTMCToken? = null
|
||||||
|
private var pointerToken: MTMCToken? = null
|
||||||
|
private var offsetToken: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setTargetToken(targetToken: MTMCToken) {
|
||||||
|
this.targetToken = targetToken
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPointerToken(pointerToken: MTMCToken) {
|
||||||
|
this.pointerToken = pointerToken
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOffsetToken(offsetToken: MTMCToken?) {
|
||||||
|
this.offsetToken = offsetToken
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
var opcode = 0
|
||||||
|
when (type) {
|
||||||
|
InstructionType.LWR -> opcode = 4
|
||||||
|
InstructionType.LBR -> opcode = 5
|
||||||
|
InstructionType.SWR -> opcode = 6
|
||||||
|
InstructionType.SBR -> opcode = 7
|
||||||
|
else -> println("Invalid instruction type")
|
||||||
|
}
|
||||||
|
val target = toInteger(targetToken!!.stringValue)
|
||||||
|
val pointer = toInteger(pointerToken!!.stringValue)
|
||||||
|
var offset = Register.PC.ordinal
|
||||||
|
if (offsetToken != null) {
|
||||||
|
offset = toInteger(offsetToken!!.stringValue)
|
||||||
|
}
|
||||||
|
output[location] = (opcode shl 4 or target).toByte()
|
||||||
|
output[location + 1] = (pointer shl 4 or offset).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 2, instruction).toInt() == 1) {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
val type = BinaryUtils.getBits(14, 2, instruction)
|
||||||
|
if (type.toInt() == 0) {
|
||||||
|
builder.append("lwr ")
|
||||||
|
} else if (type.toInt() == 1) {
|
||||||
|
builder.append("lbr ")
|
||||||
|
} else if (type.toInt() == 2) {
|
||||||
|
builder.append("swr ")
|
||||||
|
} else if (type.toInt() == 3) {
|
||||||
|
builder.append("sbr ")
|
||||||
|
}
|
||||||
|
val srcDestReg = BinaryUtils.getBits(12, 4, instruction)
|
||||||
|
val addrReg = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val offsetReg = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
builder.append(fromInteger(srcDestReg.toInt())).append(" ")
|
||||||
|
builder.append(fromInteger(addrReg.toInt())).append(" ")
|
||||||
|
builder.append(fromInteger(offsetReg.toInt()))
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/jvmMain/kotlin/mtmc/asm/instructions/MetaInstruction.kt
Normal file
90
src/jvmMain/kotlin/mtmc/asm/instructions/MetaInstruction.kt
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
|
||||||
|
class MetaInstruction(instruction: MTMCToken) :
|
||||||
|
Instruction(InstructionType.META, listOf(), instruction) {
|
||||||
|
private var originalFilePath: MTMCToken? = null
|
||||||
|
private var originaLineNumber: MTMCToken? = null
|
||||||
|
private var globalName: MTMCToken? = null
|
||||||
|
private var globalLocation: MTMCToken? = null
|
||||||
|
private var globalType: MTMCToken? = null
|
||||||
|
private var localName: MTMCToken? = null
|
||||||
|
private var localOffset: MTMCToken? = null
|
||||||
|
private var localType: MTMCToken? = null
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
val isFileDirective: Boolean
|
||||||
|
get() = "file" == this.instructionToken.stringValue
|
||||||
|
|
||||||
|
val isLineDirective: Boolean
|
||||||
|
get() = "line" == this.instructionToken.stringValue
|
||||||
|
|
||||||
|
val isGlobalDirective: Boolean
|
||||||
|
get() = "global" == this.instructionToken.stringValue
|
||||||
|
|
||||||
|
val isLocalDirective: Boolean
|
||||||
|
get() = "local" == this.instructionToken.stringValue
|
||||||
|
|
||||||
|
val isEndLocalDirective: Boolean
|
||||||
|
get() = "endlocal" == this.instructionToken.stringValue
|
||||||
|
|
||||||
|
fun setOriginalFilePath(path: MTMCToken) {
|
||||||
|
this.originalFilePath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOriginalLineNumber(lineNumber: MTMCToken) {
|
||||||
|
this.originaLineNumber = lineNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
val originalLineNumber: Int
|
||||||
|
get() = this.originaLineNumber!!.intValue()
|
||||||
|
|
||||||
|
fun setGlobalInfo(name: MTMCToken, location: MTMCToken, type: MTMCToken) {
|
||||||
|
this.globalName = name
|
||||||
|
this.globalLocation = location
|
||||||
|
this.globalType = type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLocalInfo(name: MTMCToken, offset: MTMCToken, type: MTMCToken) {
|
||||||
|
this.localName = name
|
||||||
|
this.localOffset = offset
|
||||||
|
this.localType = type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEndLocalInfo(name: MTMCToken) {
|
||||||
|
this.localName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOriginalFilePath(): String? {
|
||||||
|
return this.originalFilePath!!.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGlobalName(): String? {
|
||||||
|
return this.globalName!!.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGlobalLocation(): Int {
|
||||||
|
return this.globalLocation!!.intValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGlobalType(): String? {
|
||||||
|
return this.globalType!!.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLocalName(): String? {
|
||||||
|
return this.localName!!.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLocalOffset(): Int {
|
||||||
|
return this.localOffset!!.intValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLocalType(): String? {
|
||||||
|
return this.localType!!.stringValue
|
||||||
|
}
|
||||||
|
}
|
||||||
145
src/jvmMain/kotlin/mtmc/asm/instructions/MiscInstruction.kt
Normal file
145
src/jvmMain/kotlin/mtmc/asm/instructions/MiscInstruction.kt
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.emulator.Register.Companion.toInteger
|
||||||
|
import mtmc.os.SysCall
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class MiscInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
labels: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, labels, instructionToken) {
|
||||||
|
private var syscallType: MTMCToken? = null
|
||||||
|
private var fromRegister: MTMCToken? = null
|
||||||
|
private var toRegister: MTMCToken? = null
|
||||||
|
private var value: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setSyscallType(type: MTMCToken) {
|
||||||
|
this.syscallType = type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFrom(fromRegister: MTMCToken) {
|
||||||
|
this.fromRegister = fromRegister
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTo(toRegister: MTMCToken) {
|
||||||
|
this.toRegister = toRegister
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: MTMCToken?) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
if (type == InstructionType.SYS) {
|
||||||
|
output[location] = 0
|
||||||
|
output[location + 1] = SysCall.getValue(this.syscallType!!.stringValue)
|
||||||
|
} else if (type == InstructionType.MOV) {
|
||||||
|
val to = toInteger(toRegister!!.stringValue)
|
||||||
|
val from = toInteger(fromRegister!!.stringValue)
|
||||||
|
output[location] = 1
|
||||||
|
output[location + 1] = (to shl 4 or from).toByte()
|
||||||
|
} else if (type == InstructionType.INC) {
|
||||||
|
output[location] = 2
|
||||||
|
val to = toInteger(toRegister!!.stringValue)
|
||||||
|
var immediateVal = 1
|
||||||
|
if (value != null) {
|
||||||
|
immediateVal = value!!.intValue()
|
||||||
|
}
|
||||||
|
output[location + 1] = (to shl 4 or immediateVal).toByte()
|
||||||
|
} else if (type == InstructionType.DEC) {
|
||||||
|
output[location] = 3
|
||||||
|
val to = toInteger(toRegister!!.stringValue)
|
||||||
|
var immediateVal = 1
|
||||||
|
if (value != null) {
|
||||||
|
immediateVal = value!!.intValue()
|
||||||
|
}
|
||||||
|
output[location + 1] = (to shl 4 or immediateVal).toByte()
|
||||||
|
} else if (type == InstructionType.SETI) {
|
||||||
|
output[location] = 4
|
||||||
|
val to = toInteger(toRegister!!.stringValue)
|
||||||
|
val immediateVal = value!!.intValue()
|
||||||
|
output[location + 1] = (to shl 4 or immediateVal).toByte()
|
||||||
|
} else if (type == InstructionType.MCP) {
|
||||||
|
output[location] = 5
|
||||||
|
val from = toInteger(fromRegister!!.stringValue)
|
||||||
|
val to = toInteger(toRegister!!.stringValue)
|
||||||
|
val value = this.value!!.intValue()
|
||||||
|
output[location + 1] = (from shl 4 or to).toByte()
|
||||||
|
output[location + 2] = (value shl 8).toByte()
|
||||||
|
output[location + 3] = (value and 0xFF).toByte()
|
||||||
|
} else if (type == InstructionType.DEBUG) {
|
||||||
|
output[location] = 8
|
||||||
|
output[location + 1] = value!!.intValue().toByte()
|
||||||
|
} else if (type == InstructionType.NOP) {
|
||||||
|
output[location] = 15
|
||||||
|
output[location + 1] = 255.toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 0) {
|
||||||
|
val topNibble = BinaryUtils.getBits(12, 4, instruction)
|
||||||
|
if (topNibble.toInt() == 0) {
|
||||||
|
val builder = StringBuilder("sys ")
|
||||||
|
val bits = BinaryUtils.getBits(8, 8, instruction)
|
||||||
|
val name = SysCall.getString(bits.toByte())
|
||||||
|
builder.append(name)
|
||||||
|
return builder.toString()
|
||||||
|
} else if (topNibble.toInt() == 1) {
|
||||||
|
val builder = StringBuilder("mov ")
|
||||||
|
val to = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val from = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val toName = fromInteger(to.toInt())
|
||||||
|
builder.append(toName).append(" ")
|
||||||
|
val fromName = fromInteger(from.toInt())
|
||||||
|
builder.append(fromName)
|
||||||
|
return builder.toString()
|
||||||
|
} else if (topNibble.toInt() == 2) {
|
||||||
|
val builder = StringBuilder("inc ")
|
||||||
|
val to = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val amount = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val toName = fromInteger(to.toInt())
|
||||||
|
builder.append(toName).append(" ")
|
||||||
|
builder.append(amount.toInt())
|
||||||
|
return builder.toString()
|
||||||
|
} else if (topNibble.toInt() == 3) {
|
||||||
|
val builder = StringBuilder("dec ")
|
||||||
|
val to = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val amount = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val toName = fromInteger(to.toInt())
|
||||||
|
builder.append(toName).append(" ")
|
||||||
|
builder.append(amount.toInt())
|
||||||
|
return builder.toString()
|
||||||
|
} else if (topNibble.toInt() == 4) {
|
||||||
|
val builder = StringBuilder("seti ")
|
||||||
|
val to = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val amount = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val toName = fromInteger(to.toInt())
|
||||||
|
builder.append(toName).append(" ")
|
||||||
|
builder.append(amount.toInt())
|
||||||
|
return builder.toString()
|
||||||
|
} else if (topNibble.toInt() == 5) {
|
||||||
|
val from = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val fromName = fromInteger(from.toInt())
|
||||||
|
val to = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
val toName = fromInteger(to.toInt())
|
||||||
|
return "mcp " + fromName + " " + toName
|
||||||
|
} else if (topNibble.toInt() == 8) {
|
||||||
|
val builder = StringBuilder("debug ")
|
||||||
|
val stringIndex = BinaryUtils.getBits(8, 8, instruction)
|
||||||
|
builder.append(stringIndex.toInt())
|
||||||
|
return builder.toString()
|
||||||
|
} else if (topNibble.toInt() == 15) {
|
||||||
|
val builder = StringBuilder("noop")
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/jvmMain/kotlin/mtmc/asm/instructions/StackInstruction.kt
Normal file
108
src/jvmMain/kotlin/mtmc/asm/instructions/StackInstruction.kt
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.asm.instructions.ALUOp.Companion.fromInt
|
||||||
|
import mtmc.emulator.Register
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class StackInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var targetToken: MTMCToken? = null
|
||||||
|
private var stackRegisterToken: MTMCToken? = null
|
||||||
|
private var aluOpToken: MTMCToken? = null
|
||||||
|
private var value: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setTarget(target: MTMCToken) {
|
||||||
|
this.targetToken = target
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setStackRegister(stackRegister: MTMCToken?) {
|
||||||
|
this.stackRegisterToken = stackRegister
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setALUOp(aluOp: MTMCToken) {
|
||||||
|
this.aluOpToken = aluOp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: MTMCToken) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
var stackReg = Register.SP.ordinal
|
||||||
|
if (stackRegisterToken != null) {
|
||||||
|
stackReg = Register.Companion.toInteger(stackRegisterToken!!.stringValue)
|
||||||
|
}
|
||||||
|
if (type == InstructionType.PUSH) {
|
||||||
|
val target = Register.Companion.toInteger(targetToken!!.stringValue)
|
||||||
|
output[location] = 32
|
||||||
|
output[location + 1] = (target shl 4 or stackReg).toByte()
|
||||||
|
} else if (type == InstructionType.POP) {
|
||||||
|
val target = Register.Companion.toInteger(targetToken!!.stringValue)
|
||||||
|
output[location] = 33
|
||||||
|
output[location + 1] = (target shl 4 or stackReg).toByte()
|
||||||
|
} else if (type == InstructionType.SOP) {
|
||||||
|
val aluOp = ALUOp.toInteger(aluOpToken!!.stringValue)
|
||||||
|
output[location] = 39
|
||||||
|
output[location + 1] = (aluOp shl 4 or stackReg).toByte()
|
||||||
|
} else if (type == InstructionType.PUSHI) {
|
||||||
|
val immediateValue = value!!.intValue()
|
||||||
|
output[location] = 47
|
||||||
|
output[location + 1] = stackReg.toByte()
|
||||||
|
output[location + 2] = (immediateValue ushr 8).toByte()
|
||||||
|
output[location + 3] = immediateValue.toByte()
|
||||||
|
} else {
|
||||||
|
val stackOp: Int
|
||||||
|
stackOp = when (type) {
|
||||||
|
InstructionType.DUP -> 34
|
||||||
|
InstructionType.SWAP -> 35
|
||||||
|
InstructionType.DROP -> 36
|
||||||
|
InstructionType.OVER -> 37
|
||||||
|
InstructionType.ROT -> 38
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
output[location] = stackOp.toByte()
|
||||||
|
output[location + 1] = stackReg.toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 2) {
|
||||||
|
val opCode = BinaryUtils.getBits(12, 4, instruction)
|
||||||
|
val stackReg = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
if (opCode.toInt() == 0) {
|
||||||
|
val sourceReg = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
return "push " + fromInteger(sourceReg.toInt()) + " " + fromInteger(stackReg.toInt())
|
||||||
|
}
|
||||||
|
if (opCode.toInt() == 1) {
|
||||||
|
val destReg = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
return "pop " + fromInteger(destReg.toInt()) + " " + fromInteger(stackReg.toInt())
|
||||||
|
}
|
||||||
|
if (opCode.toInt() == 2) {
|
||||||
|
return "dup " + fromInteger(stackReg.toInt())
|
||||||
|
} else if (opCode.toInt() == 3) {
|
||||||
|
return "swap " + fromInteger(stackReg.toInt())
|
||||||
|
} else if (opCode.toInt() == 4) {
|
||||||
|
return "drop " + fromInteger(stackReg.toInt())
|
||||||
|
} else if (opCode.toInt() == 5) {
|
||||||
|
return "over " + fromInteger(stackReg.toInt())
|
||||||
|
} else if (opCode.toInt() == 6) {
|
||||||
|
return "rot " + fromInteger(stackReg.toInt())
|
||||||
|
} else if (opCode.toInt() == 7) {
|
||||||
|
val aluOp = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val op = fromInt(aluOp)
|
||||||
|
return "sop " + op + " " + fromInteger(stackReg.toInt())
|
||||||
|
} else if (opCode.toInt() == 15) {
|
||||||
|
return "pushi " + fromInteger(stackReg.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
126
src/jvmMain/kotlin/mtmc/asm/instructions/TestInstruction.kt
Normal file
126
src/jvmMain/kotlin/mtmc/asm/instructions/TestInstruction.kt
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package mtmc.asm.instructions
|
||||||
|
|
||||||
|
import mtmc.asm.Assembler
|
||||||
|
import mtmc.emulator.Register.Companion.fromInteger
|
||||||
|
import mtmc.emulator.Register.Companion.toInteger
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import mtmc.util.BinaryUtils
|
||||||
|
|
||||||
|
class TestInstruction(
|
||||||
|
type: InstructionType,
|
||||||
|
label: List<MTMCToken>,
|
||||||
|
instructionToken: MTMCToken
|
||||||
|
) : Instruction(type, label, instructionToken) {
|
||||||
|
private var first: MTMCToken? = null
|
||||||
|
private var second: MTMCToken? = null
|
||||||
|
private var value: MTMCToken? = null
|
||||||
|
|
||||||
|
fun setFirst(to: MTMCToken) {
|
||||||
|
this.first = to
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSecond(from: MTMCToken) {
|
||||||
|
this.second = from
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setImmediateValue(value: MTMCToken) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
val isImmediate: Boolean
|
||||||
|
get() = type.name.endsWith("I")
|
||||||
|
|
||||||
|
override fun genCode(output: ByteArray, assembler: Assembler) {
|
||||||
|
val firstReg = toInteger(first!!.stringValue)
|
||||||
|
if (this.isImmediate) {
|
||||||
|
val upperByte = when (type) {
|
||||||
|
InstructionType.EQI -> 56
|
||||||
|
InstructionType.NEQI -> 57
|
||||||
|
InstructionType.GTI -> 58
|
||||||
|
InstructionType.GTEI -> 59
|
||||||
|
InstructionType.LTI -> 60
|
||||||
|
InstructionType.LTEI -> 61
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
val immediateValue = value!!.intValue()
|
||||||
|
output[location] = upperByte.toByte()
|
||||||
|
output[location + 1] = (firstReg shl 4 or immediateValue).toByte()
|
||||||
|
} else {
|
||||||
|
val upperByte = when (type) {
|
||||||
|
InstructionType.EQ -> 48
|
||||||
|
InstructionType.NEQ -> 49
|
||||||
|
InstructionType.GT -> 50
|
||||||
|
InstructionType.GTE -> 51
|
||||||
|
InstructionType.LT -> 52
|
||||||
|
InstructionType.LTE -> 53
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
val secondReg = toInteger(second!!.stringValue)
|
||||||
|
output[location] = upperByte.toByte()
|
||||||
|
output[location + 1] = (firstReg shl 4 or secondReg).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun disassemble(instruction: Short): String? {
|
||||||
|
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 3) {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val opCode = BinaryUtils.getBits(12, 4, instruction)
|
||||||
|
val thirdNibble = BinaryUtils.getBits(8, 4, instruction)
|
||||||
|
val fourthNibble = BinaryUtils.getBits(4, 4, instruction)
|
||||||
|
if (opCode.toInt() == 0) {
|
||||||
|
sb.append("eq ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fromInteger(fourthNibble.toInt()))
|
||||||
|
} else if (opCode.toInt() == 1) {
|
||||||
|
sb.append("neq ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fromInteger(fourthNibble.toInt()))
|
||||||
|
} else if (opCode.toInt() == 2) {
|
||||||
|
sb.append("gt ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fromInteger(fourthNibble.toInt()))
|
||||||
|
} else if (opCode.toInt() == 3) {
|
||||||
|
sb.append("gte ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fromInteger(fourthNibble.toInt()))
|
||||||
|
} else if (opCode.toInt() == 4) {
|
||||||
|
sb.append("lt ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fromInteger(fourthNibble.toInt()))
|
||||||
|
} else if (opCode.toInt() == 5) {
|
||||||
|
sb.append("lte ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fromInteger(fourthNibble.toInt()))
|
||||||
|
}
|
||||||
|
if (opCode.toInt() == 8) {
|
||||||
|
sb.append("eqi ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fourthNibble.toInt())
|
||||||
|
} else if (opCode.toInt() == 9) {
|
||||||
|
sb.append("neqi ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fourthNibble.toInt())
|
||||||
|
} else if (opCode.toInt() == 10) {
|
||||||
|
sb.append("gti ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fourthNibble.toInt())
|
||||||
|
} else if (opCode.toInt() == 11) {
|
||||||
|
sb.append("gtei ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fourthNibble.toInt())
|
||||||
|
} else if (opCode.toInt() == 12) {
|
||||||
|
sb.append("lti ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fourthNibble.toInt())
|
||||||
|
} else if (opCode.toInt() == 13) {
|
||||||
|
sb.append("ltei ")
|
||||||
|
sb.append(fromInteger(thirdNibble.toInt()))
|
||||||
|
.append(" ").append(fourthNibble.toInt())
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package nl.astraeus.tmpl.db
|
package mtmc.db
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariConfig
|
import com.zaxxer.hikari.HikariConfig
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package nl.astraeus.tmpl.db
|
package mtmc.db
|
||||||
|
|
||||||
import nl.astraeus.tmpl.log
|
import mtmc.log
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.SQLException
|
import java.sql.SQLException
|
||||||
import java.sql.Timestamp
|
import java.sql.Timestamp
|
||||||
45
src/jvmMain/kotlin/mtmc/emulator/DebugInfo.kt
Normal file
45
src/jvmMain/kotlin/mtmc/emulator/DebugInfo.kt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
data class DebugInfo(
|
||||||
|
val debugStrings: MutableList<String>,
|
||||||
|
val assemblyFile: String?,
|
||||||
|
val assemblySource: String,
|
||||||
|
val assemblyLineNumbers: IntArray,
|
||||||
|
val originalFile: String,
|
||||||
|
val originalLineNumbers: IntArray,
|
||||||
|
val globals: Array<GlobalInfo>,
|
||||||
|
val locals: Array<Array<LocalInfo?>>
|
||||||
|
) {
|
||||||
|
fun handleDebugString(debugIndex: Short, monTanaMiniComputer: MonTanaMiniComputer) {
|
||||||
|
val debugString = debugStrings!!.get(debugIndex.toInt())
|
||||||
|
val compile = Pattern.compile("(\\$[a-zA-Z][a-zA-Z0-9])")
|
||||||
|
val matcher = compile.matcher(debugString)
|
||||||
|
val formattedString = StringBuilder()
|
||||||
|
var start = 0
|
||||||
|
var end: Int
|
||||||
|
while (matcher.find()) {
|
||||||
|
val match = matcher.group().substring(1)
|
||||||
|
try {
|
||||||
|
end = matcher.start()
|
||||||
|
formattedString.append(debugString, start, end)
|
||||||
|
val register = Register.valueOf(match.uppercase(Locale.getDefault()))
|
||||||
|
formattedString.append(monTanaMiniComputer.getRegisterValue(register).toInt())
|
||||||
|
start = matcher.end()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
formattedString.append(match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formattedString.append(debugString.substring(start))
|
||||||
|
println("DEBUG[" + System.nanoTime() + "] : " + formattedString)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
data class GlobalInfo(val name: String?, val location: Int, val type: String?)
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
data class LocalInfo(val name: String?, val offset: Int, val type: String?)
|
||||||
|
}
|
||||||
71
src/jvmMain/kotlin/mtmc/emulator/MTMCClock.kt
Normal file
71
src/jvmMain/kotlin/mtmc/emulator/MTMCClock.kt
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
import mtmc.emulator.MonTanaMiniComputer.ComputerStatus
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jbanes
|
||||||
|
*/
|
||||||
|
class MTMCClock
|
||||||
|
(private val computer: MonTanaMiniComputer) {
|
||||||
|
fun run() {
|
||||||
|
var instructions: Long = 0
|
||||||
|
var ips: Long = 0
|
||||||
|
var expected: Long = 0
|
||||||
|
var virtual: Long = 0
|
||||||
|
|
||||||
|
var startTime = System.currentTimeMillis()
|
||||||
|
var deltaStart: Long
|
||||||
|
var delta: Long
|
||||||
|
|
||||||
|
var speed: Long = 0
|
||||||
|
var pulse: Long
|
||||||
|
var ms: Long = 10
|
||||||
|
|
||||||
|
while (computer.getStatus() == ComputerStatus.EXECUTING) {
|
||||||
|
speed = max(computer.speed, 0).toLong()
|
||||||
|
pulse = (if (speed <= 0) 1000000 else max(speed / 100, 1))
|
||||||
|
ms = (if (pulse < 10) 1000 / speed else 10)
|
||||||
|
|
||||||
|
deltaStart = System.currentTimeMillis()
|
||||||
|
delta = ms - (System.currentTimeMillis() - deltaStart)
|
||||||
|
|
||||||
|
|
||||||
|
/* We've lost more than a second. Recalibrate. */
|
||||||
|
if ((expected - virtual) > pulse * 100) {
|
||||||
|
startTime = deltaStart
|
||||||
|
virtual = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Throttles to every 10ms, but "catches up" if we're behind */
|
||||||
|
if (delta > 0 && (expected - virtual) < pulse && speed != 0L) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(delta)
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions += computer.pulse(pulse)
|
||||||
|
|
||||||
|
virtual += pulse
|
||||||
|
ips = (virtual * 1000) / max(1, System.currentTimeMillis() - startTime)
|
||||||
|
expected = (System.currentTimeMillis() - startTime) * speed / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun step() {
|
||||||
|
computer.fetchAndExecute()
|
||||||
|
computer.fetchCurrentInstruction()
|
||||||
|
computer.notifyOfStepExecution()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun back() {
|
||||||
|
computer.rewind()
|
||||||
|
computer.fetchCurrentInstruction()
|
||||||
|
computer.notifyOfStepExecution()
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/jvmMain/kotlin/mtmc/emulator/MTMCConsole.kt
Normal file
131
src/jvmMain/kotlin/mtmc/emulator/MTMCConsole.kt
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
import mtmc.os.shell.Shell
|
||||||
|
import mtmc.tokenizer.MTMCScanner
|
||||||
|
import mtmc.tokenizer.MTMCToken
|
||||||
|
import java.io.Console
|
||||||
|
|
||||||
|
class MTMCConsole(private val computer: MonTanaMiniComputer) {
|
||||||
|
var mode: Mode = Mode.NON_INTERACTIVE
|
||||||
|
var sysConsole: Console? = null
|
||||||
|
|
||||||
|
// non-interactive data
|
||||||
|
private val output = StringBuffer()
|
||||||
|
private var shortValueSet = false
|
||||||
|
private var shortValue: Short = 0
|
||||||
|
private var stringValue: String? = null
|
||||||
|
|
||||||
|
// TODO invert so shell is driving and console is just IO
|
||||||
|
fun start() {
|
||||||
|
mode = Mode.INTERACTIVE
|
||||||
|
sysConsole = System.console()
|
||||||
|
Shell.printShellWelcome(computer)
|
||||||
|
while (true) {
|
||||||
|
val cmd = sysConsole!!.readLine("mtmc > ")
|
||||||
|
computer.oS.processCommand(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun println(x: String) {
|
||||||
|
print(x)
|
||||||
|
print("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun print(x: String) {
|
||||||
|
output.append(x)
|
||||||
|
if (mode == Mode.INTERACTIVE) {
|
||||||
|
kotlin.io.print(x)
|
||||||
|
} else {
|
||||||
|
if (x.contains("\n")) {
|
||||||
|
computer.notifyOfConsoleUpdate()
|
||||||
|
} else {
|
||||||
|
computer.notifyOfConsolePrinting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readChar(): Char {
|
||||||
|
if (mode == Mode.INTERACTIVE) {
|
||||||
|
val tokens = MTMCScanner(sysConsole!!.readLine(), "#").tokenize()
|
||||||
|
val token = tokens.first()
|
||||||
|
assert(token.type === MTMCToken.TokenType.CHAR)
|
||||||
|
return token.charValue()
|
||||||
|
} else {
|
||||||
|
this.shortValueSet = false
|
||||||
|
return Char(this.shortValue.toUShort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readInt(): Short {
|
||||||
|
if (mode == Mode.INTERACTIVE) {
|
||||||
|
return sysConsole!!.readLine().toShort()
|
||||||
|
} else {
|
||||||
|
this.shortValueSet = false
|
||||||
|
return shortValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasShortValue(): Boolean {
|
||||||
|
return (mode == Mode.INTERACTIVE || shortValueSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setShortValue(shortValue: Short) {
|
||||||
|
this.shortValue = shortValue
|
||||||
|
this.shortValueSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCharValue(charValue: Char) {
|
||||||
|
this.shortValue = charValue.code.toShort()
|
||||||
|
this.shortValueSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOutput(): String {
|
||||||
|
return output.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consumeLines(): String {
|
||||||
|
val index = output.lastIndexOf("\n")
|
||||||
|
val text = if (index >= 0) output.substring(0, index + 1) else ""
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
output.delete(0, index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeInt(value: Short) {
|
||||||
|
print(value.toString() + "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setStringValue(stringValue: String?) {
|
||||||
|
this.stringValue = stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readString(): String? {
|
||||||
|
if (mode == Mode.INTERACTIVE) {
|
||||||
|
return sysConsole!!.readLine()
|
||||||
|
} else {
|
||||||
|
val stringValue = this.stringValue
|
||||||
|
this.stringValue = null
|
||||||
|
return stringValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasReadString(): Boolean {
|
||||||
|
return (mode == Mode.INTERACTIVE || stringValue != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setReadString(stringValue: String?) {
|
||||||
|
this.stringValue = stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetOutput() {
|
||||||
|
output.delete(0, output.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
NON_INTERACTIVE,
|
||||||
|
INTERACTIVE,
|
||||||
|
}
|
||||||
|
}
|
||||||
284
src/jvmMain/kotlin/mtmc/emulator/MTMCDisplay.kt
Normal file
284
src/jvmMain/kotlin/mtmc/emulator/MTMCDisplay.kt
Normal file
File diff suppressed because one or more lines are too long
28
src/jvmMain/kotlin/mtmc/emulator/MTMCIO.kt
Normal file
28
src/jvmMain/kotlin/mtmc/emulator/MTMCIO.kt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MTMCIO {
|
||||||
|
var value: Int = 0
|
||||||
|
|
||||||
|
internal enum class Buttons(val mask: Int) {
|
||||||
|
UP(128),
|
||||||
|
DOWN(64),
|
||||||
|
LEFT(32),
|
||||||
|
RIGHT(16),
|
||||||
|
START(8),
|
||||||
|
SELECT(4),
|
||||||
|
B(2),
|
||||||
|
A(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun keyPressed(key: String) {
|
||||||
|
val button = Buttons.valueOf(key.uppercase(Locale.getDefault()))
|
||||||
|
value = value or button.mask
|
||||||
|
}
|
||||||
|
|
||||||
|
fun keyReleased(key: String) {
|
||||||
|
val button = Buttons.valueOf(key.uppercase(Locale.getDefault()))
|
||||||
|
value = value and button.mask.inv()
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/jvmMain/kotlin/mtmc/emulator/MTMCObserver.kt
Normal file
33
src/jvmMain/kotlin/mtmc/emulator/MTMCObserver.kt
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
interface MTMCObserver {
|
||||||
|
fun consoleUpdated()
|
||||||
|
|
||||||
|
fun consolePrinting()
|
||||||
|
|
||||||
|
fun executionUpdated()
|
||||||
|
|
||||||
|
fun filesystemUpdated()
|
||||||
|
|
||||||
|
fun registerUpdated(register: Int, value: Int)
|
||||||
|
|
||||||
|
fun memoryUpdated(address: Int, value: Byte)
|
||||||
|
|
||||||
|
fun displayUpdated()
|
||||||
|
|
||||||
|
fun instructionFetched(instruction: Short)
|
||||||
|
|
||||||
|
fun beforeExecution(instruction: Short)
|
||||||
|
|
||||||
|
fun afterExecution(instruction: Short)
|
||||||
|
|
||||||
|
fun stepExecution()
|
||||||
|
|
||||||
|
fun computerReset()
|
||||||
|
|
||||||
|
fun requestCharacter()
|
||||||
|
|
||||||
|
fun requestInteger()
|
||||||
|
|
||||||
|
fun requestString()
|
||||||
|
}
|
||||||
1030
src/jvmMain/kotlin/mtmc/emulator/MonTanaMiniComputer.kt
Normal file
1030
src/jvmMain/kotlin/mtmc/emulator/MonTanaMiniComputer.kt
Normal file
File diff suppressed because it is too large
Load Diff
76
src/jvmMain/kotlin/mtmc/emulator/Register.kt
Normal file
76
src/jvmMain/kotlin/mtmc/emulator/Register.kt
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
enum class Register {
|
||||||
|
|
||||||
|
//=== user-facing registers
|
||||||
|
T0, // temp registers
|
||||||
|
T1,
|
||||||
|
T2,
|
||||||
|
T3,
|
||||||
|
T4,
|
||||||
|
T5,
|
||||||
|
A0, // arg registers
|
||||||
|
A1,
|
||||||
|
A2,
|
||||||
|
A3,
|
||||||
|
RV, // return value
|
||||||
|
RA, // return address
|
||||||
|
FP, // frame pointer
|
||||||
|
SP, // stack pointer
|
||||||
|
BP, // break pointer
|
||||||
|
PC,
|
||||||
|
|
||||||
|
//=== non-user-facing registers
|
||||||
|
IR, // instruction register
|
||||||
|
DR, // data register
|
||||||
|
CB, // code boundary
|
||||||
|
DB, // data boundary
|
||||||
|
IO, // I/O register
|
||||||
|
FLAGS; // flags register
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun toInteger(reg: String): Int {
|
||||||
|
return valueOf(reg.uppercase()).ordinal
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromInteger(reg: Int): String {
|
||||||
|
return values()[reg].name.lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isWriteable(reg: Int): Boolean {
|
||||||
|
return reg in 0..15
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isReadable(reg: Int): Boolean {
|
||||||
|
return reg in 0..15
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isTempRegister(reg: Int): Boolean {
|
||||||
|
return reg in 0..5
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isWriteable(register: String): Boolean {
|
||||||
|
return try {
|
||||||
|
isWriteable(toInteger(register))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isReadable(register: String): Boolean {
|
||||||
|
return try {
|
||||||
|
isReadable(toInteger(register))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isTempRegister(register: String): Boolean {
|
||||||
|
return try {
|
||||||
|
isTempRegister(toInteger(register))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/jvmMain/kotlin/mtmc/emulator/RewindStep.kt
Normal file
13
src/jvmMain/kotlin/mtmc/emulator/RewindStep.kt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package mtmc.emulator
|
||||||
|
|
||||||
|
class RewindStep {
|
||||||
|
var subSteps: MutableList<Runnable?> = ArrayList<Runnable?>()
|
||||||
|
|
||||||
|
fun rewind() {
|
||||||
|
subSteps.reversed().forEach({ obj: Runnable? -> obj!!.run() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSubStep(subStep: Runnable?) {
|
||||||
|
subSteps.add(subStep)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/jvmMain/kotlin/mtmc/lang/CompilationException.java
Normal file
19
src/jvmMain/kotlin/mtmc/lang/CompilationException.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package mtmc.lang;
|
||||||
|
|
||||||
|
public class CompilationException extends Exception {
|
||||||
|
protected Span span;
|
||||||
|
|
||||||
|
public CompilationException(String message, Span span) {
|
||||||
|
super(message);
|
||||||
|
this.span = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompilationException(CompilationException parent, String message) {
|
||||||
|
super(message, parent);
|
||||||
|
this.span = parent.span;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span getSpan() {
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/jvmMain/kotlin/mtmc/lang/Language.java
Normal file
7
src/jvmMain/kotlin/mtmc/lang/Language.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package mtmc.lang;
|
||||||
|
|
||||||
|
import mtmc.os.exec.Executable;
|
||||||
|
|
||||||
|
public interface Language {
|
||||||
|
Executable compileExecutable(String filename, String source) throws ParseException, CompilationException;
|
||||||
|
}
|
||||||
55
src/jvmMain/kotlin/mtmc/lang/Location.java
Normal file
55
src/jvmMain/kotlin/mtmc/lang/Location.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package mtmc.lang;
|
||||||
|
|
||||||
|
public record Location(int index) {
|
||||||
|
|
||||||
|
public static int[] getLineNos(String source, int... indices) {
|
||||||
|
int[] out = new int[indices.length];
|
||||||
|
int index = 0, line = 1;
|
||||||
|
while (index < source.length()) {
|
||||||
|
for (int i = 0; i < indices.length; i++) {
|
||||||
|
if (indices[i] == index) {
|
||||||
|
out[i] = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cp = source.charAt(index);
|
||||||
|
if (cp == '\n') {
|
||||||
|
line += 1;
|
||||||
|
}
|
||||||
|
index += Character.charCount(cp);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineInfo getLineInfo(String source) {
|
||||||
|
int index = 0, lineStart = 0;
|
||||||
|
int lineno = 1;
|
||||||
|
int column = 1;
|
||||||
|
while (index < source.length()) {
|
||||||
|
if (index == this.index) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int cp = source.charAt(index);
|
||||||
|
|
||||||
|
if (cp == '\n') {
|
||||||
|
lineno += 1;
|
||||||
|
lineStart = index + 1;
|
||||||
|
} else {
|
||||||
|
column += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
index += Character.charCount(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (index < source.length()) {
|
||||||
|
int cp = source.charAt(index);
|
||||||
|
index += Character.charCount(cp);
|
||||||
|
if (cp == '\n') break;
|
||||||
|
}
|
||||||
|
String line = source.substring(lineStart, index);
|
||||||
|
|
||||||
|
return new LineInfo(lineno, column, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record LineInfo(int lineno, int column, String line) {}
|
||||||
|
}
|
||||||
43
src/jvmMain/kotlin/mtmc/lang/ParseException.java
Normal file
43
src/jvmMain/kotlin/mtmc/lang/ParseException.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package mtmc.lang;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ParseException extends Exception {
|
||||||
|
public final List<Message> messages;
|
||||||
|
|
||||||
|
public ParseException(Message message, Message ...rest) {
|
||||||
|
var messages = new ArrayList<Message>(1 + rest.length);
|
||||||
|
messages.add(message);
|
||||||
|
messages.addAll(Arrays.asList(rest));
|
||||||
|
this.messages = Collections.unmodifiableList(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParseException(ParseException parent, Message message, Message ...rest) {
|
||||||
|
var messages = new ArrayList<Message>( 1 + rest.length + parent.messages.size());
|
||||||
|
messages.add(message);
|
||||||
|
messages.addAll(Arrays.asList(rest));
|
||||||
|
messages.addAll(parent.messages);
|
||||||
|
this.messages = Collections.unmodifiableList(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Message(Span span, String message) {
|
||||||
|
public Message(Token token, String message) {
|
||||||
|
this(Span.of(token), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Token start() {
|
||||||
|
return span.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Token end() {
|
||||||
|
return span.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String report(String source) {
|
||||||
|
return "TODO: I ain't no snitch";
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/jvmMain/kotlin/mtmc/lang/Span.java
Normal file
17
src/jvmMain/kotlin/mtmc/lang/Span.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package mtmc.lang;
|
||||||
|
|
||||||
|
public record Span(Token start, Token end) {
|
||||||
|
|
||||||
|
public static Span of(Token token) {
|
||||||
|
return new Span(token, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Span of(Token start, Token end) {
|
||||||
|
return new Span(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnSingleLine(String source) {
|
||||||
|
int[] lines = Location.getLineNos(source, start.start(), end.end());
|
||||||
|
return lines[0] == lines[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/jvmMain/kotlin/mtmc/lang/Token.java
Normal file
15
src/jvmMain/kotlin/mtmc/lang/Token.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package mtmc.lang;
|
||||||
|
|
||||||
|
public interface Token {
|
||||||
|
Location getStart();
|
||||||
|
Location getEnd();
|
||||||
|
String getContent();
|
||||||
|
|
||||||
|
default int start() {
|
||||||
|
return getStart().index();
|
||||||
|
}
|
||||||
|
|
||||||
|
default int end() {
|
||||||
|
return getEnd().index();
|
||||||
|
}
|
||||||
|
}
|
||||||
1415
src/jvmMain/kotlin/mtmc/lang/sea/SeaCompiler.java
Normal file
1415
src/jvmMain/kotlin/mtmc/lang/sea/SeaCompiler.java
Normal file
File diff suppressed because it is too large
Load Diff
47
src/jvmMain/kotlin/mtmc/lang/sea/SeaLanguage.java
Normal file
47
src/jvmMain/kotlin/mtmc/lang/sea/SeaLanguage.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package mtmc.lang.sea;
|
||||||
|
|
||||||
|
import mtmc.lang.CompilationException;
|
||||||
|
import mtmc.lang.Language;
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
import mtmc.lang.sea.ast.Error;
|
||||||
|
import mtmc.lang.sea.ast.Unit;
|
||||||
|
import mtmc.os.exec.Executable;
|
||||||
|
|
||||||
|
public class SeaLanguage implements Language {
|
||||||
|
@Override
|
||||||
|
public Executable compileExecutable(String filename, String source) throws ParseException, CompilationException {
|
||||||
|
var tokens = Token.tokenize(source);
|
||||||
|
var parser = new SeaParser(filename, source, tokens);
|
||||||
|
Unit program = parser.parseUnit();
|
||||||
|
var errors = program.collectErrors();
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Error error : errors) {
|
||||||
|
reportError(source, sb, error.exception());
|
||||||
|
}
|
||||||
|
throw new RuntimeException(sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var compiler = new SeaCompiler(program);
|
||||||
|
return compiler.compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void reportError(String src, StringBuilder sb, ParseException e) {
|
||||||
|
sb.append("Error:\n");
|
||||||
|
for (var msg : e.messages) {
|
||||||
|
var lo = Token.getLineAndOffset(src, msg.start().start());
|
||||||
|
int lineNo = lo[0];
|
||||||
|
int column = lo[1];
|
||||||
|
var line = Token.getLineFor(src, msg.start().start());
|
||||||
|
String prefix = " %03d:%03d | ".formatted(lineNo, column);
|
||||||
|
String info = " ".repeat(prefix.length() - 2) + "| ";
|
||||||
|
sb.append(info).append(msg.message()).append('\n');
|
||||||
|
sb.append(prefix).append(line).append('\n');
|
||||||
|
sb
|
||||||
|
.repeat(' ', prefix.length() + column - 1)
|
||||||
|
.repeat('^', Math.max(1, msg.end().end() - msg.start().start()));
|
||||||
|
sb.append("\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1555
src/jvmMain/kotlin/mtmc/lang/sea/SeaParser.java
Normal file
1555
src/jvmMain/kotlin/mtmc/lang/sea/SeaParser.java
Normal file
File diff suppressed because it is too large
Load Diff
231
src/jvmMain/kotlin/mtmc/lang/sea/SeaType.java
Normal file
231
src/jvmMain/kotlin/mtmc/lang/sea/SeaType.java
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
package mtmc.lang.sea;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public sealed interface SeaType {
|
||||||
|
default int size() {
|
||||||
|
if (this == CHAR) return 2;
|
||||||
|
if (this == INT) return 2;
|
||||||
|
if (this == VOID) return 0;
|
||||||
|
if (this instanceof Pointer) return 2;
|
||||||
|
if (this instanceof Struct struct) {
|
||||||
|
int size = 0;
|
||||||
|
for (Map.Entry<String, SeaType> fieldSet : struct.fields.entrySet()) {
|
||||||
|
SeaType type = fieldSet.getValue();
|
||||||
|
size += type.size();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("sizeof " + getClass().getName() + " is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isArithmetic() {
|
||||||
|
return this == CHAR || this == INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isIntegral() {
|
||||||
|
return this == CHAR || this == INT || this instanceof Pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isStructure() {
|
||||||
|
return this instanceof Struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isInt() {
|
||||||
|
return this == INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isChar() {
|
||||||
|
return this == CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isVoid() {
|
||||||
|
return this == VOID;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isAnIntegralPointer() {
|
||||||
|
return this instanceof Pointer(SeaType component) && component.isIntegral();
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isAPointerToAnInt() {
|
||||||
|
return this instanceof Pointer(SeaType component) && component.isInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isAPointerTo(SeaType inner) {
|
||||||
|
return this instanceof Pointer(SeaType it) && it.equals(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isAPointer() {
|
||||||
|
return this instanceof Pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isFunc() {
|
||||||
|
return this instanceof Func;
|
||||||
|
}
|
||||||
|
|
||||||
|
default SeaType componentType() {
|
||||||
|
return ((Pointer) this).component;
|
||||||
|
}
|
||||||
|
|
||||||
|
default SeaType resultType() {
|
||||||
|
return ((Func) this).result();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void checkConversionTo(SeaType other) throws ConversionError {
|
||||||
|
if (this.isVoid() || other.isVoid()) throw new ConversionError(this, other, "void is not assignable to void");
|
||||||
|
if (this.isArithmetic() && other.isArithmetic()) return;
|
||||||
|
if (this.isAPointer() && other.isAPointer()) return;
|
||||||
|
if (this instanceof Initializer initializer && other instanceof Struct s) {
|
||||||
|
// this is kinda complex
|
||||||
|
// the way this should work is we process named assignments and then based on the last
|
||||||
|
// index of the named assignments, we start putting in values
|
||||||
|
// the challenge here is that the blob may have too many values or may initialize them improperly
|
||||||
|
if (initializer.values.size() != s.fields.size()) {
|
||||||
|
throw new ConversionError(this, other, "initializer has too many or too few values");
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (Map.Entry<String, SeaType> entry : s.fields.entrySet()) {
|
||||||
|
var name = entry.getKey();
|
||||||
|
var ty = entry.getValue();
|
||||||
|
var valueTy = initializer.values.get(i);
|
||||||
|
|
||||||
|
try {
|
||||||
|
valueTy.checkConversionTo(ty);
|
||||||
|
} catch (ConversionError error) {
|
||||||
|
throw new ConversionError(ty, valueTy,
|
||||||
|
"value cannot be assigned to " + ty.repr() + " for '" + name + "'", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.equals(other)) {
|
||||||
|
throw new ConversionError(this, other, this.repr() + " is not convertible to " + other.repr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConversionError extends Exception {
|
||||||
|
public final SeaType fromType, toType;
|
||||||
|
|
||||||
|
private ConversionError(SeaType fromType, SeaType toType, String message) {
|
||||||
|
super(message);
|
||||||
|
this.fromType = fromType;
|
||||||
|
this.toType = toType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConversionError(SeaType fromType, SeaType toType, String message, ConversionError parent) {
|
||||||
|
super(message, parent);
|
||||||
|
this.fromType = fromType;
|
||||||
|
this.toType = toType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isCastableTo(SeaType target) {
|
||||||
|
if (target.isVoid()) return true;
|
||||||
|
if (this.isAPointer() && target.isInt()) return true;
|
||||||
|
if (this.isArithmetic() && target.isArithmetic()) return true;
|
||||||
|
return this.equals(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
default String repr() {
|
||||||
|
if (this instanceof Pointer p) {
|
||||||
|
if (p.baseType() instanceof Func(List<SeaType> params, boolean isVararg, SeaType result)) {
|
||||||
|
var s = new StringBuilder();
|
||||||
|
s.append(result.repr()).append("(*");
|
||||||
|
var x = p.component;
|
||||||
|
while (x instanceof Pointer p2) {
|
||||||
|
x = p2.component;
|
||||||
|
s.append("*");
|
||||||
|
}
|
||||||
|
s.append(")(");
|
||||||
|
int i = 0;
|
||||||
|
for (var param : params) {
|
||||||
|
if (i > 0) s.append(", ");
|
||||||
|
s.append(param.repr());
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
s.append(")");
|
||||||
|
return s.toString();
|
||||||
|
} else {
|
||||||
|
return p.component.repr() + "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this == CHAR) return "char";
|
||||||
|
if (this == INT) return "int";
|
||||||
|
if (this == VOID) return "void";
|
||||||
|
if (this instanceof Func(List<SeaType> params, boolean isVararg, SeaType result)) {
|
||||||
|
var s = new StringBuilder();
|
||||||
|
s.append(result.repr());
|
||||||
|
s.append("(");
|
||||||
|
int i = 0;
|
||||||
|
for (var param : params) {
|
||||||
|
if (i > 0) s.append(", ");
|
||||||
|
s.append(param.repr());
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
s.append(")");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
if (this instanceof Initializer(List<SeaType> values)) {
|
||||||
|
var s = new StringBuilder();
|
||||||
|
s.append("{");
|
||||||
|
for (int i = 0; i < values.size(); i++) {
|
||||||
|
if (i > 0) s.append(", ");
|
||||||
|
s.append(values.get(i).repr());
|
||||||
|
}
|
||||||
|
s.append("}");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
if (this instanceof Struct s) {
|
||||||
|
return "struct " + s.name;
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("unknown type " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SeaType CHAR = new Primitive("char");
|
||||||
|
SeaType INT = new Primitive("int");
|
||||||
|
SeaType VOID = new Primitive("void");
|
||||||
|
|
||||||
|
record Pointer(SeaType component) implements SeaType {
|
||||||
|
SeaType baseType() {
|
||||||
|
var ty = component;
|
||||||
|
while (ty instanceof Pointer(SeaType c)) {
|
||||||
|
ty = c;
|
||||||
|
}
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Primitive implements SeaType {
|
||||||
|
// this is purely for debug info lmfao
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
private Primitive(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Func(List<SeaType> params, boolean isVararg, SeaType result) implements SeaType {
|
||||||
|
public Func(List<SeaType> params, SeaType result) {
|
||||||
|
this(params, false, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Struct(String name, LinkedHashMap<String, SeaType> fields) implements SeaType {
|
||||||
|
public SeaType field(String name) {
|
||||||
|
return fields.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Initializer(List<SeaType> values) implements SeaType {
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/jvmMain/kotlin/mtmc/lang/sea/Symbol.java
Normal file
59
src/jvmMain/kotlin/mtmc/lang/sea/Symbol.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package mtmc.lang.sea;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.ast.DeclarationFunc;
|
||||||
|
import mtmc.lang.sea.ast.DeclarationVar;
|
||||||
|
import mtmc.lang.sea.ast.StatementVar;
|
||||||
|
import mtmc.lang.sea.ast.TypeDeclaration;
|
||||||
|
|
||||||
|
public class Symbol {
|
||||||
|
public final String name;
|
||||||
|
public final SeaType type;
|
||||||
|
public final TypeDeclaration typeDecl;
|
||||||
|
public final boolean isParam, isGlobal;
|
||||||
|
|
||||||
|
public Symbol(DeclarationFunc.Param param) {
|
||||||
|
this.name = param.name.content();
|
||||||
|
this.type = param.type.type();
|
||||||
|
this.typeDecl = null;
|
||||||
|
this.isParam = true;
|
||||||
|
this.isGlobal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Symbol(DeclarationVar decl) {
|
||||||
|
this.name = decl.name();
|
||||||
|
this.type = decl.type.type();
|
||||||
|
this.typeDecl = null;
|
||||||
|
this.isParam = false;
|
||||||
|
this.isGlobal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Symbol(StatementVar stmt) {
|
||||||
|
this.name = stmt.name();
|
||||||
|
this.type = stmt.type.type();
|
||||||
|
this.typeDecl = null;
|
||||||
|
this.isParam = false;
|
||||||
|
this.isGlobal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Symbol(DeclarationFunc func) {
|
||||||
|
this.name = func.name.content();
|
||||||
|
this.type = func.type();
|
||||||
|
this.typeDecl = null;
|
||||||
|
this.isParam = false;
|
||||||
|
this.isGlobal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Symbol(TypeDeclaration declaration) {
|
||||||
|
this.name = declaration.name();
|
||||||
|
this.type = null;
|
||||||
|
this.typeDecl = declaration;
|
||||||
|
this.isParam = false;
|
||||||
|
this.isGlobal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddressable() {
|
||||||
|
if (this.typeDecl != null) throw new IllegalStateException("cannot address non-data symbol");
|
||||||
|
if (this.isParam) return false; // parameters are not addressable!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
426
src/jvmMain/kotlin/mtmc/lang/sea/Token.java
Normal file
426
src/jvmMain/kotlin/mtmc/lang/sea/Token.java
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
package mtmc.lang.sea;
|
||||||
|
|
||||||
|
import mtmc.lang.Location;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record Token(
|
||||||
|
Type type,
|
||||||
|
@NotNull
|
||||||
|
String content,
|
||||||
|
int start,
|
||||||
|
int end
|
||||||
|
) implements mtmc.lang.Token {
|
||||||
|
public static final Token SOF = new Token(Type.SOF, "", 0, 0);
|
||||||
|
public static final Token EOF = new Token(Type.EOF, "", Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
// Special
|
||||||
|
LIT_INT(null),
|
||||||
|
LIT_STR(null),
|
||||||
|
LIT_CHAR(null),
|
||||||
|
LIT_IDENT(null),
|
||||||
|
KW_TYPEDEF("typedef"),
|
||||||
|
KW_STRUCT("struct"),
|
||||||
|
KW_IF("if"),
|
||||||
|
KW_ELSE("else"),
|
||||||
|
KW_FOR("for"),
|
||||||
|
KW_WHILE("while"),
|
||||||
|
KW_DO("do"),
|
||||||
|
KW_GOTO("goto"),
|
||||||
|
KW_CONTINUE("continue"),
|
||||||
|
KW_BREAK("break"),
|
||||||
|
KW_RETURN("return"),
|
||||||
|
KW_SIZEOF("sizeof"),
|
||||||
|
KW_INT("int"),
|
||||||
|
KW_CHAR("char"),
|
||||||
|
KW_VOID("void"),
|
||||||
|
SOF(null),
|
||||||
|
EOF(null),
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
LEFT_PAREN("("),
|
||||||
|
RIGHT_PAREN(")"),
|
||||||
|
LEFT_BRACKET("["),
|
||||||
|
RIGHT_BRACKET("]"),
|
||||||
|
LEFT_BRACE("{"),
|
||||||
|
RIGHT_BRACE("}"),
|
||||||
|
|
||||||
|
// Simple Punct
|
||||||
|
DOT3("..."),
|
||||||
|
DOT("."),
|
||||||
|
SEMICOLON(";"),
|
||||||
|
COMMA(","),
|
||||||
|
COLON(":"),
|
||||||
|
TILDE("~"),
|
||||||
|
QUESTION("?"),
|
||||||
|
|
||||||
|
PLUS2("++"),
|
||||||
|
PLUS_EQ("+="),
|
||||||
|
PLUS("+"),
|
||||||
|
|
||||||
|
DASH2("--"),
|
||||||
|
DASH_EQ("-="),
|
||||||
|
ARROW("->"),
|
||||||
|
DASH("-"),
|
||||||
|
|
||||||
|
STAR_EQ("*="),
|
||||||
|
STAR("*"),
|
||||||
|
|
||||||
|
SLASH_EQ("/="),
|
||||||
|
SLASH("/"),
|
||||||
|
|
||||||
|
PERCENT_EQ("%="),
|
||||||
|
PERCENT("%"),
|
||||||
|
|
||||||
|
AMPERSAND2("&&"),
|
||||||
|
AMPERSAND_EQ("&="),
|
||||||
|
AMPERSAND("&"),
|
||||||
|
|
||||||
|
BAR2("||"),
|
||||||
|
BAR_EQ("|="),
|
||||||
|
BAR("|"),
|
||||||
|
|
||||||
|
CARET("^"),
|
||||||
|
CARET_EQ("^="),
|
||||||
|
|
||||||
|
LEFT_ARROW2_EQ("<<="),
|
||||||
|
LEFT_ARROW2("<<"),
|
||||||
|
LEFT_ARROW_EQ("<="),
|
||||||
|
LEFT_ARROW("<"),
|
||||||
|
|
||||||
|
RIGHT_ARROW2_EQ(">>="),
|
||||||
|
RIGHT_ARROW2(">>"),
|
||||||
|
RIGHT_ARROW_EQ(">="),
|
||||||
|
RIGHT_ARROW(">"),
|
||||||
|
|
||||||
|
EQUAL2("=="),
|
||||||
|
EQUAL("="),
|
||||||
|
|
||||||
|
BANG_EQ("!="),
|
||||||
|
BANG("!");
|
||||||
|
|
||||||
|
public final String lex;
|
||||||
|
|
||||||
|
public static final Type[] PUNCT;
|
||||||
|
static {
|
||||||
|
List<Type> list = new ArrayList<>();
|
||||||
|
for (Type t : Type.values()) {
|
||||||
|
if (t.lex != null) {
|
||||||
|
list.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PUNCT = list.toArray(new Type[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Type[] KEYWORDS;
|
||||||
|
static {
|
||||||
|
List<Type> list = new ArrayList<>();
|
||||||
|
for (Type t : Type.values()) {
|
||||||
|
if (t.name().startsWith("KW_")) {
|
||||||
|
list.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KEYWORDS = list.toArray(new Type[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type(String lex) {
|
||||||
|
this.lex = lex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] getLineAndOffset(String src, int index) {
|
||||||
|
int line = 1;
|
||||||
|
int column = 1;
|
||||||
|
for (int i = 0; i < index && i < src.length(); i++) {
|
||||||
|
char c = src.charAt(i);
|
||||||
|
if (c == '\n') {
|
||||||
|
line = line + 1;
|
||||||
|
column = 1;
|
||||||
|
} else {
|
||||||
|
column = column + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new int[]{line, column};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLineFor(String src, int index) {
|
||||||
|
int start = 0;
|
||||||
|
for (int i = Math.min(index, src.length() - 1); i >= 0; i--) {
|
||||||
|
if (src.charAt(i) == '\n') {
|
||||||
|
start = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int end = src.length();
|
||||||
|
for (int i = index; i < src.length(); i++) {
|
||||||
|
if (src.charAt(i) == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
end = i + 1;
|
||||||
|
}
|
||||||
|
return src.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String highlight(String src, int start, int end) {
|
||||||
|
var s = getLineAndOffset(src, start);
|
||||||
|
var e = getLineAndOffset(src, end);
|
||||||
|
|
||||||
|
int lineStart;
|
||||||
|
if (s[0] != e[0]) {
|
||||||
|
lineStart = 0;
|
||||||
|
} else {
|
||||||
|
lineStart = s[1] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lineEnd = e[1] - 1;
|
||||||
|
|
||||||
|
String line = getLineFor(src, end);
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
if (lineStart > 10) {
|
||||||
|
result.append("... ");
|
||||||
|
off += 4;
|
||||||
|
result.append(line.substring(lineStart, lineEnd));
|
||||||
|
} else {
|
||||||
|
result.append(line.substring(0, lineEnd));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append('\n');
|
||||||
|
result.repeat(' ', off + lineStart);
|
||||||
|
if (start == Integer.MAX_VALUE) {
|
||||||
|
result.append("^ (at EOL)");
|
||||||
|
} else {
|
||||||
|
result.repeat('^', lineEnd - lineStart);
|
||||||
|
result.append(" (here)");
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Token> tokenize(String src) throws TokenizeException {
|
||||||
|
List<Token> tokens = new ArrayList<>();
|
||||||
|
int offset = 0;
|
||||||
|
do {
|
||||||
|
Token token = tokenizeOne(src, offset);
|
||||||
|
if (token == null) break;
|
||||||
|
tokens.add(token);
|
||||||
|
offset = token.end();
|
||||||
|
} while (true);
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(String str, int start, String token) {
|
||||||
|
if (str == null) return false;
|
||||||
|
if (str.length() - start < token.length()) return false;
|
||||||
|
for (int i = 0; i < token.length(); i++) {
|
||||||
|
char c = str.charAt(start + i);
|
||||||
|
char d = token.charAt(i);
|
||||||
|
if (c != d) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean match(String str, int start, char c) {
|
||||||
|
if (str == null) return false;
|
||||||
|
if (str.length() - start < Character.charCount(c)) return false;
|
||||||
|
return str.charAt(start) == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Token tokenizeOne(String src, int offset) throws TokenizeException {
|
||||||
|
while (offset < src.length()) {
|
||||||
|
if (Character.isWhitespace(src.charAt(offset))) {
|
||||||
|
offset += Character.charCount(src.charAt(offset));
|
||||||
|
} else if (match(src, offset, "//")) {
|
||||||
|
offset += 2;
|
||||||
|
while (offset < src.length()) {
|
||||||
|
char c = src.charAt(offset);
|
||||||
|
offset += Character.charCount(c);
|
||||||
|
if (c == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (match(src, offset, "/*")) {
|
||||||
|
offset += 2;
|
||||||
|
while (offset < src.length()) {
|
||||||
|
if (match(src, offset, "*/")) {
|
||||||
|
offset += 2;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
offset += Character.charCount(src.charAt(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (offset >= src.length()) return null;
|
||||||
|
|
||||||
|
int start = offset;
|
||||||
|
Type type;
|
||||||
|
String content = null;
|
||||||
|
|
||||||
|
char c = src.charAt(offset);
|
||||||
|
if (Character.isDigit(c)) {
|
||||||
|
do {
|
||||||
|
offset += Character.charCount(src.charAt(offset));
|
||||||
|
} while (offset < src.length() && Character.isDigit(src.charAt(offset)));
|
||||||
|
content = src.substring(start, offset);
|
||||||
|
type = Type.LIT_INT;
|
||||||
|
} else if (Character.isLetter(c) || c == '_') {
|
||||||
|
do {
|
||||||
|
offset += Character.charCount(src.charAt(offset));
|
||||||
|
} while (offset < src.length() && (Character.isLetter(src.charAt(offset)) || Character.isDigit(src.charAt(offset)) || src.charAt(offset) == '_'));
|
||||||
|
content = src.substring(start, offset);
|
||||||
|
type = Type.LIT_IDENT;
|
||||||
|
for (var ty : Type.KEYWORDS) {
|
||||||
|
if (content.equals(ty.lex)) {
|
||||||
|
type = ty;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (c == '\'') {
|
||||||
|
offset += Character.charCount(c);
|
||||||
|
char d = src.charAt(offset);
|
||||||
|
offset += Character.charCount(d);
|
||||||
|
if (d == '\\') {
|
||||||
|
if (offset >= src.length()) throw new TokenizeException("invalid character escape " + d, start, offset);
|
||||||
|
d = src.charAt(offset);
|
||||||
|
offset += Character.charCount(d);
|
||||||
|
content = switch (d) {
|
||||||
|
case 'n':
|
||||||
|
yield "\n";
|
||||||
|
case 'r':
|
||||||
|
yield "\r";
|
||||||
|
case 't':
|
||||||
|
yield "\t";
|
||||||
|
case '\\':
|
||||||
|
yield "\\";
|
||||||
|
case '\'':
|
||||||
|
yield "'";
|
||||||
|
case '"':
|
||||||
|
yield "\"";
|
||||||
|
case '?':
|
||||||
|
yield "?";
|
||||||
|
default:
|
||||||
|
throw new TokenizeException("invalid character escape " + d, start, offset);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
content = String.valueOf(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= src.length() || src.charAt(offset) != '\'') {
|
||||||
|
throw new TokenizeException("unterminated character literal", start, offset);
|
||||||
|
}
|
||||||
|
offset += Character.charCount('\'');
|
||||||
|
type = Type.LIT_CHAR;
|
||||||
|
} else if (c == '"') {
|
||||||
|
offset += Character.charCount(src.charAt(offset));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (offset < src.length() && src.charAt(offset) != '"') {
|
||||||
|
char d = src.charAt(offset);
|
||||||
|
offset += Character.charCount(d);
|
||||||
|
|
||||||
|
if (d == '\\') {
|
||||||
|
d = src.charAt(offset);
|
||||||
|
offset += Character.charCount(d);
|
||||||
|
char s = switch (d) {
|
||||||
|
case 'n':
|
||||||
|
yield '\n';
|
||||||
|
case 'r':
|
||||||
|
yield '\r';
|
||||||
|
case 't':
|
||||||
|
yield '\t';
|
||||||
|
case '\\':
|
||||||
|
yield '\\';
|
||||||
|
case '\'':
|
||||||
|
yield '\'';
|
||||||
|
case '"':
|
||||||
|
yield '"';
|
||||||
|
case '?':
|
||||||
|
yield '?';
|
||||||
|
default:
|
||||||
|
throw new TokenizeException("invalid string escape " + d, start, offset);
|
||||||
|
};
|
||||||
|
sb.append(s);
|
||||||
|
} else if (d == '\n') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
sb.append(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= src.length() || src.charAt(offset) != '"') {
|
||||||
|
throw new TokenizeException("unterminated string literal", start, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
content = sb.toString();
|
||||||
|
offset += Character.charCount('\"');
|
||||||
|
type = Type.LIT_STR;
|
||||||
|
} else {
|
||||||
|
type = null;
|
||||||
|
for (Type t : Type.PUNCT) {
|
||||||
|
if (match(src, start, t.lex)) {
|
||||||
|
type = t;
|
||||||
|
content = t.lex;
|
||||||
|
offset += t.lex.length();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == null) {
|
||||||
|
throw new TokenizeException("unexpected character '" + src.charAt(start) + "'", start, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Objects.requireNonNull(content);
|
||||||
|
return new Token(type, content, start, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TokenizeException extends IllegalArgumentException {
|
||||||
|
public final int start, end;
|
||||||
|
|
||||||
|
public TokenizeException(String msg, int start, int end) {
|
||||||
|
super(msg);
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TokenizeException at " + start + ":" + end + ", " + getLocalizedMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof String s) return Objects.equals(content, s);
|
||||||
|
if (!(o instanceof Token token)) return false;
|
||||||
|
return end == token.end && start == token.start && Objects.equals(content, token.content) && type == token.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(type, content, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location getStart() {
|
||||||
|
return new Location(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location getEnd() {
|
||||||
|
return new Location(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContent() {
|
||||||
|
return content();
|
||||||
|
}
|
||||||
|
}
|
||||||
139
src/jvmMain/kotlin/mtmc/lang/sea/ast/Ast.java
Normal file
139
src/jvmMain/kotlin/mtmc/lang/sea/ast/Ast.java
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.Span;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public sealed abstract class Ast permits Declaration, DeclarationFunc.Param, DeclarationStruct.Field, Expression, Statement, TypeExpr, Unit {
|
||||||
|
public final Token start, end;
|
||||||
|
|
||||||
|
public Ast(Token start, Token end) {
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span span() {
|
||||||
|
return Span.of(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Ast> getChildren() {
|
||||||
|
return switch (this) {
|
||||||
|
case DeclarationFunc declarationFunc -> {
|
||||||
|
Stream<Ast> out = Stream.of(declarationFunc.returnType);
|
||||||
|
out = Stream.concat(out, declarationFunc.params.params().stream());
|
||||||
|
if (declarationFunc.body != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(declarationFunc.body));
|
||||||
|
}
|
||||||
|
yield out;
|
||||||
|
}
|
||||||
|
case DeclarationSyntaxError ignored -> Stream.empty();
|
||||||
|
case DeclarationTypedef declarationTypedef -> Stream.of(declarationTypedef.type);
|
||||||
|
case DeclarationVar declarationVar -> {
|
||||||
|
Stream<Ast> out = Stream.of(declarationVar.type);
|
||||||
|
if (declarationVar.initializer != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(declarationVar.initializer));
|
||||||
|
}
|
||||||
|
yield out;
|
||||||
|
}
|
||||||
|
case DeclarationStruct struct -> struct.fields.stream().map(x -> x);
|
||||||
|
case DeclarationStruct.Field field -> Stream.of(field.type);
|
||||||
|
case DeclarationFunc.Param param -> Stream.of(param.type);
|
||||||
|
case ExpressionAccess expressionAccess -> Stream.of(expressionAccess.value);
|
||||||
|
case ExpressionBin expressionBin -> Stream.of(expressionBin.lhs, expressionBin.rhs);
|
||||||
|
case ExpressionCall expressionCall -> {
|
||||||
|
Stream<Ast> out = Stream.of(expressionCall.functor);
|
||||||
|
out = Stream.concat(out, expressionCall.args.stream());
|
||||||
|
yield out;
|
||||||
|
}
|
||||||
|
case ExpressionInitializer init -> init.values.stream().map(x -> x);
|
||||||
|
case ExpressionCast expressionCast -> Stream.of(expressionCast.type, expressionCast.value);
|
||||||
|
case ExpressionChar ignored -> Stream.empty();
|
||||||
|
case ExpressionIdent ignored -> Stream.empty();
|
||||||
|
case ExpressionIndex expressionIndex -> Stream.of(expressionIndex.array, expressionIndex.index);
|
||||||
|
case ExpressionInteger ignored -> Stream.empty();
|
||||||
|
case ExpressionParens expressionParens -> Stream.of(expressionParens.inner);
|
||||||
|
case ExpressionPostfix expressionPostfix -> Stream.of(expressionPostfix.inner);
|
||||||
|
case ExpressionPrefix expressionPrefix -> Stream.of(expressionPrefix.inner);
|
||||||
|
case ExpressionString ignored -> Stream.empty();
|
||||||
|
case ExpressionTypeError typeError -> Stream.of(typeError.inner);
|
||||||
|
case ExpressionSyntaxError expressionSyntaxError -> {
|
||||||
|
if (expressionSyntaxError.child != null) {
|
||||||
|
yield Stream.of(expressionSyntaxError.child);
|
||||||
|
} else {
|
||||||
|
yield Stream.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ExpressionTernary expressionTernary -> Stream.of(
|
||||||
|
expressionTernary.cond,
|
||||||
|
expressionTernary.then,
|
||||||
|
expressionTernary.otherwise
|
||||||
|
);
|
||||||
|
case StatementBlock statementBlock -> statementBlock.statements.stream().map(x -> x);
|
||||||
|
case StatementBreak ignored -> Stream.empty();
|
||||||
|
case StatementContinue ignored -> Stream.empty();
|
||||||
|
case StatementDoWhile statementDoWhile -> Stream.of(statementDoWhile.body, statementDoWhile.condition);
|
||||||
|
case StatementExpression statementExpression -> Stream.of(statementExpression.expression);
|
||||||
|
case StatementFor statementFor -> {
|
||||||
|
Stream<Ast> out = Stream.empty();
|
||||||
|
if (statementFor.initExpression != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(statementFor.initExpression));
|
||||||
|
} else if (statementFor.initStatement != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(statementFor.initStatement));
|
||||||
|
}
|
||||||
|
if (statementFor.condition != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(statementFor.condition));
|
||||||
|
}
|
||||||
|
if (statementFor.inc != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(statementFor.inc));
|
||||||
|
}
|
||||||
|
out = Stream.concat(out, Stream.of(statementFor.body));
|
||||||
|
yield out;
|
||||||
|
}
|
||||||
|
case StatementGoto ignored -> Stream.empty();
|
||||||
|
case StatementIf statementIf -> {
|
||||||
|
Stream<Ast> out = Stream.of(statementIf.condition, statementIf.body);
|
||||||
|
if (statementIf.elseBody != null) out = Stream.concat(out, Stream.of(statementIf.elseBody));
|
||||||
|
yield out;
|
||||||
|
}
|
||||||
|
case StatementReturn statementReturn -> {
|
||||||
|
if (statementReturn.value == null) {
|
||||||
|
yield Stream.empty();
|
||||||
|
} else {
|
||||||
|
yield Stream.of(statementReturn.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case StatementSyntaxError ignored -> Stream.empty();
|
||||||
|
case StatementVar statementVar -> {
|
||||||
|
Stream<Ast> out = Stream.of(statementVar.type);
|
||||||
|
if (statementVar.initValue != null) {
|
||||||
|
out = Stream.concat(out, Stream.of(statementVar.initValue));
|
||||||
|
}
|
||||||
|
yield out;
|
||||||
|
}
|
||||||
|
case StatementWhile statementWhile -> Stream.of(statementWhile.condition, statementWhile.body);
|
||||||
|
case TypeExprArray typeExprArray -> Stream.of(typeExprArray.inner);
|
||||||
|
case TypeExprChar ignored -> Stream.empty();
|
||||||
|
case TypeExprInt ignored -> Stream.empty();
|
||||||
|
case TypeExprRef ignored -> Stream.empty();
|
||||||
|
case TypeExprVoid ignored -> Stream.empty();
|
||||||
|
case TypePointer ignored -> Stream.empty();
|
||||||
|
case Unit unit -> unit.declarations.stream().map(x -> x);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Error> collectErrors() {
|
||||||
|
var errors = new ArrayList<Error>();
|
||||||
|
collectErrors(errors);
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collectErrors(List<Error> errors) {
|
||||||
|
if (this instanceof Error e) {
|
||||||
|
errors.add(e);
|
||||||
|
}
|
||||||
|
getChildren().forEach(child -> child.collectErrors(errors));
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/jvmMain/kotlin/mtmc/lang/sea/ast/Declaration.java
Normal file
9
src/jvmMain/kotlin/mtmc/lang/sea/ast/Declaration.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public abstract sealed class Declaration extends Ast permits DeclarationFunc, DeclarationStruct, DeclarationSyntaxError, DeclarationTypedef, DeclarationVar {
|
||||||
|
public Declaration(Token start, Token end) {
|
||||||
|
super(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationFunc.java
Normal file
59
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationFunc.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class DeclarationFunc extends Declaration {
|
||||||
|
public final TypeExpr returnType;
|
||||||
|
public final Token name;
|
||||||
|
public final ParamList params;
|
||||||
|
public final StatementBlock body;
|
||||||
|
|
||||||
|
public DeclarationFunc(TypeExpr returnType, Token name, ParamList paramList, StatementBlock body, Token end) {
|
||||||
|
super(returnType.start, end);
|
||||||
|
this.returnType = returnType;
|
||||||
|
this.name = name;
|
||||||
|
this.params = paramList;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Param extends Ast {
|
||||||
|
public final TypeExpr type;
|
||||||
|
public final Token name;
|
||||||
|
|
||||||
|
public Param(TypeExpr type, Token name) {
|
||||||
|
super(type.start, name.end() < type.end.end() ? type.end : name);
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeaType.Func type;
|
||||||
|
public SeaType.Func type() {
|
||||||
|
if (type == null) {
|
||||||
|
var paramTypes = new ArrayList<SeaType>(params.size());
|
||||||
|
for (Param param : params.params) {
|
||||||
|
paramTypes.add(param.type.type());
|
||||||
|
}
|
||||||
|
type = new SeaType.Func(
|
||||||
|
paramTypes,
|
||||||
|
params.isVararg,
|
||||||
|
returnType.type()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ParamList(List<Param> params, boolean isVararg) {
|
||||||
|
public int size() {
|
||||||
|
return params.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeaType getParamType(int i) {
|
||||||
|
return params.get(i).type.type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationStruct.java
Normal file
54
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationStruct.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class DeclarationStruct extends Declaration implements TypeDeclaration {
|
||||||
|
public final Token name;
|
||||||
|
public final List<Field> fields;
|
||||||
|
|
||||||
|
public DeclarationStruct(Token start, Token name, List<Field> fields, Token end) {
|
||||||
|
super(start, end);
|
||||||
|
this.name = name;
|
||||||
|
this.fields = List.copyOf(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name.content();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeaType type;
|
||||||
|
@Override
|
||||||
|
public SeaType type() {
|
||||||
|
if (type == null) {
|
||||||
|
var fields = new LinkedHashMap<String, SeaType>();
|
||||||
|
for (var field : this.fields) {
|
||||||
|
fields.put(field.name(), field.type());
|
||||||
|
}
|
||||||
|
type = new SeaType.Struct(name(), fields);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Field extends Ast {
|
||||||
|
public final TypeExpr type;
|
||||||
|
public final Token name;
|
||||||
|
|
||||||
|
public Field(TypeExpr type, Token name) {
|
||||||
|
super(type.start, name);
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name.content();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeaType type() {
|
||||||
|
return type.type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class DeclarationSyntaxError extends Declaration implements SyntaxError {
|
||||||
|
public final ParseException exception;
|
||||||
|
|
||||||
|
public DeclarationSyntaxError(Token token, ParseException parseException) {
|
||||||
|
super(token, token);
|
||||||
|
this.exception = parseException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParseException exception() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationTypedef.java
Normal file
26
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationTypedef.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class DeclarationTypedef extends Declaration implements TypeDeclaration {
|
||||||
|
public final TypeExpr type;
|
||||||
|
public final Token name;
|
||||||
|
|
||||||
|
|
||||||
|
public DeclarationTypedef(Token start, TypeExpr type, Token name, Token end) {
|
||||||
|
super(start, type.end);
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name.content();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SeaType type() {
|
||||||
|
return type.type();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationVar.java
Normal file
20
src/jvmMain/kotlin/mtmc/lang/sea/ast/DeclarationVar.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class DeclarationVar extends Declaration {
|
||||||
|
public final TypeExpr type;
|
||||||
|
public final Token name;
|
||||||
|
public final Expression initializer;
|
||||||
|
|
||||||
|
public DeclarationVar(TypeExpr type, Token name, Expression initializer) {
|
||||||
|
super(type.start, initializer == null ? name : initializer.end);
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
this.initializer = initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return this.name.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/jvmMain/kotlin/mtmc/lang/sea/ast/Error.java
Normal file
7
src/jvmMain/kotlin/mtmc/lang/sea/ast/Error.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
|
||||||
|
public interface Error {
|
||||||
|
ParseException exception();
|
||||||
|
}
|
||||||
23
src/jvmMain/kotlin/mtmc/lang/sea/ast/Expression.java
Normal file
23
src/jvmMain/kotlin/mtmc/lang/sea/ast/Expression.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public sealed abstract class Expression extends Ast permits ExpressionAccess, ExpressionBin, ExpressionCall, ExpressionCast, ExpressionChar, ExpressionIdent, ExpressionIndex, ExpressionInitializer, ExpressionInteger, ExpressionParens, ExpressionPostfix, ExpressionPrefix, ExpressionString, ExpressionSyntaxError, ExpressionTernary, ExpressionTypeError {
|
||||||
|
private final SeaType type;
|
||||||
|
public Expression(Token start, Token end, SeaType type) {
|
||||||
|
super(start, end);
|
||||||
|
this.type = Objects.requireNonNull(type, "'type' cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeaType type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ValueKind {
|
||||||
|
Addressable,
|
||||||
|
Immediate
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionAccess.java
Normal file
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionAccess.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionAccess extends Expression {
|
||||||
|
public final Expression value;
|
||||||
|
public final Token access;
|
||||||
|
public final Token prop;
|
||||||
|
|
||||||
|
public ExpressionAccess(Expression value, Token access, Token prop, SeaType type) {
|
||||||
|
super(value.start, prop, type);
|
||||||
|
this.value = value;
|
||||||
|
this.access = access;
|
||||||
|
this.prop = prop;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionBin.java
Normal file
21
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionBin.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionBin extends Expression {
|
||||||
|
public final Expression lhs;
|
||||||
|
public final Token op;
|
||||||
|
public final Expression rhs;
|
||||||
|
|
||||||
|
public ExpressionBin(Expression lhs, Token op, Expression rhs, SeaType type) {
|
||||||
|
super(lhs.start, rhs.end, type);
|
||||||
|
this.lhs = lhs;
|
||||||
|
this.op = op;
|
||||||
|
this.rhs = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String op() {
|
||||||
|
return op.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionCall.java
Normal file
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionCall.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class ExpressionCall extends Expression {
|
||||||
|
public final Expression functor;
|
||||||
|
public final List<Expression> args;
|
||||||
|
|
||||||
|
public ExpressionCall(Expression functor, List<Expression> args, Token end, SeaType type) {
|
||||||
|
super(functor.start, end, type);
|
||||||
|
this.functor = functor;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionCast.java
Normal file
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionCast.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionCast extends Expression {
|
||||||
|
public final TypeExpr type;
|
||||||
|
public final Expression value;
|
||||||
|
|
||||||
|
public ExpressionCast(Token start, TypeExpr type, Expression value) {
|
||||||
|
super(start, value.end, type.type());
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionChar.java
Normal file
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionChar.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionChar extends Expression {
|
||||||
|
public ExpressionChar(Token token) {
|
||||||
|
super(token, token, SeaType.CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Character content() {
|
||||||
|
return start.content().charAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionIdent.java
Normal file
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionIdent.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionIdent extends Expression {
|
||||||
|
public final boolean isAddressable;
|
||||||
|
|
||||||
|
public ExpressionIdent(Token token, SeaType type, boolean isAddressable) {
|
||||||
|
super(token, token, type);
|
||||||
|
this.isAddressable = isAddressable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return start.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionIndex.java
Normal file
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionIndex.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionIndex extends Expression {
|
||||||
|
public final Expression array, index;
|
||||||
|
|
||||||
|
public ExpressionIndex(Expression array, Expression index, Token end, SeaType type) {
|
||||||
|
super(array.start, end, type);
|
||||||
|
this.array = array;
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class ExpressionInitializer extends Expression {
|
||||||
|
public final List<Expression> values;
|
||||||
|
|
||||||
|
private static SeaType.Initializer blobType(List<Expression> values) {
|
||||||
|
var types = new ArrayList<SeaType>();
|
||||||
|
|
||||||
|
for (var value : values) {
|
||||||
|
types.add(value.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SeaType.Initializer(types);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionInitializer(Token start, List<Expression> values, Token end) {
|
||||||
|
super(start, end, blobType(values));
|
||||||
|
this.values = List.copyOf(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionInteger.java
Normal file
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionInteger.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionInteger extends Expression {
|
||||||
|
public final int value;
|
||||||
|
|
||||||
|
public ExpressionInteger(Token start) {
|
||||||
|
super(start, start, SeaType.INT);
|
||||||
|
this.value = Integer.parseInt(start.content());
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionParens.java
Normal file
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionParens.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionParens extends Expression {
|
||||||
|
public final Expression inner;
|
||||||
|
|
||||||
|
public ExpressionParens(Token start, Expression inner, Token end) {
|
||||||
|
super(start, end, inner.type());
|
||||||
|
this.inner = inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionPostfix.java
Normal file
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionPostfix.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionPostfix extends Expression {
|
||||||
|
public Expression inner;
|
||||||
|
|
||||||
|
public ExpressionPostfix(Expression lhs, Token op, SeaType type) {
|
||||||
|
super(lhs.start, op, type);
|
||||||
|
this.inner = lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String op() {
|
||||||
|
return end.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionPrefix.java
Normal file
17
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionPrefix.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionPrefix extends Expression {
|
||||||
|
public final Expression inner;
|
||||||
|
|
||||||
|
public ExpressionPrefix(Token operator, Expression rhs, SeaType type) {
|
||||||
|
super(operator, rhs.end, type);
|
||||||
|
this.inner = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String op() {
|
||||||
|
return start.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionString.java
Normal file
18
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionString.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class ExpressionString extends Expression {
|
||||||
|
public ExpressionString(Token token) {
|
||||||
|
super(token, token, new SeaType.Pointer(SeaType.CHAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return start.content().getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String content() {
|
||||||
|
return start.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public final class ExpressionSyntaxError extends Expression implements SyntaxError {
|
||||||
|
@Nullable
|
||||||
|
public final Expression child;
|
||||||
|
public final ParseException exception;
|
||||||
|
|
||||||
|
public ExpressionSyntaxError(Token token, String message) {
|
||||||
|
this(null, token, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionSyntaxError(@Nullable Expression child, Token token, String message) {
|
||||||
|
super(child == null ? token : child.start, token, SeaType.INT);
|
||||||
|
this.child = child;
|
||||||
|
this.exception = new ParseException(new ParseException.Message(token, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParseException exception() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionTernary.java
Normal file
16
src/jvmMain/kotlin/mtmc/lang/sea/ast/ExpressionTernary.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
|
||||||
|
public final class ExpressionTernary extends Expression {
|
||||||
|
public final Expression cond;
|
||||||
|
public final Expression then;
|
||||||
|
public final Expression otherwise;
|
||||||
|
|
||||||
|
public ExpressionTernary(Expression cond, Expression then, Expression otherwise, SeaType type) {
|
||||||
|
super(cond.start, otherwise.end, type);
|
||||||
|
this.cond = cond;
|
||||||
|
this.then = then;
|
||||||
|
this.otherwise = otherwise;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
|
||||||
|
public final class ExpressionTypeError extends Expression implements Error {
|
||||||
|
public final Expression inner;
|
||||||
|
public final ParseException exception;
|
||||||
|
|
||||||
|
public ExpressionTypeError(Expression inner, String message) {
|
||||||
|
super(inner.start, inner.end, inner.type());
|
||||||
|
this.inner = inner;
|
||||||
|
this.exception = new ParseException(new ParseException.Message(inner.span(), message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParseException exception() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/jvmMain/kotlin/mtmc/lang/sea/ast/Statement.java
Normal file
25
src/jvmMain/kotlin/mtmc/lang/sea/ast/Statement.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public abstract sealed class Statement extends Ast permits StatementBlock, StatementBreak, StatementContinue, StatementDoWhile, StatementExpression, StatementFor, StatementGoto, StatementIf, StatementReturn, StatementSyntaxError, StatementVar, StatementWhile
|
||||||
|
{
|
||||||
|
private Token labelAnchor = null;
|
||||||
|
|
||||||
|
public Statement(Token start, Token end) {
|
||||||
|
super(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabelAnchor(Token labelAnchor) throws ParseException {
|
||||||
|
if (labelAnchor == null) return;
|
||||||
|
if (this.labelAnchor != null) {
|
||||||
|
throw new ParseException(new ParseException.Message(labelAnchor, "this statement has been labeled twice!!"));
|
||||||
|
}
|
||||||
|
this.labelAnchor = labelAnchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Token getLabelAnchor() {
|
||||||
|
return labelAnchor;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementBlock.java
Normal file
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementBlock.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class StatementBlock extends Statement {
|
||||||
|
public final List<Statement> statements;
|
||||||
|
|
||||||
|
public StatementBlock(Token start, List<Statement> children, Token end) {
|
||||||
|
super(start, end);
|
||||||
|
this.statements = List.copyOf(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementBreak.java
Normal file
9
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementBreak.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementBreak extends Statement {
|
||||||
|
public StatementBreak(Token breakToken) {
|
||||||
|
super(breakToken, breakToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementContinue extends Statement {
|
||||||
|
public StatementContinue(Token continueToken) {
|
||||||
|
super(continueToken, continueToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementDoWhile.java
Normal file
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementDoWhile.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementDoWhile extends Statement {
|
||||||
|
public final Statement body;
|
||||||
|
public final Expression condition;
|
||||||
|
|
||||||
|
public StatementDoWhile(Token start, Statement body, Expression condition, Token end) {
|
||||||
|
super(start, end);
|
||||||
|
this.body = body;
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
public final class StatementExpression extends Statement {
|
||||||
|
public final Expression expression;
|
||||||
|
|
||||||
|
public StatementExpression(Expression expression) {
|
||||||
|
super(expression.start, expression.end);
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementFor.java
Normal file
20
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementFor.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementFor extends Statement {
|
||||||
|
public final Expression initExpression;
|
||||||
|
public final StatementVar initStatement;
|
||||||
|
public final Expression condition;
|
||||||
|
public final Expression inc;
|
||||||
|
public final Statement body;
|
||||||
|
|
||||||
|
public StatementFor(Token start, Expression initExpression, StatementVar initStatement, Expression condition, Expression inc, Statement body) {
|
||||||
|
super(start, body.end);
|
||||||
|
this.initExpression = initExpression;
|
||||||
|
this.initStatement = initStatement;
|
||||||
|
this.condition = condition;
|
||||||
|
this.inc = inc;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementGoto.java
Normal file
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementGoto.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementGoto extends Statement {
|
||||||
|
public final Token label;
|
||||||
|
|
||||||
|
public StatementGoto(Token start, Token label) {
|
||||||
|
super(start, label);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementIf.java
Normal file
16
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementIf.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementIf extends Statement {
|
||||||
|
public final Expression condition;
|
||||||
|
public final Statement body;
|
||||||
|
public final Statement elseBody;
|
||||||
|
|
||||||
|
public StatementIf(Token start, Expression condition, Statement body, Statement elseBody) {
|
||||||
|
super(start, elseBody == null ? body.end : elseBody.end);
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body;
|
||||||
|
this.elseBody = elseBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementReturn.java
Normal file
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementReturn.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementReturn extends Statement {
|
||||||
|
public final Expression value;
|
||||||
|
|
||||||
|
public StatementReturn(Token start, Expression value) {
|
||||||
|
super(start, value == null ? start : value.end);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementSyntaxError extends Statement implements SyntaxError {
|
||||||
|
public final ParseException exception;
|
||||||
|
|
||||||
|
public StatementSyntaxError(Token token, ParseException exception) {
|
||||||
|
super(token, token);
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParseException exception() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementVar.java
Normal file
20
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementVar.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementVar extends Statement {
|
||||||
|
public final TypeExpr type;
|
||||||
|
public final Token name;
|
||||||
|
public final Expression initValue;
|
||||||
|
|
||||||
|
public StatementVar(TypeExpr type, Token name, Expression initValue) {
|
||||||
|
super(type.start, initValue == null ? name : initValue.end);
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
this.initValue = initValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name.content();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementWhile.java
Normal file
14
src/jvmMain/kotlin/mtmc/lang/sea/ast/StatementWhile.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class StatementWhile extends Statement {
|
||||||
|
public final Expression condition;
|
||||||
|
public final Statement body;
|
||||||
|
|
||||||
|
public StatementWhile(Token start, Expression condition, Statement body) {
|
||||||
|
super(start, body.end);
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/jvmMain/kotlin/mtmc/lang/sea/ast/SyntaxError.java
Normal file
7
src/jvmMain/kotlin/mtmc/lang/sea/ast/SyntaxError.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.ParseException;
|
||||||
|
|
||||||
|
public interface SyntaxError extends Error {
|
||||||
|
ParseException exception();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
|
||||||
|
public interface TypeDeclaration {
|
||||||
|
String name();
|
||||||
|
SeaType type();
|
||||||
|
}
|
||||||
19
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExpr.java
Normal file
19
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExpr.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public abstract sealed class TypeExpr extends Ast permits TypeExprArray, TypeExprChar, TypeExprInt, TypeExprRef, TypeExprVoid, TypePointer {
|
||||||
|
private final SeaType type;
|
||||||
|
|
||||||
|
public TypeExpr(Token start, Token end, SeaType type) {
|
||||||
|
super(start, end);
|
||||||
|
this.type = Objects.requireNonNull(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeaType type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprArray.java
Normal file
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprArray.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class TypeExprArray extends TypeExpr {
|
||||||
|
public final TypeExpr inner;
|
||||||
|
|
||||||
|
public TypeExprArray(TypeExpr inner, Token end) {
|
||||||
|
super(inner.start, end, new SeaType.Pointer(inner.type()));
|
||||||
|
this.inner = inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprChar.java
Normal file
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprChar.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class TypeExprChar extends TypeExpr {
|
||||||
|
public final Token token;
|
||||||
|
|
||||||
|
public TypeExprChar(Token token) {
|
||||||
|
super(token, token, SeaType.CHAR);
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprInt.java
Normal file
10
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprInt.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class TypeExprInt extends TypeExpr {
|
||||||
|
public TypeExprInt(Token token) {
|
||||||
|
super(token, token, SeaType.INT);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprRef.java
Normal file
12
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprRef.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class TypeExprRef extends TypeExpr {
|
||||||
|
public final TypeDeclaration decl;
|
||||||
|
|
||||||
|
public TypeExprRef(Token name, TypeDeclaration decl) {
|
||||||
|
super(name, name, decl.type());
|
||||||
|
this.decl = decl;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprVoid.java
Normal file
10
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypeExprVoid.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class TypeExprVoid extends TypeExpr {
|
||||||
|
public TypeExprVoid(Token token) {
|
||||||
|
super(token, token, SeaType.VOID);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypePointer.java
Normal file
13
src/jvmMain/kotlin/mtmc/lang/sea/ast/TypePointer.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.SeaType;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
public final class TypePointer extends TypeExpr {
|
||||||
|
public final TypeExpr component;
|
||||||
|
|
||||||
|
public TypePointer(TypeExpr component, Token star) {
|
||||||
|
super(component.start, star, new SeaType.Pointer(component.type()));
|
||||||
|
this.component = component;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/jvmMain/kotlin/mtmc/lang/sea/ast/Unit.java
Normal file
22
src/jvmMain/kotlin/mtmc/lang/sea/ast/Unit.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package mtmc.lang.sea.ast;
|
||||||
|
|
||||||
|
import mtmc.lang.sea.Symbol;
|
||||||
|
import mtmc.lang.sea.Token;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class Unit extends Ast {
|
||||||
|
public final String source;
|
||||||
|
public final String filename;
|
||||||
|
public final List<Declaration> declarations;
|
||||||
|
public final LinkedHashMap<String, Symbol> symbols;
|
||||||
|
|
||||||
|
public Unit(String filename, String source, List<Declaration> declarations, LinkedHashMap<String, Symbol> globals) {
|
||||||
|
super(Token.SOF, Token.EOF);
|
||||||
|
this.source = source;
|
||||||
|
this.filename = filename;
|
||||||
|
this.declarations = declarations;
|
||||||
|
this.symbols = globals;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user