working on altered Pipe syntax

This commit is contained in:
Irmen de Jong 2022-02-24 23:35:16 +01:00
parent df35dfe3bf
commit 96ba895b84
19 changed files with 219 additions and 235 deletions

View File

@ -337,7 +337,7 @@ class AsmGen(internal val program: Program,
is RepeatLoop -> translate(stmt)
is When -> translate(stmt)
is AnonymousScope -> translate(stmt)
is Pipe -> translatePipeExpression(stmt.expressions, stmt, true, false)
is Pipe -> translatePipeExpression(stmt.source, stmt.segments, stmt, isStatement = true, pushResultOnEstack = false)
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
@ -2837,10 +2837,14 @@ $repeatLabel lda $counterVar
}
}
internal fun translatePipeExpression(expressions: Iterable<Expression>, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) {
internal fun translatePipeExpression(source: Expression, segments: Iterable<FunctionCallExpression>, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) {
// TODO more efficient code generation to avoid needless assignments to the temp var
TODO("translatePipeExpression")
/*
// the first term: an expression (could be anything) producing a value.
val subroutine = scope.definingSubroutine!!
val firstTerm = expressions.first()
@ -2886,6 +2890,7 @@ $repeatLabel lda $counterVar
}
}
}
*/
}
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {

View File

@ -37,7 +37,8 @@ internal class ExpressionsAsmGen(private val program: Program,
is IdentifierReference -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PipeExpression -> asmgen.translatePipeExpression(expression.expressions, expression,false, true)
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 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")

View File

@ -283,7 +283,7 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterByte(assign.target, CpuRegister.A)
}
is PipeExpression -> {
asmgen.translatePipeExpression(value.expressions, value, false, false)
asmgen.translatePipeExpression(value.source, value.segments, value, false, false)
val resultDt = value.inferType(program)
val register =
if(resultDt.isBytes) RegisterOrPair.A

View File

@ -334,48 +334,44 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
override fun after(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
val expressions = pipeExpr.expressions
if(expressions.size==2 && expressions[0].isSimple) {
require(pipeExpr.segments.isNotEmpty())
val segments = pipeExpr.segments
if(segments.size==1 && segments[0].isSimple) {
// just replace with a normal function call
val funcname = expressions[1] as IdentifierReference
val arg = expressions[0]
val funcname = segments[1].target
val arg = segments[0]
val call = FunctionCallExpression(funcname.copy(), mutableListOf(arg), arg.position)
return listOf(IAstModification.ReplaceNode(pipeExpr, call, parent))
}
require(expressions.size>=2) { "pipe expression should have 2 or more parts" }
val firstValue = expressions.first()
val firstValue = pipeExpr.source
if(firstValue.isSimple) {
val funcname = expressions[1] as IdentifierReference
val funcname = pipeExpr.segments[0].target
val first = FunctionCallExpression(funcname.copy(), mutableListOf(firstValue), firstValue.position)
val newExprs = mutableListOf<Expression>(first)
newExprs.addAll(expressions.drop(2))
return listOf(IAstModification.ReplaceNode(pipeExpr, PipeExpression(newExprs, pipeExpr.position), parent))
}
val singleExpr = expressions.singleOrNull()
if(singleExpr!=null) {
val callExpr = singleExpr as FunctionCallExpression
val call = FunctionCallExpression(callExpr.target, callExpr.args, callExpr.position)
return listOf(IAstModification.ReplaceNode(pipeExpr, call, parent))
val newSegments = mutableListOf(first)
newSegments.addAll(pipeExpr.segments.drop(1))
return listOf(IAstModification.ReplaceNode(pipeExpr, PipeExpression(first, newSegments, pipeExpr.position), parent))
}
return noModifications
}
override fun after(pipe: Pipe, parent: Node): Iterable<IAstModification> {
val expressions = pipe.expressions
val firstValue = expressions.first()
if(firstValue.isSimple) {
val funcname = expressions[1] as IdentifierReference
val first = FunctionCallExpression(funcname.copy(), mutableListOf(firstValue), firstValue.position)
val newExprs = mutableListOf<Expression>(first)
newExprs.addAll(expressions.drop(2))
return listOf(IAstModification.ReplaceNode(pipe, Pipe(newExprs, pipe.position), parent))
}
val singleExpr = expressions.singleOrNull()
if(singleExpr!=null) {
val callExpr = singleExpr as FunctionCallExpression
val call = FunctionCallStatement(callExpr.target, callExpr.args, true, callExpr.position)
require(pipe.segments.isNotEmpty())
val segments = pipe.segments
if(segments.size==1 && segments[0].isSimple) {
// just replace with a normal function call
val funcname = segments[1].target
val arg = segments[0]
val call = FunctionCallExpression(funcname.copy(), mutableListOf(arg), arg.position)
return listOf(IAstModification.ReplaceNode(pipe, call, parent))
}
val firstValue = pipe.source
if(firstValue.isSimple) {
val funcname = pipe.segments[0].target
val first = FunctionCallExpression(funcname.copy(), mutableListOf(firstValue), firstValue.position)
val newSegments = mutableListOf(first)
newSegments.addAll(pipe.segments.drop(1))
return listOf(IAstModification.ReplaceNode(pipe, Pipe(first, newSegments, pipe.position), parent))
}
return noModifications
}

View File

@ -1244,9 +1244,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(pipe: PipeExpression) {
processPipe(pipe.expressions, pipe)
processPipe(pipe.source, pipe.segments, pipe)
if(errors.noErrors()) {
val last = pipe.expressions.last() as IdentifierReference
val last = pipe.segments.last().target
when (val target = last.targetStatement(program)!!) {
is BuiltinFunctionPlaceholder -> {
if (!BuiltinFunctions.getValue(target.name).hasReturn)
@ -1265,73 +1265,67 @@ internal class AstChecker(private val program: Program,
}
override fun visit(pipe: Pipe) {
processPipe(pipe.expressions, pipe)
processPipe(pipe.source, pipe.segments, pipe)
if(errors.noErrors()) {
super.visit(pipe)
}
}
private fun processPipe(expressions: List<Expression>, scope: Node) {
private fun processPipe(source: Expression, segments: List<FunctionCallExpression>, scope: Node) {
// first expression is just any expression producing a value
// all other expressions should be the name of a unary function that returns a single value
// the last expression should be the name of a unary function whose return value we don't care about.
if (expressions.size < 2) {
if (segments.isEmpty()) {
errors.err("pipe is missing one or more expressions", scope.position)
} else {
// invalid size and other issues will be handled by the ast checker later.
var valueDt = expressions[0].inferType(program).getOrElse {
throw FatalAstException("invalid dt ${expressions[0]} @ ${scope.position}")
var valueDt = source.inferType(program).getOrElse {
throw FatalAstException("invalid dt")
}
for(expr in expressions.drop(1)) { // just keep the first expression value as-is
val functionName = expr as? IdentifierReference
val target = functionName?.targetStatement(program)
if(functionName!=null && target!=null) {
for(funccall in segments) {
val target = funccall.target.targetStatement(program)
if(target!=null) {
when (target) {
is BuiltinFunctionPlaceholder -> {
val func = BuiltinFunctions.getValue(target.name)
if(func.parameters.size!=1)
errors.err("can only use unary function", expr.position)
else if(!func.hasReturn && expr !== expressions.last())
errors.err("function must return a single value", expr.position)
errors.err("can only use unary function", funccall.position)
else if(!func.hasReturn && 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 withfunction argument ${paramDts.toList()}", functionName.position)
errors.err("pipe value datatype $valueDt incompatible with function argument ${paramDts.toList()}", funccall.position)
if(errors.noErrors()) {
// type can depend on the argument(s) of the function. For now, we only deal with unary functions,
// so we know there must be a single argument. Take its type from the previous expression in the pipe chain.
val zero = defaultZero(valueDt, expr.position)
val zero = defaultZero(valueDt, funccall.position)
valueDt = builtinFunctionReturnType(func.name, listOf(zero), program).getOrElse { DataType.UNDEFINED }
}
}
is Subroutine -> {
if(target.parameters.size!=1)
errors.err("can only use unary function", expr.position)
else if(target.returntypes.size!=1 && expr !== expressions.last())
errors.err("function must return a single value", expr.position)
errors.err("can only use unary function", 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", functionName.position)
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", functionName.position)
errors.err("final pipe value datatype can't be stored in pipe ending variable", funccall.position)
}
else -> {
throw FatalAstException("weird function")
}
}
} else {
if(expr is IFunctionCall)
errors.err("use only the name of the function, not a call", expr.position)
else
errors.err("can only use unary function", expr.position)
}
}
}

View File

@ -1,14 +1,12 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.NumericDatatypes
import prog8.ast.base.SyntaxError
import prog8.ast.base.VarDeclType
import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.BuiltinFunctions
import prog8.compilerinterface.Encoding
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IErrorReporter
@ -113,4 +111,44 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
}
return noModifications
}
override fun after(pipe: Pipe, parent: Node): Iterable<IAstModification> {
return process(pipe, parent)
}
override fun after(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
return process(pipeExpr, parent)
}
private fun process(pipe: IPipe, parent: Node): Iterable<IAstModification> {
// add the "missing" first argument to each function call in the pipe segments
// so that all function call related checks just pass
// might have to remove it again when entering code generation pass, or just replace it there
// with the proper output value of the previous pipe segment.
return pipe.segments.map {
val firstArgDt = when (val target = it.target.targetStatement(program)) {
is Subroutine -> target.parameters.first().type
is BuiltinFunctionPlaceholder -> BuiltinFunctions.getValue(target.name).parameters.first().possibleDatatypes.first()
else -> DataType.UNDEFINED
}
val dummyFirstArg = when (firstArgDt) {
in IntegerDatatypes -> {
IdentifierReference(
getTempRegisterName(InferredTypes.InferredType.known(firstArgDt)),
pipe.position
)
}
DataType.FLOAT -> {
val (name, _) = program.getTempVar(DataType.FLOAT)
IdentifierReference(name, pipe.position)
}
else -> throw FatalAstException("weird dt")
}
IAstModification.SetExpression(
{ newexpr -> it.args.add(0, newexpr) },
dummyFirstArg, parent
)
}
}
}

View File

@ -412,26 +412,6 @@ internal class StatementReorderer(val program: Program,
checkUnusedReturnValues(functionCallStatement, function, program, errors)
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
override fun after(pipe: Pipe, parent: Node): Iterable<IAstModification> {
val last = pipe.expressions.lastOrNull() as? IdentifierReference
val variable = last?.targetVarDecl(program)
if(variable!=null) {
val target = AssignTarget(last, null, null, last.position)
if(pipe.expressions.size>2)
{
val value = PipeExpression(pipe.expressions.dropLast(1).toMutableList(), pipe.position)
val assign = Assignment(target, value, AssignmentOrigin.OPTIMIZER, pipe.position)
return listOf(IAstModification.ReplaceNode(pipe, assign, parent))
}
else if(pipe.expressions.size==2)
{
val assign = Assignment(target, pipe.expressions[0], AssignmentOrigin.OPTIMIZER, pipe.position)
return listOf(IAstModification.ReplaceNode(pipe, assign, parent))
}
}
return noModifications
}
}

View File

@ -50,7 +50,7 @@ internal class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
val target = call.target.targetStatement(program)
if (target is Subroutine) {
if(call.args.size != target.parameters.size)
return "invalid number of arguments"
return "invalid number of arguments (#1)" // TODO how does this relate to the same error in AstIdentifiersChecker
val paramtypes = target.parameters.map { it.type }
val mismatch = argtypes.zip(paramtypes).indexOfFirst { !argTypeCompatible(it.first, it.second) }
if(mismatch>=0) {
@ -79,7 +79,7 @@ internal class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
else if (target is BuiltinFunctionPlaceholder) {
val func = BuiltinFunctions.getValue(target.name)
if(call.args.size != func.parameters.size)
return "invalid number of arguments"
return "invalid number of arguments (#2)" // TODO how does this relate to the same error in AstIdentifiersChecker
val paramtypes = func.parameters.map { it.possibleDatatypes }
argtypes.zip(paramtypes).forEachIndexed { index, pair ->
val anyCompatible = pair.second.any { argTypeCompatible(pair.first, it) }

View File

@ -25,17 +25,17 @@ class TestPipes: FunSpec({
main {
sub start() {
1.234 |> addfloat
|> floats.print_f
1.234 |> addfloat()
|> floats.print_f()
9999 |> addword
|> txt.print_uw
9999 |> addword()
|> txt.print_uw()
; these are optimized into just the function calls:
9999 |> abs |> txt.print_uw
9999 |> txt.print_uw
99 |> abs |> txt.print_ub
99 |> txt.print_ub
9999 |> abs() |> txt.print_uw()
9999 |> txt.print_uw()
99 |> abs() |> txt.print_ub()
99 |> txt.print_ub()
}
sub addfloat(float fl) -> float {
@ -50,14 +50,16 @@ class TestPipes: FunSpec({
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 7
val pipef = stmts[0] as Pipe
pipef.expressions.size shouldBe 2
pipef.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
pipef.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipef.source shouldBe instanceOf<NumericLiteral>()
pipef.segments.size shouldBe 2
pipef.segments[0] shouldBe instanceOf<FunctionCallExpression>()
pipef.segments[1] shouldBe instanceOf<IdentifierReference>()
val pipew = stmts[1] as Pipe
pipew.expressions.size shouldBe 2
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipef.source shouldBe instanceOf<NumericLiteral>()
pipew.segments.size shouldBe 2
pipew.segments[0] shouldBe instanceOf<FunctionCallExpression>()
pipew.segments[1] shouldBe instanceOf<IdentifierReference>()
var stmt = stmts[2] as FunctionCallStatement
stmt.target.nameInSource shouldBe listOf("txt", "print_uw")
@ -127,15 +129,17 @@ class TestPipes: FunSpec({
stmts.size shouldBe 8
val assignf = stmts[1] as Assignment
val pipef = assignf.value as PipeExpression
pipef.expressions.size shouldBe 2
pipef.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
pipef.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipef.source shouldBe instanceOf<NumericLiteral>()
pipef.segments.size shouldBe 2
pipef.segments[0] shouldBe instanceOf<FunctionCallExpression>()
pipef.segments[1] shouldBe instanceOf<IdentifierReference>()
val assignw = stmts[3] as Assignment
val pipew = assignw.value as PipeExpression
pipew.expressions.size shouldBe 2
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipew.source shouldBe instanceOf<NumericLiteral>()
pipew.segments.size shouldBe 2
pipew.segments[0] shouldBe instanceOf<FunctionCallExpression>()
pipew.segments[1] shouldBe instanceOf<IdentifierReference>()
var assigncc = stmts[5] as Assignment
val value = assigncc.value as NumericLiteral
@ -143,9 +147,10 @@ class TestPipes: FunSpec({
assigncc = stmts[6] as Assignment
val pipecc = assigncc.value as PipeExpression
pipecc.expressions.size shouldBe 2
pipecc.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipecc.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipecc.source shouldBe instanceOf<NumericLiteral>()
pipecc.segments.size shouldBe 2
pipecc.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipecc.segments[1] shouldBe instanceOf<IdentifierReference>()
}
test("correct pipe expressions with variables at end") {
@ -171,9 +176,10 @@ class TestPipes: FunSpec({
val assignw = stmts[4] as Assignment
val pipew = assignw.value as PipeExpression
pipew.expressions.size shouldBe 2
pipew.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipew.source shouldBe instanceOf<NumericLiteral>()
pipew.segments.size shouldBe 2
pipew.segments[0] shouldBe instanceOf<FunctionCallExpression>()
pipew.segments[1] shouldBe instanceOf<IdentifierReference>()
val assigncc = stmts[5] as Assignment
val value = assigncc.value as NumericLiteral
@ -221,14 +227,16 @@ class TestPipes: FunSpec({
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 7
val pipef = stmts[4] as Pipe
pipef.expressions.size shouldBe 2
pipef.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipef.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipef.source shouldBe instanceOf<NumericLiteral>()
pipef.segments.size shouldBe 2
pipef.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipef.segments[1] shouldBe instanceOf<IdentifierReference>()
val pipew = stmts[5] as Pipe
pipew.expressions.size shouldBe 2
pipew.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipew.expressions[1] shouldBe instanceOf<IdentifierReference>()
pipew.source shouldBe instanceOf<NumericLiteral>()
pipew.segments.size shouldBe 2
pipew.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipew.segments[1] shouldBe instanceOf<IdentifierReference>()
}
test("pipe statement with type errors") {

View File

@ -471,18 +471,19 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
override fun visit(pipe: Pipe) {
printPipe(pipe.expressions)
printPipe(pipe.source, pipe.segments)
}
override fun visit(pipe: PipeExpression) {
printPipe(pipe.expressions)
printPipe(pipe.source, pipe.segments)
}
private fun printPipe(expressions: Iterable<Expression>) {
expressions.first().accept(this)
private fun printPipe(source: Expression, segments: Iterable<FunctionCallExpression>) {
source.accept(this)
segments.first().accept(this)
outputln("")
scopelevel++
expressions.drop(1).forEach {
segments.drop(1).forEach {
outputi("|> ")
it.accept(this)
outputln("")

View File

@ -4,8 +4,7 @@ import prog8.ast.base.FatalAstException
import prog8.ast.base.ParentSentinel
import prog8.ast.base.Position
import prog8.ast.base.findParentNode
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
@ -17,6 +16,15 @@ const val internedStringsModuleName = "prog8_interned_strings"
interface IFunctionCall {
var target: IdentifierReference
var args: MutableList<Expression>
val position: Position
var parent: Node // will be linked correctly later (late init)
}
interface IPipe {
var source: Expression
val segments: MutableList<FunctionCallExpression>
val position: Position
var parent: Node // will be linked correctly later (late init)
}
interface IStatementContainer {

View File

@ -443,8 +443,12 @@ private fun Prog8ANTLRParser.ExpressionContext.toAst() : Expression {
if(addressof()!=null)
return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
if(pipe!=null)
return PipeExpression(pipesource.toAst(), pipetarget.toAst(), toPosition())
if(pipesegment()!=null)
return PipeExpression(
expression(0).toAst(),
pipesegment().map { it.functioncall().toAst() }.toMutableList(),
toPosition()
)
throw FatalAstException(text)
}
@ -602,7 +606,9 @@ private fun Prog8ANTLRParser.VardeclContext.toAst(type: VarDeclType, value: Expr
}
private fun Prog8ANTLRParser.PipestmtContext.toAst(): Pipe {
val source = this.source.toAst()
val target = this.target.toAst()
return Pipe(source, target, toPosition())
return Pipe(
expression().toAst(),
pipesegment().map { it.functioncall().toAst() }.toMutableList(),
toPosition()
)
}

View File

@ -1,11 +1,8 @@
package prog8.ast.expressions
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.*
import prog8.ast.antlr.escape
import prog8.ast.base.*
import prog8.ast.internedStringsModuleName
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
@ -1077,65 +1074,34 @@ class ContainmentCheck(var element: Expression,
}
}
class PipeExpression(val expressions: MutableList<Expression>, override val position: Position): Expression() {
class PipeExpression(override var source: Expression,
override val segments: MutableList<FunctionCallExpression>,
override val position: Position): Expression(), IPipe {
override lateinit var parent: Node
constructor(source: Expression, target: Expression, position: Position) : this(mutableListOf(), position) {
if(source is PipeExpression) {
expressions.addAll(source.expressions)
expressions.add(target)
} else {
expressions.add(source)
expressions.add(target)
}
}
override val isSimple = false
override fun linkParents(parent: Node) {
this.parent=parent
expressions.forEach { it.linkParents(this) }
source.linkParents(this)
segments.forEach { it.linkParents(this) }
}
override fun copy(): PipeExpression = PipeExpression(expressions.map {it.copy()}.toMutableList(), position)
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 referencesIdentifier(nameInSource: List<String>) =
expressions.any { it.referencesIdentifier(nameInSource) }
override fun inferType(program: Program): InferredTypes.InferredType = inferType(program, expressions)
private fun inferType(program: Program, functionNames: List<Expression>): InferredTypes.InferredType {
val identifier = functionNames.last() as? IdentifierReference
if(identifier!=null) {
when(val target = identifier.targetStatement(program)) {
is BuiltinFunctionPlaceholder -> {
val typeOfPrev = inferType(program, functionNames.dropLast(1))
return if(typeOfPrev.isKnown) {
val zero = defaultZero(typeOfPrev.getOr(DataType.UNDEFINED), identifier.position)
val args = mutableListOf<Expression>(zero)
program.builtinFunctions.returnType(identifier.nameInSource[0], args)
} else {
InferredTypes.InferredType.unknown()
}
}
is Subroutine -> {
val call = FunctionCallExpression(identifier, mutableListOf(), identifier.position)
return call.inferType(program)
}
is VarDecl -> {
return InferredTypes.InferredType.known(target.datatype)
}
else -> return InferredTypes.InferredType.unknown()
}
}
return functionNames.last().inferType(program)
}
override fun inferType(program: Program) = segments.last().inferType(program)
override fun replaceChildNode(node: Node, replacement: Node) {
require(node is Expression)
require(replacement is Expression)
val idx = expressions.indexOf(node)
expressions[idx] = replacement
if(node===source) {
source = replacement
} else {
val idx = segments.indexOf(node)
segments[idx] = replacement as FunctionCallExpression
}
}
}

View File

@ -1022,35 +1022,30 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
}
class Pipe(val expressions: MutableList<Expression>, override val position: Position): Statement() {
class Pipe(override var source: Expression,
override val segments: MutableList<FunctionCallExpression>,
override val position: Position): Statement(), IPipe {
override lateinit var parent: Node
constructor(source: Expression, target: Expression, position: Position) : this(mutableListOf(), position) {
if(source is PipeExpression)
expressions.addAll(source.expressions)
else
expressions.add(source)
if(target is PipeExpression)
expressions.addAll(target.expressions)
else
expressions.add(target)
}
override fun linkParents(parent: Node) {
this.parent = parent
expressions.forEach { it.linkParents(this) }
source.linkParents(this)
segments.forEach { it.linkParents(this) }
}
override fun copy() = Pipe(expressions.map { it.copy() }.toMutableList(), position)
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)
val idx = expressions.indexOf(node)
expressions[idx] = replacement
if(node===source) {
source = replacement
} else {
val idx = segments.indexOf(node)
segments[idx] = replacement as FunctionCallExpression
}
}
}

View File

@ -473,13 +473,15 @@ abstract class AstWalker {
fun visit(pipe: Pipe, parent: Node) {
track(before(pipe, parent), pipe, parent)
pipe.expressions.forEach { it.accept(this, pipe) }
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.expressions.forEach { it.accept(this, pipe) }
pipe.source.accept(this, pipe)
pipe.segments.forEach { it.accept(this, pipe) }
track(after(pipe, parent), pipe, parent)
}
}

View File

@ -189,10 +189,12 @@ interface IAstVisitor {
}
fun visit(pipe: Pipe) {
pipe.expressions.forEach { it.accept(this) }
pipe.source.accept(this)
pipe.segments.forEach { it.accept(this) }
}
fun visit(pipe: PipeExpression) {
pipe.expressions.forEach { it.accept(this) }
pipe.source.accept(this)
pipe.segments.forEach { it.accept(this) }
}
}

View File

@ -5,7 +5,9 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
@ -126,30 +128,6 @@ class CallGraph(private val program: Program, private val allowMissingIdentifier
allAssemblyNodes.add(inlineAssembly)
}
override fun visit(pipe: PipeExpression) {
processPipe(pipe.expressions, pipe)
super.visit(pipe)
}
override fun visit(pipe: Pipe) {
processPipe(pipe.expressions, pipe)
super.visit(pipe)
}
private fun processPipe(expressions: Iterable<Expression>, pipe: Node) {
expressions.forEach {
if(it is IdentifierReference){
val otherSub = it.targetSubroutine(program)
if(otherSub!=null) {
pipe.definingSubroutine?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub) + otherSub
calledBy[otherSub] = calledBy.getValue(otherSub) + pipe
}
}
}
}
}
fun checkRecursiveCalls(errors: IErrorReporter) {
val cycles = recursionCycles()
if(cycles.any()) {

View File

@ -2,14 +2,17 @@
main {
sub start() {
ubyte xx = 30
ubyte cc
for cc in 32 to 124 {
txt.chrout(cc)
}
txt.waitkey()
txt.clear_screen()
txt.print("\nHello!\nWorld\n")
cc=0
cc = 30 |> sin8u |> cos8u |> cc
txt.print_ub(cc)
txt.nl()
cc=0
cc = xx |> sin8u |> cos8u |> cc
txt.print_ub(cc)
txt.nl()
repeat {
}

View File

@ -183,10 +183,9 @@ expression :
| directmemory
| addressof
| expression typecast
| pipesource = expression EOL? pipe=PIPE EOL? pipetarget = expression
| expression pipesegment+
;
typecast : 'as' datatype;
arrayindexed : scoped_identifier arrayindex ;
@ -208,7 +207,9 @@ returnstmt : 'return' expression? ;
breakstmt : 'break';
pipestmt: source=expression pipe=PIPE EOL? target=expression ;
pipestmt : expression pipesegment+;
pipesegment : EOL? PIPE functioncall ;
identifier : NAME ;