mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
fix functions
This commit is contained in:
parent
666b9b2263
commit
69ff680eaf
@ -267,23 +267,34 @@ You can also create loops by using the ``goto`` statement, but this should be av
|
||||
Conditional Execution
|
||||
---------------------
|
||||
|
||||
@todo
|
||||
|
||||
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::
|
||||
|
||||
if[_XX] [<expression>] goto <label>
|
||||
|
||||
|
||||
The if-status XX is one of: [cc, cs, vc, vs, eq, ne, true, not, zero, pos, neg, lt, gt, le, ge]
|
||||
.. 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?
|
||||
|
||||
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::
|
||||
.. 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::
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
if_not A > 55 goto more_iterations
|
||||
|
||||
|
||||
Conditional jumps are compiled into 6502's branching instructions (such as ``bne`` and ``bcc``) so
|
||||
|
@ -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
|
||||
|
@ -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)*
|
||||
;
|
||||
|
93
il65/examples/numbergame.ill
Normal file
93
il65/examples/numbergame.ill
Normal 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
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,8 @@
|
||||
XY = hopla*2+hopla1
|
||||
A = "derp" * %000100
|
||||
|
||||
main.foo(1,2,3)
|
||||
|
||||
mega:
|
||||
X=1
|
||||
cool:
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user