mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
get rid of newexpr compiler option
This commit is contained in:
parent
bb95484c8a
commit
bdf8aa9168
@ -37,16 +37,17 @@ class PtNodeGroup : PtNode(Position.DUMMY)
|
|||||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||||
// Note that as an exception, the 'name' is not read-only
|
// Note that as an exception, the 'name' is not read-only
|
||||||
// but a var. This is to allow for cheap node renames.
|
// but a var. This is to allow for cheap node renames.
|
||||||
val scopedName: String by lazy {
|
val scopedName: String
|
||||||
var namedParent: PtNode = this.parent
|
get() {
|
||||||
if(namedParent is PtProgram)
|
var namedParent: PtNode = this.parent
|
||||||
name
|
return if(namedParent is PtProgram)
|
||||||
else {
|
name
|
||||||
while (namedParent !is PtNamedNode)
|
else {
|
||||||
namedParent = namedParent.parent
|
while (namedParent !is PtNamedNode)
|
||||||
namedParent.scopedName + "." + name
|
namedParent = namedParent.parent
|
||||||
|
namedParent.scopedName + "." + name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -63,9 +64,9 @@ class PtProgram(
|
|||||||
children.asSequence().filterIsInstance<PtBlock>()
|
children.asSequence().filterIsInstance<PtBlock>()
|
||||||
|
|
||||||
fun entrypoint(): PtSub? =
|
fun entrypoint(): PtSub? =
|
||||||
allBlocks().firstOrNull { it.name == "main" }
|
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" }
|
||||||
?.children
|
?.children
|
||||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
|
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var asmListfile: Boolean = false,
|
var asmListfile: Boolean = false,
|
||||||
var experimentalCodegen: Boolean = false,
|
var experimentalCodegen: Boolean = false,
|
||||||
var varsHighBank: Int? = null,
|
var varsHighBank: Int? = null,
|
||||||
var useNewExprCode: Boolean = false,
|
|
||||||
var splitWordArrays: Boolean = false,
|
var splitWordArrays: Boolean = false,
|
||||||
var evalStackBaseAddress: UInt? = null,
|
var evalStackBaseAddress: UInt? = null,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
|
@ -31,7 +31,8 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
|||||||
|
|
||||||
printAst(program, true, ::println)
|
printAst(program, true, ::println)
|
||||||
|
|
||||||
val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>()
|
val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
|
||||||
|
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
|
||||||
|
|
||||||
fun prefixNamedNode(node: PtNamedNode) {
|
fun prefixNamedNode(node: PtNamedNode) {
|
||||||
node.name = "p8_${node.name}"
|
node.name = "p8_${node.name}"
|
||||||
@ -51,7 +52,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
|||||||
val stNode = st.lookup(node.name)!!
|
val stNode = st.lookup(node.name)!!
|
||||||
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
|
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
|
||||||
val index = node.parent.children.indexOf(node)
|
val index = node.parent.children.indexOf(node)
|
||||||
nodesToPrefix += node.parent to index
|
functionCallsToPrefix += node.parent to index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtIdentifier -> {
|
is PtIdentifier -> {
|
||||||
@ -105,13 +106,22 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
|||||||
val node = parent.children[index]
|
val node = parent.children[index]
|
||||||
when(node) {
|
when(node) {
|
||||||
is PtIdentifier -> parent.children[index] = node.prefix(parent)
|
is PtIdentifier -> parent.children[index] = node.prefix(parent)
|
||||||
is PtFunctionCall -> parent.children[index] = node.prefix(parent)
|
is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last")
|
||||||
is PtJump -> parent.children[index] = node.prefix(parent)
|
is PtJump -> parent.children[index] = node.prefix(parent)
|
||||||
is PtVariable -> parent.children[index] = node.prefix(parent)
|
is PtVariable -> parent.children[index] = node.prefix(parent)
|
||||||
else -> throw AssemblyError("weird node to prefix $node")
|
else -> throw AssemblyError("weird node to prefix $node")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
functionCallsToPrefix.forEach { (parent, index) ->
|
||||||
|
val node = parent.children[index]
|
||||||
|
if(node is PtFunctionCall) {
|
||||||
|
parent.children[index] = node.prefix(parent)
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("expected PtFunctionCall")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return SymbolTableMaker(program, options).make()
|
return SymbolTableMaker(program, options).make()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -658,7 +668,6 @@ class AsmGen6502Internal (
|
|||||||
private fun translate(stmt: PtIfElse) {
|
private fun translate(stmt: PtIfElse) {
|
||||||
val condition = stmt.condition as? PtBinaryExpression
|
val condition = stmt.condition as? PtBinaryExpression
|
||||||
if(condition!=null) {
|
if(condition!=null) {
|
||||||
require(!options.useNewExprCode)
|
|
||||||
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
|
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
|
||||||
if (stmt.elseScope.children.isEmpty()) {
|
if (stmt.elseScope.children.isEmpty()) {
|
||||||
val jump = stmt.ifScope.children.singleOrNull()
|
val jump = stmt.ifScope.children.singleOrNull()
|
||||||
@ -1118,18 +1127,10 @@ $repeatLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
|
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
|
||||||
val left: PtExpression
|
if (pointerOffsetExpr !is PtBinaryExpression) return null
|
||||||
val right: PtExpression
|
val operator = pointerOffsetExpr.operator
|
||||||
val operator: String
|
val left = pointerOffsetExpr.left
|
||||||
|
val right = pointerOffsetExpr.right
|
||||||
if (pointerOffsetExpr is PtBinaryExpression) {
|
|
||||||
require(!options.useNewExprCode)
|
|
||||||
operator = pointerOffsetExpr.operator
|
|
||||||
left = pointerOffsetExpr.left
|
|
||||||
right = pointerOffsetExpr.right
|
|
||||||
}
|
|
||||||
else return null
|
|
||||||
|
|
||||||
if (operator != "+") return null
|
if (operator != "+") return null
|
||||||
val leftDt = left.type
|
val leftDt = left.type
|
||||||
val rightDt = right.type
|
val rightDt = right.type
|
||||||
@ -1241,10 +1242,7 @@ $repeatLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? {
|
internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? {
|
||||||
val stScope = asmgen.symbolTable.lookup(name)
|
val stScope = asmgen.symbolTable.lookup(name) ?: return null
|
||||||
require(stScope!=null) {
|
|
||||||
"invalid name lookup $name"
|
|
||||||
}
|
|
||||||
val node = stScope.astNode
|
val node = stScope.astNode
|
||||||
if(node is PtSubroutineParameter)
|
if(node is PtSubroutineParameter)
|
||||||
return node
|
return node
|
||||||
@ -2892,7 +2890,6 @@ $repeatLabel""")
|
|||||||
out(" sta P8ESTACK_LO,x | dex")
|
out(" sta P8ESTACK_LO,x | dex")
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!options.useNewExprCode)
|
|
||||||
val addrExpr = expr.address as PtBinaryExpression
|
val addrExpr = expr.address as PtBinaryExpression
|
||||||
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||||
if(pushResultOnEstack)
|
if(pushResultOnEstack)
|
||||||
|
@ -751,7 +751,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||||
val pointer = result?.first as? PtIdentifier
|
val pointer = result?.first as? PtIdentifier
|
||||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||||
@ -813,7 +812,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
} else fallback()
|
} else fallback()
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||||
val pointer = result?.first as? PtIdentifier
|
val pointer = result?.first as? PtIdentifier
|
||||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||||
|
@ -248,8 +248,6 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: PtBinaryExpression) {
|
private fun translateExpression(expr: PtBinaryExpression) {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
|
|
||||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||||
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
||||||
return
|
return
|
||||||
|
@ -313,7 +313,9 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
asmgen.out("${sub.name}\t$asmStartScope")
|
asmgen.out("${sub.name}\t$asmStartScope")
|
||||||
|
|
||||||
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
val scope = symboltable.lookupOrElse(sub.scopedName) {
|
||||||
|
throw AssemblyError("lookup ${sub.scopedName}")
|
||||||
|
}
|
||||||
require(scope.type==StNodeType.SUBROUTINE)
|
require(scope.type==StNodeType.SUBROUTINE)
|
||||||
val varsInSubroutine = getVars(scope)
|
val varsInSubroutine = getVars(scope)
|
||||||
|
|
||||||
|
@ -158,7 +158,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
|
assignMemoryByte(assign.target, null, value.address as PtIdentifier)
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
val addrExpr = value.address as PtBinaryExpression
|
val addrExpr = value.address as PtBinaryExpression
|
||||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||||
@ -313,7 +312,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
||||||
// All remaining binary expressions just evaluate via the stack for now.
|
// All remaining binary expressions just evaluate via the stack for now.
|
||||||
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||||
@ -359,7 +357,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
if(expr.operator in ComparisonOperators) {
|
if(expr.operator in ComparisonOperators) {
|
||||||
if(expr.right.asConstInteger() == 0) {
|
if(expr.right.asConstInteger() == 0) {
|
||||||
if(expr.operator == "==" || expr.operator=="!=") {
|
if(expr.operator == "==" || expr.operator=="!=") {
|
||||||
@ -1155,7 +1152,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
"==" -> {
|
"==" -> {
|
||||||
when(val dt = expr.left.type) {
|
when(val dt = expr.left.type) {
|
||||||
@ -1335,7 +1331,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
|
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
val addrExpr = value.address as PtBinaryExpression
|
val addrExpr = value.address as PtBinaryExpression
|
||||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||||
asmgen.out(" ldy #0")
|
asmgen.out(" ldy #0")
|
||||||
@ -3487,7 +3482,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
asmgen.storeAIntoPointerVar(addressExpr)
|
asmgen.storeAIntoPointerVar(addressExpr)
|
||||||
}
|
}
|
||||||
addressExpr is PtBinaryExpression -> {
|
addressExpr is PtBinaryExpression -> {
|
||||||
require(!asmgen.options.useNewExprCode)
|
|
||||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
|
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
|
||||||
storeViaExprEval()
|
storeViaExprEval()
|
||||||
}
|
}
|
||||||
|
@ -105,27 +105,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
value.add(origAssign.value)
|
value.add(origAssign.value)
|
||||||
} else {
|
} else {
|
||||||
require(origAssign.operator.endsWith('='))
|
require(origAssign.operator.endsWith('='))
|
||||||
if(codeGen.options.useNewExprCode) {
|
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
||||||
// X += Y -> temp = X, temp += Y, X = temp
|
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||||
val tempvar = codeGen.getReusableTempvar(origAssign.definingSub()!!, origAssign.target.type)
|
value.add(left)
|
||||||
val assign = PtAssignment(origAssign.position)
|
value.add(origAssign.value)
|
||||||
val target = PtAssignTarget(origAssign.position)
|
|
||||||
target.add(tempvar)
|
|
||||||
assign.add(target)
|
|
||||||
assign.add(origAssign.target.children.single())
|
|
||||||
val augAssign = PtAugmentedAssign(origAssign.operator, origAssign.position)
|
|
||||||
augAssign.add(target)
|
|
||||||
augAssign.add(origAssign.value)
|
|
||||||
val assignBack = PtAssignment(origAssign.position)
|
|
||||||
assignBack.add(origAssign.target)
|
|
||||||
assignBack.add(tempvar)
|
|
||||||
return translateRegularAssign(assign) + translate(augAssign) + translateRegularAssign(assignBack)
|
|
||||||
} else {
|
|
||||||
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
|
||||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
|
||||||
value.add(left)
|
|
||||||
value.add(origAssign.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val normalAssign = PtAssignment(origAssign.position)
|
val normalAssign = PtAssignment(origAssign.position)
|
||||||
normalAssign.add(origAssign.target)
|
normalAssign.add(origAssign.target)
|
||||||
@ -347,19 +330,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return Pair(result, tr.resultReg)
|
return Pair(result, tr.resultReg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(codeGen.options.useNewExprCode) {
|
val mult: PtExpression
|
||||||
val tr = expressionEval.translateExpression(array.index)
|
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||||
result += tr.chunks
|
mult.children += array.index
|
||||||
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, immediate = itemsize), null)
|
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
||||||
return Pair(result, tr.resultReg)
|
val tr = expressionEval.translateExpression(mult)
|
||||||
} else {
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val mult: PtExpression
|
return Pair(result, tr.resultReg)
|
||||||
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
|
||||||
mult.children += array.index
|
|
||||||
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
|
||||||
val tr = expressionEval.translateExpression(mult)
|
|
||||||
addToResult(result, tr, tr.resultReg, -1)
|
|
||||||
return Pair(result, tr.resultReg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -340,7 +340,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||||
require(!codeGen.options.useNewExprCode)
|
|
||||||
val vmDt = irType(binExpr.left.type)
|
val vmDt = irType(binExpr.left.type)
|
||||||
val signed = binExpr.left.type in SignedDatatypes
|
val signed = binExpr.left.type in SignedDatatypes
|
||||||
return when(binExpr.operator) {
|
return when(binExpr.operator) {
|
||||||
|
@ -905,7 +905,6 @@ class IRCodeGen(
|
|||||||
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
|
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
|
||||||
when (condition) {
|
when (condition) {
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
require(!options.useNewExprCode)
|
|
||||||
if(condition.operator !in ComparisonOperators)
|
if(condition.operator !in ComparisonOperators)
|
||||||
throw AssemblyError("if condition should only be a binary comparison expression")
|
throw AssemblyError("if condition should only be a binary comparison expression")
|
||||||
|
|
||||||
@ -919,7 +918,6 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// if X --> meaning: if X!=0
|
// if X --> meaning: if X!=0
|
||||||
require(options.useNewExprCode)
|
|
||||||
val irDt = irType(condition.type)
|
val irDt = irType(condition.type)
|
||||||
val signed = condition.type in SignedDatatypes
|
val signed = condition.type in SignedDatatypes
|
||||||
return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
||||||
|
@ -54,7 +54,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank (0=keep active), on other systems it is ignored.")
|
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank (0=keep active), on other systems it is ignored.")
|
||||||
val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
|
|
||||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||||
|
|
||||||
@ -135,7 +134,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
varsHighBank,
|
varsHighBank,
|
||||||
useNewExprCode == true,
|
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
evalStackAddr,
|
evalStackAddr,
|
||||||
splitWordArrays == true,
|
splitWordArrays == true,
|
||||||
@ -205,7 +203,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
varsHighBank,
|
varsHighBank,
|
||||||
useNewExprCode == true,
|
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
evalStackAddr,
|
evalStackAddr,
|
||||||
splitWordArrays == true,
|
splitWordArrays == true,
|
||||||
|
@ -6,10 +6,10 @@ package prog8.buildversion
|
|||||||
const val MAVEN_GROUP = "prog8"
|
const val MAVEN_GROUP = "prog8"
|
||||||
const val MAVEN_NAME = "compiler"
|
const val MAVEN_NAME = "compiler"
|
||||||
const val VERSION = "9.1-SNAPSHOT"
|
const val VERSION = "9.1-SNAPSHOT"
|
||||||
const val GIT_REVISION = -1
|
const val GIT_REVISION = 3914
|
||||||
const val GIT_SHA = "bf86e1f41d9c97d3b56e91e5217bcda258fa3e09"
|
const val GIT_SHA = "bb95484c8abd6a65220e542913d59d337d833c86"
|
||||||
const val GIT_DATE = "UNKNOWN"
|
const val GIT_DATE = "2023-07-02T04:15:09Z"
|
||||||
const val GIT_BRANCH = "prefixing"
|
const val GIT_BRANCH = "prefixing"
|
||||||
const val BUILD_DATE = "2023-06-30T23:38:56Z"
|
const val BUILD_DATE = "2023-07-02T13:23:24Z"
|
||||||
const val BUILD_UNIX_TIME = 1688168336048L
|
const val BUILD_UNIX_TIME = 1688304204488L
|
||||||
const val DIRTY = 1
|
const val DIRTY = 1
|
||||||
|
@ -4,12 +4,11 @@ import com.github.michaelbull.result.onFailure
|
|||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.*
|
import prog8.code.target.*
|
||||||
import prog8.codegen.vm.VmCodeGen
|
import prog8.codegen.vm.VmCodeGen
|
||||||
@ -37,7 +36,6 @@ class CompilerArguments(val filepath: Path,
|
|||||||
val asmListfile: Boolean,
|
val asmListfile: Boolean,
|
||||||
val experimentalCodegen: Boolean,
|
val experimentalCodegen: Boolean,
|
||||||
val varsHighBank: Int?,
|
val varsHighBank: Int?,
|
||||||
val useNewExprCode: Boolean,
|
|
||||||
val compilationTarget: String,
|
val compilationTarget: String,
|
||||||
val evalStackBaseAddress: UInt?,
|
val evalStackBaseAddress: UInt?,
|
||||||
val splitWordArrays: Boolean,
|
val splitWordArrays: Boolean,
|
||||||
@ -79,7 +77,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
asmListfile = args.asmListfile
|
asmListfile = args.asmListfile
|
||||||
experimentalCodegen = args.experimentalCodegen
|
experimentalCodegen = args.experimentalCodegen
|
||||||
varsHighBank = args.varsHighBank
|
varsHighBank = args.varsHighBank
|
||||||
useNewExprCode = args.useNewExprCode
|
|
||||||
evalStackBaseAddress = args.evalStackBaseAddress
|
evalStackBaseAddress = args.evalStackBaseAddress
|
||||||
splitWordArrays = args.splitWordArrays
|
splitWordArrays = args.splitWordArrays
|
||||||
outputDir = args.outputDir.normalize()
|
outputDir = args.outputDir.normalize()
|
||||||
@ -412,17 +409,6 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
|||||||
else
|
else
|
||||||
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}")
|
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}")
|
||||||
|
|
||||||
if(compilerOptions.useNewExprCode) {
|
|
||||||
if(compilerOptions.compTarget.machine.cpu !in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
|
|
||||||
// the IR code gen backend has its own, better, version of dealing with binary expressions.
|
|
||||||
throw IllegalArgumentException("'newexpr' expression rewrite should not be used with compilation target ${compilerOptions.compTarget.name}")
|
|
||||||
}
|
|
||||||
|
|
||||||
transformNewExpressions(program)
|
|
||||||
}
|
|
||||||
|
|
||||||
// printAst(program, true) { println(it) }
|
|
||||||
|
|
||||||
val stMaker = SymbolTableMaker(program, compilerOptions)
|
val stMaker = SymbolTableMaker(program, compilerOptions)
|
||||||
val symbolTable = stMaker.make()
|
val symbolTable = stMaker.make()
|
||||||
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
|
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
|
||||||
@ -434,188 +420,3 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformNewExpressions(program: PtProgram) {
|
|
||||||
val newVariables = mutableMapOf<PtSub, MutableList<PtVariable>>()
|
|
||||||
var countByteVars = 0
|
|
||||||
var countWordVars = 0
|
|
||||||
var countFloatVars = 0
|
|
||||||
// TODO: find a reliable way to reuse more temp vars across expressions
|
|
||||||
|
|
||||||
fun getExprVar(type: DataType, pos: Position, scope: PtSub): PtIdentifier {
|
|
||||||
val count = when(type) {
|
|
||||||
in ByteDatatypes -> {
|
|
||||||
countByteVars++
|
|
||||||
countByteVars
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
countWordVars++
|
|
||||||
countWordVars
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
countFloatVars++
|
|
||||||
countFloatVars
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("weird dt")
|
|
||||||
}
|
|
||||||
val name = "p8p_exprvar_${count}_${type.toString().lowercase()}"
|
|
||||||
var subVars = newVariables[scope]
|
|
||||||
if(subVars==null) {
|
|
||||||
subVars = mutableListOf()
|
|
||||||
newVariables[scope] = subVars
|
|
||||||
}
|
|
||||||
if(subVars.all { it.name!=name }) {
|
|
||||||
subVars.add(PtVariable(name, type, ZeropageWish.DONTCARE, null, null, pos))
|
|
||||||
}
|
|
||||||
return PtIdentifier("${scope.scopedName}.$name", type, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun transformExpr(expr: PtBinaryExpression): Pair<PtExpression, List<IPtAssignment>> {
|
|
||||||
// depth first process the expression tree
|
|
||||||
val scope = expr.definingSub()!!
|
|
||||||
val assignments = mutableListOf<IPtAssignment>()
|
|
||||||
|
|
||||||
fun transformOperand(node: PtExpression): PtNode {
|
|
||||||
return when(node) {
|
|
||||||
is PtNumber, is PtIdentifier, is PtArray, is PtString, is PtMachineRegister -> node
|
|
||||||
is PtBinaryExpression -> {
|
|
||||||
val (replacement, subAssigns) = transformExpr(node)
|
|
||||||
assignments.addAll(subAssigns)
|
|
||||||
replacement
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val variable = getExprVar(node.type, node.position, scope)
|
|
||||||
val assign = PtAssignment(node.position)
|
|
||||||
val target = PtAssignTarget(variable.position)
|
|
||||||
target.add(variable)
|
|
||||||
assign.add(target)
|
|
||||||
assign.add(node)
|
|
||||||
assignments.add(assign)
|
|
||||||
variable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val newLeft = transformOperand(expr.left)
|
|
||||||
val newRight = transformOperand(expr.right)
|
|
||||||
|
|
||||||
// process the binexpr
|
|
||||||
|
|
||||||
val resultVar =
|
|
||||||
if(expr.type == expr.left.type) {
|
|
||||||
getExprVar(expr.type, expr.position, scope)
|
|
||||||
} else {
|
|
||||||
if(expr.operator in ComparisonOperators && expr.type in ByteDatatypes) {
|
|
||||||
// this is very common and should be dealth with correctly; byte==0, word>42
|
|
||||||
val varType = if(expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
|
|
||||||
getExprVar(varType, expr.position, scope)
|
|
||||||
}
|
|
||||||
else if(expr.left.type in PassByReferenceDatatypes && expr.type==DataType.UBYTE) {
|
|
||||||
// this is common and should be dealth with correctly; for instance "name"=="john"
|
|
||||||
val varType = if (expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
|
|
||||||
getExprVar(varType, expr.position, scope)
|
|
||||||
} else if(expr.left.type equalsSize expr.type) {
|
|
||||||
getExprVar(expr.type, expr.position, scope)
|
|
||||||
} else {
|
|
||||||
TODO("expression type differs from left operand type! got ${expr.left.type} expected ${expr.type} ${expr.position}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(resultVar.name!=(newLeft as? PtIdentifier)?.name) {
|
|
||||||
// resultvar = left
|
|
||||||
val assign1 = PtAssignment(newLeft.position)
|
|
||||||
val target1 = PtAssignTarget(resultVar.position)
|
|
||||||
target1.add(resultVar)
|
|
||||||
assign1.add(target1)
|
|
||||||
assign1.add(newLeft)
|
|
||||||
assignments.add(assign1)
|
|
||||||
}
|
|
||||||
// resultvar {oper}= right
|
|
||||||
val operator = if(expr.operator in ComparisonOperators) expr.operator else expr.operator+'='
|
|
||||||
val assign2 = PtAugmentedAssign(operator, newRight.position)
|
|
||||||
val target2 = PtAssignTarget(resultVar.position)
|
|
||||||
target2.add(resultVar.copy())
|
|
||||||
assign2.add(target2)
|
|
||||||
assign2.add(newRight)
|
|
||||||
assignments.add(assign2)
|
|
||||||
return Pair(resultVar, assignments)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isProperStatement(node: PtNode): Boolean {
|
|
||||||
return when(node) {
|
|
||||||
is PtAssignment -> true
|
|
||||||
is PtAugmentedAssign -> true
|
|
||||||
is PtBreakpoint -> true
|
|
||||||
is PtConditionalBranch -> true
|
|
||||||
is PtForLoop -> true
|
|
||||||
is PtIfElse -> true
|
|
||||||
is PtIncludeBinary -> true
|
|
||||||
is PtInlineAssembly -> true
|
|
||||||
is PtJump -> true
|
|
||||||
is PtAsmSub -> true
|
|
||||||
is PtLabel -> true
|
|
||||||
is PtSub -> true
|
|
||||||
is PtVariable -> true
|
|
||||||
is PtNop -> true
|
|
||||||
is PtPostIncrDecr -> true
|
|
||||||
is PtRepeatLoop -> true
|
|
||||||
is PtReturn -> true
|
|
||||||
is PtWhen -> true
|
|
||||||
is PtBuiltinFunctionCall -> node.void
|
|
||||||
is PtFunctionCall -> node.void
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun transform(node: PtNode, parent: PtNode) {
|
|
||||||
if(node is PtBinaryExpression) {
|
|
||||||
node.children.toTypedArray().forEach {
|
|
||||||
transform(it, node)
|
|
||||||
}
|
|
||||||
val (rep, assignments) = transformExpr(node)
|
|
||||||
var replacement = rep
|
|
||||||
if(!(rep.type equalsSize node.type)) {
|
|
||||||
if(rep.type in NumericDatatypes && node.type in ByteDatatypes) {
|
|
||||||
replacement = PtTypeCast(node.type, node.position)
|
|
||||||
replacement.add(rep)
|
|
||||||
} else
|
|
||||||
TODO("cast replacement type ${rep.type} -> ${node.type}")
|
|
||||||
}
|
|
||||||
var idx = parent.children.indexOf(node)
|
|
||||||
parent.children[idx] = replacement
|
|
||||||
replacement.parent = parent
|
|
||||||
// find the statement above which we should insert the assignments
|
|
||||||
var stmt = node
|
|
||||||
while(!isProperStatement(stmt))
|
|
||||||
stmt = stmt.parent
|
|
||||||
idx = stmt.parent.children.indexOf(stmt)
|
|
||||||
assignments.reversed().forEach {
|
|
||||||
stmt.parent.add(idx, it as PtNode)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node.children.toTypedArray().forEach { child -> transform(child, node) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
program.allBlocks().forEach { block ->
|
|
||||||
block.children.toTypedArray().forEach {
|
|
||||||
transform(it, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the new variables
|
|
||||||
newVariables.forEach { (sub, vars) ->
|
|
||||||
vars.forEach {
|
|
||||||
sub.add(0, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extra check to see that all PtBinaryExpressions have been transformed
|
|
||||||
fun binExprCheck(node: PtNode) {
|
|
||||||
if(node is PtBinaryExpression)
|
|
||||||
throw IllegalArgumentException("still got binexpr $node ${node.position}")
|
|
||||||
node.children.forEach { binExprCheck(it) }
|
|
||||||
}
|
|
||||||
binExprCheck(program)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
|||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
varsHighBank = null,
|
varsHighBank = null,
|
||||||
useNewExprCode = false,
|
|
||||||
compilationTarget = target.name,
|
compilationTarget = target.name,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
splitWordArrays = false,
|
splitWordArrays = false,
|
||||||
|
@ -50,7 +50,6 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
|||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
varsHighBank = null,
|
varsHighBank = null,
|
||||||
useNewExprCode = false,
|
|
||||||
compilationTarget = Cx16Target.NAME,
|
compilationTarget = Cx16Target.NAME,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
splitWordArrays = false,
|
splitWordArrays = false,
|
||||||
|
@ -17,9 +17,9 @@ class TestVariables: FunSpec({
|
|||||||
ubyte @shared bytevar = 0
|
ubyte @shared bytevar = 0
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda arrayvar
|
lda p8_arrayvar
|
||||||
lda stringvar
|
lda p8_stringvar
|
||||||
lda bytevar
|
lda p8_bytevar
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,10 @@ main {
|
|||||||
result.codegenAst!!.name shouldBe result.compilerAst.name
|
result.codegenAst!!.name shouldBe result.compilerAst.name
|
||||||
result.codegenAst!!.children.size shouldBeGreaterThan 2
|
result.codegenAst!!.children.size shouldBeGreaterThan 2
|
||||||
val start = result.codegenAst!!.entrypoint()!!
|
val start = result.codegenAst!!.entrypoint()!!
|
||||||
start.name shouldBe "start"
|
start.name shouldBe "p8_start"
|
||||||
start.children.size shouldBeGreaterThan 2
|
start.children.size shouldBeGreaterThan 2
|
||||||
val seed = start.children[0] as PtVariable
|
val seed = start.children[0] as PtVariable
|
||||||
seed.name shouldBe "seed"
|
seed.name shouldBe "p8_seed"
|
||||||
seed.value shouldBe null
|
seed.value shouldBe null
|
||||||
seed.type shouldBe DataType.ARRAY_UW
|
seed.type shouldBe DataType.ARRAY_UW
|
||||||
val assign = start.children[1] as PtAssignment
|
val assign = start.children[1] as PtAssignment
|
||||||
@ -161,10 +161,8 @@ main {
|
|||||||
qq = 16000 + c*${'$'}0008
|
qq = 16000 + c*${'$'}0008
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
compileText(C64Target(), true, text, writeAssembly = true, useNewExprCode = false) shouldNotBe null
|
compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null
|
||||||
compileText(VMTarget(), true, text, writeAssembly = true, useNewExprCode = false) shouldNotBe null
|
compileText(VMTarget(), true, text, writeAssembly = true) shouldNotBe null
|
||||||
compileText(C64Target(), true, text, writeAssembly = true, useNewExprCode = true) shouldNotBe null
|
|
||||||
// no newexpr for IR targets: compileText(VMTarget(), true, text, writeAssembly = true, useNewExprCode = true) shouldNotBe null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("builtin func in float expression") {
|
test("builtin func in float expression") {
|
||||||
|
@ -18,7 +18,6 @@ internal fun compileFile(
|
|||||||
errors: IErrorReporter? = null,
|
errors: IErrorReporter? = null,
|
||||||
writeAssembly: Boolean = true,
|
writeAssembly: Boolean = true,
|
||||||
optFloatExpr: Boolean = true,
|
optFloatExpr: Boolean = true,
|
||||||
useNewExprCode: Boolean = false
|
|
||||||
) : CompilationResult? {
|
) : CompilationResult? {
|
||||||
val filepath = fileDir.resolve(fileName)
|
val filepath = fileDir.resolve(fileName)
|
||||||
assumeReadableFile(filepath)
|
assumeReadableFile(filepath)
|
||||||
@ -32,7 +31,6 @@ internal fun compileFile(
|
|||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
varsHighBank = null,
|
varsHighBank = null,
|
||||||
useNewExprCode = useNewExprCode,
|
|
||||||
platform.name,
|
platform.name,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
symbolDefs = emptyMap(),
|
symbolDefs = emptyMap(),
|
||||||
@ -55,11 +53,10 @@ internal fun compileText(
|
|||||||
errors: IErrorReporter? = null,
|
errors: IErrorReporter? = null,
|
||||||
writeAssembly: Boolean = true,
|
writeAssembly: Boolean = true,
|
||||||
optFloatExpr: Boolean = true,
|
optFloatExpr: Boolean = true,
|
||||||
useNewExprCode: Boolean = false
|
|
||||||
) : CompilationResult? {
|
) : CompilationResult? {
|
||||||
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
|
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
|
||||||
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
|
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
|
||||||
filePath.toFile().writeText(sourceText)
|
filePath.toFile().writeText(sourceText)
|
||||||
return compileFile(platform, optimize, filePath.parent, filePath.name,
|
return compileFile(platform, optimize, filePath.parent, filePath.name,
|
||||||
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, useNewExprCode=useNewExprCode)
|
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,6 @@ class RequestParser : Take {
|
|||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
splitWordArrays = false,
|
splitWordArrays = false,
|
||||||
varsHighBank = null,
|
varsHighBank = null,
|
||||||
useNewExprCode = false
|
|
||||||
)
|
)
|
||||||
val compilationResult = compileProgram(args)
|
val compilationResult = compileProgram(args)
|
||||||
return RsJson(Jsonding())
|
return RsJson(Jsonding())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user