From b72bd805e1f8938fef5aa1a731193b30a876397e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 19 Sep 2018 02:41:35 +0200 Subject: [PATCH] sub decl checking --- compiler/antlr/prog8.g4 | 2 +- compiler/examples/mandelbrot.p8 | 5 +- compiler/examples/test.p8 | 68 ++++++++++++++-------- compiler/src/prog8/Main.kt | 5 +- compiler/src/prog8/ast/AstChecker.kt | 28 ++++++++- compiler/src/prog8/compiler/Compiler.kt | 1 + compiler/src/prog8/parser/prog8Parser.java | 22 +++---- compiler/src/prog8/stackvm/StackVm.kt | 5 +- 8 files changed, 92 insertions(+), 44 deletions(-) diff --git a/compiler/antlr/prog8.g4 b/compiler/antlr/prog8.g4 index 3fa7ba4d4..af3c30464 100644 --- a/compiler/antlr/prog8.g4 +++ b/compiler/antlr/prog8.g4 @@ -63,11 +63,11 @@ statement : | branch_stmt | subroutine | inlineasm - | labeldef | returnstmt | forloop | breakstmt | continuestmt + | labeldef // @todo whileloop, repeatloop ; diff --git a/compiler/examples/mandelbrot.p8 b/compiler/examples/mandelbrot.p8 index 437951747..b3e99081c 100644 --- a/compiler/examples/mandelbrot.p8 +++ b/compiler/examples/mandelbrot.p8 @@ -17,12 +17,13 @@ word plotx byte ploty + _vm_write_str("Calculating Mandelbrot fractal, have patience...\n") _vm_gfx_clearscr(11) for pixely in 0 to height { ; @todo 255 as upper limit doesn't work it overflows the loop for pixelx in 0 to width { - xx=flt(pixelx)/width/3+0.2 ; @todo fix division to return float always, add // integer division - yy=flt(pixely)/height/3.6+0.4 + xx=pixelx/width/3+0.2 + yy=pixely/height/3.6+0.4 x=0.0 y=0.0 diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 064c15839..303a38c06 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -6,33 +6,51 @@ sub start() -> () { - const word width = 159 - const word height = 127 - word pixelx - byte pixely - float xx - float yy - float x = 4999.999 - float y - float x2 - byte iter - word plotx = 40000 - byte ploty + if(X) goto yesx + else goto nox - ;yy = pixelx/width/3+0.2 ; @todo fix division to return float always, add // integer division - ;xx = flt(pixelx)/width/3+0.2 ; @todo fix division to return float always, add // integer division - _vm_write_num(plotx) - _vm_write_char($8d) - plotx //= 3 ; @todo fix division to return float always, add // integer division - _vm_write_num(plotx) - _vm_write_char($8d) +yesx: - x2 = x/33.33 ; @todo fix division to return float always, add // integer division - _vm_write_num(x2) - _vm_write_char($8d) - x2 = x//33.33 ; @todo fix division to return float always, add // integer division - _vm_write_num(x2) - _vm_write_char($8d) + if(X) { + A=0 + goto yesx ;; @todo fix undefined symbol error + return + } else { + A=1 + goto nox + } + + return + + sub bla() -> () { + return + sub fooz() -> () { + return + } + + sub fooz2() -> () { + return + } + return + + } + + A=45 + +nox: + word i + + for i in 10 to 20 { + if(i>12) goto fout ;; @todo fix undefined symbol error + break + continue + + + bla() ;; @todo fix undefined symbol error + } + +fout: + return } } diff --git a/compiler/src/prog8/Main.kt b/compiler/src/prog8/Main.kt index eca10572d..0ecf65a90 100644 --- a/compiler/src/prog8/Main.kt +++ b/compiler/src/prog8/Main.kt @@ -15,7 +15,10 @@ import kotlin.system.exitProcess fun main(args: Array) { println("\nProg8 compiler by Irmen de Jong (irmen@razorvine.net)") - println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") + // @todo software license string + // println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") + println("**** This is a prerelease version. Please do not distribute! ****\n") + if(args.size != 1) { System.err.println("requires one argument: name of module file") exitProcess(1) diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 319b978e7..d676b9ee4 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -139,10 +139,19 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: checkResult.add(SyntaxError("block memory address must be valid integer 0..\$ffff", block.position)) } - checkSubroutinesPrecededByReturnOrJump(block.statements) + checkSubroutinesPrecededByReturnOrJumpAndFollowedByLabelOrSub(block.statements) return super.process(block) } + override fun process(label: Label): IStatement { + // scope check + if(label.parent !is Block && label.parent !is Subroutine) { + checkResult.add(SyntaxError("Labels can only be defined in the scope of a block or within another subroutine", label.position)) + } + return super.process(label) + } + + /** * Check subroutine definition */ @@ -166,7 +175,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: err("return registers should be unique") super.process(subroutine) - checkSubroutinesPrecededByReturnOrJump(subroutine.statements) + checkSubroutinesPrecededByReturnOrJumpAndFollowedByLabelOrSub(subroutine.statements) // subroutine must contain at least one 'return' or 'goto' // (or if it has an asm block, that must contain a 'rts' or 'jmp') @@ -184,12 +193,24 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: } } + // scope check + if(subroutine.parent !is Block && subroutine.parent !is Subroutine) { + err("subroutines can only be defined in the scope of a block or within another subroutine") + } + return subroutine } - private fun checkSubroutinesPrecededByReturnOrJump(statements: MutableList) { + private fun checkSubroutinesPrecededByReturnOrJumpAndFollowedByLabelOrSub(statements: MutableList) { + // @todo hmm, or move all the subroutines at the end of the block? (no fall-through execution) var preceding: IStatement = BuiltinFunctionStatementPlaceholder + var checkNext = false for (stmt in statements) { + if(checkNext) { + if(stmt !is Label && stmt !is Subroutine) + checkResult.add(SyntaxError("preceding subroutine definition at line ${preceding.position.line} should be followed here by a label, another subroutine statement, or nothing", stmt.position)) + return + } if(stmt is Subroutine) { if(preceding !is Return && preceding !is Jump @@ -198,6 +219,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: && preceding !is BuiltinFunctionStatementPlaceholder) { checkResult.add(SyntaxError("subroutine definition should be preceded by a return, jump, vardecl, or another subroutine statement", stmt.position)) } + checkNext=true } preceding = stmt } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 7fdf63a6b..92241d9a9 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -116,6 +116,7 @@ class Compiler(private val options: CompilationOptions) { class VarGatherer(private val stackvmProg: StackVmProgram): IAstProcessor { // collect all the VarDecls to make them into one global list + // @todo maybe keep the block structure intact and allocate them per block? this is needed eventually for the actual 6502 code generation so... override fun process(decl: VarDecl): IStatement { if(decl.type == VarDeclType.MEMORY) TODO("stackVm doesn't support memory vars for now") diff --git a/compiler/src/prog8/parser/prog8Parser.java b/compiler/src/prog8/parser/prog8Parser.java index 769776672..d9a22590f 100644 --- a/compiler/src/prog8/parser/prog8Parser.java +++ b/compiler/src/prog8/parser/prog8Parser.java @@ -367,9 +367,6 @@ public class prog8Parser extends Parser { public InlineasmContext inlineasm() { return getRuleContext(InlineasmContext.class,0); } - public LabeldefContext labeldef() { - return getRuleContext(LabeldefContext.class,0); - } public ReturnstmtContext returnstmt() { return getRuleContext(ReturnstmtContext.class,0); } @@ -382,6 +379,9 @@ public class prog8Parser extends Parser { public ContinuestmtContext continuestmt() { return getRuleContext(ContinuestmtContext.class,0); } + public LabeldefContext labeldef() { + return getRuleContext(LabeldefContext.class,0); + } public StatementContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); } @@ -497,35 +497,35 @@ public class prog8Parser extends Parser { enterOuterAlt(_localctx, 15); { setState(133); - labeldef(); + returnstmt(); } break; case 16: enterOuterAlt(_localctx, 16); { setState(134); - returnstmt(); + forloop(); } break; case 17: enterOuterAlt(_localctx, 17); { setState(135); - forloop(); + breakstmt(); } break; case 18: enterOuterAlt(_localctx, 18); { setState(136); - breakstmt(); + continuestmt(); } break; case 19: enterOuterAlt(_localctx, 19); { setState(137); - continuestmt(); + labeldef(); } break; } @@ -3496,9 +3496,9 @@ public class prog8Parser extends Parser { "\2\2y\u008d\5\16\b\2z\u008d\5\24\13\2{\u008d\5\22\n\2|\u008d\5\26\f\2"+ "}\u008d\5\30\r\2~\u008d\5\36\20\2\177\u008d\5 \21\2\u0080\u008d\5\f\7"+ "\2\u0081\u008d\5$\23\2\u0082\u008d\5*\26\2\u0083\u008d\5Z.\2\u0084\u008d"+ - "\5^\60\2\u0085\u008d\5L\'\2\u0086\u008d\5J&\2\u0087\u008d\5\n\6\2\u0088"+ - "\u008d\5.\30\2\u0089\u008d\5b\62\2\u008a\u008d\5\60\31\2\u008b\u008d\5"+ - "\62\32\2\u008cy\3\2\2\2\u008cz\3\2\2\2\u008c{\3\2\2\2\u008c|\3\2\2\2\u008c"+ + "\5^\60\2\u0085\u008d\5L\'\2\u0086\u008d\5J&\2\u0087\u008d\5.\30\2\u0088"+ + "\u008d\5b\62\2\u0089\u008d\5\60\31\2\u008a\u008d\5\62\32\2\u008b\u008d"+ + "\5\n\6\2\u008cy\3\2\2\2\u008cz\3\2\2\2\u008c{\3\2\2\2\u008c|\3\2\2\2\u008c"+ "}\3\2\2\2\u008c~\3\2\2\2\u008c\177\3\2\2\2\u008c\u0080\3\2\2\2\u008c\u0081"+ "\3\2\2\2\u008c\u0082\3\2\2\2\u008c\u0083\3\2\2\2\u008c\u0084\3\2\2\2\u008c"+ "\u0085\3\2\2\2\u008c\u0086\3\2\2\2\u008c\u0087\3\2\2\2\u008c\u0088\3\2"+ diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 812084a2f..7452f4011 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -1464,7 +1464,10 @@ class StackVm(val traceOutputFile: String?) { fun main(args: Array) { println("\nProg8 StackVM by Irmen de Jong (irmen@razorvine.net)") - println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") + // @todo software license string + // println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") + println("**** This is a prerelease version. Please do not distribute! ****\n") + if(args.size != 1) { System.err.println("requires one argument: name of stackvm sourcecode file") exitProcess(1)