Pipe expression "|>" removed from the language

This commit is contained in:
Irmen de Jong 2022-06-12 18:41:42 +02:00
parent dca092fd7c
commit 5a756aaed9
32 changed files with 44 additions and 1176 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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,10 +223,6 @@ 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)
// 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") 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)
@ -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)

View File

@ -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()) {

View File

@ -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

View File

@ -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>()

View File

@ -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)
} }
} }

View File

@ -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")
}
}
}
}
}
} }

View File

@ -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"
}
})

View File

@ -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()
} }

View File

@ -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--
}
} }

View File

@ -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>

View File

@ -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()
)
}

View File

@ -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) {

View File

@ -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.

View File

@ -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)
}
} }

View File

@ -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) }
}
} }

View File

@ -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).

View File

@ -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:**

View File

@ -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``

View File

@ -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

View File

@ -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":

View File

@ -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)* ;