fix functions

This commit is contained in:
Irmen de Jong 2018-08-14 02:22:59 +02:00
parent 666b9b2263
commit 69ff680eaf
9 changed files with 671 additions and 425 deletions

View File

@ -267,23 +267,34 @@ You can also create loops by using the ``goto`` statement, but this should be av
Conditional Execution
---------------------
@todo
.. todo::
not sure how to handle direct translation into
[cc, cs, vc, vs, eq, ne, true, not, zero, pos, neg, lt, gt, le, ge]
It defaults to 'true' (=='ne', not-zero) if omitted. ('pos' will translate into 'pl', 'neg' into 'mi')
@todo signed: lts==neg?, gts==eq+pos?, les==neg+eq?, ges==pos?
.. todo::
eventually allow local variable definitions inside the sub blocks but for now,
they have to use the same variables as the block the ``if`` statement itself is in.
Conditional execution means that the flow of execution changes based on certiain conditions,
rather than having fixed gotos or subroutine calls. IL65 has a *conditional goto* statement for this,
that is translated into a comparison (if needed) and then a conditional branch instruction::
rather than having fixed gotos or subroutine calls::
if[_XX] [<expression>] goto <label>
if A > 4 goto overflow
if X == 3 then Y = 4
if X == 3 then Y = 4 else A = 2
if X == 5 {
Y = 99
} else {
A = 3
}
condition = arithmetic expression or logical expression or comparison expression or status_register_flags ( ``SR.cs`` , ``SR.cc``, ``SR.pl`` etc... @todo )
The if-status XX is one of: [cc, cs, vc, vs, eq, ne, true, not, zero, pos, neg, lt, gt, le, ge]
It defaults to 'true' (=='ne', not-zero) if omitted. ('pos' will translate into 'pl', 'neg' into 'mi')
@todo signed: lts==neg?, gts==eq+pos?, les==neg+eq?, ges==pos?
The <expression> is optional. If it is provided, it will be evaluated first. Only the [true] and [not] and [zero]
if-statuses can be used when such a *comparison expression* is used. An example is::
if_not A > 55 goto more_iterations
Conditional jumps are compiled into 6502's branching instructions (such as ``bne`` and ``bcc``) so

View File

@ -467,8 +467,15 @@ conditional execution
@todo::
if <condition> <single_statement or subblock>
[else <single_statement or subblock>]
if <condition> goto <location>
if <condition> then <simple_stateument> [else <simple_statement> ]
if <condition> {
<statements>
}
[ else {
<alternative statements>
} ]
condition = arithmetic expression
or logical expression

View File

@ -62,6 +62,7 @@ statement :
| augassignment
| unconditionaljump
| postincrdecr
| functioncall_stmt
| subroutine
| inlineasm
| labeldef
@ -138,6 +139,12 @@ functioncall :
(identifier | scoped_identifier) '(' expression_list? ')'
;
functioncall_stmt :
(identifier | scoped_identifier) '(' expression_list? ')'
;
expression_list :
expression (',' expression)*
;

View File

@ -0,0 +1,93 @@
%output prg
%import c64lib
%import mathlib
~ main {
str name = "?" * 80
str guess = "?" * 80
byte secretnumber = 0
byte attempts_left = 10
memory word freadstr_arg = $22 ; argument for FREADSTR
start:
c64.init_system()
c64.VMCSB |= 2 ; lowercase charset
; greeting
c64scr.print_string("Enter your name: ")
Y = c64scr.input_chars(name)
c64.CHROUT("\n")
c64.CHROUT("\n")
c64scr.print_string("Hello, ")
c64scr.print_string(name)
c64.CHROUT(".")
c64.CHROUT("\n")
; create a secret random number from 1-100
c64.RNDA(0) ; fac = rnd(0)
c64.MUL10() ; fac *= 10
c64.MUL10() ; .. and now *100
c64.FADDH() ; add 0.5..
c64.FADDH() ; and again, so +1 total
AY = c64flt.GETADRAY()
secretnumber=A
;A=math.randbyte()
;A+=c64.RASTER
;A-=c64.TIME_LO
;X,secretnumber=math.divmod_bytes(A, 99)
c64scr.print_string("I am thinking of a number from 1 to 100!You'll have to guess it!\n")
printloop:
c64scr.print_string("\nYou have ")
c64scr.print_byte_decimal(attempts_left)
c64scr.print_string(" guess")
; @todo comparison expression so we can do if attempts_left>0 ...
A = attempts_left
A--
; @todo (conditional): if_zero A goto ask_guess
c64scr.print_string("es")
ask_guess:
c64scr.print_string(" left.\nWhat is your next guess? ")
Y = c64scr.input_chars(guess)
c64.CHROUT("\n")
freadstr_arg = guess
c64.FREADSTR(A)
AY = c64flt.GETADRAY()
A -= secretnumber ; @todo condition so we can do if guess > secretnumber....
; @todo (conditional): if_zero goto correct_guess
; @todo (conditional): if_gt goto too_high
c64scr.print_string("That is too ")
c64scr.print_string("low!\n")
goto continue
correct_guess:
c64scr.print_string("\nThat's my number, impressive!\n")
goodbye()
return
too_high:
c64scr.print_string("That is too ")
c64scr.print_string("high!\n")
continue:
attempts_left--
; @todo (conditional): if_zero attempts_left goto game_over
goto printloop
game_over:
c64scr.print_string("\nToo bad! It was: ")
c64scr.print_byte_decimal(secretnumber)
c64.CHROUT("\n")
return goodbye()
goodbye() ; @todo fix subroutine usage tracking, it doesn't register this one
return
sub goodbye ()->() {
c64scr.print_string("\nThanks for playing. Bye!\n")
return
}
}

