mirror of
https://github.com/irmen/prog8.git
synced 2024-09-30 15:57:06 +00:00
namespaces
This commit is contained in:
parent
0b8e3183b7
commit
4c250fdc17
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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++
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user