allow the last term in a pipe statement to be a variable, rewrites this as var = <rest of pipe>

This commit is contained in:
Irmen de Jong 2022-02-21 02:31:34 +01:00
parent 60b2c44a44
commit 21e9723bb2
7 changed files with 99 additions and 18 deletions

View File

@ -1285,11 +1285,11 @@ internal class AstChecker(private val program: Program,
for(expr in expressions.drop(1)) { // just keep the first expression value as-is for(expr in expressions.drop(1)) { // just keep the first expression value as-is
val functionName = expr as? IdentifierReference val functionName = expr as? IdentifierReference
val function = functionName?.targetStatement(program) val target = functionName?.targetStatement(program)
if(functionName!=null && function!=null) { if(functionName!=null && target!=null) {
when (function) { when (target) {
is BuiltinFunctionPlaceholder -> { is BuiltinFunctionPlaceholder -> {
val func = BuiltinFunctions.getValue(function.name) val func = BuiltinFunctions.getValue(target.name)
if(func.parameters.size!=1) if(func.parameters.size!=1)
errors.err("can only use unary function", expr.position) errors.err("can only use unary function", expr.position)
else if(!func.hasReturn && expr !== expressions.last()) else if(!func.hasReturn && expr !== expressions.last())
@ -1307,17 +1307,21 @@ internal class AstChecker(private val program: Program,
} }
} }
is Subroutine -> { is Subroutine -> {
if(function.parameters.size!=1) if(target.parameters.size!=1)
errors.err("can only use unary function", expr.position) errors.err("can only use unary function", expr.position)
else if(function.returntypes.size!=1 && expr !== expressions.last()) else if(target.returntypes.size!=1 && expr !== expressions.last())
errors.err("function must return a single value", expr.position) errors.err("function must return a single value", expr.position)
val paramDt = function.parameters.firstOrNull()?.type val paramDt = target.parameters.firstOrNull()?.type
if(paramDt!=null && !(valueDt isAssignableTo paramDt)) 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", functionName.position)
if(function.returntypes.isNotEmpty()) if(target.returntypes.isNotEmpty())
valueDt = function.returntypes.single() 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)
} }
else -> { else -> {
throw FatalAstException("weird function") throw FatalAstException("weird function")

View File

@ -412,6 +412,26 @@ internal class StatementReorderer(val program: Program,
checkUnusedReturnValues(functionCallStatement, function, program, errors) checkUnusedReturnValues(functionCallStatement, function, program, errors)
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) 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

@ -142,12 +142,44 @@ class TestPipes: FunSpec({
value.number shouldBe 194.0 value.number shouldBe 194.0
assigncc = stmts[6] as Assignment assigncc = stmts[6] as Assignment
val pipecc = assignw.value as PipeExpression val pipecc = assigncc.value as PipeExpression
pipecc.expressions.size shouldBe 2 pipecc.expressions.size shouldBe 2
pipecc.expressions[0] shouldBe instanceOf<FunctionCallExpression>() pipecc.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
pipecc.expressions[1] shouldBe instanceOf<IdentifierReference>() pipecc.expressions[1] shouldBe instanceOf<IdentifierReference>()
} }
test("correct pipe expressions with variables at end") {
val text = """
%import textio
main {
sub start() {
uword @shared ww
ubyte @shared cc
9999 |> addword |> addword |> ww
30 |> sin8u |> cos8u |> cc ; will be optimized away into a const number
}
sub addword(uword ww) -> uword {
return ww+2222
}
}
"""
val result = compileText(C64Target(), true, text, writeAssembly = true).assertSuccess()
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 7
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>()
val assigncc = stmts[5] as Assignment
val value = assigncc.value as NumericLiteral
value.number shouldBe 194.0
}
test("incorrect type in pipe expression") { test("incorrect type in pipe expression") {
val text = """ val text = """
%option enable_floats %option enable_floats

View File

@ -701,12 +701,13 @@ If you want to ignore a return value of a subroutine, you should prefix the call
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 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). 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:**
The order of evaluation of arguments is *unspecified* and should not be relied upon. The order of evaluation of arguments to a single function call is *unspecified* and should not be relied upon.
There is no guarantee of a left-to-right or right-to-left evaluation of the call arguments. There is no guarantee of a left-to-right or right-to-left evaluation of the call arguments.
.. caution:: .. caution::

View File

@ -549,6 +549,15 @@ pipe: ``|>``
|> determine_score |> determine_score
|> add_bonus |> add_bonus
Finally, if you like the left-to-right flow, it's possible to use the name of a variable as the last term. This just means that the pipe's resulting value is
stored in that variable (it's just another way of writing an assignment). So the above can also be written as::
uword score
get_player(1)
|> determine_score
|> add_bonus
|> score
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,7 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- allow the last term in a pipe *statement* to be a variable, rewrite this as var = <rest of pipe> - sin8u(30) is different from sin8u(xx) where xx=30, so the const eval is off. Also test/fix other builtin functions!
Need help with Need help with

View File

@ -3,13 +3,28 @@
main { main {
sub start() { sub start() {
ubyte xx = 30 ubyte xx = 30
ubyte cc byte cc
; cc = 30 |> sin8u |> cos8u ; cc=0
; 30 |> sin8u |> cos8u |> cc
; txt.print_ub(cc) ; txt.print_ub(cc)
; txt.nl() ; txt.nl()
cc = xx |> sin8u |> cos8u ; cc=0
txt.print_ub(cc) ; xx |> sin8u |> cos8u |> cc
; txt.print_ub(cc)
; txt.nl()
100 |> cc
txt.print_b(cc)
txt.nl()
-100 |> abs |> abs |> cc
txt.print_b(cc)
txt.nl()
cc |> abs |> abs |> cc
txt.print_b(cc)
txt.nl()
cc = -100
cc |> abs |> abs |> cc
txt.print_b(cc)
txt.nl() txt.nl()
repeat { repeat {