mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-11 12:29:46 +00:00
Added break and continue statements
This commit is contained in:
parent
ccb6e35a29
commit
50ddd52786
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
* Added return dispatch statements.
|
* Added return dispatch statements.
|
||||||
|
|
||||||
|
* Added `break` and `continue` statements.
|
||||||
|
|
||||||
* Added octal and quaternary literals.
|
* Added octal and quaternary literals.
|
||||||
|
|
||||||
* Fixed several optimization bugs.
|
* Fixed several optimization bugs.
|
||||||
|
@ -14,6 +14,7 @@ object OptimizationPresets {
|
|||||||
UnusedGlobalVariables,
|
UnusedGlobalVariables,
|
||||||
)
|
)
|
||||||
val AssOpt: List[AssemblyOptimization] = List[AssemblyOptimization](
|
val AssOpt: List[AssemblyOptimization] = List[AssemblyOptimization](
|
||||||
|
UnusedLabelRemoval,
|
||||||
AlwaysGoodOptimizations.NonetAddition,
|
AlwaysGoodOptimizations.NonetAddition,
|
||||||
AlwaysGoodOptimizations.PointlessSignCheck,
|
AlwaysGoodOptimizations.PointlessSignCheck,
|
||||||
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
||||||
@ -125,6 +126,7 @@ object OptimizationPresets {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val Good: List[AssemblyOptimization] = List[AssemblyOptimization](
|
val Good: List[AssemblyOptimization] = List[AssemblyOptimization](
|
||||||
|
UnusedLabelRemoval,
|
||||||
AlwaysGoodOptimizations.Adc0Optimization,
|
AlwaysGoodOptimizations.Adc0Optimization,
|
||||||
AlwaysGoodOptimizations.BitPackingUnpacking,
|
AlwaysGoodOptimizations.BitPackingUnpacking,
|
||||||
AlwaysGoodOptimizations.BranchInPlaceRemoval,
|
AlwaysGoodOptimizations.BranchInPlaceRemoval,
|
||||||
@ -185,6 +187,7 @@ object OptimizationPresets {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val QuickPreset: List[AssemblyOptimization] = List[AssemblyOptimization](
|
val QuickPreset: List[AssemblyOptimization] = List[AssemblyOptimization](
|
||||||
|
UnusedLabelRemoval,
|
||||||
AlwaysGoodOptimizations.Adc0Optimization,
|
AlwaysGoodOptimizations.Adc0Optimization,
|
||||||
AlwaysGoodOptimizations.BranchInPlaceRemoval,
|
AlwaysGoodOptimizations.BranchInPlaceRemoval,
|
||||||
AlwaysGoodOptimizations.CommonBranchBodyOptimization,
|
AlwaysGoodOptimizations.CommonBranchBodyOptimization,
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
package millfork.compiler
|
package millfork.compiler
|
||||||
|
|
||||||
import millfork.{CompilationFlag, CompilationOptions}
|
import millfork.{CompilationFlag, CompilationOptions}
|
||||||
import millfork.env.{Environment, MangledFunction, NormalFunction}
|
import millfork.env.{Environment, Label, NormalFunction}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
case class CompilationContext(env: Environment, function: NormalFunction, extraStackOffset: Int, options: CompilationOptions){
|
case class CompilationContext(env: Environment,
|
||||||
|
function: NormalFunction,
|
||||||
|
extraStackOffset: Int,
|
||||||
|
options: CompilationOptions,
|
||||||
|
breakLabels: Map[String, Label] = Map(),
|
||||||
|
continueLabels: Map[String, Label] = Map()){
|
||||||
def withInlinedEnv(environment: Environment): CompilationContext = {
|
def withInlinedEnv(environment: Environment): CompilationContext = {
|
||||||
val newEnv = new Environment(Some(env), MfCompiler.nextLabel("en"))
|
val newEnv = new Environment(Some(env), MfCompiler.nextLabel("en"))
|
||||||
newEnv.things ++= environment.things
|
newEnv.things ++= environment.things
|
||||||
copy(env = newEnv)
|
copy(env = newEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def addLabels(names: Set[String], breakLabel: Label, continueLabel: Label): CompilationContext = {
|
||||||
|
var b = breakLabels
|
||||||
|
var c = continueLabels
|
||||||
|
names.foreach{name =>
|
||||||
|
b += (name -> breakLabel)
|
||||||
|
c += (name -> continueLabel)
|
||||||
|
}
|
||||||
|
this.copy(breakLabels = b, continueLabels = c)
|
||||||
|
}
|
||||||
|
|
||||||
def addStack(i: Int): CompilationContext = this.copy(extraStackOffset = extraStackOffset + i)
|
def addStack(i: Int): CompilationContext = this.copy(extraStackOffset = extraStackOffset + i)
|
||||||
|
|
||||||
|
@ -29,13 +29,15 @@ object MacroExpander {
|
|||||||
case ReturnDispatchStatement(i,ps, bs) => ReturnDispatchStatement(i.replaceVariable(paramName, target), ps.map(fx), bs.map{
|
case ReturnDispatchStatement(i,ps, bs) => ReturnDispatchStatement(i.replaceVariable(paramName, target), ps.map(fx), bs.map{
|
||||||
case ReturnDispatchBranch(l, fu, pps) => ReturnDispatchBranch(l, f(fu), pps.map(f))
|
case ReturnDispatchBranch(l, fu, pps) => ReturnDispatchBranch(l, f(fu), pps.map(f))
|
||||||
})
|
})
|
||||||
case WhileStatement(c, b) => WhileStatement(f(c), b.map(gx))
|
case WhileStatement(c, b, i, n) => WhileStatement(f(c), b.map(gx), i.map(gx), n)
|
||||||
case DoWhileStatement(b, c) => DoWhileStatement(b.map(gx), f(c))
|
case DoWhileStatement(b, i, c, n) => DoWhileStatement(b.map(gx), i.map(gx), f(c), n)
|
||||||
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
|
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
|
||||||
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
|
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
|
||||||
case s:AssemblyStatement => s.copy(expression = f(s.expression))
|
case s:AssemblyStatement => s.copy(expression = f(s.expression))
|
||||||
case Assignment(d,s) => Assignment(fx(d), f(s))
|
case Assignment(d,s) => Assignment(fx(d), f(s))
|
||||||
case BlockStatement(s) => BlockStatement(s.map(gx))
|
case BlockStatement(s) => BlockStatement(s.map(gx))
|
||||||
|
case BreakStatement(s) => if (s == paramName) BreakStatement(target.toString) else stmt
|
||||||
|
case ContinueStatement(s) => if (s == paramName) ContinueStatement(target.toString) else stmt
|
||||||
case _ =>
|
case _ =>
|
||||||
println(stmt)
|
println(stmt)
|
||||||
???
|
???
|
||||||
|
@ -111,6 +111,11 @@ object StatementCompiler {
|
|||||||
ExpressionCompiler.compile(ctx, e, None, NoBranching)
|
ExpressionCompiler.compile(ctx, e, None, NoBranching)
|
||||||
}
|
}
|
||||||
case ExpressionStatement(e) =>
|
case ExpressionStatement(e) =>
|
||||||
|
e match {
|
||||||
|
case VariableExpression(_) | LiteralExpression(_, _) =>
|
||||||
|
ErrorReporting.warn("Pointless expression statement", ctx.options, statement.position)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
ExpressionCompiler.compile(ctx, e, None, NoBranching)
|
ExpressionCompiler.compile(ctx, e, None, NoBranching)
|
||||||
case BlockStatement(s) =>
|
case BlockStatement(s) =>
|
||||||
s.flatMap(compile(ctx, _))
|
s.flatMap(compile(ctx, _))
|
||||||
@ -227,74 +232,67 @@ object StatementCompiler {
|
|||||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
case WhileStatement(condition, bodyPart) =>
|
case WhileStatement(condition, bodyPart, incrementPart, labels) =>
|
||||||
|
val start = MfCompiler.nextLabel("wh")
|
||||||
|
val middle = MfCompiler.nextLabel("he")
|
||||||
|
val inc = MfCompiler.nextLabel("fp")
|
||||||
|
val end = MfCompiler.nextLabel("ew")
|
||||||
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
|
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
|
||||||
val bodyBlock = compile(ctx, bodyPart)
|
val bodyBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), bodyPart)
|
||||||
|
val incrementBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), incrementPart)
|
||||||
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum > 100
|
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum > 100
|
||||||
condType match {
|
condType match {
|
||||||
case ConstantBooleanType(_, true) =>
|
case ConstantBooleanType(_, true) =>
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
val start = MfCompiler.nextLabel("wh")
|
|
||||||
List(labelChunk(start), bodyBlock, jmpChunk(start)).flatten
|
|
||||||
case ConstantBooleanType(_, false) => Nil
|
case ConstantBooleanType(_, false) => Nil
|
||||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||||
if (largeBodyBlock) {
|
if (largeBodyBlock) {
|
||||||
val start = MfCompiler.nextLabel("wh")
|
|
||||||
val middle = MfCompiler.nextLabel("he")
|
|
||||||
val end = MfCompiler.nextLabel("ew")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||||
List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), bodyBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
} else {
|
} else {
|
||||||
val start = MfCompiler.nextLabel("wh")
|
|
||||||
val end = MfCompiler.nextLabel("ew")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||||
List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
}
|
}
|
||||||
case BuiltInBooleanType =>
|
case BuiltInBooleanType =>
|
||||||
if (largeBodyBlock) {
|
if (largeBodyBlock) {
|
||||||
val start = MfCompiler.nextLabel("wh")
|
|
||||||
val middle = MfCompiler.nextLabel("he")
|
|
||||||
val end = MfCompiler.nextLabel("ew")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
|
||||||
List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
} else {
|
} else {
|
||||||
val start = MfCompiler.nextLabel("wh")
|
|
||||||
val end = MfCompiler.nextLabel("ew")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
|
||||||
List(labelChunk(start), conditionBlock, bodyBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), conditionBlock, bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
case DoWhileStatement(bodyPart, condition) =>
|
case DoWhileStatement(bodyPart, incrementPart, condition, labels) =>
|
||||||
|
val start = MfCompiler.nextLabel("do")
|
||||||
|
val inc = MfCompiler.nextLabel("fp")
|
||||||
|
val end = MfCompiler.nextLabel("od")
|
||||||
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
|
val condType = ExpressionCompiler.getExpressionType(ctx, condition)
|
||||||
val bodyBlock = compile(ctx, bodyPart)
|
val bodyBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), bodyPart)
|
||||||
|
val incrementBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), incrementPart)
|
||||||
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum > 100
|
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum > 100
|
||||||
condType match {
|
condType match {
|
||||||
case ConstantBooleanType(_, true) =>
|
case ConstantBooleanType(_, true) =>
|
||||||
val start = MfCompiler.nextLabel("do")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||||
List(labelChunk(start), bodyBlock, jmpChunk(start)).flatten
|
List(labelChunk(start),bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
case ConstantBooleanType(_, false) => bodyBlock
|
case ConstantBooleanType(_, false) =>
|
||||||
|
List(bodyBlock, labelChunk(inc), incrementBlock, labelChunk(end)).flatten
|
||||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||||
val start = MfCompiler.nextLabel("do")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||||
if (largeBodyBlock) {
|
if (largeBodyBlock) {
|
||||||
val end = MfCompiler.nextLabel("od")
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfFalse, end), jmpChunk(start), labelChunk(end)).flatten
|
||||||
List(labelChunk(start), bodyBlock, conditionBlock, branchChunk(jumpIfFalse, end), jmpChunk(start), labelChunk(end)).flatten
|
|
||||||
} else {
|
} else {
|
||||||
List(labelChunk(start), bodyBlock, conditionBlock, branchChunk(jumpIfTrue, start)).flatten
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
|
||||||
}
|
}
|
||||||
case BuiltInBooleanType =>
|
case BuiltInBooleanType =>
|
||||||
val start = MfCompiler.nextLabel("do")
|
|
||||||
if (largeBodyBlock) {
|
if (largeBodyBlock) {
|
||||||
val end = MfCompiler.nextLabel("od")
|
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
|
||||||
List(labelChunk(start), bodyBlock, conditionBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
} else {
|
} else {
|
||||||
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
|
val conditionBlock = ExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
|
||||||
List(labelChunk(start), bodyBlock, conditionBlock).flatten
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, labelChunk(end)).flatten
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
||||||
@ -307,32 +305,36 @@ object StatementCompiler {
|
|||||||
val one = LiteralExpression(1, 1)
|
val one = LiteralExpression(1, 1)
|
||||||
val increment = ExpressionStatement(FunctionCallExpression("+=", List(vex, one)))
|
val increment = ExpressionStatement(FunctionCallExpression("+=", List(vex, one)))
|
||||||
val decrement = ExpressionStatement(FunctionCallExpression("-=", List(vex, one)))
|
val decrement = ExpressionStatement(FunctionCallExpression("-=", List(vex, one)))
|
||||||
|
val names = Set("", "for", variable)
|
||||||
(direction, env.eval(start), env.eval(end)) match {
|
(direction, env.eval(start), env.eval(end)) match {
|
||||||
|
|
||||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
|
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
|
||||||
compile(ctx, Assignment(vex, f.start) :: f.body)
|
val end = MfCompiler.nextLabel("of")
|
||||||
|
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
|
||||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
|
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
|
||||||
Nil
|
Nil
|
||||||
|
|
||||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
|
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
|
||||||
compile(ctx, Assignment(vex, f.start) :: f.body)
|
val end = MfCompiler.nextLabel("of")
|
||||||
|
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
|
||||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
|
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
|
||||||
Nil
|
Nil
|
||||||
|
|
||||||
case (ForDirection.ParallelUntil, Some(NumericConstant(0, ssize)), Some(NumericConstant(e, _))) if e > 0 =>
|
case (ForDirection.ParallelUntil, Some(NumericConstant(0, ssize)), Some(NumericConstant(e, _))) if e > 0 =>
|
||||||
compile(ctx, List(
|
compile(ctx, List(
|
||||||
Assignment(vex, f.end),
|
Assignment(vex, f.end),
|
||||||
DoWhileStatement(decrement :: f.body, FunctionCallExpression("!=", List(vex, f.start)))
|
DoWhileStatement(Nil, decrement :: f.body, FunctionCallExpression("!=", List(vex, f.start)), names)
|
||||||
))
|
))
|
||||||
|
|
||||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
|
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
|
||||||
compile(ctx, Assignment(vex, LiteralExpression(s, ssize)) :: f.body)
|
val end = MfCompiler.nextLabel("of")
|
||||||
|
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, LiteralExpression(s, ssize)) :: f.body) ++ labelChunk(end)
|
||||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
|
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
|
||||||
Nil
|
Nil
|
||||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(0, esize))) if s > 0 =>
|
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(0, esize))) if s > 0 =>
|
||||||
compile(ctx, List(
|
compile(ctx, List(
|
||||||
Assignment(vex, f.start),
|
Assignment(vex, f.start),
|
||||||
DoWhileStatement(f.body :+ decrement, FunctionCallExpression("!=", List(vex, f.end)))
|
DoWhileStatement(f.body, List(decrement), FunctionCallExpression("!=", List(vex, f.end)), names)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
@ -341,7 +343,7 @@ object StatementCompiler {
|
|||||||
Assignment(vex, f.start),
|
Assignment(vex, f.start),
|
||||||
WhileStatement(
|
WhileStatement(
|
||||||
FunctionCallExpression("<", List(vex, f.end)),
|
FunctionCallExpression("<", List(vex, f.end)),
|
||||||
f.body :+ increment),
|
f.body, List(increment), names),
|
||||||
))
|
))
|
||||||
// case (ForDirection.To | ForDirection.ParallelTo, _, Some(NumericConstant(n, _))) if n > 0 && n < 255 =>
|
// case (ForDirection.To | ForDirection.ParallelTo, _, Some(NumericConstant(n, _))) if n > 0 && n < 255 =>
|
||||||
// compile(ctx, List(
|
// compile(ctx, List(
|
||||||
@ -351,16 +353,17 @@ object StatementCompiler {
|
|||||||
// f.body :+ increment),
|
// f.body :+ increment),
|
||||||
// ))
|
// ))
|
||||||
case (ForDirection.To | ForDirection.ParallelTo, _, _) =>
|
case (ForDirection.To | ForDirection.ParallelTo, _, _) =>
|
||||||
val label = MfCompiler.nextLabel("to")
|
|
||||||
compile(ctx, List(
|
compile(ctx, List(
|
||||||
Assignment(vex, f.start),
|
Assignment(vex, f.start),
|
||||||
WhileStatement(
|
WhileStatement(
|
||||||
VariableExpression("true"),
|
VariableExpression("true"),
|
||||||
f.body :+ IfStatement(
|
f.body,
|
||||||
|
List(IfStatement(
|
||||||
FunctionCallExpression("==", List(vex, f.end)),
|
FunctionCallExpression("==", List(vex, f.end)),
|
||||||
List(AssemblyStatement(JMP, AddrMode.Absolute, VariableExpression(label), elidable = true)),
|
List(BreakStatement(variable)),
|
||||||
List(increment))),
|
List(increment)
|
||||||
AssemblyStatement(LABEL, AddrMode.DoesNotExist, VariableExpression(label), elidable=true)
|
)),
|
||||||
|
names),
|
||||||
))
|
))
|
||||||
case (ForDirection.DownTo, _, _) =>
|
case (ForDirection.DownTo, _, _) =>
|
||||||
compile(ctx, List(
|
compile(ctx, List(
|
||||||
@ -368,12 +371,34 @@ object StatementCompiler {
|
|||||||
IfStatement(
|
IfStatement(
|
||||||
FunctionCallExpression(">=", List(vex, f.end)),
|
FunctionCallExpression(">=", List(vex, f.end)),
|
||||||
List(DoWhileStatement(
|
List(DoWhileStatement(
|
||||||
f.body :+ decrement,
|
f.body,
|
||||||
FunctionCallExpression("!=", List(vex, f.end))
|
List(decrement),
|
||||||
|
FunctionCallExpression("!=", List(vex, f.end)),
|
||||||
|
names
|
||||||
)),
|
)),
|
||||||
Nil)
|
Nil)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BreakStatement(l) =>
|
||||||
|
ctx.breakLabels.get(l) match {
|
||||||
|
case None =>
|
||||||
|
if (l == "") ErrorReporting.error("`break` outside a loop", statement.position)
|
||||||
|
else ErrorReporting.error("Invalid label: " + l, statement.position)
|
||||||
|
Nil
|
||||||
|
case Some(label) =>
|
||||||
|
List(AssemblyLine.absolute(JMP, label))
|
||||||
|
}
|
||||||
|
|
||||||
|
case ContinueStatement(l) =>
|
||||||
|
ctx.continueLabels.get(l) match {
|
||||||
|
case None =>
|
||||||
|
if (l == "") ErrorReporting.error("`continue` outside a loop", statement.position)
|
||||||
|
else ErrorReporting.error("Invalid label: " + l, statement.position)
|
||||||
|
Nil
|
||||||
|
case Some(label) =>
|
||||||
|
List(AssemblyLine.absolute(JMP, label))
|
||||||
|
}
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ case class IfStatement(condition: Expression, thenBranch: List[ExecutableStateme
|
|||||||
override def getAllExpressions: List[Expression] = condition :: (thenBranch ++ elseBranch).flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = condition :: (thenBranch ++ elseBranch).flatMap(_.getAllExpressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class WhileStatement(condition: Expression, body: List[ExecutableStatement]) extends ExecutableStatement {
|
case class WhileStatement(condition: Expression, body: List[ExecutableStatement], increment: List[ExecutableStatement], labels: Set[String] = Set("", "while")) extends ExecutableStatement {
|
||||||
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ case class ForStatement(variable: String, start: Expression, end: Expression, di
|
|||||||
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: start :: end :: body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: start :: end :: body.flatMap(_.getAllExpressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DoWhileStatement(body: List[ExecutableStatement], condition: Expression) extends ExecutableStatement {
|
case class DoWhileStatement(body: List[ExecutableStatement], increment: List[ExecutableStatement], condition: Expression, labels: Set[String] = Set("", "do")) extends ExecutableStatement {
|
||||||
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +196,14 @@ case class BlockStatement(body: List[ExecutableStatement]) extends ExecutableSta
|
|||||||
override def getAllExpressions: List[Expression] = body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = body.flatMap(_.getAllExpressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class BreakStatement(label: String) extends ExecutableStatement {
|
||||||
|
override def getAllExpressions: List[Expression] = Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case class ContinueStatement(label: String) extends ExecutableStatement {
|
||||||
|
override def getAllExpressions: List[Expression] = Nil
|
||||||
|
}
|
||||||
|
|
||||||
object AssemblyStatement {
|
object AssemblyStatement {
|
||||||
def implied(opcode: Opcode.Value, elidable: Boolean) = AssemblyStatement(opcode, AddrMode.Implied, LiteralExpression(0, 1), elidable)
|
def implied(opcode: Opcode.Value, elidable: Boolean) = AssemblyStatement(opcode, AddrMode.Implied, LiteralExpression(0, 1), elidable)
|
||||||
|
|
||||||
|
@ -312,7 +312,16 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
|||||||
case (p, l, r) => Assignment(l, r).pos(p)
|
case (p, l, r) => Assignment(l, r).pos(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
def keywordStatement: P[ExecutableStatement] = P(returnOrDispatchStatement | ifStatement | whileStatement | forStatement | doWhileStatement | inlineAssembly | assignmentStatement)
|
def keywordStatement: P[ExecutableStatement] = P(
|
||||||
|
returnOrDispatchStatement |
|
||||||
|
ifStatement |
|
||||||
|
whileStatement |
|
||||||
|
forStatement |
|
||||||
|
doWhileStatement |
|
||||||
|
breakStatement |
|
||||||
|
continueStatement |
|
||||||
|
inlineAssembly |
|
||||||
|
assignmentStatement)
|
||||||
|
|
||||||
def executableStatement: P[ExecutableStatement] = (position() ~ P(keywordStatement | expressionStatement)).map { case (p, s) => s.pos(p) }
|
def executableStatement: P[ExecutableStatement] = (position() ~ P(keywordStatement | expressionStatement)).map { case (p, s) => s.pos(p) }
|
||||||
|
|
||||||
@ -404,6 +413,10 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
|||||||
|
|
||||||
def returnOrDispatchStatement: P[ExecutableStatement] = "return" ~ !letterOrDigit ~/ HWS ~ (dispatchStatementBody | mlExpression(nonStatementLevel).?.map(ReturnStatement))
|
def returnOrDispatchStatement: P[ExecutableStatement] = "return" ~ !letterOrDigit ~/ HWS ~ (dispatchStatementBody | mlExpression(nonStatementLevel).?.map(ReturnStatement))
|
||||||
|
|
||||||
|
def breakStatement: P[ExecutableStatement] = ("break" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => BreakStatement(l.getOrElse("")))
|
||||||
|
|
||||||
|
def continueStatement: P[ExecutableStatement] = ("continue" ~ !letterOrDigit ~/ HWS ~ identifier.?).map(l => ContinueStatement(l.getOrElse("")))
|
||||||
|
|
||||||
def ifStatement: P[ExecutableStatement] = for {
|
def ifStatement: P[ExecutableStatement] = for {
|
||||||
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
|
condition <- "if" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
|
||||||
thenBranch <- AWS ~/ executableStatements
|
thenBranch <- AWS ~/ executableStatements
|
||||||
@ -413,7 +426,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
|||||||
def whileStatement: P[ExecutableStatement] = for {
|
def whileStatement: P[ExecutableStatement] = for {
|
||||||
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
|
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
|
||||||
body <- AWS ~ executableStatements
|
body <- AWS ~ executableStatements
|
||||||
} yield WhileStatement(condition, body.toList)
|
} yield WhileStatement(condition, body.toList, Nil)
|
||||||
|
|
||||||
def forDirection: P[ForDirection.Value] =
|
def forDirection: P[ForDirection.Value] =
|
||||||
("parallel" ~ HWS ~ "to").!.map(_ => ForDirection.ParallelTo) |
|
("parallel" ~ HWS ~ "to").!.map(_ => ForDirection.ParallelTo) |
|
||||||
@ -439,7 +452,7 @@ case class MfParser(filename: String, input: String, currentDirectory: String, o
|
|||||||
def doWhileStatement: P[ExecutableStatement] = for {
|
def doWhileStatement: P[ExecutableStatement] = for {
|
||||||
body <- "do" ~ !letterOrDigit ~/ AWS ~ executableStatements ~/ AWS
|
body <- "do" ~ !letterOrDigit ~/ AWS ~ executableStatements ~/ AWS
|
||||||
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
|
condition <- "while" ~ !letterOrDigit ~/ HWS ~/ mlExpression(nonStatementLevel)
|
||||||
} yield DoWhileStatement(body.toList, condition)
|
} yield DoWhileStatement(body.toList, Nil, condition)
|
||||||
|
|
||||||
def functionDefinition: P[DeclarationStatement] = for {
|
def functionDefinition: P[DeclarationStatement] = for {
|
||||||
p <- position()
|
p <- position()
|
||||||
|
108
src/test/scala/millfork/test/BreakContinueSuite.scala
Normal file
108
src/test/scala/millfork/test/BreakContinueSuite.scala
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package millfork.test
|
||||||
|
|
||||||
|
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun}
|
||||||
|
import org.scalatest.{FunSuite, Matchers}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karol Stasiak
|
||||||
|
*/
|
||||||
|
class BreakContinueSuite extends FunSuite with Matchers {
|
||||||
|
|
||||||
|
test("Break from one-iteration loop 1") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| output = 0
|
||||||
|
| do {
|
||||||
|
| break
|
||||||
|
| output += 1
|
||||||
|
| } while false
|
||||||
|
| }
|
||||||
|
""".stripMargin)(_.readByte(0xc000) should equal(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Break from one-iteration loop 2") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| output = 0
|
||||||
|
| byte i
|
||||||
|
| for i,0,to,0 {
|
||||||
|
| break
|
||||||
|
| output += 1
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin)(_.readByte(0xc000) should equal(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Break from infinite loop 1") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| output = 0
|
||||||
|
| while true {
|
||||||
|
| output += 1
|
||||||
|
| break
|
||||||
|
| output += 1
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin)(_.readByte(0xc000) should equal(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Break and continue from infinite loop 1") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| output = 0
|
||||||
|
| while true {
|
||||||
|
| if output != 0 { break }
|
||||||
|
| output += 1
|
||||||
|
| continue
|
||||||
|
| output += 1
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin)(_.readByte(0xc000) should equal(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Nested break") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| void main () {
|
||||||
|
| output = 0
|
||||||
|
| do {
|
||||||
|
| output += 1
|
||||||
|
| while true {
|
||||||
|
| break while
|
||||||
|
| }
|
||||||
|
| output += 1
|
||||||
|
| } while false
|
||||||
|
| }
|
||||||
|
""".stripMargin)(_.readByte(0xc000) should equal(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Continue in for loop 1") {
|
||||||
|
EmuBenchmarkRun(
|
||||||
|
"""
|
||||||
|
| byte output @$c000
|
||||||
|
| byte counter @$c001
|
||||||
|
| void main () {
|
||||||
|
| output = 0
|
||||||
|
| byte i
|
||||||
|
| for i,0,paralleluntil,50 {
|
||||||
|
| counter += 1
|
||||||
|
| if i != 30 { continue }
|
||||||
|
| output = i
|
||||||
|
| break
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin){m =>
|
||||||
|
m.readByte(0xc000) should equal(30)
|
||||||
|
m.readByte(0xc001) should be > 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user