more complete for loops, various comma separated lists can now be split over multiple lines

This commit is contained in:
Irmen de Jong 2018-10-02 22:52:05 +02:00
parent af0d52b5c2
commit 18c6165325
10 changed files with 800 additions and 587 deletions

View File

@ -163,7 +163,7 @@ functioncall_stmt :
expression_list :
expression (',' expression)*
expression (',' EOL? expression)*
;
returnstmt : 'return' expression_list? ;
@ -186,7 +186,7 @@ wordsuffix : '.w' ;
booleanliteral : 'true' | 'false' ;
arrayliteral : '[' expression (',' expression)* ']' ;
arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ;
stringliteral : STRING ;
@ -219,11 +219,11 @@ statement_block :
;
sub_params : sub_param (',' sub_param)* ;
sub_params : sub_param (',' EOL? sub_param)* ;
sub_param : identifier ':' datatype;
sub_returns : datatype (',' datatype)* ;
sub_returns : datatype (',' EOL? datatype)* ;
asmsubroutine :
'asmsub' identifier '(' asmsub_params? ')'
@ -232,13 +232,13 @@ asmsubroutine :
asmsub_address : '=' address=integerliteral ;
asmsub_params : asmsub_param (',' asmsub_param)* ;
asmsub_params : asmsub_param (',' EOL? asmsub_param)* ;
asmsub_param : identifier ':' datatype '@' (register | statusregister);
clobber : register (',' register)* ;
asmsub_returns : asmsub_return (',' asmsub_return)* ;
asmsub_returns : asmsub_return (',' EOL? asmsub_return)* ;
asmsub_return : datatype '@' (register | statusregister) ;

View File

@ -17,7 +17,7 @@
const word height = 200
float[6] xcoor = [-1.0, 1.0, 1.0, 0.5, 0.2, -1.0]
float[6] ycoor = [1.0, 1.0, -1.0, -0.3, -0.6, -1.0]
float[6] ycoor = [0.2, 1.0, -1.0, -0.3, -0.6, -1.0]
float[len(xcoor)] rotatedx
float[len(ycoor)] rotatedy
@ -69,8 +69,10 @@
}
for i in 0 to len(xcoor)-2 {
_vm_gfx_line(toscreenx(rotatedx[i]), toscreeny(rotatedy[i]), toscreenx(rotatedx[i+1]), toscreeny(rotatedy[i+1]), i+7)
_vm_gfx_line(toscreenx(rotatedx[i]), toscreeny(rotatedy[i]),
toscreenx(rotatedx[i+1]), toscreeny(rotatedy[i+1]), i+7)
}
_vm_gfx_line(toscreenx(rotatedx[len(xcoor)-1]), toscreeny(rotatedy[len(xcoor)-1]), toscreenx(rotatedx[0]), toscreeny(rotatedy[0]), 14)
_vm_gfx_line(toscreenx(rotatedx[len(xcoor)-1]), toscreeny(rotatedy[len(xcoor)-1]),
toscreenx(rotatedx[0]), toscreeny(rotatedy[0]), 14)
}
}

View File

