mirror of
https://github.com/irmen/prog8.git
synced 2024-12-27 05:29:38 +00:00
fix returntype handling of builtinfunctions, fix errors in pipe expressions
This commit is contained in:
parent
c4fe3ecc0a
commit
60b2c44a44
@ -968,14 +968,27 @@ $repeatLabel lda $counterVar
|
|||||||
assemblyLines.add(assembly)
|
assemblyLines.add(assembly)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair {
|
internal fun returnRegisterOfFunction(it: IdentifierReference, argumentTypesForBuiltinFunc: List<DataType>?): RegisterOrPair {
|
||||||
return when (val targetRoutine = it.targetStatement(program)!!) {
|
return when (val targetRoutine = it.targetStatement(program)!!) {
|
||||||
is BuiltinFunctionPlaceholder -> {
|
is BuiltinFunctionPlaceholder -> {
|
||||||
when (BuiltinFunctions.getValue(targetRoutine.name).known_returntype) {
|
val func = BuiltinFunctions.getValue(targetRoutine.name)
|
||||||
|
when (func.returnType) {
|
||||||
in ByteDatatypes -> RegisterOrPair.A
|
in ByteDatatypes -> RegisterOrPair.A
|
||||||
in WordDatatypes -> RegisterOrPair.AY
|
in WordDatatypes -> RegisterOrPair.AY
|
||||||
DataType.FLOAT -> RegisterOrPair.FAC1
|
DataType.FLOAT -> RegisterOrPair.FAC1
|
||||||
else -> throw AssemblyError("weird returntype")
|
else -> {
|
||||||
|
if(!func.hasReturn)
|
||||||
|
throw AssemblyError("func has no returntype")
|
||||||
|
else {
|
||||||
|
val args = argumentTypesForBuiltinFunc!!.map { defaultZero(it, Position.DUMMY) }
|
||||||
|
when(builtinFunctionReturnType(func.name, args, program).getOrElse { DataType.UNDEFINED }) {
|
||||||
|
in ByteDatatypes -> RegisterOrPair.A
|
||||||
|
in WordDatatypes -> RegisterOrPair.AY
|
||||||
|
DataType.FLOAT -> RegisterOrPair.FAC1
|
||||||
|
else -> throw AssemblyError("weird returntype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Subroutine -> targetRoutine.asmReturnvaluesRegisters.single().registerOrPair!!
|
is Subroutine -> targetRoutine.asmReturnvaluesRegisters.single().registerOrPair!!
|
||||||
@ -2833,7 +2846,7 @@ $repeatLabel lda $counterVar
|
|||||||
var valueDt = firstTerm.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
var valueDt = firstTerm.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
||||||
var valueSource: AsmAssignSource =
|
var valueSource: AsmAssignSource =
|
||||||
if(firstTerm is IFunctionCall) {
|
if(firstTerm is IFunctionCall) {
|
||||||
val resultReg = returnRegisterOfFunction(firstTerm.target)
|
val resultReg = returnRegisterOfFunction(firstTerm.target, listOf(valueDt))
|
||||||
assignExpressionToRegister(firstTerm, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))
|
assignExpressionToRegister(firstTerm, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
||||||
} else {
|
} else {
|
||||||
@ -2844,7 +2857,7 @@ $repeatLabel lda $counterVar
|
|||||||
// directly assign their argument from the previous call's returnvalue.
|
// directly assign their argument from the previous call's returnvalue.
|
||||||
expressions.drop(1).dropLast(1).forEach {
|
expressions.drop(1).dropLast(1).forEach {
|
||||||
valueDt = functioncallAsmGen.translateUnaryFunctionCallWithArgSource(it as IdentifierReference, valueSource, false, subroutine)
|
valueDt = functioncallAsmGen.translateUnaryFunctionCallWithArgSource(it as IdentifierReference, valueSource, false, subroutine)
|
||||||
val resultReg = returnRegisterOfFunction(it)
|
val resultReg = returnRegisterOfFunction(it, listOf(valueDt))
|
||||||
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,7 @@ import prog8.ast.statements.DirectMemoryWrite
|
|||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import prog8.compilerinterface.AssemblyError
|
import prog8.compilerinterface.*
|
||||||
import prog8.compilerinterface.BuiltinFunctions
|
|
||||||
import prog8.compilerinterface.CpuType
|
|
||||||
import prog8.compilerinterface.FSignature
|
|
||||||
|
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||||
@ -60,7 +57,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
|
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
|
||||||
fcall.linkParents(scope)
|
fcall.linkParents(scope)
|
||||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
|
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
|
||||||
return if(isStatement) DataType.UNDEFINED else func.known_returntype!!
|
if(isStatement) {
|
||||||
|
return DataType.UNDEFINED
|
||||||
|
} else {
|
||||||
|
return builtinFunctionReturnType(func.name, argExpressions, program).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
@ -335,6 +335,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
|||||||
|
|
||||||
override fun after(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val expressions = pipeExpr.expressions
|
val expressions = pipeExpr.expressions
|
||||||
|
if(expressions.size==2 && expressions[0].isSimple) {
|
||||||
|
// just replace with a normal function call
|
||||||
|
val funcname = expressions[1] as IdentifierReference
|
||||||
|
val arg = expressions[0]
|
||||||
|
val call = FunctionCallExpression(funcname.copy(), mutableListOf(arg), arg.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(pipeExpr, call, parent))
|
||||||
|
}
|
||||||
|
require(expressions.size>=2) { "pipe expression should have 2 or more parts" }
|
||||||
val firstValue = expressions.first()
|
val firstValue = expressions.first()
|
||||||
if(firstValue.isSimple) {
|
if(firstValue.isSimple) {
|
||||||
val funcname = expressions[1] as IdentifierReference
|
val funcname = expressions[1] as IdentifierReference
|
||||||
|
@ -238,7 +238,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
is PrefixExpression,
|
is PrefixExpression,
|
||||||
is BinaryExpression,
|
is BinaryExpression,
|
||||||
is TypecastExpression,
|
is TypecastExpression,
|
||||||
is FunctionCallExpression -> { /* don't remove */ }
|
is PipeExpression,
|
||||||
|
is IFunctionCall -> { /* don't remove */ }
|
||||||
else -> {
|
else -> {
|
||||||
if(assign1.value !is IFunctionCall)
|
if(assign1.value !is IFunctionCall)
|
||||||
linesToRemove.add(assign1)
|
linesToRemove.add(assign1)
|
||||||
|
@ -171,8 +171,6 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(func.known_returntype==null)
|
|
||||||
return null // builtin function $name can't be used here because it doesn't return a value
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -1245,27 +1245,30 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
override fun visit(pipe: PipeExpression) {
|
override fun visit(pipe: PipeExpression) {
|
||||||
processPipe(pipe.expressions, pipe)
|
processPipe(pipe.expressions, pipe)
|
||||||
val last = pipe.expressions.last() as IdentifierReference
|
if(errors.noErrors()) {
|
||||||
val target = last.targetStatement(program)!!
|
val last = pipe.expressions.last() as IdentifierReference
|
||||||
when(target) {
|
when (val target = last.targetStatement(program)!!) {
|
||||||
is BuiltinFunctionPlaceholder -> {
|
is BuiltinFunctionPlaceholder -> {
|
||||||
if(BuiltinFunctions.getValue(target.name).known_returntype==null)
|
if (!BuiltinFunctions.getValue(target.name).hasReturn)
|
||||||
errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
||||||
|
}
|
||||||
|
is Subroutine -> {
|
||||||
|
if (target.returntypes.isEmpty())
|
||||||
|
errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
||||||
|
else if (target.returntypes.size != 1)
|
||||||
|
errors.err("invalid pipe expression; last term doesn't return a single value", last.position)
|
||||||
|
}
|
||||||
|
else -> errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
||||||
}
|
}
|
||||||
is Subroutine -> {
|
super.visit(pipe)
|
||||||
if(target.returntypes.isEmpty())
|
|
||||||
errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
|
||||||
else if(target.returntypes.size!=1)
|
|
||||||
errors.err("invalid pipe expression; last term doesn't return a single value", last.position)
|
|
||||||
}
|
|
||||||
else -> errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
|
||||||
}
|
}
|
||||||
super.visit(pipe)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(pipe: Pipe) {
|
override fun visit(pipe: Pipe) {
|
||||||
processPipe(pipe.expressions, pipe)
|
processPipe(pipe.expressions, pipe)
|
||||||
super.visit(pipe)
|
if(errors.noErrors()) {
|
||||||
|
super.visit(pipe)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processPipe(expressions: List<Expression>, scope: Node) {
|
private fun processPipe(expressions: List<Expression>, scope: Node) {
|
||||||
@ -1289,14 +1292,19 @@ internal class AstChecker(private val program: Program,
|
|||||||
val func = BuiltinFunctions.getValue(function.name)
|
val func = BuiltinFunctions.getValue(function.name)
|
||||||
if(func.parameters.size!=1)
|
if(func.parameters.size!=1)
|
||||||
errors.err("can only use unary function", expr.position)
|
errors.err("can only use unary function", expr.position)
|
||||||
else if(func.known_returntype==null && expr !== expressions.last())
|
else if(!func.hasReturn && expr !== expressions.last())
|
||||||
errors.err("function must return a single value", expr.position)
|
errors.err("function must return a single value", expr.position)
|
||||||
|
|
||||||
val paramDts = func.parameters.firstOrNull()?.possibleDatatypes
|
val paramDts = func.parameters.firstOrNull()?.possibleDatatypes
|
||||||
if(paramDts!=null && !paramDts.any { valueDt isAssignableTo it })
|
if(paramDts!=null && !paramDts.any { valueDt isAssignableTo it })
|
||||||
errors.err("pipe value datatype $valueDt incompatible withfunction argument ${paramDts.toList()}", functionName.position)
|
errors.err("pipe value datatype $valueDt incompatible withfunction argument ${paramDts.toList()}", functionName.position)
|
||||||
|
|
||||||
valueDt = func.known_returntype!!
|
if(errors.noErrors()) {
|
||||||
|
// type can depend on the argument(s) of the function. For now, we only deal with unary functions,
|
||||||
|
// so we know there must be a single argument. Take its type from the previous expression in the pipe chain.
|
||||||
|
val zero = defaultZero(valueDt, expr.position)
|
||||||
|
valueDt = builtinFunctionReturnType(func.name, listOf(zero), program).getOrElse { DataType.UNDEFINED }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
if(function.parameters.size!=1)
|
if(function.parameters.size!=1)
|
||||||
|
93
compiler/test/TestBuiltinFunctions.kt
Normal file
93
compiler/test/TestBuiltinFunctions.kt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package prog8tests
|
||||||
|
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.NumericDatatypes
|
||||||
|
import prog8.ast.base.RegisterOrPair
|
||||||
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
|
class TestBuiltinFunctions: FunSpec({
|
||||||
|
|
||||||
|
test("pure func with fixed type") {
|
||||||
|
val func = BuiltinFunctions.getValue("sin8u")
|
||||||
|
func.name shouldBe "sin8u"
|
||||||
|
func.parameters.size shouldBe 1
|
||||||
|
func.parameters[0].name shouldBe "angle8"
|
||||||
|
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UBYTE)
|
||||||
|
func.pure shouldBe true
|
||||||
|
func.hasReturn shouldBe true
|
||||||
|
func.returnType shouldBe DataType.UBYTE
|
||||||
|
|
||||||
|
val conv = func.callConvention(listOf(DataType.UBYTE))
|
||||||
|
conv.params.size shouldBe 1
|
||||||
|
conv.params[0].dt shouldBe DataType.UBYTE
|
||||||
|
conv.params[0].reg shouldBe RegisterOrPair.A
|
||||||
|
conv.params[0].variable shouldBe false
|
||||||
|
conv.returns.dt shouldBe DataType.UBYTE
|
||||||
|
conv.returns.floatFac1 shouldBe false
|
||||||
|
conv.returns.reg shouldBe RegisterOrPair.A
|
||||||
|
}
|
||||||
|
|
||||||
|
test("not-pure func with fixed type") {
|
||||||
|
val func = BuiltinFunctions.getValue("rnd")
|
||||||
|
func.name shouldBe "rnd"
|
||||||
|
func.parameters.size shouldBe 0
|
||||||
|
func.pure shouldBe false
|
||||||
|
func.hasReturn shouldBe true
|
||||||
|
func.returnType shouldBe DataType.UBYTE
|
||||||
|
|
||||||
|
val conv = func.callConvention(emptyList())
|
||||||
|
conv.params.size shouldBe 0
|
||||||
|
conv.returns.dt shouldBe DataType.UBYTE
|
||||||
|
conv.returns.floatFac1 shouldBe false
|
||||||
|
conv.returns.reg shouldBe RegisterOrPair.A
|
||||||
|
}
|
||||||
|
|
||||||
|
test("func without return type") {
|
||||||
|
val func = BuiltinFunctions.getValue("poke")
|
||||||
|
func.name shouldBe "poke"
|
||||||
|
func.parameters.size shouldBe 2
|
||||||
|
func.parameters[0].name shouldBe "address"
|
||||||
|
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
|
||||||
|
func.parameters[1].name shouldBe "value"
|
||||||
|
func.parameters[1].possibleDatatypes shouldBe arrayOf(DataType.UBYTE)
|
||||||
|
func.pure shouldBe false
|
||||||
|
func.hasReturn shouldBe false
|
||||||
|
func.returnType shouldBe null
|
||||||
|
|
||||||
|
val conv = func.callConvention(listOf(DataType.UWORD, DataType.UBYTE))
|
||||||
|
conv.params.size shouldBe 2
|
||||||
|
conv.params[0].dt shouldBe DataType.UWORD
|
||||||
|
conv.params[0].reg shouldBe null
|
||||||
|
conv.params[0].variable shouldBe true
|
||||||
|
conv.params[1].dt shouldBe DataType.UBYTE
|
||||||
|
conv.params[1].reg shouldBe null
|
||||||
|
conv.params[1].variable shouldBe true
|
||||||
|
conv.returns.dt shouldBe null
|
||||||
|
conv.returns.floatFac1 shouldBe false
|
||||||
|
conv.returns.reg shouldBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("func with variable return type") {
|
||||||
|
val func = BuiltinFunctions.getValue("abs")
|
||||||
|
func.name shouldBe "abs"
|
||||||
|
func.parameters.size shouldBe 1
|
||||||
|
func.parameters[0].name shouldBe "value"
|
||||||
|
func.parameters[0].possibleDatatypes.toSet() shouldBe NumericDatatypes.toSet()
|
||||||
|
func.pure shouldBe true
|
||||||
|
func.hasReturn shouldBe true
|
||||||
|
func.returnType shouldBe null
|
||||||
|
|
||||||
|
val conv = func.callConvention(listOf(DataType.UWORD))
|
||||||
|
conv.params.size shouldBe 1
|
||||||
|
conv.params[0].dt shouldBe DataType.UWORD
|
||||||
|
conv.params[0].reg shouldBe RegisterOrPair.AY
|
||||||
|
conv.params[0].variable shouldBe false
|
||||||
|
conv.returns.dt shouldBe DataType.UWORD
|
||||||
|
conv.returns.floatFac1 shouldBe false
|
||||||
|
conv.returns.reg shouldBe RegisterOrPair.AY
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -4,10 +4,9 @@ import io.kotest.core.spec.style.FunSpec
|
|||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.string.shouldContain
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import prog8.ast.expressions.FunctionCallExpression
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.PipeExpression
|
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.ast.statements.Pipe
|
import prog8.ast.statements.Pipe
|
||||||
import prog8.codegen.target.C64Target
|
import prog8.codegen.target.C64Target
|
||||||
import prog8tests.helpers.ErrorReporterForTests
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
@ -32,6 +31,11 @@ class TestPipes: FunSpec({
|
|||||||
9999 |> addword
|
9999 |> addword
|
||||||
|> txt.print_uw
|
|> txt.print_uw
|
||||||
|
|
||||||
|
; these are optimized into just the function calls:
|
||||||
|
9999 |> abs |> txt.print_uw
|
||||||
|
9999 |> txt.print_uw
|
||||||
|
99 |> abs |> txt.print_ub
|
||||||
|
99 |> txt.print_ub
|
||||||
}
|
}
|
||||||
|
|
||||||
sub addfloat(float fl) -> float {
|
sub addfloat(float fl) -> float {
|
||||||
@ -44,7 +48,7 @@ class TestPipes: FunSpec({
|
|||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess()
|
val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess()
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.program.entrypoint.statements
|
||||||
stmts.size shouldBe 3
|
stmts.size shouldBe 7
|
||||||
val pipef = stmts[0] as Pipe
|
val pipef = stmts[0] as Pipe
|
||||||
pipef.expressions.size shouldBe 2
|
pipef.expressions.size shouldBe 2
|
||||||
pipef.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
pipef.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||||
@ -54,6 +58,15 @@ class TestPipes: FunSpec({
|
|||||||
pipew.expressions.size shouldBe 2
|
pipew.expressions.size shouldBe 2
|
||||||
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||||
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
||||||
|
|
||||||
|
var stmt = stmts[2] as FunctionCallStatement
|
||||||
|
stmt.target.nameInSource shouldBe listOf("txt", "print_uw")
|
||||||
|
stmt = stmts[3] as FunctionCallStatement
|
||||||
|
stmt.target.nameInSource shouldBe listOf("txt", "print_uw")
|
||||||
|
stmt = stmts[4] as FunctionCallStatement
|
||||||
|
stmt.target.nameInSource shouldBe listOf("txt", "print_ub")
|
||||||
|
stmt = stmts[5] as FunctionCallStatement
|
||||||
|
stmt.target.nameInSource shouldBe listOf("txt", "print_ub")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("incorrect type in pipe statement") {
|
test("incorrect type in pipe statement") {
|
||||||
@ -94,6 +107,8 @@ class TestPipes: FunSpec({
|
|||||||
uword @shared ww = 9999 |> addword
|
uword @shared ww = 9999 |> addword
|
||||||
|> addword
|
|> addword
|
||||||
|
|
||||||
|
ubyte @shared cc = 30 |> sin8u |> cos8u ; will be optimized away into a const number
|
||||||
|
cc = cc |> sin8u |> cos8u
|
||||||
}
|
}
|
||||||
|
|
||||||
sub addfloat(float fl) -> float {
|
sub addfloat(float fl) -> float {
|
||||||
@ -102,11 +117,14 @@ class TestPipes: FunSpec({
|
|||||||
sub addword(uword ww) -> uword {
|
sub addword(uword ww) -> uword {
|
||||||
return ww+2222
|
return ww+2222
|
||||||
}
|
}
|
||||||
}
|
sub addbyte(ubyte bb) -> ubyte {
|
||||||
|
return bb+1
|
||||||
|
}
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess()
|
val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess()
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.program.entrypoint.statements
|
||||||
stmts.size shouldBe 5
|
stmts.size shouldBe 8
|
||||||
val assignf = stmts[1] as Assignment
|
val assignf = stmts[1] as Assignment
|
||||||
val pipef = assignf.value as PipeExpression
|
val pipef = assignf.value as PipeExpression
|
||||||
pipef.expressions.size shouldBe 2
|
pipef.expressions.size shouldBe 2
|
||||||
@ -118,6 +136,16 @@ class TestPipes: FunSpec({
|
|||||||
pipew.expressions.size shouldBe 2
|
pipew.expressions.size shouldBe 2
|
||||||
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||||
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
||||||
|
|
||||||
|
var assigncc = stmts[5] as Assignment
|
||||||
|
val value = assigncc.value as NumericLiteral
|
||||||
|
value.number shouldBe 194.0
|
||||||
|
|
||||||
|
assigncc = stmts[6] as Assignment
|
||||||
|
val pipecc = assignw.value as PipeExpression
|
||||||
|
pipecc.expressions.size shouldBe 2
|
||||||
|
pipecc.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||||
|
pipecc.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("incorrect type in pipe expression") {
|
test("incorrect type in pipe expression") {
|
||||||
@ -143,4 +171,50 @@ class TestPipes: FunSpec({
|
|||||||
errors.errors.size shouldBe 1
|
errors.errors.size shouldBe 1
|
||||||
errors.errors[0] shouldContain "incompatible"
|
errors.errors[0] shouldContain "incompatible"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("correct pipe statement with builtin expression") {
|
||||||
|
val text = """
|
||||||
|
%import textio
|
||||||
|
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword ww = 9999
|
||||||
|
ubyte bb = 99
|
||||||
|
ww |> abs |> txt.print_uw
|
||||||
|
bb |> abs |> txt.print_ub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess()
|
||||||
|
val stmts = result.program.entrypoint.statements
|
||||||
|
stmts.size shouldBe 7
|
||||||
|
val pipef = stmts[4] as Pipe
|
||||||
|
pipef.expressions.size shouldBe 2
|
||||||
|
pipef.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
||||||
|
pipef.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
||||||
|
|
||||||
|
val pipew = stmts[5] as Pipe
|
||||||
|
pipew.expressions.size shouldBe 2
|
||||||
|
pipew.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
||||||
|
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
|
||||||
|
}
|
||||||
|
|
||||||
|
test("pipe statement with type errors") {
|
||||||
|
val text = """
|
||||||
|
%import textio
|
||||||
|
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword ww = 9999
|
||||||
|
9999 |> abs |> txt.print_ub
|
||||||
|
ww |> abs |> txt.print_ub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors).assertFailure()
|
||||||
|
errors.errors.size shouldBe 2
|
||||||
|
errors.errors[0] shouldContain "UWORD incompatible"
|
||||||
|
errors.errors[1] shouldContain "UWORD incompatible"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -1098,17 +1098,34 @@ class PipeExpression(val expressions: MutableList<Expression>, override val posi
|
|||||||
override fun referencesIdentifier(nameInSource: List<String>) =
|
override fun referencesIdentifier(nameInSource: List<String>) =
|
||||||
expressions.any { it.referencesIdentifier(nameInSource) }
|
expressions.any { it.referencesIdentifier(nameInSource) }
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType = inferType(program, expressions)
|
||||||
val last = expressions.last()
|
|
||||||
val type = last.inferType(program)
|
private fun inferType(program: Program, functionNames: List<Expression>): InferredTypes.InferredType {
|
||||||
if(type.isKnown)
|
val identifier = functionNames.last() as? IdentifierReference
|
||||||
return type
|
|
||||||
val identifier = last as? IdentifierReference
|
|
||||||
if(identifier!=null) {
|
if(identifier!=null) {
|
||||||
val call = FunctionCallExpression(identifier, mutableListOf(), identifier.position)
|
val target = identifier.targetStatement(program)
|
||||||
return call.inferType(program)
|
when(target) {
|
||||||
|
is BuiltinFunctionPlaceholder -> {
|
||||||
|
val typeOfPrev = inferType(program, functionNames.dropLast(1))
|
||||||
|
return if(typeOfPrev.isKnown) {
|
||||||
|
val zero = defaultZero(typeOfPrev.getOr(DataType.UNDEFINED), identifier.position)
|
||||||
|
val args = mutableListOf<Expression>(zero)
|
||||||
|
program.builtinFunctions.returnType(identifier.nameInSource[0], args)
|
||||||
|
} else {
|
||||||
|
InferredTypes.InferredType.unknown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Subroutine -> {
|
||||||
|
val call = FunctionCallExpression(identifier, mutableListOf(), identifier.position)
|
||||||
|
return call.inferType(program)
|
||||||
|
}
|
||||||
|
is VarDecl -> {
|
||||||
|
return InferredTypes.InferredType.known(target.datatype)
|
||||||
|
}
|
||||||
|
else -> return InferredTypes.InferredType.unknown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return InferredTypes.InferredType.unknown()
|
return functionNames.last().inferType(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
@ -9,7 +9,7 @@ import kotlin.math.*
|
|||||||
|
|
||||||
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
|
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
|
||||||
|
|
||||||
class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||||
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
|
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
|
||||||
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
|
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -35,35 +35,42 @@ class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
|||||||
class FSignature(val name: String,
|
class FSignature(val name: String,
|
||||||
val pure: Boolean, // does it have side effects?
|
val pure: Boolean, // does it have side effects?
|
||||||
val parameters: List<FParam>,
|
val parameters: List<FParam>,
|
||||||
val known_returntype: DataType?, // specify return type if fixed, otherwise null if it depends on the arguments
|
val hasReturn: Boolean, // is there a return value at all?
|
||||||
|
val returnType: DataType?, // specify return type if fixed, otherwise null if it depends on the arguments
|
||||||
val constExpressionFunc: ConstExpressionCaller? = null) {
|
val constExpressionFunc: ConstExpressionCaller? = null) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(hasReturn || returnType==null) { "$name has invalid return spec" }
|
||||||
|
}
|
||||||
|
|
||||||
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
||||||
val returns = when(known_returntype) {
|
val returns: ReturnConvention
|
||||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(known_returntype, RegisterOrPair.A, false)
|
if(hasReturn) {
|
||||||
DataType.UWORD, DataType.WORD -> ReturnConvention(known_returntype, RegisterOrPair.AY, false)
|
returns = when (returnType) {
|
||||||
DataType.FLOAT -> ReturnConvention(known_returntype, null, true)
|
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
|
||||||
in PassByReferenceDatatypes -> ReturnConvention(known_returntype!!, RegisterOrPair.AY, false)
|
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
|
||||||
else -> {
|
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
||||||
val paramType = actualParamTypes.first()
|
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
||||||
if(pure)
|
else -> {
|
||||||
// return type depends on arg type
|
// return type depends on arg type
|
||||||
when(paramType) {
|
when (val paramType = actualParamTypes.first()) {
|
||||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
||||||
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||||
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
||||||
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||||
else -> ReturnConvention(paramType, null, false)
|
else -> ReturnConvention(paramType, null, false)
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
ReturnConvention(paramType, null, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
require(returnType==null)
|
||||||
|
returns = ReturnConvention(null, null, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||||
actualParamTypes.size==1 -> {
|
actualParamTypes.size==1 -> {
|
||||||
// one parameter? via register/registerpair
|
// one parameter goes via register/registerpair
|
||||||
val paramConv = when(val paramType = actualParamTypes[0]) {
|
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||||
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||||
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
@ -74,7 +81,7 @@ class FSignature(val name: String,
|
|||||||
CallConvention(listOf(paramConv), returns)
|
CallConvention(listOf(paramConv), returns)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// multiple parameters? via variables
|
// multiple parameters go via variables
|
||||||
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||||
CallConvention(paramConvs, returns)
|
CallConvention(paramConvs, returns)
|
||||||
}
|
}
|
||||||
@ -88,78 +95,78 @@ class FSignature(val name: String,
|
|||||||
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
|
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
|
||||||
private val functionSignatures: List<FSignature> = listOf(
|
private val functionSignatures: List<FSignature> = listOf(
|
||||||
// this set of function have no return value and operate in-place:
|
// this set of function have no return value and operate in-place:
|
||||||
FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false,null),
|
||||||
FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null),
|
||||||
FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null),
|
||||||
FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), false, null),
|
||||||
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), false, null),
|
||||||
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), false, null),
|
||||||
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
|
// cmp returns a status in the carry flag, but not a proper return value
|
||||||
|
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), false, null),
|
||||||
// these few have a return value depending on the argument(s):
|
// these few have a return value depending on the argument(s):
|
||||||
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
||||||
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
||||||
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), true, null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||||
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), true, null, ::builtinAbs), // type depends on argument
|
||||||
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), true, null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||||
FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE, ::builtinSizeof),
|
FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), true, DataType.UBYTE, ::builtinSizeof),
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), true, DataType.BYTE, ::builtinSgn ),
|
||||||
FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||||
FSignature("sin8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
FSignature("sin8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinSin8 ),
|
||||||
FSignature("sin8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
FSignature("sin8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinSin8u ),
|
||||||
FSignature("sin16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
FSignature("sin16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinSin16 ),
|
||||||
FSignature("sin16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
FSignature("sin16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinSin16u ),
|
||||||
FSignature("sinr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinSinR8 ),
|
FSignature("sinr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinSinR8 ),
|
||||||
FSignature("sinr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSinR8u ),
|
FSignature("sinr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinSinR8u ),
|
||||||
FSignature("sinr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinSinR16 ),
|
FSignature("sinr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinSinR16 ),
|
||||||
FSignature("sinr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinSinR16u ),
|
FSignature("sinr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinSinR16u ),
|
||||||
FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
||||||
FSignature("cos8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
FSignature("cos8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinCos8 ),
|
||||||
FSignature("cos8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
FSignature("cos8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinCos8u ),
|
||||||
FSignature("cos16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
FSignature("cos16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinCos16 ),
|
||||||
FSignature("cos16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
FSignature("cos16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinCos16u ),
|
||||||
FSignature("cosr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinCosR8 ),
|
FSignature("cosr8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.BYTE, ::builtinCosR8 ),
|
||||||
FSignature("cosr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCosR8u ),
|
FSignature("cosr8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UBYTE, ::builtinCosR8u ),
|
||||||
FSignature("cosr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinCosR16 ),
|
FSignature("cosr16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.WORD, ::builtinCosR16 ),
|
||||||
FSignature("cosr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinCosR16u ),
|
FSignature("cosr16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinCosR16u ),
|
||||||
FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
||||||
FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
||||||
FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
||||||
FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
||||||
FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||||
FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||||
FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||||
FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||||
FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, ::round) },
|
FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, ::round) },
|
||||||
FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||||
FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), true, DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||||
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), true, DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||||
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), true, DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||||
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
|
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
|
||||||
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), true, DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||||
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), true, DataType.UWORD, ::builtinMkword),
|
||||||
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), true, DataType.UBYTE),
|
||||||
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), true, DataType.UWORD),
|
||||||
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), false,null),
|
||||||
FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), false,null),
|
||||||
FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), false,null),
|
||||||
FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), null),
|
FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), false, null),
|
||||||
FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), null),
|
FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), false, null),
|
||||||
FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), null),
|
FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), false, null),
|
||||||
FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), null),
|
FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), false, null),
|
||||||
FSignature("rsave" , false, emptyList(), null),
|
FSignature("rsave" , false, emptyList(), false,null),
|
||||||
FSignature("rsavex" , false, emptyList(), null),
|
FSignature("rsavex" , false, emptyList(), false,null),
|
||||||
FSignature("rrestore" , false, emptyList(), null),
|
FSignature("rrestore" , false, emptyList(), false,null),
|
||||||
FSignature("rrestorex" , false, emptyList(), null),
|
FSignature("rrestorex" , false, emptyList(), false,null),
|
||||||
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
|
FSignature("rnd" , false, emptyList(), true, DataType.UBYTE),
|
||||||
FSignature("rndw" , false, emptyList(), DataType.UWORD),
|
FSignature("rndw" , false, emptyList(), true, DataType.UWORD),
|
||||||
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
FSignature("rndf" , false, emptyList(), true, DataType.FLOAT),
|
||||||
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), true, DataType.UWORD),
|
||||||
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), false, null),
|
||||||
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), false, null),
|
||||||
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), false, null),
|
||||||
|
)
|
||||||
)
|
|
||||||
|
|
||||||
val BuiltinFunctions = functionSignatures.associateBy { it.name }
|
val BuiltinFunctions = functionSignatures.associateBy { it.name }
|
||||||
|
|
||||||
@ -203,8 +210,10 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
}
|
}
|
||||||
|
|
||||||
val func = BuiltinFunctions.getValue(function)
|
val func = BuiltinFunctions.getValue(function)
|
||||||
if(func.known_returntype!=null)
|
if(func.returnType!=null)
|
||||||
return InferredTypes.knownFor(func.known_returntype)
|
return InferredTypes.knownFor(func.returnType)
|
||||||
|
if(!func.hasReturn)
|
||||||
|
return InferredTypes.InferredType.void()
|
||||||
|
|
||||||
// function has return values, but the return type depends on the arguments
|
// function has return values, but the return type depends on the arguments
|
||||||
return when (function) {
|
return when (function) {
|
||||||
|
@ -3,7 +3,8 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
...
|
- allow the last term in a pipe *statement* to be a variable, rewrite this as var = <rest of pipe>
|
||||||
|
|
||||||
|
|
||||||
Need help with
|
Need help with
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
@ -2,73 +2,17 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte xx = 200
|
ubyte xx = 30
|
||||||
ubyte yy = 100
|
ubyte cc
|
||||||
uword @shared cc
|
|
||||||
|
|
||||||
ubyte[200] array
|
; cc = 30 |> sin8u |> cos8u
|
||||||
|
; txt.print_ub(cc)
|
||||||
|
; txt.nl()
|
||||||
|
cc = xx |> sin8u |> cos8u
|
||||||
|
txt.print_ub(cc)
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
cc=array[xx+yy+10]
|
|
||||||
|
|
||||||
cc = xx-yy>10
|
|
||||||
|
|
||||||
uword qq = 100
|
|
||||||
cmp(qq,xx)
|
|
||||||
|
|
||||||
simple(xx+yy)
|
|
||||||
void routine(xx+yy, yy+99, 99, true)
|
|
||||||
uword @shared zz = mkword(xx+yy,yy+99)
|
|
||||||
zz = routine(1000+xx+yy, yy+99, 55, true)
|
|
||||||
|
|
||||||
|
|
||||||
txt.print("1300 199 55 1 ?:\n")
|
|
||||||
txt.print_uw(r_arg)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(r_arg2)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(r_arg3)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(r_arg4)
|
|
||||||
txt.spc()
|
|
||||||
|
|
||||||
memory.mem()
|
|
||||||
repeat {
|
repeat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uword @shared r_arg
|
|
||||||
ubyte @shared r_arg2
|
|
||||||
ubyte @shared r_arg3
|
|
||||||
ubyte @shared r_arg4
|
|
||||||
|
|
||||||
asmsub simple(ubyte arg @A) {
|
|
||||||
%asm {{
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub routine(uword arg @AY, ubyte arg2 @X, ubyte arg3 @R0, ubyte arg4 @Pc) -> ubyte @A {
|
|
||||||
%asm {{
|
|
||||||
pha
|
|
||||||
lda #0
|
|
||||||
adc #0
|
|
||||||
sta r_arg4
|
|
||||||
pla
|
|
||||||
sta r_arg
|
|
||||||
sty r_arg+1
|
|
||||||
stx r_arg2
|
|
||||||
lda cx16.r0
|
|
||||||
sta r_arg3
|
|
||||||
lda #99
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memory {
|
|
||||||
sub mem() {
|
|
||||||
%asm {{
|
|
||||||
nop
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user