This commit is contained in:
Irmen de Jong 2018-08-09 01:54:43 +02:00
parent cee0f5bd2a
commit 437c948c05
13 changed files with 275 additions and 13 deletions

View File

@ -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
^^^^^^^^

View File

@ -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
^^^^^^^^^^^^^^

3
il65/antlr/Makefile Normal file
View File

@ -0,0 +1,3 @@
parser:
./antlr.sh -o ../src/net/razorvine/il65/parser -listener -visitor -package net.razorvine.il65.parser tinybasic.g4

View File

@ -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 $*

View File

@ -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 $*

113
il65/antlr/il65.g4 Normal file
View File

@ -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
;

View File

@ -28,8 +28,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
grammar tinybasic;
program
: line*
EOF
;
line

13
il65/il65.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="lib" level="project" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

70
il65/src/AST.kt Normal file
View File

@ -0,0 +1,70 @@
package il65.ast
import net.razorvine.il65.parser.tinybasicParser
interface Node
interface Statement : Node
data class Program(val lines: List<Line>) : Node
data class Line(val number: Int?, val statement: Statement) : Node
data class Print(val exprlist: List<Expression>) : 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<Var>) : 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<TermListTerm>) : Node
data class Term(val factor: Factor, val factors: List<FactorListFactor>) : 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<Var> {
return emptyList()
}
fun tinybasicParser.ExprlistContext.toAst() : List<Expression> {
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())
}

20
il65/src/Main.kt Normal file
View File

@ -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<String>) {
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)
}