From 96ba895b8427c5f5929959d253e6c14ce0b3c7fc Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 24 Feb 2022 23:35:16 +0100 Subject: [PATCH] working on altered Pipe syntax --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 9 ++- .../codegen/cpu6502/ExpressionsAsmGen.kt | 3 +- .../cpu6502/assignment/AssignmentAsmGen.kt | 2 +- .../prog8/optimizer/ExpressionSimplifier.kt | 54 +++++++------- .../compiler/astprocessing/AstChecker.kt | 46 ++++++------ .../compiler/astprocessing/AstPreprocessor.kt | 48 +++++++++++-- .../astprocessing/StatementReorderer.kt | 20 ------ .../astprocessing/VerifyFunctionArgTypes.kt | 4 +- compiler/test/TestPipes.kt | 72 ++++++++++--------- .../src/prog8/ast/AstToSourceTextConverter.kt | 11 +-- compilerAst/src/prog8/ast/AstToplevel.kt | 12 +++- .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 16 +++-- .../prog8/ast/expressions/AstExpressions.kt | 66 +++++------------ .../src/prog8/ast/statements/AstStatements.kt | 29 ++++---- compilerAst/src/prog8/ast/walk/AstWalker.kt | 6 +- compilerAst/src/prog8/ast/walk/IAstVisitor.kt | 6 +- .../src/prog8/compilerinterface/CallGraph.kt | 28 +------- examples/test.p8 | 15 ++-- parser/antlr/Prog8ANTLR.g4 | 7 +- 19 files changed, 219 insertions(+), 235 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index af761fc24..9c60c2a0e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -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, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) { + internal fun translatePipeExpression(source: Expression, segments: Iterable, 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?) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index c67e8529d..dded4d6fd 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -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") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 35851980b..50a8121ac 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -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 diff --git a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt index a355569ae..2082f2374 100644 --- a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt @@ -334,48 +334,44 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr } override fun after(pipeExpr: PipeExpression, parent: Node): Iterable { - 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(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 { - 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(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 } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 0c67d5ff8..fc7f074e7 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -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, scope: Node) { + private fun processPipe(source: Expression, segments: List, 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) } } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt index 98d6249d1..be6fb552c 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -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 { + return process(pipe, parent) + } + + override fun after(pipeExpr: PipeExpression, parent: Node): Iterable { + return process(pipeExpr, parent) + } + + private fun process(pipe: IPipe, parent: Node): Iterable { + // 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 + ) + } + } } diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 5064f59e4..acc7ac861 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -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 { - 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 - } } diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index 8c8d0a4f8..c6e0d4721 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -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) } diff --git a/compiler/test/TestPipes.kt b/compiler/test/TestPipes.kt index da78c0a0d..b34715652 100644 --- a/compiler/test/TestPipes.kt +++ b/compiler/test/TestPipes.kt @@ -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() - pipef.expressions[1] shouldBe instanceOf() + pipef.source shouldBe instanceOf() + pipef.segments.size shouldBe 2 + pipef.segments[0] shouldBe instanceOf() + pipef.segments[1] shouldBe instanceOf() val pipew = stmts[1] as Pipe - pipew.expressions.size shouldBe 2 - pipew.expressions[0] shouldBe instanceOf() - pipew.expressions[1] shouldBe instanceOf() + pipef.source shouldBe instanceOf() + pipew.segments.size shouldBe 2 + pipew.segments[0] shouldBe instanceOf() + pipew.segments[1] shouldBe instanceOf() 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() - pipef.expressions[1] shouldBe instanceOf() + pipef.source shouldBe instanceOf() + pipef.segments.size shouldBe 2 + pipef.segments[0] shouldBe instanceOf() + pipef.segments[1] shouldBe instanceOf() val assignw = stmts[3] as Assignment val pipew = assignw.value as PipeExpression - pipew.expressions.size shouldBe 2 - pipew.expressions[0] shouldBe instanceOf() - pipew.expressions[1] shouldBe instanceOf() + pipew.source shouldBe instanceOf() + pipew.segments.size shouldBe 2 + pipew.segments[0] shouldBe instanceOf() + pipew.segments[1] shouldBe instanceOf() 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() - pipecc.expressions[1] shouldBe instanceOf() + pipecc.source shouldBe instanceOf() + pipecc.segments.size shouldBe 2 + pipecc.segments[0] shouldBe instanceOf() + pipecc.segments[1] shouldBe instanceOf() } 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() - pipew.expressions[1] shouldBe instanceOf() + pipew.source shouldBe instanceOf() + pipew.segments.size shouldBe 2 + pipew.segments[0] shouldBe instanceOf() + pipew.segments[1] shouldBe instanceOf() 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() - pipef.expressions[1] shouldBe instanceOf() + pipef.source shouldBe instanceOf() + pipef.segments.size shouldBe 2 + pipef.segments[0] shouldBe instanceOf() + pipef.segments[1] shouldBe instanceOf() val pipew = stmts[5] as Pipe - pipew.expressions.size shouldBe 2 - pipew.expressions[0] shouldBe instanceOf() - pipew.expressions[1] shouldBe instanceOf() + pipew.source shouldBe instanceOf() + pipew.segments.size shouldBe 2 + pipew.segments[0] shouldBe instanceOf() + pipew.segments[1] shouldBe instanceOf() } test("pipe statement with type errors") { diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 41b6b99a2..25b80ca5c 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -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) { - expressions.first().accept(this) + private fun printPipe(source: Expression, segments: Iterable) { + source.accept(this) + segments.first().accept(this) outputln("") scopelevel++ - expressions.drop(1).forEach { + segments.drop(1).forEach { outputi("|> ") it.accept(this) outputln("") diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 4f46aa455..557d8eac0 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -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 + val position: Position + var parent: Node // will be linked correctly later (late init) +} + +interface IPipe { + var source: Expression + val segments: MutableList + val position: Position + var parent: Node // will be linked correctly later (late init) } interface IStatementContainer { diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index a01a7174a..50bf7c1ad 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -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() + ) } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index a6abb04b3..954f235a5 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -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, override val position: Position): Expression() { +class PipeExpression(override var source: Expression, + override val segments: MutableList, + 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) = + 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) = - expressions.any { it.referencesIdentifier(nameInSource) } - - override fun inferType(program: Program): InferredTypes.InferredType = inferType(program, expressions) - - private fun inferType(program: Program, functionNames: List): 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(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 + } } } diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index be4f7a5c2..cb72d4467 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -1022,35 +1022,30 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position } -class Pipe(val expressions: MutableList, override val position: Position): Statement() { +class Pipe(override var source: Expression, + override val segments: MutableList, + 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 + } } } diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index f5cb8e416..72158d364 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -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) } } diff --git a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt index e6d5414a4..fae5249ce 100644 --- a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt +++ b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt @@ -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) } } } diff --git a/compilerInterfaces/src/prog8/compilerinterface/CallGraph.kt b/compilerInterfaces/src/prog8/compilerinterface/CallGraph.kt index a4edf105c..7aac061a7 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/CallGraph.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/CallGraph.kt @@ -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, 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()) { diff --git a/examples/test.p8 b/examples/test.p8 index 9a89082f0..636f20447 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 { } diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index bd7a07eec..aad9b8f08 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -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 ;