mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
Pipe expression "|>" removed from the language
This commit is contained in:
parent
dca092fd7c
commit
5a756aaed9
@ -149,19 +149,6 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
|
|
||||||
init {
|
|
||||||
if(!void)
|
|
||||||
require(type!=DataType.UNDEFINED)
|
|
||||||
}
|
|
||||||
|
|
||||||
val segments: List<PtExpression>
|
|
||||||
get() = children.map { it as PtExpression }
|
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children.single() as PtExpression
|
get() = children.single() as PtExpression
|
||||||
|
@ -334,7 +334,6 @@ class AsmGen(internal val program: Program,
|
|||||||
is RepeatLoop -> translate(stmt)
|
is RepeatLoop -> translate(stmt)
|
||||||
is When -> translate(stmt)
|
is When -> translate(stmt)
|
||||||
is AnonymousScope -> translate(stmt)
|
is AnonymousScope -> translate(stmt)
|
||||||
is Pipe -> translatePipeExpression(stmt.source, stmt.segments, stmt, isStatement = true, pushResultOnEstack = false)
|
|
||||||
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
|
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
|
||||||
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
||||||
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
|
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
|
||||||
@ -444,12 +443,6 @@ class AsmGen(internal val program: Program,
|
|||||||
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
||||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
||||||
|
|
||||||
private fun translateBuiltinFunctionCallExpression(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine): DataType =
|
|
||||||
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, false, scope)
|
|
||||||
|
|
||||||
private fun translateBuiltinFunctionCallStatement(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine) =
|
|
||||||
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, true, scope)
|
|
||||||
|
|
||||||
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
|
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
|
||||||
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
|
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
|
||||||
|
|
||||||
@ -2812,123 +2805,6 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translatePipeExpression(source: Expression, segments: List<Expression>, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) {
|
|
||||||
|
|
||||||
// TODO more efficient code generation to avoid needless assignments to the temp var
|
|
||||||
|
|
||||||
// the source: an expression (could be anything) producing a value.
|
|
||||||
// one or more segment expressions, all are a IFunctionCall node, and LACKING the implicit first argument.
|
|
||||||
// when 'isStatement'=true, the last segment expression should be treated as a funcion call statement (discarding any result value if there is one)
|
|
||||||
|
|
||||||
val subroutine = scope.definingSubroutine!!
|
|
||||||
var valueDt = source.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
|
||||||
var valueSource: AsmAssignSource =
|
|
||||||
if(source is IFunctionCall) {
|
|
||||||
val resultReg = returnRegisterOfFunction(source.target)
|
|
||||||
assignExpressionToRegister(source, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))
|
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
|
||||||
} else {
|
|
||||||
AsmAssignSource.fromAstSource(source, program, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the segments (except the last one): function calls taking one or more parameters and producing a value.
|
|
||||||
// directly assign their first argument from the previous call's returnvalue (and take the rest, if any, from the call itself)
|
|
||||||
segments.dropLast(1).forEach {
|
|
||||||
it as IFunctionCall
|
|
||||||
valueDt = translateFunctionCallWithFirstArg(it, valueSource, false, subroutine)
|
|
||||||
val resultReg = returnRegisterOfFunction(it.target)
|
|
||||||
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
|
||||||
}
|
|
||||||
// the last segment: function call taking one or more parameters and optionally producing a result value.
|
|
||||||
val lastCall = segments.last() as IFunctionCall
|
|
||||||
if(isStatement) {
|
|
||||||
translateFunctionCallWithFirstArg(lastCall, valueSource, true, subroutine)
|
|
||||||
} else {
|
|
||||||
valueDt = translateFunctionCallWithFirstArg(lastCall, valueSource, false, subroutine)
|
|
||||||
if(pushResultOnEstack) {
|
|
||||||
when (valueDt) {
|
|
||||||
in ByteDatatypes -> {
|
|
||||||
out(" sta P8ESTACK_LO,x | dex")
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
out(" jsr floats.push_fac1")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateFunctionCallWithFirstArg(
|
|
||||||
fcall: IFunctionCall,
|
|
||||||
firstArg: AsmAssignSource,
|
|
||||||
isStatement: Boolean,
|
|
||||||
scope: Subroutine
|
|
||||||
): DataType {
|
|
||||||
|
|
||||||
if(fcall.args.isNotEmpty())
|
|
||||||
TODO("deal with additional args (non-unary function): ${fcall.target.nameInSource} (... , ${fcall.args.joinToString(", ")})")
|
|
||||||
|
|
||||||
when(val targetStmt = fcall.target.targetStatement(program)!!) {
|
|
||||||
is BuiltinFunctionPlaceholder -> {
|
|
||||||
return if(isStatement) {
|
|
||||||
translateBuiltinFunctionCallStatement(fcall, firstArg, scope)
|
|
||||||
DataType.UNDEFINED
|
|
||||||
} else {
|
|
||||||
translateBuiltinFunctionCallExpression(fcall, firstArg, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Subroutine -> {
|
|
||||||
val argDt = targetStmt.parameters.single().type
|
|
||||||
if(targetStmt.isAsmSubroutine) {
|
|
||||||
// argument via registers
|
|
||||||
val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!!
|
|
||||||
val assignArgument = AsmAssignment(
|
|
||||||
firstArg,
|
|
||||||
AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this),
|
|
||||||
false, program.memsizer, fcall.position
|
|
||||||
)
|
|
||||||
translateNormalAssignment(assignArgument)
|
|
||||||
} else {
|
|
||||||
val assignArgument: AsmAssignment =
|
|
||||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(targetStmt)) {
|
|
||||||
// argument goes via registers as optimization
|
|
||||||
val paramReg: RegisterOrPair = when(argDt) {
|
|
||||||
in ByteDatatypes -> RegisterOrPair.A
|
|
||||||
in WordDatatypes -> RegisterOrPair.AY
|
|
||||||
DataType.FLOAT -> RegisterOrPair.FAC1
|
|
||||||
else -> throw AssemblyError("invalid dt")
|
|
||||||
}
|
|
||||||
AsmAssignment(
|
|
||||||
firstArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg),
|
|
||||||
false, program.memsizer, fcall.position
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// arg goes via parameter variable
|
|
||||||
val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name)
|
|
||||||
AsmAssignment(
|
|
||||||
firstArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName),
|
|
||||||
false, program.memsizer, fcall.position
|
|
||||||
)
|
|
||||||
}
|
|
||||||
translateNormalAssignment(assignArgument)
|
|
||||||
}
|
|
||||||
if(targetStmt.shouldSaveX())
|
|
||||||
saveRegisterLocal(CpuRegister.X, scope)
|
|
||||||
out(" jsr ${asmSymbolName(fcall.target)}")
|
|
||||||
if(targetStmt.shouldSaveX())
|
|
||||||
restoreRegisterLocal(CpuRegister.X)
|
|
||||||
return if(isStatement) DataType.UNDEFINED else targetStmt.returntypes.single()
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invalid call target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {
|
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {
|
||||||
// note: because A is pushed first so popped last, saving A is often not required here.
|
// note: because A is pushed first so popped last, saving A is often not required here.
|
||||||
val parameter = target.subroutineParameter
|
val parameter = target.subroutineParameter
|
||||||
|
@ -11,7 +11,6 @@ import prog8.code.core.*
|
|||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import prog8.compiler.BuiltinFunctions
|
import prog8.compiler.BuiltinFunctions
|
||||||
import prog8.compiler.FSignature
|
import prog8.compiler.FSignature
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
|
||||||
|
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||||
@ -29,44 +28,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateFunctionCallWithFirstArg(bfc: IFunctionCall, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
|
|
||||||
val name = bfc.target.nameInSource.single()
|
|
||||||
val func = BuiltinFunctions.getValue(name)
|
|
||||||
val argExpression =
|
|
||||||
when(singleArg.kind) {
|
|
||||||
SourceStorageKind.LITERALNUMBER -> singleArg.number!!
|
|
||||||
SourceStorageKind.EXPRESSION -> singleArg.expression!!
|
|
||||||
SourceStorageKind.ARRAY -> singleArg.array!!
|
|
||||||
else -> {
|
|
||||||
// TODO make it so that we can assign efficiently from something else as an expression....namely: register(s)
|
|
||||||
// this is useful in pipe expressions for instance, to skip the use of a temporary variable
|
|
||||||
// but for now, just assign it to a temporary variable and use that as a source
|
|
||||||
// Idea: to do this without having to rewrite every single function in translateFunctioncall(),
|
|
||||||
// hack a special IdentifierReference like "!6502.A/X/Y/AX/AY/XY" to reference a cpu register
|
|
||||||
val tempvar = asmgen.getTempVarName(singleArg.datatype)
|
|
||||||
val assignTempvar = AsmAssignment(
|
|
||||||
singleArg,
|
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, singleArg.datatype, scope, variableAsmName = asmgen.asmVariableName(tempvar)),
|
|
||||||
false, program.memsizer, Position.DUMMY
|
|
||||||
)
|
|
||||||
assignAsmGen.translateNormalAssignment(assignTempvar)
|
|
||||||
// now use an expression to assign this tempvar
|
|
||||||
val ident = IdentifierReference(tempvar, Position.DUMMY)
|
|
||||||
ident.linkParents(scope)
|
|
||||||
ident
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val argExpressions = mutableListOf(argExpression)
|
|
||||||
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
|
|
||||||
fcall.linkParents(scope)
|
|
||||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
|
|
||||||
return if(isStatement) {
|
|
||||||
DataType.UNDEFINED
|
|
||||||
} else {
|
|
||||||
builtinFunctionReturnType(func.name).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?) {
|
||||||
if (discardResult && func.pure)
|
if (discardResult && func.pure)
|
||||||
return // can just ignore the whole function call altogether
|
return // can just ignore the whole function call altogether
|
||||||
|
@ -34,8 +34,6 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
||||||
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||||
is PipeExpression -> asmgen.translatePipeExpression(expression.source, expression.segments,
|
|
||||||
expression, isStatement = false, pushResultOnEstack = true )
|
|
||||||
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
||||||
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
|
@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
|||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
|
|
||||||
|
@ -290,16 +290,6 @@ internal class AssignmentAsmGen(private val program: Program,
|
|||||||
containmentCheckIntoA(value)
|
containmentCheckIntoA(value)
|
||||||
assignRegisterByte(assign.target, CpuRegister.A)
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
is PipeExpression -> {
|
|
||||||
asmgen.translatePipeExpression(value.source, value.segments, value, isStatement = false, pushResultOnEstack = false)
|
|
||||||
val resultDt = value.inferType(program)
|
|
||||||
val register =
|
|
||||||
if(resultDt.isBytes) RegisterOrPair.A
|
|
||||||
else if(resultDt.isWords) RegisterOrPair.AY
|
|
||||||
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
|
|
||||||
else throw AssemblyError("invalid dt")
|
|
||||||
asmgen.assignRegister(register, assign.target)
|
|
||||||
}
|
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(value.operator in ComparisonOperators) {
|
if(value.operator in ComparisonOperators) {
|
||||||
// TODO real optimized code for comparison expressions that yield a boolean result value
|
// TODO real optimized code for comparison expressions that yield a boolean result value
|
||||||
|
@ -172,7 +172,6 @@ class AstToXmlConverter(internal val program: PtProgram,
|
|||||||
is PtMemoryByte -> write(it)
|
is PtMemoryByte -> write(it)
|
||||||
is PtMemMapped -> write(it)
|
is PtMemMapped -> write(it)
|
||||||
is PtNumber -> write(it)
|
is PtNumber -> write(it)
|
||||||
is PtPipe -> write(it)
|
|
||||||
is PtPostIncrDecr -> write(it)
|
is PtPostIncrDecr -> write(it)
|
||||||
is PtPrefix -> write(it)
|
is PtPrefix -> write(it)
|
||||||
is PtRange -> write(it)
|
is PtRange -> write(it)
|
||||||
@ -204,14 +203,6 @@ class AstToXmlConverter(internal val program: PtProgram,
|
|||||||
xml.endElt()
|
xml.endElt()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun write(pipe: PtPipe) {
|
|
||||||
xml.elt("pipe")
|
|
||||||
xml.attr("type", pipe.type.name)
|
|
||||||
xml.startChildren()
|
|
||||||
pipe.children.forEach { writeNode(it) }
|
|
||||||
xml.endElt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun write(array: PtArray) {
|
private fun write(array: PtArray) {
|
||||||
xml.elt("array")
|
xml.elt("array")
|
||||||
xml.attr("type", array.type.name)
|
xml.attr("type", array.type.name)
|
||||||
|
@ -84,7 +84,6 @@ class CodeGen(internal val program: PtProgram,
|
|||||||
is PtReturn -> translate(node)
|
is PtReturn -> translate(node)
|
||||||
is PtJump -> translate(node)
|
is PtJump -> translate(node)
|
||||||
is PtWhen -> translate(node)
|
is PtWhen -> translate(node)
|
||||||
is PtPipe -> expressionEval.translate(node, 0)
|
|
||||||
is PtForLoop -> translate(node)
|
is PtForLoop -> translate(node)
|
||||||
is PtIfElse -> translate(node)
|
is PtIfElse -> translate(node)
|
||||||
is PtPostIncrDecr -> translate(node)
|
is PtPostIncrDecr -> translate(node)
|
||||||
|
@ -63,7 +63,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
||||||
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
|
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
|
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
|
||||||
is PtPipe -> code += translate(expr, resultRegister)
|
|
||||||
is PtRange,
|
is PtRange,
|
||||||
is PtArray,
|
is PtArray,
|
||||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||||
@ -72,46 +71,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
|
|
||||||
val segments = pipe.segments
|
|
||||||
var valueDt = segments[0].type
|
|
||||||
var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister
|
|
||||||
|
|
||||||
fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression {
|
|
||||||
return when (segment) {
|
|
||||||
is PtFunctionCall -> {
|
|
||||||
val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position)
|
|
||||||
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
|
||||||
segWithArg.children.addAll(segment.args)
|
|
||||||
segWithArg
|
|
||||||
}
|
|
||||||
is PtBuiltinFunctionCall -> {
|
|
||||||
val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.hasNoSideEffects, segment.type, segment.position)
|
|
||||||
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
|
||||||
segWithArg.children.addAll(segment.args)
|
|
||||||
segWithArg
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird segment type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val code = VmCodeChunk()
|
|
||||||
code += translateExpression(segments[0], valueReg, -1)
|
|
||||||
for (segment in segments.subList(1, segments.size-1)) {
|
|
||||||
val sourceReg = valueReg
|
|
||||||
val sourceDt = valueDt
|
|
||||||
if(segment.type!=valueDt) {
|
|
||||||
valueDt = segment.type
|
|
||||||
valueReg = codeGen.vmRegisters.nextFree()
|
|
||||||
}
|
|
||||||
val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt)
|
|
||||||
code += translateExpression(segmentWithImplicitArgument, valueReg, -1)
|
|
||||||
}
|
|
||||||
val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt)
|
|
||||||
code += translateExpression(segWithArg, resultRegister, -1)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||||
val code = VmCodeChunk()
|
val code = VmCodeChunk()
|
||||||
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
|
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.IStatementContainer
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.IfElse
|
||||||
|
import prog8.ast.statements.Jump
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.*
|
import prog8.code.core.AssociativeOperators
|
||||||
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.IntegerDatatypes
|
||||||
|
import prog8.code.core.NumericDatatypes
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@ -331,31 +339,6 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(pipeExpr: PipeExpression, parent: Node) = processPipe(pipeExpr, parent)
|
|
||||||
override fun after(pipe: Pipe, parent: Node) = processPipe(pipe, parent)
|
|
||||||
|
|
||||||
private fun processPipe(pipe: IPipe, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(pipe.source.isSimple) {
|
|
||||||
val segments = pipe.segments
|
|
||||||
if(segments.size==1) {
|
|
||||||
// replace the whole pipe with a normal function call
|
|
||||||
val funcname = (segments[0] as IFunctionCall).target
|
|
||||||
val call = if(pipe is Pipe)
|
|
||||||
FunctionCallStatement(funcname, mutableListOf(pipe.source), true, pipe.position)
|
|
||||||
else
|
|
||||||
FunctionCallExpression(funcname, mutableListOf(pipe.source), pipe.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(pipe as Node, call, parent))
|
|
||||||
} else if(segments.size>1) {
|
|
||||||
// replace source+firstsegment by firstsegment(source) call as the new source
|
|
||||||
val firstSegment = segments.removeAt(0) as IFunctionCall
|
|
||||||
val newArgs = listOf(pipe.source) + firstSegment.args
|
|
||||||
val call = FunctionCallExpression(firstSegment.target, newArgs.toMutableList(), pipe.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(pipe.source, call, pipe as Node))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||||
return when {
|
return when {
|
||||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.BinaryExpression
|
||||||
|
import prog8.ast.expressions.NumericLiteral
|
||||||
|
import prog8.ast.expressions.PrefixExpression
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
@ -236,7 +239,6 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
is PrefixExpression,
|
is PrefixExpression,
|
||||||
is BinaryExpression,
|
is BinaryExpression,
|
||||||
is TypecastExpression,
|
is TypecastExpression,
|
||||||
is PipeExpression,
|
|
||||||
is IFunctionCall -> { /* don't remove */ }
|
is IFunctionCall -> { /* don't remove */ }
|
||||||
else -> {
|
else -> {
|
||||||
if(assign1.value !is IFunctionCall)
|
if(assign1.value !is IFunctionCall)
|
||||||
|
@ -50,7 +50,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
is InlineAssembly -> transform(statement)
|
is InlineAssembly -> transform(statement)
|
||||||
is Jump -> transform(statement)
|
is Jump -> transform(statement)
|
||||||
is Label -> transform(statement)
|
is Label -> transform(statement)
|
||||||
is Pipe -> transform(statement)
|
|
||||||
is PostIncrDecr -> transform(statement)
|
is PostIncrDecr -> transform(statement)
|
||||||
is RepeatLoop -> transform(statement)
|
is RepeatLoop -> transform(statement)
|
||||||
is Return -> transform(statement)
|
is Return -> transform(statement)
|
||||||
@ -80,7 +79,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
is FunctionCallExpression -> transform(expr)
|
is FunctionCallExpression -> transform(expr)
|
||||||
is IdentifierReference -> transform(expr)
|
is IdentifierReference -> transform(expr)
|
||||||
is NumericLiteral -> transform(expr)
|
is NumericLiteral -> transform(expr)
|
||||||
is PipeExpression -> transform(expr)
|
|
||||||
is PrefixExpression -> transform(expr)
|
is PrefixExpression -> transform(expr)
|
||||||
is RangeExpression -> transform(expr)
|
is RangeExpression -> transform(expr)
|
||||||
is StringLiteral -> transform(expr)
|
is StringLiteral -> transform(expr)
|
||||||
@ -225,11 +223,7 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
|
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
|
||||||
val (target, _) = targetOf(srcCall.target)
|
val (target, _) = targetOf(srcCall.target)
|
||||||
val type = srcCall.inferType(program).getOrElse {
|
val type = srcCall.inferType(program).getOrElse {
|
||||||
if((srcCall.parent as? Pipe)?.segments?.last() === srcCall)
|
throw FatalAstException("unknown dt $srcCall")
|
||||||
// for a pipe, the last segment is allowed to be a call to a function not returning anything.
|
|
||||||
DataType.UNDEFINED
|
|
||||||
else
|
|
||||||
throw FatalAstException("unknown dt $srcCall")
|
|
||||||
}
|
}
|
||||||
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
|
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
|
||||||
for (arg in srcCall.args)
|
for (arg in srcCall.args)
|
||||||
@ -287,14 +281,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
private fun transform(label: Label): PtLabel =
|
private fun transform(label: Label): PtLabel =
|
||||||
PtLabel(label.name, label.position)
|
PtLabel(label.name, label.position)
|
||||||
|
|
||||||
private fun transform(srcPipe: Pipe): PtPipe {
|
|
||||||
val pipe = PtPipe(DataType.UNDEFINED, true, srcPipe.position)
|
|
||||||
pipe.add(transformExpression(srcPipe.source))
|
|
||||||
for (segment in srcPipe.segments)
|
|
||||||
pipe.add(transformExpression(segment))
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transform(src: PostIncrDecr): PtPostIncrDecr {
|
private fun transform(src: PostIncrDecr): PtPostIncrDecr {
|
||||||
val post = PtPostIncrDecr(src.operator, src.position)
|
val post = PtPostIncrDecr(src.operator, src.position)
|
||||||
post.add(transform(src.target))
|
post.add(transform(src.target))
|
||||||
@ -459,15 +445,6 @@ class IntermediateAstMaker(val program: Program) {
|
|||||||
private fun transform(number: NumericLiteral): PtNumber =
|
private fun transform(number: NumericLiteral): PtNumber =
|
||||||
PtNumber(number.type, number.number, number.position)
|
PtNumber(number.type, number.number, number.position)
|
||||||
|
|
||||||
private fun transform(srcPipe: PipeExpression): PtPipe {
|
|
||||||
val type = srcPipe.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
|
||||||
val pipe = PtPipe(type, false, srcPipe.position)
|
|
||||||
pipe.add(transformExpression(srcPipe.source))
|
|
||||||
for (segment in srcPipe.segments)
|
|
||||||
pipe.add(transformExpression(segment))
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
|
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
|
||||||
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||||
val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position)
|
val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position)
|
||||||
|
@ -503,7 +503,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(constVal==null) {
|
if(constVal==null) {
|
||||||
val sourceDatatype = assignment.value.inferType(program)
|
val sourceDatatype = assignment.value.inferType(program)
|
||||||
if (sourceDatatype.isUnknown) {
|
if (sourceDatatype.isUnknown) {
|
||||||
if (assignment.value !is FunctionCallExpression && assignment.value !is PipeExpression)
|
if (assignment.value !is FunctionCallExpression)
|
||||||
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
|
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
|
||||||
} else {
|
} else {
|
||||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
||||||
@ -1009,7 +1009,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||||
if(targetStatement!=null) {
|
if(targetStatement!=null) {
|
||||||
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
||||||
checkUnusedReturnValues(functionCallStatement, targetStatement, program, errors)
|
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
val funcName = functionCallStatement.target.nameInSource
|
val funcName = functionCallStatement.target.nameInSource
|
||||||
@ -1100,33 +1100,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(pipe: PipeExpression) {
|
|
||||||
process(pipe)
|
|
||||||
super.visit(pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(pipe: Pipe) {
|
|
||||||
process(pipe)
|
|
||||||
super.visit(pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun process(pipe: IPipe) {
|
|
||||||
if(pipe.source in pipe.segments)
|
|
||||||
throw InternalCompilerException("pipe source and segments should all be different nodes")
|
|
||||||
if (pipe.segments.isEmpty())
|
|
||||||
throw FatalAstException("pipe is missing one or more expressions")
|
|
||||||
if(pipe.segments.any { it !is IFunctionCall })
|
|
||||||
throw FatalAstException("pipe segments can only be function calls")
|
|
||||||
|
|
||||||
if(compilerOptions.compTarget !is VMTarget) {
|
|
||||||
pipe.segments.forEach {
|
|
||||||
it as IFunctionCall
|
|
||||||
if (it.args.size > 0)
|
|
||||||
errors.err("only unary functions supported in pipe expressions for now", it.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||||
if(postIncrDecr.target.identifier != null) {
|
if(postIncrDecr.target.identifier != null) {
|
||||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||||
@ -1498,7 +1471,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, program: Program, errors: IErrorReporter) {
|
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||||
if (!call.void) {
|
if (!call.void) {
|
||||||
// check for unused return values
|
// check for unused return values
|
||||||
if (target is Subroutine && target.returntypes.isNotEmpty()) {
|
if (target is Subroutine && target.returntypes.isNotEmpty()) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.IPipe
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.FunctionCallExpression
|
import prog8.ast.expressions.FunctionCallExpression
|
||||||
@ -151,32 +150,20 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
|||||||
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
|
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
|
||||||
|
|
||||||
private fun visitFunctionCall(call: IFunctionCall) {
|
private fun visitFunctionCall(call: IFunctionCall) {
|
||||||
val isPartOfPipeSegments = (call.parent as? IPipe)?.segments?.contains(call as Node) == true
|
|
||||||
val errormessageAboutArgs = if(isPartOfPipeSegments) "invalid number of arguments in piped call" else "invalid number of arguments"
|
|
||||||
when (val target = call.target.targetStatement(program)) {
|
when (val target = call.target.targetStatement(program)) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
// if the call is part of a Pipe, the number of arguments in the call should be 1 less than the number of parameters
|
val expectedNumberOfArgs: Int = target.parameters.size
|
||||||
val expectedNumberOfArgs: Int = if(isPartOfPipeSegments) {
|
|
||||||
target.parameters.size - 1
|
|
||||||
} else {
|
|
||||||
target.parameters.size
|
|
||||||
}
|
|
||||||
if(call.args.size != expectedNumberOfArgs) {
|
if(call.args.size != expectedNumberOfArgs) {
|
||||||
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||||
errors.err(errormessageAboutArgs, pos)
|
errors.err("invalid number of arguments", pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BuiltinFunctionPlaceholder -> {
|
is BuiltinFunctionPlaceholder -> {
|
||||||
val func = BuiltinFunctions.getValue(target.name)
|
val func = BuiltinFunctions.getValue(target.name)
|
||||||
// if the call is part of a Pipe, the number of arguments in the call should be 1 less than the number of parameters
|
val expectedNumberOfArgs: Int = func.parameters.size
|
||||||
val expectedNumberOfArgs: Int = if(isPartOfPipeSegments) {
|
|
||||||
func.parameters.size-1
|
|
||||||
} else {
|
|
||||||
func.parameters.size
|
|
||||||
}
|
|
||||||
if(call.args.size != expectedNumberOfArgs) {
|
if(call.args.size != expectedNumberOfArgs) {
|
||||||
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||||
errors.err(errormessageAboutArgs, pos)
|
errors.err("invalid number of arguments", pos)
|
||||||
}
|
}
|
||||||
if(func.name=="memory") {
|
if(func.name=="memory") {
|
||||||
val name = call.args[0] as? StringLiteral
|
val name = call.args[0] as? StringLiteral
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.IPipe
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
@ -8,7 +7,10 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.*
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.ICompilationTarget
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
|
import prog8.code.core.NumericDatatypes
|
||||||
|
|
||||||
|
|
||||||
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
|
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
@ -25,32 +27,6 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
|||||||
return super.before(string, parent)
|
return super.before(string, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(pipe: Pipe, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(pipe.source is PipeExpression) {
|
|
||||||
// correct Antlr parse tree quirk: turn nested pipe into single flat pipe
|
|
||||||
val psrc = pipe.source as PipeExpression
|
|
||||||
val newSource = psrc.source
|
|
||||||
val newSegments = psrc.segments
|
|
||||||
newSegments += pipe.segments.single()
|
|
||||||
return listOf(IAstModification.ReplaceNode(pipe as Node, Pipe(newSource, newSegments, pipe.position), parent))
|
|
||||||
} else if(pipe.source is IPipe)
|
|
||||||
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(pipeExpr.source is PipeExpression) {
|
|
||||||
// correct Antlr parse tree quirk; turn nested pipe into single flat pipe
|
|
||||||
val psrc = pipeExpr.source as PipeExpression
|
|
||||||
val newSource = psrc.source
|
|
||||||
val newSegments = psrc.segments
|
|
||||||
newSegments += pipeExpr.segments.single()
|
|
||||||
return listOf(IAstModification.ReplaceNode(pipeExpr as Node, PipeExpression(newSource, newSegments, pipeExpr.position), parent))
|
|
||||||
} else if(pipeExpr.source is IPipe)
|
|
||||||
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
|
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
|
||||||
val modifications = mutableListOf<IAstModification>()
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
@ -419,7 +419,7 @@ internal class StatementReorderer(val program: Program,
|
|||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val function = functionCallStatement.target.targetStatement(program)
|
val function = functionCallStatement.target.targetStatement(program)
|
||||||
?: throw FatalAstException("no target for $functionCallStatement")
|
?: throw FatalAstException("no target for $functionCallStatement")
|
||||||
checkUnusedReturnValues(functionCallStatement, function, program, errors)
|
checkUnusedReturnValues(functionCallStatement, function, errors)
|
||||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.IPipe
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.FunctionCallExpression
|
import prog8.ast.expressions.FunctionCallExpression
|
||||||
import prog8.ast.expressions.PipeExpression
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
import prog8.ast.expressions.TypecastExpression
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
@ -15,7 +11,6 @@ import prog8.code.core.DataType
|
|||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.compiler.BuiltinFunctions
|
import prog8.compiler.BuiltinFunctions
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
|
||||||
|
|
||||||
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
||||||
|
|
||||||
@ -54,16 +49,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||||||
return Pair("argument ${firstUnknownDt+1} invalid argument type", call.args[firstUnknownDt].position)
|
return Pair("argument ${firstUnknownDt+1} invalid argument type", call.args[firstUnknownDt].position)
|
||||||
val argtypes = argITypes.map { it.getOr(DataType.UNDEFINED) }
|
val argtypes = argITypes.map { it.getOr(DataType.UNDEFINED) }
|
||||||
val target = call.target.targetStatement(program)
|
val target = call.target.targetStatement(program)
|
||||||
val isPartOfPipeSegments = (call.parent as? IPipe)?.segments?.contains(call as Node) == true
|
|
||||||
val errormessageAboutArgs = if(isPartOfPipeSegments) "invalid number of arguments in piped call" else "invalid number of arguments"
|
|
||||||
if (target is Subroutine) {
|
if (target is Subroutine) {
|
||||||
val consideredParamTypes: List<DataType> = if(isPartOfPipeSegments) {
|
val consideredParamTypes: List<DataType> = target.parameters.map { it.type }
|
||||||
target.parameters.drop(1).map { it.type } // skip first one (the implicit first arg), this is checked elsewhere
|
|
||||||
} else {
|
|
||||||
target.parameters.map { it.type }
|
|
||||||
}
|
|
||||||
if(argtypes.size != consideredParamTypes.size)
|
if(argtypes.size != consideredParamTypes.size)
|
||||||
return Pair(errormessageAboutArgs, call.position)
|
return Pair("invalid number of arguments", call.position)
|
||||||
val mismatch = argtypes.zip(consideredParamTypes).indexOfFirst { !argTypeCompatible(it.first, it.second) }
|
val mismatch = argtypes.zip(consideredParamTypes).indexOfFirst { !argTypeCompatible(it.first, it.second) }
|
||||||
if(mismatch>=0) {
|
if(mismatch>=0) {
|
||||||
val actual = argtypes[mismatch].toString()
|
val actual = argtypes[mismatch].toString()
|
||||||
@ -90,13 +79,9 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||||||
}
|
}
|
||||||
else if (target is BuiltinFunctionPlaceholder) {
|
else if (target is BuiltinFunctionPlaceholder) {
|
||||||
val func = BuiltinFunctions.getValue(target.name)
|
val func = BuiltinFunctions.getValue(target.name)
|
||||||
val consideredParamTypes: List<Array<DataType>> = if(isPartOfPipeSegments) {
|
val consideredParamTypes: List<Array<DataType>> = func.parameters.map { it.possibleDatatypes }
|
||||||
func.parameters.drop(1).map { it.possibleDatatypes } // skip first one (the implicit first arg), this is checked elsewhere
|
|
||||||
} else {
|
|
||||||
func.parameters.map { it.possibleDatatypes }
|
|
||||||
}
|
|
||||||
if(argtypes.size != consideredParamTypes.size)
|
if(argtypes.size != consideredParamTypes.size)
|
||||||
return Pair(errormessageAboutArgs, call.position)
|
return Pair("invalid number of arguments", call.position)
|
||||||
argtypes.zip(consideredParamTypes).forEachIndexed { index, pair ->
|
argtypes.zip(consideredParamTypes).forEachIndexed { index, pair ->
|
||||||
val anyCompatible = pair.second.any { argTypeCompatible(pair.first, it) }
|
val anyCompatible = pair.second.any { argTypeCompatible(pair.first, it) }
|
||||||
if (!anyCompatible) {
|
if (!anyCompatible) {
|
||||||
@ -115,88 +100,4 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(pipe: PipeExpression) {
|
|
||||||
processPipe(pipe.source, pipe.segments)
|
|
||||||
if(errors.noErrors()) {
|
|
||||||
val last = (pipe.segments.last() as IFunctionCall).target
|
|
||||||
when (val target = last.targetStatement(program)!!) {
|
|
||||||
is BuiltinFunctionPlaceholder -> {
|
|
||||||
if (BuiltinFunctions.getValue(target.name).returnType==null)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
super.visit(pipe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(pipe: Pipe) {
|
|
||||||
processPipe(pipe.source, pipe.segments)
|
|
||||||
if(errors.noErrors()) {
|
|
||||||
super.visit(pipe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processPipe(source: Expression, segments: List<Expression>) {
|
|
||||||
|
|
||||||
val sourceArg = (source as? IFunctionCall)?.args?.firstOrNull()
|
|
||||||
if(sourceArg!=null && segments.any { (it as IFunctionCall).args.firstOrNull() === sourceArg})
|
|
||||||
throw FatalAstException("some pipe segment first arg is replicated from the source functioncall arg")
|
|
||||||
|
|
||||||
// invalid size and other issues will be handled by the ast checker later.
|
|
||||||
var valueDt = source.inferType(program).getOrElse {
|
|
||||||
throw FatalAstException("invalid dt")
|
|
||||||
}
|
|
||||||
|
|
||||||
for(funccall in segments) {
|
|
||||||
val target = (funccall as IFunctionCall).target.targetStatement(program)
|
|
||||||
if(target!=null) {
|
|
||||||
when (target) {
|
|
||||||
is BuiltinFunctionPlaceholder -> {
|
|
||||||
val func = BuiltinFunctions.getValue(target.name)
|
|
||||||
if(func.parameters.isEmpty())
|
|
||||||
errors.err("function must have at least one parameter", funccall.position)
|
|
||||||
else if(func.returnType==null && funccall !== segments.last())
|
|
||||||
errors.err("function must return a single value", funccall.position)
|
|
||||||
|
|
||||||
val paramDts = func.parameters.firstOrNull()?.possibleDatatypes
|
|
||||||
if(paramDts!=null && !paramDts.any { valueDt isAssignableTo it })
|
|
||||||
errors.err("pipe value datatype $valueDt incompatible with function argument ${paramDts.toList()}", funccall.position)
|
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
|
||||||
valueDt = builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Subroutine -> {
|
|
||||||
if(target.parameters.isEmpty())
|
|
||||||
errors.err("function must have at least one parameter", funccall.position)
|
|
||||||
else if(target.returntypes.size!=1 && funccall !== segments.last())
|
|
||||||
errors.err("function must return a single value", funccall.position)
|
|
||||||
|
|
||||||
val paramDt = target.parameters.firstOrNull()?.type
|
|
||||||
if(paramDt!=null && !(valueDt isAssignableTo paramDt))
|
|
||||||
errors.err("pipe value datatype $valueDt incompatible with function argument $paramDt", funccall.position)
|
|
||||||
|
|
||||||
if(target.returntypes.isNotEmpty())
|
|
||||||
valueDt = target.returntypes.single()
|
|
||||||
}
|
|
||||||
is VarDecl -> {
|
|
||||||
if(!(valueDt isAssignableTo target.datatype))
|
|
||||||
errors.err("final pipe value datatype can't be stored in pipe ending variable", funccall.position)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw FatalAstException("weird function")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,512 +0,0 @@
|
|||||||
package prog8tests
|
|
||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.kotest.matchers.string.shouldContain
|
|
||||||
import io.kotest.matchers.types.instanceOf
|
|
||||||
import prog8.ast.IFunctionCall
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
|
||||||
import prog8.ast.statements.Pipe
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import prog8.code.core.SourceCode
|
|
||||||
import prog8.code.target.C64Target
|
|
||||||
import prog8.code.target.VMTarget
|
|
||||||
import prog8.compiler.astprocessing.AstPreprocessor
|
|
||||||
import prog8.parser.Prog8Parser.parseModule
|
|
||||||
import prog8tests.helpers.*
|
|
||||||
|
|
||||||
|
|
||||||
class TestPipes: FunSpec({
|
|
||||||
|
|
||||||
test("pipe expression parse tree after preprocessing") {
|
|
||||||
val text = """
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
uword xx = 9999 |> func1() |> func2()
|
|
||||||
|> func1() |> func2()
|
|
||||||
|> func1()
|
|
||||||
}
|
|
||||||
sub func1(uword arg) -> uword {
|
|
||||||
return arg+1111
|
|
||||||
}
|
|
||||||
sub func2(uword arg) -> uword {
|
|
||||||
return arg+2222
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val src = SourceCode.Text(text)
|
|
||||||
val module = parseModule(src)
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
|
||||||
program.addModule(module)
|
|
||||||
val preprocess = AstPreprocessor(program, errors, C64Target())
|
|
||||||
preprocess.visit(program)
|
|
||||||
errors.errors.size shouldBe 0
|
|
||||||
preprocess.applyModifications()
|
|
||||||
|
|
||||||
program.entrypoint.statements.size shouldBe 1
|
|
||||||
val pipe = (program.entrypoint.statements.single() as VarDecl).value as PipeExpression
|
|
||||||
pipe.source shouldBe NumericLiteral(DataType.UWORD, 9999.0, Position.DUMMY)
|
|
||||||
pipe.segments.size shouldBe 5
|
|
||||||
var call = pipe.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func1")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func2")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[2] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func1")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[3] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func2")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[4] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func1")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
}
|
|
||||||
|
|
||||||
test("pipe statement parse tree after preprocessing") {
|
|
||||||
val text = """
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
9999 |> func1() |> func2()
|
|
||||||
|> func1() |> func2()
|
|
||||||
|> func3()
|
|
||||||
}
|
|
||||||
sub func1(uword arg) -> uword {
|
|
||||||
return arg+1111
|
|
||||||
}
|
|
||||||
sub func2(uword arg) -> uword {
|
|
||||||
return arg+2222
|
|
||||||
}
|
|
||||||
sub func3(uword arg) {
|
|
||||||
; nothing
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val src = SourceCode.Text(text)
|
|
||||||
val module = parseModule(src)
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
|
||||||
program.addModule(module)
|
|
||||||
val preprocess = AstPreprocessor(program, errors, C64Target())
|
|
||||||
preprocess.visit(program)
|
|
||||||
errors.errors.size shouldBe 0
|
|
||||||
preprocess.applyModifications()
|
|
||||||
|
|
||||||
program.entrypoint.statements.size shouldBe 1
|
|
||||||
val pipe = program.entrypoint.statements.single() as Pipe
|
|
||||||
pipe.source shouldBe NumericLiteral(DataType.UWORD, 9999.0, Position.DUMMY)
|
|
||||||
pipe.segments.size shouldBe 5
|
|
||||||
var call = pipe.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func1")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func2")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[2] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func1")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[3] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func2")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
call = pipe.segments[4] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("func3")
|
|
||||||
call.args.size shouldBe 0
|
|
||||||
}
|
|
||||||
|
|
||||||
test("correct pipe statements (no opt)") {
|
|
||||||
val text = """
|
|
||||||
%import floats
|
|
||||||
%import textio
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
1.234 |> addfloat()
|
|
||||||
|> floats.print_f()
|
|
||||||
|
|
||||||
startvalue(99) |> addword()
|
|
||||||
|> txt.print_uw()
|
|
||||||
|
|
||||||
9999 |> abs() |> txt.print_uw()
|
|
||||||
9999 |> txt.print_uw()
|
|
||||||
99 |> abs() |> lsb() |> txt.print_ub()
|
|
||||||
99 |> txt.print_ub()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub startvalue(ubyte arg) -> uword {
|
|
||||||
return arg+9999
|
|
||||||
}
|
|
||||||
sub addfloat(float fl) -> float {
|
|
||||||
return fl+2.22
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val result = compileText(C64Target(), optimize = false, text, writeAssembly = true)!!
|
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 7
|
|
||||||
val pipef = stmts[0] as Pipe
|
|
||||||
pipef.source shouldBe instanceOf<NumericLiteral>()
|
|
||||||
pipef.segments.size shouldBe 2
|
|
||||||
var call = pipef.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("addfloat")
|
|
||||||
call = pipef.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("floats", "print_f")
|
|
||||||
|
|
||||||
val pipew = stmts[1] as Pipe
|
|
||||||
pipef.source shouldBe instanceOf<NumericLiteral>()
|
|
||||||
pipew.segments.size shouldBe 2
|
|
||||||
call = pipew.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("addword")
|
|
||||||
call = pipew.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("txt", "print_uw")
|
|
||||||
|
|
||||||
stmts[2] shouldBe instanceOf<Pipe>()
|
|
||||||
stmts[3] shouldBe instanceOf<Pipe>()
|
|
||||||
stmts[4] shouldBe instanceOf<Pipe>()
|
|
||||||
stmts[5] shouldBe instanceOf<Pipe>()
|
|
||||||
}
|
|
||||||
|
|
||||||
test("correct pipe statements (with opt)") {
|
|
||||||
val text = """
|
|
||||||
%import floats
|
|
||||||
%import textio
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
1.234 |> addfloat()
|
|
||||||
|> floats.print_f()
|
|
||||||
|
|
||||||
startvalue(99) |> addword()
|
|
||||||
|> txt.print_uw()
|
|
||||||
|
|
||||||
; these should be 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 startvalue(ubyte arg) -> uword {
|
|
||||||
return arg+9999
|
|
||||||
}
|
|
||||||
sub addfloat(float fl) -> float {
|
|
||||||
return fl+2.22
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val result = compileText(C64Target(), optimize = true, text, writeAssembly = true)!!
|
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 7
|
|
||||||
val pipef = stmts[0] as Pipe
|
|
||||||
pipef.source shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
(pipef.source as IFunctionCall).target.nameInSource shouldBe listOf("addfloat")
|
|
||||||
pipef.segments.size shouldBe 1
|
|
||||||
val callf = pipef.segments[0] as IFunctionCall
|
|
||||||
callf.target.nameInSource shouldBe listOf("floats", "print_f")
|
|
||||||
|
|
||||||
val pipew = stmts[1] as Pipe
|
|
||||||
pipef.source shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
(pipew.source as IFunctionCall).target.nameInSource shouldBe listOf("startvalue")
|
|
||||||
pipew.segments.size shouldBe 2
|
|
||||||
val callw = pipew.segments[1] as IFunctionCall
|
|
||||||
callw.target.nameInSource shouldBe listOf("txt", "print_uw")
|
|
||||||
|
|
||||||
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") {
|
|
||||||
val text = """
|
|
||||||
%option enable_floats
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
1.234 |> addfloat()
|
|
||||||
|> addword() |> addword()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub addfloat(float fl) -> float {
|
|
||||||
return fl+2.22
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
|
||||||
errors.errors.size shouldBe 1
|
|
||||||
errors.errors[0] shouldContain "incompatible"
|
|
||||||
}
|
|
||||||
|
|
||||||
test("correct pipe expressions (no opt)") {
|
|
||||||
val text = """
|
|
||||||
%import floats
|
|
||||||
%import textio
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
float @shared fl = 1.234 |> addfloat()
|
|
||||||
|> addfloat()
|
|
||||||
|
|
||||||
uword @shared ww = startvalue(99) |> addword()
|
|
||||||
|> addword()
|
|
||||||
|
|
||||||
ubyte @shared cc = 30 |> abs() |> sqrt16()
|
|
||||||
cc = cc |> abs() |> sqrt16()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub startvalue(ubyte arg) -> uword {
|
|
||||||
return arg+9999
|
|
||||||
}
|
|
||||||
sub addfloat(float fl) -> float {
|
|
||||||
return fl+2.22
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val result = compileText(C64Target(), optimize = false, text, writeAssembly = true)!!
|
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 8
|
|
||||||
val assignf = stmts[1] as Assignment
|
|
||||||
val pipef = assignf.value as PipeExpression
|
|
||||||
pipef.source shouldBe instanceOf<NumericLiteral>()
|
|
||||||
pipef.segments.size shouldBe 2
|
|
||||||
var call = pipef.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("addfloat")
|
|
||||||
call = pipef.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("addfloat")
|
|
||||||
|
|
||||||
|
|
||||||
val assignw = stmts[3] as Assignment
|
|
||||||
val pipew = assignw.value as PipeExpression
|
|
||||||
pipew.source shouldBe instanceOf<IFunctionCall>()
|
|
||||||
pipew.segments.size shouldBe 2
|
|
||||||
call = pipew.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("addword")
|
|
||||||
call = pipew.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("addword")
|
|
||||||
|
|
||||||
var assigncc = stmts[5] as Assignment
|
|
||||||
val value = assigncc.value as PipeExpression
|
|
||||||
value.source shouldBe NumericLiteral(DataType.UBYTE, 30.0, Position.DUMMY)
|
|
||||||
value.segments.size shouldBe 2
|
|
||||||
call = value.segments[0] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("abs")
|
|
||||||
call = value.segments[1] as IFunctionCall
|
|
||||||
call.target.nameInSource shouldBe listOf("sqrt16")
|
|
||||||
|
|
||||||
assigncc = stmts[6] as Assignment
|
|
||||||
val pipecc = assigncc.value as PipeExpression
|
|
||||||
pipecc.source shouldBe instanceOf<IdentifierReference>()
|
|
||||||
pipecc.segments.size shouldBe 2
|
|
||||||
pipecc.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
|
||||||
pipecc.segments[1] shouldBe instanceOf<BuiltinFunctionCall>()
|
|
||||||
}
|
|
||||||
|
|
||||||
test("correct pipe expressions (with opt)") {
|
|
||||||
val text = """
|
|
||||||
%import floats
|
|
||||||
%import textio
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
float @shared fl = 1.234 |> addfloat()
|
|
||||||
|> addfloat()
|
|
||||||
|
|
||||||
uword @shared ww = startvalue(99) |> addword()
|
|
||||||
|> addword()
|
|
||||||
|
|
||||||
ubyte @shared cc = 80 |> abs() |> sqrt16() ; will be optimized away into a const number
|
|
||||||
cc = cc |> abs() |> sqrt16()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub startvalue(ubyte arg) -> uword {
|
|
||||||
return arg+9999
|
|
||||||
}
|
|
||||||
sub addfloat(float fl) -> float {
|
|
||||||
return fl+2.22
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
val result = compileText(C64Target(), optimize = true, text, writeAssembly = true)!!
|
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 8
|
|
||||||
val assignf = stmts[1] as Assignment
|
|
||||||
val pipef = assignf.value as PipeExpression
|
|
||||||
pipef.source shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
pipef.segments.size shouldBe 1
|
|
||||||
pipef.segments[0] shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
|
|
||||||
val assignw = stmts[3] as Assignment
|
|
||||||
val pipew = assignw.value as PipeExpression
|
|
||||||
pipew.source shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
pipew.segments.size shouldBe 2
|
|
||||||
pipew.segments[0] shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
pipew.segments[1] shouldBe instanceOf<FunctionCallExpression>()
|
|
||||||
|
|
||||||
var assigncc = stmts[5] as Assignment
|
|
||||||
val value = assigncc.value as NumericLiteral
|
|
||||||
value.number shouldBe 8.0
|
|
||||||
|
|
||||||
assigncc = stmts[6] as Assignment
|
|
||||||
val pipecc = assigncc.value as PipeExpression
|
|
||||||
pipecc.source shouldBe instanceOf<BuiltinFunctionCall>()
|
|
||||||
(pipecc.source as BuiltinFunctionCall).target.nameInSource shouldBe listOf("abs")
|
|
||||||
|
|
||||||
pipecc.segments.size shouldBe 1
|
|
||||||
pipecc.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
|
||||||
(pipecc.segments[0] as BuiltinFunctionCall).target.nameInSource shouldBe listOf("sqrt16")
|
|
||||||
}
|
|
||||||
|
|
||||||
test("incorrect type in pipe expression") {
|
|
||||||
val text = """
|
|
||||||
%option enable_floats
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
uword result = 1.234 |> addfloat()
|
|
||||||
|> addword() |> addword()
|
|
||||||
}
|
|
||||||
|
|
||||||
sub addfloat(float fl) -> float {
|
|
||||||
return fl+2.22
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
|
||||||
errors.errors.size shouldBe 1
|
|
||||||
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() |> lsb() |> txt.print_ub()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 7
|
|
||||||
val pipeww = stmts[4] as Pipe
|
|
||||||
pipeww.source shouldBe instanceOf<BuiltinFunctionCall>()
|
|
||||||
pipeww.segments.size shouldBe 1
|
|
||||||
pipeww.segments[0] shouldBe instanceOf<IFunctionCall>()
|
|
||||||
|
|
||||||
val pipebb = stmts[5] as Pipe
|
|
||||||
pipebb.source shouldBe instanceOf<BuiltinFunctionCall>()
|
|
||||||
pipebb.segments.size shouldBe 2
|
|
||||||
pipebb.segments[0] shouldBe instanceOf<IFunctionCall>()
|
|
||||||
pipebb.segments[1] shouldBe instanceOf<IFunctionCall>()
|
|
||||||
}
|
|
||||||
|
|
||||||
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(), optimize = false, text, writeAssembly = true, errors=errors) shouldBe null
|
|
||||||
errors.errors.size shouldBe 2
|
|
||||||
errors.errors[0] shouldContain "UWORD incompatible"
|
|
||||||
errors.errors[1] shouldContain "UWORD incompatible"
|
|
||||||
}
|
|
||||||
|
|
||||||
test("pipe detects invalid number of args") {
|
|
||||||
val text = """
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
uword ww = startvalue() |> addword()
|
|
||||||
|> addword()
|
|
||||||
|
|
||||||
ubyte cc = 30 |> abs(99) |> sqrt16(22)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub startvalue(ubyte arg) -> uword {
|
|
||||||
return arg+9999
|
|
||||||
}
|
|
||||||
sub addword(uword ww) -> uword {
|
|
||||||
return ww+2222
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
compileText(C64Target(), optimize = false, text, writeAssembly = false, errors=errors) shouldBe null
|
|
||||||
errors.errors.size shouldBe 3
|
|
||||||
errors.errors[0] shouldContain ":4:32: invalid number of arguments"
|
|
||||||
errors.errors[1] shouldContain ":7:42: invalid number of arguments"
|
|
||||||
errors.errors[2] shouldContain ":7:56: invalid number of arguments"
|
|
||||||
}
|
|
||||||
|
|
||||||
test("non-unary funcions in pipe ok for target virtual") {
|
|
||||||
val text = """
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
uword @shared wvalue = add(3,4) |> add(48) |> mkword(234)
|
|
||||||
}
|
|
||||||
sub add(ubyte first, ubyte second) -> ubyte {
|
|
||||||
return first+second
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
val result = compileText(VMTarget(), optimize = false, text, writeAssembly = true, errors=errors)!!
|
|
||||||
errors.errors.size shouldBe 0
|
|
||||||
errors.warnings.size shouldBe 0
|
|
||||||
result.program.entrypoint.statements.size shouldBe 3
|
|
||||||
}
|
|
||||||
|
|
||||||
test("non-unary funcions in pipe not yet ok for other targets") {
|
|
||||||
// NOTE: once other targets also support this, merge this into the test above
|
|
||||||
val text = """
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
uword @shared wvalue = add(3,4) |> add(48) |> mkword(234)
|
|
||||||
}
|
|
||||||
sub add(ubyte first, ubyte second) -> ubyte {
|
|
||||||
return first+second
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
val errors = ErrorReporterForTests()
|
|
||||||
compileText(C64Target(), optimize = false, text, writeAssembly = true, errors=errors) shouldBe null
|
|
||||||
errors.errors.size shouldBe 2
|
|
||||||
errors.errors[0] shouldContain "only unary"
|
|
||||||
errors.errors[1] shouldContain "only unary"
|
|
||||||
}
|
|
||||||
})
|
|
@ -21,7 +21,7 @@ class TestIntermediateAst: FunSpec({
|
|||||||
ubyte cc
|
ubyte cc
|
||||||
ubyte[] array = [1,2,3]
|
ubyte[] array = [1,2,3]
|
||||||
cc = 11 in array
|
cc = 11 in array
|
||||||
cc = cc |> lsb() |> sqrt16()
|
cc = sqrt16(lsb(cc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@ -53,9 +53,9 @@ class TestIntermediateAst: FunSpec({
|
|||||||
|
|
||||||
val containment = (entry.children[2] as PtAssignment).value as PtContainmentCheck
|
val containment = (entry.children[2] as PtAssignment).value as PtContainmentCheck
|
||||||
(containment.element as PtNumber).number shouldBe 11.0
|
(containment.element as PtNumber).number shouldBe 11.0
|
||||||
val pipe = (entry.children[3] as PtAssignment).value as PtPipe
|
val fcall = (entry.children[3] as PtAssignment).value as PtFunctionCall
|
||||||
pipe.void shouldBe false
|
fcall.void shouldBe false
|
||||||
pipe.type shouldBe DataType.UBYTE
|
fcall.type shouldBe DataType.UBYTE
|
||||||
ast.print()
|
ast.print()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,26 +461,4 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
|||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
outputln("")
|
outputln("")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(pipe: Pipe) {
|
|
||||||
printPipe(pipe.source, pipe.segments)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(pipe: PipeExpression) {
|
|
||||||
printPipe(pipe.source, pipe.segments)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun printPipe(source: Expression, segments: Iterable<Expression>) {
|
|
||||||
source.accept(this)
|
|
||||||
output(" |> ")
|
|
||||||
segments.first().accept(this)
|
|
||||||
outputln("")
|
|
||||||
scopelevel++
|
|
||||||
segments.drop(1).forEach {
|
|
||||||
outputi("|> ")
|
|
||||||
it.accept(this)
|
|
||||||
outputln("")
|
|
||||||
}
|
|
||||||
scopelevel--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -31,13 +31,6 @@ interface IFunctionCall {
|
|||||||
var parent: Node // will be linked correctly later (late init)
|
var parent: Node // will be linked correctly later (late init)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPipe {
|
|
||||||
var source: Expression
|
|
||||||
val segments: MutableList<Expression> // are all function calls
|
|
||||||
val position: Position
|
|
||||||
var parent: Node // will be linked correctly later (late init)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IStatementContainer {
|
interface IStatementContainer {
|
||||||
val statements: MutableList<Statement>
|
val statements: MutableList<Statement>
|
||||||
|
|
||||||
|
@ -146,9 +146,6 @@ private fun Prog8ANTLRParser.StatementContext.toAst() : Statement {
|
|||||||
val whenstmt = whenstmt()?.toAst()
|
val whenstmt = whenstmt()?.toAst()
|
||||||
if(whenstmt!=null) return whenstmt
|
if(whenstmt!=null) return whenstmt
|
||||||
|
|
||||||
val pipestmt = pipestmt()?.toAst()
|
|
||||||
if(pipestmt!=null) return pipestmt
|
|
||||||
|
|
||||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,13 +434,6 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
|
|||||||
if(addressof()!=null)
|
if(addressof()!=null)
|
||||||
return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
|
return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
|
||||||
|
|
||||||
if(pipesegment()!=null)
|
|
||||||
return PipeExpression(
|
|
||||||
expression(0).toAst(),
|
|
||||||
pipesegment().map { it.functioncall().toAst() }.toMutableList(),
|
|
||||||
toPosition()
|
|
||||||
)
|
|
||||||
|
|
||||||
throw FatalAstException(text)
|
throw FatalAstException(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,11 +595,3 @@ private fun Prog8ANTLRParser.VardeclContext.toAst(type: VarDeclType, value: Expr
|
|||||||
toPosition()
|
toPosition()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Prog8ANTLRParser.PipestmtContext.toAst(): Pipe {
|
|
||||||
return Pipe(
|
|
||||||
expression().toAst(),
|
|
||||||
pipesegment().map { it.functioncall().toAst() }.toMutableList(),
|
|
||||||
toPosition()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package prog8.ast.expressions
|
package prog8.ast.expressions
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.IPipe
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ExpressionError
|
import prog8.ast.base.ExpressionError
|
||||||
@ -975,13 +974,6 @@ class FunctionCallExpression(override var target: IdentifierReference,
|
|||||||
if(target.nameInSource.size>1)
|
if(target.nameInSource.size>1)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
// If the function call is part of a Pipe segments, the number of args will be 1 less than the number of parameters required
|
|
||||||
// because of the implicit first argument. We don't know this first argument here. Assume it is not a constant,
|
|
||||||
// which means that this function call cannot be a constant either.
|
|
||||||
val pipeParentSegments = (parent as? IPipe)?.segments ?: emptyList()
|
|
||||||
if(this in pipeParentSegments)
|
|
||||||
return null
|
|
||||||
|
|
||||||
val resultValue: NumericLiteral? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
|
val resultValue: NumericLiteral? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
|
||||||
if(withDatatypeCheck) {
|
if(withDatatypeCheck) {
|
||||||
val resultDt = this.inferType(program)
|
val resultDt = this.inferType(program)
|
||||||
@ -1112,38 +1104,6 @@ class ContainmentCheck(var element: Expression,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PipeExpression(override var source: Expression,
|
|
||||||
override val segments: MutableList<Expression>, // are all function calls
|
|
||||||
override val position: Position): Expression(), IPipe {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override val isSimple = false
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent=parent
|
|
||||||
source.linkParents(this)
|
|
||||||
segments.forEach { it.linkParents(this) }
|
|
||||||
}
|
|
||||||
override fun copy(): PipeExpression = PipeExpression(source.copy(), segments.map {it.copy()}.toMutableList(), position)
|
|
||||||
override fun constValue(program: Program): NumericLiteral? = null
|
|
||||||
override fun referencesIdentifier(nameInSource: List<String>) =
|
|
||||||
source.referencesIdentifier(nameInSource) || segments.any { it.referencesIdentifier(nameInSource) }
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
|
||||||
override fun inferType(program: Program) = segments.last().inferType(program)
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(node is Expression)
|
|
||||||
require(replacement is Expression)
|
|
||||||
if(node===source) {
|
|
||||||
source = replacement
|
|
||||||
} else {
|
|
||||||
require(replacement is IFunctionCall)
|
|
||||||
val idx = segments.indexOf(node)
|
|
||||||
segments[idx] = replacement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun invertCondition(cond: Expression): BinaryExpression? {
|
fun invertCondition(cond: Expression): BinaryExpression? {
|
||||||
if(cond is BinaryExpression) {
|
if(cond is BinaryExpression) {
|
||||||
|
@ -1064,34 +1064,6 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Pipe(override var source: Expression,
|
|
||||||
override val segments: MutableList<Expression>, // are all function calls
|
|
||||||
override val position: Position): Statement(), IPipe {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent = parent
|
|
||||||
source.linkParents(this)
|
|
||||||
segments.forEach { it.linkParents(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun copy() = Pipe(source.copy(), segments.map { it.copy() }.toMutableList(), position)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(node is Expression)
|
|
||||||
require(replacement is Expression)
|
|
||||||
if(node===source) {
|
|
||||||
source = replacement
|
|
||||||
} else {
|
|
||||||
require(replacement is IFunctionCall)
|
|
||||||
val idx = segments.indexOf(node)
|
|
||||||
segments[idx] = replacement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls to builtin functions will be replaced with this node just before handing the Ast to the codegen.
|
// Calls to builtin functions will be replaced with this node just before handing the Ast to the codegen.
|
||||||
// this is meant to eventually (?) be able to not have any FunctionCallStatement nodes to worry about anymore
|
// this is meant to eventually (?) be able to not have any FunctionCallStatement nodes to worry about anymore
|
||||||
// in the codegen, because they have been converted into GoSub (for instance) or this node.
|
// in the codegen, because they have been converted into GoSub (for instance) or this node.
|
||||||
|
@ -121,8 +121,6 @@ abstract class AstWalker {
|
|||||||
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(pipe: Pipe, parent: Node): Iterable<IAstModification> = noModifications
|
|
||||||
open fun before(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> = noModifications
|
|
||||||
|
|
||||||
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
@ -166,8 +164,6 @@ abstract class AstWalker {
|
|||||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(pipe: Pipe, parent: Node): Iterable<IAstModification> = noModifications
|
|
||||||
open fun after(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> = noModifications
|
|
||||||
|
|
||||||
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
||||||
|
|
||||||
@ -470,19 +466,5 @@ abstract class AstWalker {
|
|||||||
whenChoice.statements.accept(this, whenChoice)
|
whenChoice.statements.accept(this, whenChoice)
|
||||||
track(after(whenChoice, parent), whenChoice, parent)
|
track(after(whenChoice, parent), whenChoice, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(pipe: Pipe, parent: Node) {
|
|
||||||
track(before(pipe, parent), pipe, parent)
|
|
||||||
pipe.source.accept(this, pipe)
|
|
||||||
pipe.segments.forEach { it.accept(this, pipe) }
|
|
||||||
track(after(pipe, parent), pipe, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(pipe: PipeExpression, parent: Node) {
|
|
||||||
track(before(pipe, parent), pipe, parent)
|
|
||||||
pipe.source.accept(this, pipe)
|
|
||||||
pipe.segments.forEach { it.accept(this, pipe) }
|
|
||||||
track(after(pipe, parent), pipe, parent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,14 +187,4 @@ interface IAstVisitor {
|
|||||||
whenChoice.values?.forEach { it.accept(this) }
|
whenChoice.values?.forEach { it.accept(this) }
|
||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(pipe: Pipe) {
|
|
||||||
pipe.source.accept(this)
|
|
||||||
pipe.segments.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(pipe: PipeExpression) {
|
|
||||||
pipe.source.accept(this)
|
|
||||||
pipe.segments.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,6 @@ Language features
|
|||||||
- Conditional branches to map directly on processor branch instructions
|
- Conditional branches to map directly on processor branch instructions
|
||||||
- ``when`` statement to avoid if-else chains
|
- ``when`` statement to avoid if-else chains
|
||||||
- ``in`` expression for concise and efficient multi-value/containment test
|
- ``in`` expression for concise and efficient multi-value/containment test
|
||||||
- pipe operator ``|>`` to rewrite nested function call expressions in a more readable chained form
|
|
||||||
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||||
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
||||||
- Floating point math also supported if the target system provides floating point library routines (C64 and Cx16 both do).
|
- Floating point math also supported if the target system provides floating point library routines (C64 and Cx16 both do).
|
||||||
|
@ -701,10 +701,6 @@ The arguments in parentheses after the function name, should match the parameter
|
|||||||
If you want to ignore a return value of a subroutine, you should prefix the call with the ``void`` keyword.
|
If you want to ignore a return value of a subroutine, you should prefix the call with the ``void`` keyword.
|
||||||
Otherwise the compiler will issue a warning about discarding a result value.
|
Otherwise the compiler will issue a warning about discarding a result value.
|
||||||
|
|
||||||
Deeply nested function calls can be rewritten as a chain using the *pipe operator* ``|>`` as long as they
|
|
||||||
are unary functions (taking a single argument). Various possibilities of using this operator are explained
|
|
||||||
in the syntax reference for this operator.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
**Order of evaluation:**
|
**Order of evaluation:**
|
||||||
|
|
||||||
|
@ -526,29 +526,6 @@ containment check: ``in``
|
|||||||
txt.print("email address seems ok")
|
txt.print("email address seems ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
pipe: ``|>``
|
|
||||||
Used as an alternative to nesting function calls. The pipe operator is used to 'pipe' the value
|
|
||||||
into the next function. You write a pipe as a sequence of function calls. You don't write
|
|
||||||
the arguments to the functions though: the value of one segment in the pipe, will be used as the argument
|
|
||||||
for the next function call in the sequence.
|
|
||||||
|
|
||||||
*note:* It only works on unary functions (taking a single argument) for now.
|
|
||||||
|
|
||||||
For example, this: ``txt.print_uw(add_bonus(determine_score(get_player(1))))``
|
|
||||||
can be rewritten as::
|
|
||||||
|
|
||||||
get_player(1)
|
|
||||||
|> determine_score()
|
|
||||||
|> add_bonus()
|
|
||||||
|> txt.print_uw()
|
|
||||||
|
|
||||||
A pipe can also be written as an expression that returns a value, for example ``uword score = add_bonus(determine_score(get_player(1)))`` ::
|
|
||||||
|
|
||||||
uword score = get_player(1)
|
|
||||||
|> determine_score()
|
|
||||||
|> add_bonus()
|
|
||||||
|
|
||||||
|
|
||||||
address of: ``&``
|
address of: ``&``
|
||||||
This is a prefix operator that can be applied to a string or array variable or literal value.
|
This is a prefix operator that can be applied to a string or array variable or literal value.
|
||||||
It results in the memory address (UWORD) of that string or array in memory: ``uword a = &stringvar``
|
It results in the memory address (UWORD) of that string or array in memory: ``uword a = &stringvar``
|
||||||
|
@ -3,7 +3,6 @@ TODO
|
|||||||
|
|
||||||
For next release
|
For next release
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
- pipe operator: (targets other than 'Virtual'): allow non-unary function calls in the pipe that specify the other argument(s) in the calls. Already working for VM target.
|
|
||||||
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -73,9 +72,6 @@ Optimizations:
|
|||||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once (if?) 6502-codegen is no longer done from
|
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once (if?) 6502-codegen is no longer done from
|
||||||
the old CompilerAst, those checks should probably be removed, or be made permanent
|
the old CompilerAst, those checks should probably be removed, or be made permanent
|
||||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served
|
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served
|
||||||
- translateUnaryFunctioncall() in BuiltinFunctionsAsmGen: should be able to assign parameters to a builtin function directly from register(s), this will make the use of a builtin function in a pipe expression more efficient without using a temporary variable
|
|
||||||
compare ``aa = startvalue(1) |> sin8u() |> cos8u() |> sin8u() |> cos8u()``
|
|
||||||
versus: ``aa = cos8u(sin8u(cos8u(sin8u(startvalue(1)))))`` the second one contains no sta cx16.r9L in between.
|
|
||||||
- AssignmentAsmGen.assignExpression() -> improve code gen for assigning boolean comparison expressions
|
- AssignmentAsmGen.assignExpression() -> improve code gen for assigning boolean comparison expressions
|
||||||
Check what the vm target does here, maybe just do this as part of the vm -> 6502 codegen.
|
Check what the vm target does here, maybe just do this as part of the vm -> 6502 codegen.
|
||||||
- when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
- when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
||||||
|
@ -42,8 +42,10 @@ main {
|
|||||||
|
|
||||||
; 9+10+42 = 61
|
; 9+10+42 = 61
|
||||||
; 200-61-2 = 137
|
; 200-61-2 = 137
|
||||||
ubyte result = 9 |> func1(10) |> func2(2)
|
; ubyte result = 9 |> func1(10) |> func2(2)
|
||||||
txt.print_ub(result)
|
; $090a $0a02
|
||||||
|
uword resultw = 9 |> mkword(10) |> lsb() |> mkword(2)
|
||||||
|
txt.print_uw(resultw)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
|
|
||||||
; a "pixelshader":
|
; a "pixelshader":
|
||||||
|
@ -55,10 +55,6 @@ ARRAYSIG :
|
|||||||
'[]'
|
'[]'
|
||||||
;
|
;
|
||||||
|
|
||||||
PIPE :
|
|
||||||
'|>'
|
|
||||||
;
|
|
||||||
|
|
||||||
cpuregister: 'A' | 'X' | 'Y';
|
cpuregister: 'A' | 'X' | 'Y';
|
||||||
register: 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' | 'Pc' | 'Pz' | 'Pn' | 'Pv' | 'R0' | 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6' | 'R7' | 'R8' | 'R9' | 'R10' | 'R11' | 'R12' | 'R13' | 'R14' | 'R15';
|
register: 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' | 'Pc' | 'Pz' | 'Pn' | 'Pv' | 'R0' | 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6' | 'R7' | 'R8' | 'R9' | 'R10' | 'R11' | 'R12' | 'R13' | 'R14' | 'R15';
|
||||||
|
|
||||||
@ -100,7 +96,6 @@ statement :
|
|||||||
| whenstmt
|
| whenstmt
|
||||||
| breakstmt
|
| breakstmt
|
||||||
| labeldef
|
| labeldef
|
||||||
| pipestmt
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
@ -183,7 +178,6 @@ expression :
|
|||||||
| directmemory
|
| directmemory
|
||||||
| addressof
|
| addressof
|
||||||
| expression typecast
|
| expression typecast
|
||||||
| expression pipesegment+
|
|
||||||
;
|
;
|
||||||
|
|
||||||
typecast : 'as' datatype;
|
typecast : 'as' datatype;
|
||||||
@ -207,10 +201,6 @@ returnstmt : 'return' expression? ;
|
|||||||
|
|
||||||
breakstmt : 'break';
|
breakstmt : 'break';
|
||||||
|
|
||||||
pipestmt : expression pipesegment+;
|
|
||||||
|
|
||||||
pipesegment : EOL? PIPE functioncall ;
|
|
||||||
|
|
||||||
identifier : NAME ;
|
identifier : NAME ;
|
||||||
|
|
||||||
scoped_identifier : NAME ('.' NAME)* ;
|
scoped_identifier : NAME ('.' NAME)* ;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user