mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
when statement extended with multiple choice values
This commit is contained in:
parent
cc078503e3
commit
14cabde5cf
@ -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")
|
||||
|
@ -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())
|
||||
|
@ -213,7 +213,7 @@ interface IAstModifyingVisitor {
|
||||
}
|
||||
|
||||
fun visit(whenChoice: WhenChoice) {
|
||||
whenChoice.value?.accept(this)
|
||||
whenChoice.values?.forEach { it.accept(this) }
|
||||
whenChoice.statements.accept(this)
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ interface IAstVisitor {
|
||||
}
|
||||
|
||||
fun visit(whenChoice: WhenChoice) {
|
||||
whenChoice.value?.accept(this)
|
||||
whenChoice.values?.forEach { it.accept(this) }
|
||||
whenChoice.statements.accept(this)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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 ) ;
|
||||
|
@ -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"+
|
||||
|
Loading…
x
Reference in New Issue
Block a user