From 437c948c05eb77fa91ecacaad5c0a44737b73205 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 9 Aug 2018 01:54:43 +0200 Subject: [PATCH] grammar --- docs/source/programming.rst | 39 ++++-- docs/source/syntaxreference.rst | 24 +++- il65/antlr/Makefile | 3 + {antlr => il65/antlr}/antlr.sh | 2 +- {antlr => il65/antlr}/examples/tinybasic.bas | 0 {antlr => il65/antlr}/grun.sh | 2 +- il65/antlr/il65.g4 | 113 ++++++++++++++++++ .../antlr}/lib/antlr-4.7.1-complete.jar | Bin .../antlr}/lib/antlr-runtime-4.7.1.jar | Bin {antlr/grammar => il65/antlr}/tinybasic.g4 | 2 + il65/il65.iml | 13 ++ il65/src/AST.kt | 70 +++++++++++ il65/src/Main.kt | 20 ++++ 13 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 il65/antlr/Makefile rename {antlr => il65/antlr}/antlr.sh (79%) rename {antlr => il65/antlr}/examples/tinybasic.bas (100%) rename {antlr => il65/antlr}/grun.sh (77%) create mode 100644 il65/antlr/il65.g4 rename {antlr => il65/antlr}/lib/antlr-4.7.1-complete.jar (100%) rename {antlr => il65/antlr}/lib/antlr-runtime-4.7.1.jar (100%) rename {antlr/grammar => il65/antlr}/tinybasic.g4 (99%) create mode 100644 il65/il65.iml create mode 100644 il65/src/AST.kt create mode 100644 il65/src/Main.kt diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 66d508d95..1b5a96500 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -135,12 +135,12 @@ The compiler expects a ``start`` subroutine in the ``main`` block for this, taking no parameters and having no return value. As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call):: - ~ main { - sub start () -> () { - ; program entrypoint code here - return - } - } + ~ main { + sub start () -> () { + ; program entrypoint code here + return + } + } The ``main`` module is always relocated to the start of your programs address space, and the ``start`` subroutine (the entrypoint) will be on the @@ -148,18 +148,39 @@ first address. This will also be the address that the BASIC loader program (if g calls with the SYS statement. -Variables and data ------------------- +Variables and values +-------------------- -:: +Variables are named values that can change during the execution of the program. +When declaring a variable it is possible to specify the initial value it should get. +Values will usually be part of an expression or assignment statement:: 12345 ; integer number + $aa43 ; hex integer number + %100101 ; binary integer number "Hi, I am a string" ; text string -33.456e52 ; floating point number byte counter = 42 ; variable of size 8 bits, with initial value 42 +Special types: const and memory-mapped +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using ``const``, the value of the 'variable' can no longer be changed. +You'll have to specify the initial value expression. This value is then used +by the compiler everywhere you refer to the constant (and no storage is allocated +for the constant itself). + +When using ``memory``, the variable will point to specific location in memory, +rather than being newly allocated. The initial value (mandatory) must be a valid +memory address. Reading the variable will read the given data type from the +address you specified, and setting the varible will directly modify that memory location(s):: + + const byte max_age = 2000 - 1974 ; max_age will be the constant value 26 + memory word SCREENCOLORS = $d020 ; a 16-bit word at the addres $d020-$d021 + + Integers ^^^^^^^^ diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 140b37f56..396b8071b 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -136,14 +136,15 @@ Directives Identifiers ----------- -Naming things in IL65 is done via valid *identifiers*. They start with a letter, and after that, -must consist of letters, numbers, or underscores. Examples of valid identifiers:: +Naming things in IL65 is done via valid *identifiers*. They start with a letter or underscore, +and after that, a combination of letters, numbers, or underscores. Examples of valid identifiers:: a A monkey COUNTER Better_Name_2 + _something_strange_ Code blocks @@ -246,6 +247,25 @@ type identifier type storage size example var declara **@todo signed integers (byte and word)?** +Memory mapped variables +^^^^^^^^^^^^^^^^^^^^^^^ + +The ``memory`` keyword is used in front of a data type keyword, to say that no storage +should be allocated by the compiler. Instead, the value assigned to the variable (mandatory now!) +should be the *memory address* where the value is located:: + + memory byte BORDER = $d020 + + +Constants +^^^^^^^^^ + +All variables can be assigned new values unless you use the ``const`` keyword. +The initial value will now be evaluated at compile time (it must be a compile time constant expression) +and no storage is allocated for the constant:: + + const byte max_age = 99 + Reserved names ^^^^^^^^^^^^^^ diff --git a/il65/antlr/Makefile b/il65/antlr/Makefile new file mode 100644 index 000000000..3ac22bca6 --- /dev/null +++ b/il65/antlr/Makefile @@ -0,0 +1,3 @@ + +parser: + ./antlr.sh -o ../src/net/razorvine/il65/parser -listener -visitor -package net.razorvine.il65.parser tinybasic.g4 diff --git a/antlr/antlr.sh b/il65/antlr/antlr.sh similarity index 79% rename from antlr/antlr.sh rename to il65/antlr/antlr.sh index de6f9cb9b..6daeeb9cc 100755 --- a/antlr/antlr.sh +++ b/il65/antlr/antlr.sh @@ -1,6 +1,6 @@ #!/bin/sh -PROJECT=~/Projects/IL65/antlr +PROJECT=~/Projects/IL65/il65/antlr export CLASSPATH=".:${PROJECT}/lib/antlr-4.7.1-complete.jar:${CLASSPATH}" java -jar ${PROJECT}/lib/antlr-4.7.1-complete.jar $* diff --git a/antlr/examples/tinybasic.bas b/il65/antlr/examples/tinybasic.bas similarity index 100% rename from antlr/examples/tinybasic.bas rename to il65/antlr/examples/tinybasic.bas diff --git a/antlr/grun.sh b/il65/antlr/grun.sh similarity index 77% rename from antlr/grun.sh rename to il65/antlr/grun.sh index 20b9109d9..8a613e92e 100755 --- a/antlr/grun.sh +++ b/il65/antlr/grun.sh @@ -1,6 +1,6 @@ #!/bin/sh -PROJECT=~/Projects/IL65/antlr +PROJECT=~/Projects/IL65/il65/antlr export CLASSPATH=".:${PROJECT}/lib/antlr-4.7.1-complete.jar:${CLASSPATH}" java org.antlr.v4.gui.TestRig $* diff --git a/il65/antlr/il65.g4 b/il65/antlr/il65.g4 new file mode 100644 index 000000000..f13e0c64f --- /dev/null +++ b/il65/antlr/il65.g4 @@ -0,0 +1,113 @@ +/* +IL65 lexer and parser grammar +*/ + +grammar il65; + + +NAME : [a-zA-Z_][a-zA-Z0-9_]* ; +DEC_INTEGER : ('0'..'9') | (('1'..'9')('0'..'9')+); +HEX_INTEGER : '$' (('a'..'f') | ('A'..'F') | ('0'..'9'))+ ; +BIN_INTEGER : '%' ('0' | '1')+ ; + + +module : + line* + EOF + ; + +line : + directive + | vardecl + | assignment + | augassignment + ; + + +directive : + '%' singlename (literalvalue)? + ; + +vardecl: + datatype arrayspec? singlename ('=' expression)? + ; + +datatype: + 'byte' | 'word' | 'float' | 'str' | 'str_p' | 'str_s' | 'str_ps' + ; + +arrayspec: + '[' expression (',' expression)? ']' + ; + +assignment : + assign_target '=' expression + ; + +augassignment : + assign_target ('+=' | '-=' | '/=' | '//=' | '*=' | '**=' | + '<<=' | '>>=' | '<<@=' | '>>@=' | '&=' | '|=' | '^=') expression + ; + + +expression : + unary_expression + | '(' expression ')' + | expression '**' expression + | expression ('*' | '/' | '//' | '**') expression + | expression ('+' | '-' | '%') expression + | expression ('<<' | '>>' | '<<@' | '>>@' | '&' | '|' | '^') expression + | expression ('and' | 'or' | 'xor') expression + | expression ('==' | '!=' | '<' | '>' | '<=' | '>=') expression + | literalvalue + | register + | dottedname + | singlename + ; + +unary_expression: + '~' expression + | ('+' | '-') expression + | 'not' expression + ; + +singlename: + NAME + ; + +dottedname: + NAME ('.' NAME)+ + ; + +register: + 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' | 'SC' | 'SI' | 'SZ' + ; + +literalvalue: + BIN_INTEGER | HEX_INTEGER | DEC_INTEGER + | 'true' | 'false' + | array + ; + +array: + '[' expression (',' expression)* ']' + ; + + +assign_target: + register + | singlename + | dottedname + ; + +COMMENT : + ';' ~[\r\n]* -> channel(1) + ; + +WS : + [ \t] -> skip + ; + +EOL : + [\r\n]+ -> skip + ; diff --git a/antlr/lib/antlr-4.7.1-complete.jar b/il65/antlr/lib/antlr-4.7.1-complete.jar similarity index 100% rename from antlr/lib/antlr-4.7.1-complete.jar rename to il65/antlr/lib/antlr-4.7.1-complete.jar diff --git a/antlr/lib/antlr-runtime-4.7.1.jar b/il65/antlr/lib/antlr-runtime-4.7.1.jar similarity index 100% rename from antlr/lib/antlr-runtime-4.7.1.jar rename to il65/antlr/lib/antlr-runtime-4.7.1.jar diff --git a/antlr/grammar/tinybasic.g4 b/il65/antlr/tinybasic.g4 similarity index 99% rename from antlr/grammar/tinybasic.g4 rename to il65/antlr/tinybasic.g4 index 8d49e5a38..c00b68818 100644 --- a/antlr/grammar/tinybasic.g4 +++ b/il65/antlr/tinybasic.g4 @@ -28,8 +28,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. grammar tinybasic; + program : line* + EOF ; line diff --git a/il65/il65.iml b/il65/il65.iml new file mode 100644 index 000000000..4c1895405 --- /dev/null +++ b/il65/il65.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/il65/src/AST.kt b/il65/src/AST.kt new file mode 100644 index 000000000..0c8cc61c7 --- /dev/null +++ b/il65/src/AST.kt @@ -0,0 +1,70 @@ +package il65.ast + +import net.razorvine.il65.parser.tinybasicParser + +interface Node +interface Statement : Node + +data class Program(val lines: List) : Node +data class Line(val number: Int?, val statement: Statement) : Node + +data class Print(val exprlist: List) : Statement +data class If(val expression1: Expression, val relop: Char, val expression2: Expression, val then: Statement) : Statement +data class Goto(val number: Int) : Statement +data class Input(val varlist: List) : Statement +data class Let(val vara: Var, val expression: Expression) : Statement +data class Gosub(val expression: Expression): Statement +class Return: Statement +class Clear : Statement +class ListStmt : Statement +class Run : Statement +class End : Statement + +data class TermListTerm(val operator: Char, val term: Term) : Node +data class FactorListFactor(val operator: Char, val factor: Factor) : Node +data class Expression(val unaryOp: Char?, val term: Term, val terms: List) : Node +data class Term(val factor: Factor, val factors: List) : Node +data class Factor(val thing: String) : Node +data class Var(val thing: String): Node + + +fun tinybasicParser.ProgramContext.toAst(): Program = Program(this.line().map { + Line(it.number().toAst(), it.statement().toAst()) +}) + +fun tinybasicParser.NumberContext.toAst(): Int = this.DIGIT().joinToString(separator = "").toInt() + +fun tinybasicParser.StatementContext.toAst(): Statement = + when (this.children[0].text) { + "INPUT" -> Input(this.varlist().toAst()) + "IF" -> If(this.expression(0).toAst(), this.relop().toAst(), this.expression(1).toAst(), this.statement().toAst()) + "PRINT" -> Print(this.exprlist().toAst()) + "GOTO" -> Goto(this.number().toAst()) + "LET" -> Let(this.vara().toAst(), this.expression(0).toAst()) + "GOSUB" -> Gosub(this.expression(0).toAst()) + "RETURN" -> Return() + "CLEAR" -> Clear() + "LIST" -> ListStmt() + "RUN" -> Run() + "END" -> End() + else -> Let(this.vara().toAst(), this.expression(0).toAst()) +} + +fun tinybasicParser.RelopContext.toAst() : Char = this.text[0] + +fun tinybasicParser.VarlistContext.toAst() : List { + return emptyList() +} + +fun tinybasicParser.ExprlistContext.toAst() : List { + return emptyList() +} + +fun tinybasicParser.VaraContext.toAst() : Var = Var(this.text) + +fun tinybasicParser.ExpressionContext.toAst() : Expression { + val unaryOp = '+' + val term = Term(Factor("derp"), emptyList()) + return Expression(unaryOp, term, emptyList()) +} + diff --git a/il65/src/Main.kt b/il65/src/Main.kt new file mode 100644 index 000000000..82325eaf9 --- /dev/null +++ b/il65/src/Main.kt @@ -0,0 +1,20 @@ +package il65 + +import il65.ast.* +import net.razorvine.il65.parser.tinybasicLexer +import net.razorvine.il65.parser.tinybasicParser +import org.antlr.v4.runtime.CharStreams +import org.antlr.v4.runtime.CommonTokenStream + + +fun main(args: Array) { + println("Reading source file: ${args[0]}") + val input = CharStreams.fromFileName(args[0]) + val lexer = tinybasicLexer(input) + val tokens = CommonTokenStream(lexer) + val parser = tinybasicParser(tokens) + val parseTree: tinybasicParser.ProgramContext = parser.program() + val program = parseTree.toAst() + println(program) +} +