when statement extended with multiple choice values

This commit is contained in:
Irmen de Jong 2019-07-10 00:25:21 +02:00
parent cc078503e3
commit 14cabde5cf
15 changed files with 73 additions and 47 deletions

View File

@ -554,13 +554,13 @@ private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
}
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
val value = expression()?.toAst()
val values = expression_list()?.toAst()
val stmt = statement()?.toAst()
val stmt_block = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
if(stmt!=null)
stmt_block.add(stmt)
val scope = AnonymousScope(stmt_block, toPosition())
return WhenChoice(value, scope, toPosition())
return WhenChoice(values, scope, toPosition())
}
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")

View File

@ -927,13 +927,15 @@ internal class AstChecker(private val program: Program,
override fun visit(whenChoice: WhenChoice) {
val whenStmt = whenChoice.parent as WhenStatement
if(whenChoice.value!=null) {
if(whenChoice.values!=null) {
val conditionType = whenStmt.condition.inferType(program)
val constvalue = whenChoice.value.constValue(program)
when {
constvalue == null -> checkResult.add(SyntaxError("choice value must be a constant", whenChoice.position))
constvalue.type !in IntegerDatatypes -> checkResult.add(SyntaxError("choice value must be a byte or word", whenChoice.position))
constvalue.type != conditionType -> checkResult.add(SyntaxError("choice value datatype differs from condition value", whenChoice.position))
val constvalues = whenChoice.values.map { it.constValue(program) }
for(constvalue in constvalues) {
when {
constvalue == null -> checkResult.add(SyntaxError("choice value must be a constant", whenChoice.position))
constvalue.type !in IntegerDatatypes -> checkResult.add(SyntaxError("choice value must be a byte or word", whenChoice.position))
constvalue.type != conditionType -> checkResult.add(SyntaxError("choice value datatype differs from condition value", whenChoice.position))
}
}
} else {
if(whenChoice !== whenStmt.choices.last())

View File

@ -213,7 +213,7 @@ interface IAstModifyingVisitor {
}
fun visit(whenChoice: WhenChoice) {
whenChoice.value?.accept(this)
whenChoice.values?.forEach { it.accept(this) }
whenChoice.statements.accept(this)
}
}

View File

@ -163,7 +163,7 @@ interface IAstVisitor {
}
fun visit(whenChoice: WhenChoice) {
whenChoice.value?.accept(this)
whenChoice.values?.forEach { it.accept(this) }
whenChoice.statements.accept(this)
}
}

View File

@ -277,9 +277,22 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
}
override fun visit(whenStatement: WhenStatement): IStatement {
// sort the choices in low-to-high value order
// make sure all choices are just for one single value
val choices = whenStatement.choices.toList()
for(choice in choices) {
if(choice.values==null || choice.values.size==1)
continue
for(v in choice.values) {
val newchoice=WhenChoice(listOf(v), choice.statements, choice.position)
newchoice.parent = choice.parent
whenStatement.choices.add(newchoice)
}
whenStatement.choices.remove(choice)
}
// sort the choices in low-to-high value order (nulls last)
whenStatement.choices
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.value?.constValue(program)?.asIntegerValue}))
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.values?.single()?.constValue(program)?.asIntegerValue}))
return super.visit(whenStatement)
}
}

View File

