namespaces

This commit is contained in:
Irmen de Jong 2018-08-13 20:28:15 +02:00
parent 0b8e3183b7
commit 4c250fdc17
8 changed files with 250 additions and 204 deletions

View File

@ -14,10 +14,20 @@
X = 42
return 44
label_in_extra2:
X = 33
sub sub_in_extra2() -> () {
return
}
sub another_sub_in_extra2() -> () {
return
}
}
~ main {
~ main2 {
; this is imported
X = 42

View File

@ -1,14 +1,14 @@
%zp full
%address 33
~ extra {
~ extra3 {
; this is imported
X = 42
return 44
}
~ extra2 {
~ extra233 {
; this is imported
X = 42
@ -16,7 +16,7 @@
}
~ main {
~ mainzzz {
; this is imported
X = 42

View File

@ -1,35 +1,35 @@
~ main $c003 {
memory byte derp = $ffdd
const byte hopla=55-33
; const byte hopla2=55-hopla
const float zwop = -1.7014118345e+38
const byte hopla2=55-hopla
const float zwop = -1.7014118345e+38
const float zwop = -1.7014118345e+38 + derp.foo.bar
A = "derp" * %000100
mega:
X=1
cool:
Y=2
sub foo () -> () {
A=99
return 33
X =33
mega:
cool:
}
some_label_def: A=44
return 1+999
%breakpoint
%asminclude "derp", hopsa
%asmbinary "derp", 0, 200
}
%option enable_floats
%import imported
%import imported
%import imported
%import imported
%import imported
%import imported2
%import imported
%import imported2

View File

@ -53,11 +53,8 @@ fun loadModule(filePath: Path) : Module {
// TODO the comments:
// tokens.commentTokens().forEach { println(it) }
// convert to Ast and optimize
// convert to Ast
val moduleAst = parseTree.toAst(moduleName,true)
moduleAst.optimize()
moduleAst.linkParents()
moduleAst.checkValid()
importedModules[moduleAst.name] = moduleAst
// process imports
@ -126,14 +123,17 @@ fun main(args: Array<String>) {
try {
val filepath = Paths.get(args[0]).normalize()
val moduleAst = loadModule(filepath)
moduleAst.optimize() // one final global optimization
moduleAst.linkParents() // re-link parents in final configuration
moduleAst.checkValid() // check if final tree is valid
moduleAst.linkParents()
var globalNamespace = moduleAst.namespace()
globalNamespace.debugPrint()
// todo compile to asm...
moduleAst.lines.forEach {
println(it)
}
// moduleAst.optimize(namespace)
// moduleAst.checkValid() // check if final tree is valid
//
// // todo compile to asm...
// moduleAst.lines.forEach {
// println(it)
// }
} catch(sx: SyntaxError) {
sx.printError()
} catch (px: ParsingFailedError) {

View File

@ -48,33 +48,41 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
interface IAstProcessor {
// override the ones you want to act upon
fun process(module: Module) {
module.lines = module.lines.map { it.process(this) }
}
fun process(expr: PrefixExpression): IExpression {
expr.expression = expr.expression.process(this)
return expr
}
fun process(expr: BinaryExpression): IExpression {
expr.left = expr.left.process(this)
expr.right = expr.right.process(this)
return expr
}
fun process(directive: Directive): IStatement {
return directive
}
fun process(block: Block): IStatement {
block.statements = block.statements.map { it.process(this) }
return block
}
fun process(decl: VarDecl): IStatement {
decl.value = decl.value?.process(this)
decl.arrayspec?.process(this)
return decl
}
fun process(subroutine: Subroutine): IStatement {
subroutine.statements = subroutine.statements.map { it.process(this) }
return subroutine
}
fun process(functionCall: FunctionCall): IExpression {
functionCall.arglist = functionCall.arglist.map { it.process(this) }
return functionCall
}
fun process(jump: Jump): IStatement {
return jump
}
fun process(functionCall: FunctionCall): IExpression {
return functionCall
}
}
@ -90,6 +98,40 @@ interface IStatement : Node {
}
interface INameScope {
val name: String
val position: Position?
var statements: List<IStatement>
fun subScopes(): List<INameScope> = statements.filter { it is INameScope }.map { it as INameScope }
fun definedNames(): List<IStatement> = statements.filter { it is Label || it is VarDecl }
fun lookup(scopedName: List<String>) : IStatement? {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
fun debugPrint() {
fun printNames(indent: Int, namespace: INameScope) {
println(" ".repeat(4*indent) + "${namespace.name} -> ${namespace::class.simpleName} at ${namespace.position}")
namespace.definedNames().forEach {
val name =
when(it) {
is Label -> it.name
is VarDecl -> it.name
else -> throw AstException("expected label or vardecl")
}
println(" ".repeat(4 * (1 + indent)) + "$name -> ${it::class.simpleName} at ${it.position}")
}
namespace.subScopes().forEach {
printNames(indent+1, it)
}
}
printNames(0, this)
}
}
data class Module(val name: String,
var lines: List<IStatement>) : Node {
override var position: Position? = null
@ -106,12 +148,20 @@ data class Module(val name: String,
fun process(processor: IAstProcessor) {
processor.process(this)
}
fun namespace(): INameScope {
class GlobalNamespace(override val name: String,
override var statements: List<IStatement>,
override val position: Position?) : INameScope
return GlobalNamespace("<<<global>>>", lines, position)
}
}
data class Block(val name: String,
data class Block(override val name: String,
val address: Int?,
var statements: List<IStatement>) : IStatement {
override var statements: List<IStatement>) : IStatement, INameScope {
override var position: Position? = null
override var parent: Node? = null
@ -121,6 +171,10 @@ data class Block(val name: String,
}
override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String {
return "Block(name=$name, address=$address, ${statements.size} statements)"
}
}
@ -217,10 +271,12 @@ data class VarDecl(val type: VarDeclType,
val isScalar = arrayspec==null
val isArray = arrayspec!=null && arrayspec.y==null
val isMatrix = arrayspec?.y != null
val arraySizeX : Int?
get() = arrayspec?.x?.constValue()?.intvalue
val arraySizeY : Int?
get() = arrayspec?.y?.constValue()?.intvalue
fun arraySizeX(namespace: INameScope) : Int? {
return arrayspec?.x?.constValue(namespace)?.intvalue
}
fun arraySizeY(namespace: INameScope) : Int? {
return arrayspec?.y?.constValue(namespace)?.intvalue
}
}
@ -255,7 +311,7 @@ data class AssignTarget(val register: Register?, val identifier: Identifier?) :
interface IExpression: Node {
fun constValue() : LiteralValue?
fun constValue(namespace: INameScope): LiteralValue?
fun process(processor: IAstProcessor): IExpression
}
@ -271,7 +327,7 @@ data class PrefixExpression(val operator: String, var expression: IExpression) :
expression.linkParents(this)
}
override fun constValue(): LiteralValue? {
override fun constValue(namespace: INameScope): LiteralValue? {
throw ExpressionException("should have been optimized away before const value was asked")
}
@ -289,7 +345,7 @@ data class BinaryExpression(var left: IExpression, val operator: String, var rig
right.linkParents(this)
}
override fun constValue(): LiteralValue? {
override fun constValue(namespace: INameScope): LiteralValue? {
throw ExpressionException("should have been optimized away before const value was asked")
}
@ -332,7 +388,7 @@ data class LiteralValue(val intvalue: Int? = null,
arrayvalue?.forEach {it.linkParents(this)}
}
override fun constValue(): LiteralValue? = this
override fun constValue(namespace: INameScope): LiteralValue? = this
override fun process(processor: IAstProcessor) = this
}
@ -347,7 +403,7 @@ data class RangeExpr(var from: IExpression, var to: IExpression) : IExpression {
to.linkParents(this)
}
override fun constValue(): LiteralValue? = null
override fun constValue(namespace: INameScope): LiteralValue? = null
override fun process(processor: IAstProcessor): IExpression {
from = from.process(processor)
to = to.process(processor)
@ -364,12 +420,12 @@ data class RegisterExpr(val register: Register) : IExpression {
this.parent = parent
}
override fun constValue(): LiteralValue? = null
override fun constValue(namespace: INameScope): LiteralValue? = null
override fun process(processor: IAstProcessor) = this
}
data class Identifier(val name: String, val scope: List<String>) : IExpression {
data class Identifier(val scopedName: List<String>) : IExpression {
override var position: Position? = null
override var parent: Node? = null
@ -377,9 +433,18 @@ data class Identifier(val name: String, val scope: List<String>) : IExpression {
this.parent = parent
}
override fun constValue(): LiteralValue? {
// @todo should look up the location and return its value if that is a compile time const
return null
override fun constValue(namespace: INameScope): LiteralValue? {
val node = namespace.lookup(scopedName)
return if(node==null) null
else {
var vardecl = node as VarDecl
if(vardecl!=null){
if(vardecl.type!=VarDeclType.CONST)
throw SyntaxError("constant expected", position)
return vardecl.value?.constValue(namespace)
}
throw SyntaxError("expected a literal value", position)
}
}
override fun process(processor: IAstProcessor) = this
@ -425,7 +490,7 @@ data class FunctionCall(var location: Identifier, var arglist: List<IExpression>
arglist.forEach { it.linkParents(this) }
}
override fun constValue(): LiteralValue? {
override fun constValue(namespace: INameScope): LiteralValue? {
// if the function is a built-in function and the args are consts, should evaluate!
return null
}
@ -446,11 +511,11 @@ data class InlineAssembly(val assembly: String) : IStatement {
}
data class Subroutine(val name: String,
data class Subroutine(override val name: String,
val parameters: List<SubroutineParameter>,
val returnvalues: List<SubroutineReturnvalue>,
val address: Int?,
var statements: List<IStatement>) : IStatement {
override var statements: List<IStatement>) : IStatement, INameScope {
override var position: Position? = null
override var parent: Node? = null
@ -462,6 +527,10 @@ data class Subroutine(val name: String,
}
override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String {
return "Subroutine(name=$name, address=$address, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements)"
}
}
@ -607,8 +676,8 @@ private fun il65Parser.StatementContext.toAst(withPosition: Boolean) : IStatemen
val jump = unconditionaljump()?.toAst(withPosition)
if(jump!=null) return jump
val returnstmt = returnstmt()
if(returnstmt!=null) return Return(returnstmt.expression_list().toAst(withPosition))
val returnstmt = returnstmt()?.toAst(withPosition)
if(returnstmt!=null) return returnstmt
val sub = subroutine()?.toAst(withPosition)
if(sub!=null) return sub
@ -627,6 +696,11 @@ private fun il65Parser.InlineasmContext.toAst(withPosition: Boolean): IStatement
}
private fun il65Parser.ReturnstmtContext.toAst(withPosition: Boolean) : IStatement {
val values = expression_list()
return Return(values?.toAst(withPosition) ?: emptyList())
}
private fun il65Parser.UnconditionaljumpContext.toAst(withPosition: Boolean): IStatement {
val address = integerliteral()?.toAst()
@ -795,17 +869,14 @@ private fun il65Parser.Expression_listContext.toAst(withPosition: Boolean) = exp
private fun il65Parser.IdentifierContext.toAst(withPosition: Boolean) : Identifier {
val ident = Identifier(text, emptyList())
val ident = Identifier(listOf(text))
ident.position = toPosition(withPosition)
return ident
}
private fun il65Parser.Scoped_identifierContext.toAst(withPosition: Boolean) : Identifier {
val names = NAME()
val name = names.last().text
val scope = names.take(names.size-1)
val ident = Identifier(name, scope.map { it.text })
val ident = Identifier(NAME().map { it.text })
ident.position = toPosition(withPosition)
return ident
}

View File

@ -3,8 +3,8 @@ package il65.ast
import il65.ParsingFailedError
fun Module.checkValid() {
val checker = AstChecker()
fun Module.checkValid(globalNamespace: INameScope) {
val checker = AstChecker(globalNamespace)
this.process(checker)
val checkResult = checker.result()
checkResult.forEach {
@ -15,7 +15,7 @@ fun Module.checkValid() {
}
class AstChecker : IAstProcessor {
class AstChecker(val globalNamespace: INameScope) : IAstProcessor {
private val checkResult: MutableList<SyntaxError> = mutableListOf()
private val blockNames: HashMap<String, Position?> = hashMapOf()
@ -23,16 +23,8 @@ class AstChecker : IAstProcessor {
return checkResult
}
override fun process(module: Module) {
module.lines.forEach { it.process(this) }
}
override fun process(functionCall: FunctionCall): IExpression {
functionCall.arglist.map{it.process(this)}
return functionCall
}
override fun process(jump: Jump): IStatement {
super.process(jump)
if(jump.address!=null && (jump.address < 0 || jump.address > 65535))
checkResult.add(SyntaxError("jump address must be valid 0..\$ffff", jump.position))
return jump
@ -48,7 +40,8 @@ class AstChecker : IAstProcessor {
} else {
blockNames[block.name] = block.position
}
block.statements.forEach { it.process(this) }
super.process(block)
// check if labels are unique
val labels = block.statements.filter { it is Label }.map { it as Label }
@ -81,7 +74,7 @@ class AstChecker : IAstProcessor {
if(uniqueResults.size!=subroutine.returnvalues.size)
err("return registers should be unique")
subroutine.statements.forEach { it.process(this) }
super.process(subroutine)
// check if labels are unique
val labels = subroutine.statements.filter { it is Label }.map { it as Label }
@ -143,104 +136,7 @@ class AstChecker : IAstProcessor {
}
}
decl.arrayspec?.process(this)
decl.value?.process(this)
return decl
}
private fun checkConstInitializerValueArray(decl: VarDecl) {
val value = decl.value as LiteralValue
// init value should either be a scalar or an array with the same dimensions as the arrayspec.
if(decl.isArray) {
if(value.arrayvalue==null) {
checkValueRange(decl.datatype, value.constValue()!!, value.position)
}
else {
if (value.arrayvalue.size != decl.arraySizeX)
checkResult.add(SyntaxError("initializer array size mismatch (expecting ${decl.arraySizeX})", decl.position))
else {
value.arrayvalue.forEach {
checkValueRange(decl.datatype, it.constValue()!!, it.position)
}
}
}
}
if(decl.isMatrix) {
if(value.arrayvalue==null) {
checkValueRange(decl.datatype, value.constValue()!!, value.position)
}
else {
if (value.arrayvalue.size != decl.arraySizeX!! * decl.arraySizeY!!)
checkResult.add(SyntaxError("initializer array size mismatch (expecting ${decl.arraySizeX!! * decl.arraySizeY!!}", decl.position))
else {
value.arrayvalue.forEach {
checkValueRange(decl.datatype, it.constValue()!!, it.position)
}
}
}
}
}
private fun checkValueRange(datatype: DataType, value: LiteralValue, position: Position?) {
fun err(msg: String) {
checkResult.add(SyntaxError(msg, position))
}
when (datatype) {
DataType.FLOAT -> {
val number = value.asFloat(false)
if (number!=null && (number > 1.7014118345e+38 || number < -1.7014118345e+38))
err("floating point value out of range for MFLPT format")
}
DataType.BYTE -> {
val number = value.asInt(false)
if (number!=null && (number < 0 || number > 255))
err("value out of range for unsigned byte")
}
DataType.WORD -> {
val number = value.asInt(false)
if (number!=null && (number < 0 || number > 65535))
err("value out of range for unsigned word")
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = value.strvalue
if (str!=null && (str.isEmpty() || str.length > 65535))
err("string length must be 1..65535")
}
}
}
private fun checkConstInitializerValueScalar(decl: VarDecl) {
fun err(msg: String) {
checkResult.add(SyntaxError(msg, decl.position))
}
val value = decl.value as LiteralValue
when (decl.datatype) {
DataType.FLOAT -> {
val number = value.asFloat(false)
if (number == null)
err("need a const float initializer value")
}
DataType.BYTE -> {
val number = value.asInt(false)
if (number == null)
err("need a const integer initializer value")
}
DataType.WORD -> {
val number = value.asInt(false)
if (number == null)
err("need a const integer initializer value")
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = value.strvalue
if (str == null)
err("need a const string initializer value")
}
}
return super.process(decl)
}
/**
@ -307,7 +203,100 @@ class AstChecker : IAstProcessor {
}
else -> throw AstException("invalid directive ${directive.directive}")
}
return directive
return super.process(directive)
}
private fun checkConstInitializerValueArray(decl: VarDecl) {
val value = decl.value as LiteralValue
// init value should either be a scalar or an array with the same dimensions as the arrayspec.
if(decl.isArray) {
if(value.arrayvalue==null) {
checkValueRange(decl.datatype, value.constValue(globalNamespace)!!, value.position)
}
else {
val expected = decl.arraySizeX(globalNamespace)
if (value.arrayvalue.size != expected)
checkResult.add(SyntaxError("initializer array size mismatch (expecting $expected)", decl.position))
else {
value.arrayvalue.forEach {
checkValueRange(decl.datatype, it.constValue(globalNamespace)!!, it.position)
}
}
}
}
if(decl.isMatrix) {
if(value.arrayvalue==null) {
checkValueRange(decl.datatype, value.constValue(globalNamespace)!!, value.position)
}
else {
val expected = decl.arraySizeX(globalNamespace)!! * decl.arraySizeY(globalNamespace)!!
if (value.arrayvalue.size != expected)
checkResult.add(SyntaxError("initializer array size mismatch (expecting $expected)", decl.position))
else {
value.arrayvalue.forEach {
checkValueRange(decl.datatype, it.constValue(globalNamespace)!!, it.position)
}
}
}
}
}
private fun checkValueRange(datatype: DataType, value: LiteralValue, position: Position?) {
fun err(msg: String) {
checkResult.add(SyntaxError(msg, position))
}
when (datatype) {
DataType.FLOAT -> {
val number = value.asFloat(false)
if (number!=null && (number > 1.7014118345e+38 || number < -1.7014118345e+38))
err("floating point value out of range for MFLPT format")
}
DataType.BYTE -> {
val number = value.asInt(false)
if (number!=null && (number < 0 || number > 255))
err("value out of range for unsigned byte")
}
DataType.WORD -> {
val number = value.asInt(false)
if (number!=null && (number < 0 || number > 65535))
err("value out of range for unsigned word")
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = value.strvalue
if (str!=null && (str.isEmpty() || str.length > 65535))
err("string length must be 1..65535")
}
}
}
private fun checkConstInitializerValueScalar(decl: VarDecl) {
fun err(msg: String) {
checkResult.add(SyntaxError(msg, decl.position))
}
val value = decl.value as LiteralValue
when (decl.datatype) {
DataType.FLOAT -> {
val number = value.asFloat(false)
if (number == null)
err("need a const float initializer value")
}
DataType.BYTE -> {
val number = value.asInt(false)
if (number == null)
err("need a const integer initializer value")
}
DataType.WORD -> {
val number = value.asInt(false)
if (number == null)
err("need a const integer initializer value")
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = value.strvalue
if (str == null)
err("need a const string initializer value")
}
}
}
}

View File

@ -3,8 +3,8 @@ package il65.ast
import kotlin.math.pow
fun Module.optimize() {
val optimizer = AstOptimizer()
fun Module.optimize(globalNamespace: INameScope) {
val optimizer = AstOptimizer(globalNamespace)
this.process(optimizer)
if(optimizer.optimizationsDone==0)
println("[${this.name}] 0 optimizations performed")
@ -14,10 +14,11 @@ fun Module.optimize() {
optimizer.reset()
this.process(optimizer)
}
this.linkParents() // re-link in final configuration
}
class AstOptimizer : IAstProcessor {
class AstOptimizer(val globalNamespace: INameScope) : IAstProcessor {
var optimizationsDone: Int = 0
private set
@ -25,30 +26,6 @@ class AstOptimizer : IAstProcessor {
optimizationsDone = 0
}
override fun process(module: Module) {
module.lines = module.lines.map { it.process(this) }
}
override fun process(block: Block): IStatement {
block.statements = block.statements.map { it.process(this) }
return block
}
override fun process(subroutine: Subroutine): IStatement {
subroutine.statements = subroutine.statements.map { it.process(this) }
return subroutine
}
override fun process(functionCall: FunctionCall): IExpression {
functionCall.arglist = functionCall.arglist.map{it.process(this)}
return functionCall
}
override fun process(decl: VarDecl): IStatement {
decl.value = decl.value?.process(this)
decl.arrayspec?.process(this)
return decl
}
/**
* Try to process a unary prefix expression.
@ -56,7 +33,7 @@ class AstOptimizer : IAstProcessor {
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
*/
override fun process(expr: PrefixExpression): IExpression {
expr.expression = expr.expression.process(this) // process sub expression first
super.process(expr)
val subexpr = expr.expression
if (subexpr is LiteralValue) {
@ -106,13 +83,11 @@ class AstOptimizer : IAstProcessor {
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
*/
override fun process(expr: BinaryExpression): IExpression {
val evaluator = ConstExprEvaluator()
// process sub expressions first
expr.left = expr.left.process(this)
expr.right = expr.right.process(this)
super.process(expr)
val leftconst = expr.left.constValue()
val rightconst = expr.right.constValue()
val evaluator = ConstExprEvaluator()
val leftconst = expr.left.constValue(globalNamespace)
val rightconst = expr.right.constValue(globalNamespace)
return when {
leftconst != null && rightconst != null -> {
optimizationsDone++

View File

@ -23,6 +23,7 @@ class ImportedAstChecker : IAstProcessor {
}
override fun process(module: Module) {
super.process(module)
val newLines : MutableList<IStatement> = mutableListOf()
module.lines.forEach {
val stmt = it.process(this)