mirror of
https://github.com/irmen/prog8.git
synced 2025-02-25 20:29:04 +00:00
working on altered Pipe syntax
This commit is contained in:
parent
df35dfe3bf
commit
96ba895b84
@ -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?) {
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) }
|
||||
|
@ -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") {
|
||||
|
@ -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("")
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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 {
|
||||
}
|
||||
|
@ -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 ;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user