@ -687,35 +687,40 @@ class WhenStatement(val condition: IExpression,
choices.forEach { it.linkParents(this) }
}
fun choiceValues(program: Program): List<Pair<Int?, WhenChoice>> {
fun choiceValues(program: Program): List<Pair<List<Int>?, WhenChoice>> {
// only gives sensible results when the choices are all valid (constant integers)
return choices
.map {
val cv = it.value?.constValue(program)
if(cv==null)
null to it
else
cv.asNumericValue!!.toInt() to it
}
val result = mutableListOf<Pair<List<Int>?, WhenChoice>>()
for(choice in choices) {
if(choice.values==null)
result.add(null to choice)
else {
val values = choice.values.map { it.constValue(program)?.asNumericValue?.toInt() }
if(values.contains(null))
result.add(null to choice)
else
result.add(values.filterNotNull() to choice)
}
}
return result
}
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
}
class WhenChoice(val value: IExpression?, // if null, this is the 'else' part
class WhenChoice(val values: List<IExpression>?, // if null, this is the 'else' part
val statements: AnonymousScope,
override val position: Position) : Node {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
value?.linkParents(this)
values?.forEach { it.linkParents(this) }
statements.linkParents(this)
this.parent = parent
}
override fun toString(): String {
return "Choice($value at $position)"
return "Choice($values at $position)"
}
fun accept(visitor: IAstVisitor) = visitor.visit(this)

View File

@ -397,11 +397,15 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
}
override fun visit(whenChoice: WhenChoice) {
if(whenChoice.value==null)
if(whenChoice.values==null)
outputi("else -> ")
else {
outputi("")
whenChoice.value.accept(this)
for(value in whenChoice.values) {
value.accept(this)
if(value !== whenChoice.values.last())
output(",")
}
output(" -> ")
}
if(whenChoice.statements.statements.size==1)

View File

@ -2096,11 +2096,11 @@ internal class Compiler(private val program: Program) {
val choiceLabels = mutableListOf<String>()
for(choice in whenstmt.choiceValues(program)) {
val choiceVal = choice.first
if(choiceVal==null) {
if(choice.first==null) {
// the else clause
translate(choice.second.statements)
} else {
val choiceVal = choice.first!!.single()
val rval = RuntimeValue(conditionDt!!, choiceVal)
if (conditionDt in ByteDatatypes) {
prog.instr(Opcode.DUP_B)

View File

@ -28,7 +28,7 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
numberOfOptimizations++
}
removeLines = optimizeStoreLoadSame(linesByFour)
removeLines = optimizeCmpSequence(linesByFour)
if(removeLines.isNotEmpty()) {
for (i in removeLines.reversed())
lines.removeAt(i)
@ -36,7 +36,7 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
numberOfOptimizations++
}
removeLines = optimizeCmpSequence(linesByFour)
removeLines = optimizeStoreLoadSame(linesByFour)
if(removeLines.isNotEmpty()) {
for (i in removeLines.reversed())
lines.removeAt(i)

View File

@ -445,12 +445,12 @@ class AstVm(val program: Program) {
is WhenStatement -> {
val condition=evaluate(stmt.condition, evalCtx)
for(choice in stmt.choices) {
if(choice.value==null) {
if(choice.values==null) {
// the 'else' choice
executeAnonymousScope(choice.statements)
break
} else {
val value = choice.value.constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
val value = choice.values.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
val rtval = RuntimeValue.from(value, evalCtx.program.heap)
if(condition==rtval) {
executeAnonymousScope(choice.statements)

View File

@ -2,7 +2,9 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="Python 3.7 (py3)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>

View File

@ -121,14 +121,10 @@ waitkey:
sub keypress(ubyte key) {
when key {
157 -> move_left()
',' -> move_left()
29 -> move_right()
'/' -> move_right()
17 -> move_down_faster()
'.' -> move_down_faster()
145 -> drop_down_immediately()
' ' -> drop_down_immediately()
157, ',' -> move_left()
29, '/' -> move_right()
17, '.' -> move_down_faster()
145, ' ' -> drop_down_immediately()
'z' -> {
; no joystick equivalent (there is only 1 fire button)
; rotate counter clockwise

View File

@ -15,6 +15,10 @@
aa=30
yy=2
c64scr.print_ub(7)
c64scr.print("?: ")
check(3, 4)
c64scr.print_ub(aa+yy)
c64scr.print("?: ")
check(aa, yy)
@ -60,7 +64,7 @@
10 -> {
c64scr.print("ten")
}
5 -> c64scr.print("five")
5, 6, 7 -> c64scr.print("five or six or seven")
30 -> c64scr.print("thirty")
31 -> c64scr.print("thirty1")
32 -> c64scr.print("thirty2")

View File

@ -282,4 +282,4 @@ repeatloop: 'repeat' (statement | statement_block) EOL? 'until' expression ;
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;
when_choice: (expression | 'else' ) '->' (statement | statement_block ) ;
when_choice: (expression_list | 'else' ) '->' (statement | statement_block ) ;

View File

@ -4840,8 +4840,8 @@ public class prog8Parser extends Parser {
}
public static class When_choiceContext extends ParserRuleContext {
public ExpressionContext expression() {
return getRuleContext(ExpressionContext.class,0);
public Expression_listContext expression_list() {
return getRuleContext(Expression_listContext.class,0);
}
public StatementContext statement() {
return getRuleContext(StatementContext.class,0);
@ -4886,7 +4886,7 @@ public class prog8Parser extends Parser {
case SINGLECHAR:
{
setState(724);
expression(0);
expression_list();
}
break;
case T__90:
@ -5296,7 +5296,7 @@ public class prog8Parser extends Parser {
"\u02cc\3\2\2\2\u02ce\u02d1\3\2\2\2\u02cf\u02cd\3\2\2\2\u02cf\u02d0\3\2"+
"\2\2\u02d0\u02d2\3\2\2\2\u02d1\u02cf\3\2\2\2\u02d2\u02d4\7X\2\2\u02d3"+
"\u02d5\7s\2\2\u02d4\u02d3\3\2\2\2\u02d4\u02d5\3\2\2\2\u02d5\u0083\3\2"+
"\2\2\u02d6\u02d9\5(\25\2\u02d7\u02d9\7]\2\2\u02d8\u02d6\3\2\2\2\u02d8"+
"\2\2\u02d6\u02d9\5\66\34\2\u02d7\u02d9\7]\2\2\u02d8\u02d6\3\2\2\2\u02d8"+
"\u02d7\3\2\2\2\u02d9\u02da\3\2\2\2\u02da\u02dd\7V\2\2\u02db\u02de\5\b"+
"\5\2\u02dc\u02de\5^\60\2\u02dd\u02db\3\2\2\2\u02dd\u02dc\3\2\2\2\u02de"+
"\u0085\3\2\2\2e\u0088\u008a\u0091\u0096\u00b2\u00ba\u00be\u00c5\u00c8"+