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
|
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")
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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::
|
||||||
|
@ -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``
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user