@ -2,24 +2,32 @@
~ main {
const word width = 320
const word height = 200
sub start() {
_vm_gfx_clearscr(0)
_vm_gfx_text(5, 5, 7, "Swirl !!!")
const word width = 320
const word height = 200
float x
float y
float t
byte color
while(1) {
x = ((sin(t*1.01) +cos(t*1.1234)) * width/4.1) + width/2
y = ((cos(t)+sin(t*0.03456)) * height/4.1) + height/2
_vm_gfx_pixel(floor(x),floor(y), color//16)
x = sin(t*1.01) + cos(t*1.1234)
y = cos(t) + sin(t*0.03456)
_vm_gfx_pixel(screenx(x), screeny(y), color//16)
t += 0.01
color++
}
}
sub screenx(x: float) -> word {
return floor(x * width/4.1) + width // 2
}
sub screeny(y: float) -> word {
return floor(y * height/4.1) + height // 2
}
}

View File

@ -10,7 +10,7 @@ sub start() {
word[10] warray1
word[10] warray2 = 112233
word[10] warray3 = [1,2,3,4,5,6,7,8,9, 65535]
word[10] warray3 = [1,2000,3000,4,5,6,7,8,9, 65535]
byte[4,5] mvar1
byte[4,5] mvar2 = 22
@ -23,40 +23,31 @@ sub start() {
float[3] farray4 = [1,2,35566]
float[3] farray5 = [1,2.22334,3.1415]
str name = "irmen"
byte i
word w
;warray3[1] = warray3[1] + 1
warray3[1] += 1
;warray3[1] ++
_vm_write_str(name)
_vm_write_char('\n')
name[2] = '@'
_vm_write_str(name)
_vm_write_char('\n')
for i in barray3 {
_vm_write_num(i)
_vm_write_char('\n')
}
for w in warray3 {
_vm_write_num(w)
_vm_write_char('\n')
}
; for i in 0 to 2 {
; _vm_write_num(farray5[i])
; _vm_write_char('\n')
; }
; for w in [1,2,3777] { ;@todo loop over array literal
; _vm_write_num(w)
; _vm_write_char('\n')
; }
;
; for i in barray3 { ; @todo loop over symbol
; _vm_write_num(i)
; _vm_write_char('\n')
; }
;
; for i in "hello" { ; @todo loop over string
; _vm_write_num(i)
; _vm_write_char('\n')
; }
;
; for w in "hello" { ; @todo loop over string
; _vm_write_num(w)
; _vm_write_char('\n')
; }
for i in name {
_vm_write_num(i)
_vm_write_char('\n')
}
return

View File

@ -1258,7 +1258,7 @@ class FunctionCall(override var target: IdentifierReference,
override fun resultingDatatype(namespace: INameScope, heap: HeapValues): DataType? {
val constVal = constValue(namespace, heap,false)
if(constVal!=null)
return constVal.resultingDatatype(namespace, heap)
return constVal.type
val stmt = target.targetStatement(namespace) ?: return null
when (stmt) {
is BuiltinFunctionStatementPlaceholder -> {

View File

@ -95,6 +95,10 @@ class AstChecker(private val namespace: INameScope,
override fun process(forLoop: ForLoop): IStatement {
if(forLoop.body.isEmpty())
printWarning("for loop body is empty", forLoop.position)
if(forLoop.iterable is LiteralValue)
checkResult.add(SyntaxError("currently not possible to loop over a literal value directly, use a variable instead", forLoop.position)) // todo loop over literals
if(!forLoop.iterable.isIterable(namespace, heap)) {
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
} else {

View File

@ -821,24 +821,111 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
}
}
} else {
val iterableValue: LiteralValue?
if(loop.iterable is LiteralValue) {
if (!loop.iterable.isIterable(namespace, heap))
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
iterableValue = loop.iterable as LiteralValue
} else if(loop.iterable is IdentifierReference) {
val idRef = loop.iterable as IdentifierReference
iterableValue = ((idRef.targetStatement(namespace) as? VarDecl)?.value as? LiteralValue)
if(iterableValue!=null && !iterableValue.isIterable(namespace, heap))
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
} else {
throw CompilerException("loopvar is something strange ${loop.iterable}")
// ok, must be a literalvalue
val iterableValue: LiteralValue
when {
loop.iterable is LiteralValue -> {
TODO("loop over literal value (move literal to auto-generated heap variable)")
}
loop.iterable is IdentifierReference -> {
val idRef = loop.iterable as IdentifierReference
val vardecl = (idRef.targetStatement(namespace) as VarDecl)
iterableValue = vardecl.value as LiteralValue
if(!iterableValue.isIterable(namespace, heap))
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
}
else -> throw CompilerException("loopvar is something strange ${loop.iterable}")
}
TODO("LOOP OVER ITERABLE VALUE (array/matrix/string) $iterableValue")
translateForOverIterableVar(loop, loopVarDt, iterableValue)
}
}
private fun translateForOverIterableVar(loop: ForLoop, varDt: DataType, iterableValue: LiteralValue) {
if(varDt==DataType.BYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY, DataType.MATRIX))
throw CompilerException("loop variable type doesn't match iterableValue type (byte)")
else if(varDt==DataType.WORD && iterableValue.type != DataType.ARRAY_W)
throw CompilerException("loop variable type doesn't match iterableValue type (word)")
else if(varDt==DataType.FLOAT && iterableValue.type != DataType.ARRAY_F)
throw CompilerException("loop variable type doesn't match iterableValue type (float)")
val numElements: Int
val indexVar: String
when(iterableValue.type) {
DataType.BYTE,
DataType.WORD,
DataType.FLOAT -> throw CompilerException("non-iterableValue type")
DataType.STR,
DataType.STR_P,
DataType.STR_S,
DataType.STR_PS -> {
numElements = iterableValue.strvalue?.length ?: heap.get(iterableValue.heapId!!).str!!.length
indexVar = if(numElements>255) "XY" else "X"
}
DataType.ARRAY,
DataType.ARRAY_W,
DataType.MATRIX -> {
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).array!!.size
indexVar = if(numElements>255) "XY" else "X"
}
DataType.ARRAY_F -> {
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).doubleArray!!.size
indexVar = if(numElements>255) "XY" else "X"
}
}
if(indexVar=="X" && loop.loopRegister!=null && loop.loopRegister in setOf(Register.X, Register.AX, Register.XY))
throw CompilerException("loopVar cannot use X register because it is needed as internal index")
if(indexVar=="XY" && loop.loopRegister!=null && loop.loopRegister in setOf(Register.X, Register.AX, Register.Y, Register.AY, Register.XY))
throw CompilerException("loopVar cannot use X and Y registers because they are needed as internal index")
/**
* indexVar = 0
* loop:
* LV = iterableValue[indexVar]
* ..body..
* ..break statement: goto break
* ..continue statement: goto continue
* ..
* continue:
* IV++
* if IV!=numElements goto loop
* break:
* nop
*/
val loopLabel = makeLabel("loop")
val continueLabel = makeLabel("continue")
val breakLabel = makeLabel("break")
continueStmtLabelStack.push(continueLabel)
breakStmtLabelStack.push(breakLabel)
stackvmProg.instr(Opcode.PUSH, Value(if(numElements<=255) DataType.BYTE else DataType.WORD, 0))
stackvmProg.instr(Opcode.POP_VAR, callLabel = indexVar)
stackvmProg.label(loopLabel)
val assignTarget = if(loop.loopRegister!=null)
AssignTarget(loop.loopRegister, null, null, loop.position)
else
AssignTarget(null, loop.loopVar, null, loop.position)
val arrayspec = ArraySpec(RegisterExpr(Register.valueOf(indexVar), loop.position), null, loop.position)
val assignLv = Assignment(assignTarget, null, ArrayIndexedExpression(loop.iterable as IdentifierReference, null, arrayspec, loop.position), loop.position)
assignLv.linkParents(loop.parent)
translate(assignLv)
translate(loop.body)
stackvmProg.label(continueLabel)
stackvmProg.instr(Opcode.INC_VAR, callLabel = indexVar)
// TODO: optimize edge cases if last value = 255 or 0 (for bytes) etc. to avoid PUSH / SUB opcodes and make use of the wrapping around of the value.
stackvmProg.instr(Opcode.PUSH, Value(varDt, numElements))
stackvmProg.instr(Opcode.PUSH_VAR, callLabel = indexVar)
stackvmProg.instr(Opcode.SUB)
stackvmProg.instr(Opcode.BNZ, callLabel = loopLabel)
stackvmProg.label(breakLabel)
stackvmProg.instr(Opcode.NOP)
breakStmtLabelStack.pop()
continueStmtLabelStack.pop()
}
private fun translateForOverConstantRange(varname: String, varDt: DataType, range: IntProgression, body: MutableList<IStatement>) {
/**
* for LV in start..last { body }

View File

@ -16,7 +16,7 @@ import prog8.functions.BuiltinFunctions
todo statement optimization: X+=1, X-=1 --> X++/X-- ,
todo remove statements that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
todo optimize addition with self into shift 1 (A+=A -> A<<=1)
todo assignment optimization: optimize some simple multiplications into shifts (A*=8 -> A<<=3)
todo assignment optimization: optimize some simple multiplications and divisions into shifts (A*=2 -> lsl(A), X=X/2 -> lsr(X) )
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
todo merge sequence of assignments into one (as long as the value is a constant and the target not a MEMORY type!)
todo report more always true/always false conditions

File diff suppressed because it is too large Load Diff

View File

@ -926,13 +926,13 @@ class StackVm(private var traceOutputFile: String?) {
DataType.ARRAY, DataType.MATRIX -> evalstack.push(Value(DataType.BYTE, array.array!![index]))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index]))
DataType.ARRAY_F -> evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index]))
DataType.BYTE,
DataType.WORD,
DataType.FLOAT,
DataType.STR,
DataType.STR_P,
DataType.STR_S,
DataType.STR_PS -> throw VmExecutionException("not a proper array/matrix var") // todo: allow strings
DataType.STR_PS -> evalstack.push(Value(DataType.BYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]))
DataType.BYTE,
DataType.WORD,
DataType.FLOAT -> throw VmExecutionException("not a proper array/matrix var")
}
}
}
@ -965,13 +965,19 @@ class StackVm(private var traceOutputFile: String?) {
throw VmExecutionException("writing a non-float value into float array")
array.doubleArray!![index] = value.numericValue().toDouble()
}
DataType.BYTE,
DataType.WORD,
DataType.FLOAT,
DataType.STR,
DataType.STR_P,
DataType.STR_S,
DataType.STR_PS -> throw VmExecutionException("not a proper array/matrix var") // todo: allow strings
DataType.STR_PS -> {
if(value.type!=DataType.BYTE)
throw VmExecutionException("writing a non-byte value into a string")
val chars = array.str!!.toCharArray()
chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0]
heap.update(variable.heapId, chars.joinToString(""))
}
DataType.BYTE,
DataType.WORD,
DataType.FLOAT -> throw VmExecutionException("not a proper array/matrix var")
}
}
}