mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
allow the last term in a pipe statement to be a variable, rewrites this as var = <rest of pipe>
This commit is contained in:
parent
60b2c44a44
commit
21e9723bb2
@ -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
|
||||
val functionName = expr as? IdentifierReference
|
||||
val function = functionName?.targetStatement(program)
|
||||
if(functionName!=null && function!=null) {
|
||||
when (function) {
|
||||
val target = functionName?.targetStatement(program)
|
||||
if(functionName!=null && target!=null) {
|
||||
when (target) {
|
||||
is BuiltinFunctionPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(function.name)
|
||||
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())
|
||||
@ -1307,17 +1307,21 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
is Subroutine -> {
|
||||
if(function.parameters.size!=1)
|
||||
if(target.parameters.size!=1)
|
||||
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)
|
||||
|
||||
val paramDt = function.parameters.firstOrNull()?.type
|
||||
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)
|
||||
|
||||
if(function.returntypes.isNotEmpty())
|
||||
valueDt = function.returntypes.single()
|
||||
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)
|
||||
}
|
||||
else -> {
|
||||
throw FatalAstException("weird function")
|
||||
|
@ -412,6 +412,26 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -142,12 +142,44 @@ class TestPipes: FunSpec({
|
||||
value.number shouldBe 194.0
|
||||
|
||||
assigncc = stmts[6] as Assignment
|
||||
val pipecc = assignw.value as PipeExpression
|
||||
val pipecc = assigncc.value as PipeExpression
|
||||
pipecc.expressions.size shouldBe 2
|
||||
pipecc.expressions[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||
pipecc.expressions[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
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") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
|
@ -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.
|
||||
|
||||
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::
|
||||
**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.
|
||||
|
||||
.. caution::
|
||||
|
@ -549,6 +549,15 @@ pipe: ``|>``
|
||||
|> determine_score
|
||||
|> 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: ``&``
|
||||
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``
|
||||
|
@ -3,7 +3,7 @@ TODO
|
||||
|
||||
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
|
||||
|
@ -3,13 +3,28 @@
|
||||
main {
|
||||
sub start() {
|
||||
ubyte xx = 30
|
||||
ubyte cc
|
||||
byte cc
|
||||
|
||||
; cc = 30 |> sin8u |> cos8u
|
||||
; cc=0
|
||||
; 30 |> sin8u |> cos8u |> cc
|
||||
; txt.print_ub(cc)
|
||||
; txt.nl()
|
||||
cc = xx |> sin8u |> cos8u
|
||||
txt.print_ub(cc)
|
||||
; cc=0
|
||||
; 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()
|
||||
|
||||
repeat {
|
||||
|
Loading…
Reference in New Issue
Block a user