View File

@ -20,6 +20,8 @@
XY = hopla*2+hopla1
A = "derp" * %000100
main.foo(1,2,3)
mega:
X=1
cool:

View File

@ -125,7 +125,7 @@ fun main(args: Array<String>) {
val moduleAst = loadModule(filepath)
moduleAst.linkParents()
val globalNamespace = moduleAst.namespace()
globalNamespace.debugPrint()
// globalNamespace.debugPrint()
moduleAst.optimize(globalNamespace)
moduleAst.checkValid(globalNamespace) // check if final tree is valid

View File

@ -81,6 +81,10 @@ interface IAstProcessor {
functionCall.arglist = functionCall.arglist.map { it.process(this) }
return functionCall
}
fun process(functionCall: FunctionCallStatement): IStatement {
functionCall.arglist = functionCall.arglist.map { it.process(this) }
return functionCall
}
fun process(identifier: Identifier): IExpression {
return identifier
}
@ -102,6 +106,11 @@ interface IStatement : Node {
}
interface IFunctionCall {
var location: Identifier
var arglist: List<IExpression>
}
interface INameScope {
val name: String
val position: Position?
@ -474,8 +483,7 @@ data class Identifier(val scopedName: List<String>) : IExpression {
// todo add to a list of errors instead
throw SyntaxError("name should be a constant, instead of: ${node::class.simpleName}", position)
} else if(vardecl.type!=VarDeclType.CONST) {
// todo add to a list of errors instead
throw SyntaxError("name should be a constant, instead of: ${vardecl.type}", position)
return null
}
return vardecl.value?.constValue(namespace)
}
@ -513,7 +521,7 @@ data class Jump(val address: Int?, val identifier: Identifier?) : IStatement {
}
data class FunctionCall(var location: Identifier, var arglist: List<IExpression>) : IExpression {
data class FunctionCall(override var location: Identifier, override var arglist: List<IExpression>) : IExpression, IFunctionCall {
override var position: Position? = null
override var parent: Node? = null
@ -550,6 +558,20 @@ data class FunctionCall(var location: Identifier, var arglist: List<IExpression>
}
data class FunctionCallStatement(override var location: Identifier, override var arglist: List<IExpression>) : IStatement, IFunctionCall {
override var position: Position? = null
override var parent: Node? = null
override fun linkParents(parent: Node) {
this.parent = parent
location.linkParents(this)
arglist.forEach { it.linkParents(this) }
}
override fun process(processor: IAstProcessor) = processor.process(this)
}
data class InlineAssembly(val assembly: String) : IStatement {
override var position: Position? = null
override var parent: Node? = null
@ -736,10 +758,39 @@ private fun il65Parser.StatementContext.toAst(withPosition: Boolean) : IStatemen
val asm = inlineasm()?.toAst(withPosition)
if(asm!=null) return asm
val fcall = functioncall_stmt()?.toAst(withPosition)
if(fcall!=null) return fcall
throw UnsupportedOperationException(text)
}
private fun il65Parser.Functioncall_stmtContext.toAst(withPosition: Boolean): IStatement {
val location =
if(identifier()!=null) identifier()?.toAst(withPosition)
else scoped_identifier()?.toAst(withPosition)
val fcall = if(expression_list()==null)
FunctionCallStatement(location!!, emptyList())
else
FunctionCallStatement(location!!, expression_list().toAst(withPosition))
fcall.position = toPosition(withPosition)
return fcall
}
private fun il65Parser.FunctioncallContext.toAst(withPosition: Boolean): FunctionCall {
val location =
if(identifier()!=null) identifier()?.toAst(withPosition)
else scoped_identifier()?.toAst(withPosition)
val fcall = if(expression_list()==null)
FunctionCall(location!!, emptyList())
else
FunctionCall(location!!, expression_list().toAst(withPosition))
fcall.position = toPosition(withPosition)
return fcall
}
private fun il65Parser.InlineasmContext.toAst(withPosition: Boolean): IStatement {
val asm = InlineAssembly(INLINEASMBLOCK().text)
asm.position = toPosition(withPosition)
@ -892,16 +943,8 @@ private fun il65Parser.ExpressionContext.toAst(withPosition: Boolean) : IExpress
return expr
}
val funcall = functioncall()
if(funcall!=null) {
val location = funcall.identifier().toAst(withPosition)
val fcall = if(funcall.expression_list()==null)
FunctionCall(location, emptyList())
else
FunctionCall(location, funcall.expression_list().toAst(withPosition))
fcall.position = funcall.toPosition(withPosition)
return fcall
}
val funcall = functioncall()?.toAst(withPosition)
if(funcall!=null) return funcall
if (rangefrom!=null && rangeto!=null) {
val rexp = RangeExpr(rangefrom.toAst(withPosition), rangeto.toAst(withPosition))

View File

@ -167,7 +167,8 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
}
VarDeclType.MEMORY -> {
if(decl.value !is LiteralValue)
throw AstException("${decl.value?.position} value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}). This is likely a bug in the AstOptimizer")
// @todo normal error reporting
throw AstException("${decl.value?.position} value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).")
val value = decl.value as LiteralValue
if(value.intvalue==null || value.intvalue<0 || value.intvalue>65535) {

File diff suppressed because it is too large Load Diff