mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
bunch of new builtin functions, const expression evaluation now also done for array literals
This commit is contained in:
parent
76d07a2de8
commit
d9865a4b97
@ -1,5 +1,5 @@
|
|||||||
.wy-nav-content {
|
.wy-nav-content {
|
||||||
max-width: 900px;
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* override table width restrictions */
|
/* override table width restrictions */
|
||||||
|
@ -172,6 +172,10 @@ Note that the various keywords for the data type and variable type (``byte``, ``
|
|||||||
cannot be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
cannot be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
||||||
for instance.
|
for instance.
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
matrix datatype
|
||||||
|
|
||||||
|
|
||||||
Variables that represent CPU hardware registers
|
Variables that represent CPU hardware registers
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -423,14 +427,14 @@ log10(x)
|
|||||||
sqrt(x)
|
sqrt(x)
|
||||||
Square root.
|
Square root.
|
||||||
|
|
||||||
max(x [, y, ...])
|
|
||||||
Maximum of the values x, y, ...
|
|
||||||
|
|
||||||
min(x [, y, ...])
|
|
||||||
Minumum of the values x, y, ...
|
|
||||||
|
|
||||||
round(x)
|
round(x)
|
||||||
Rounds the floating point to an integer.
|
Rounds the floating point to the closest integer.
|
||||||
|
|
||||||
|
floor (x)
|
||||||
|
Rounds the floating point down to an integer towards minus infinity.
|
||||||
|
|
||||||
|
ceil(x)
|
||||||
|
Rounds the floating point up to an integer towards positive infinity.
|
||||||
|
|
||||||
rad(x)
|
rad(x)
|
||||||
Degrees to radians.
|
Degrees to radians.
|
||||||
@ -438,47 +442,66 @@ rad(x)
|
|||||||
deg(x)
|
deg(x)
|
||||||
Radians to degrees.
|
Radians to degrees.
|
||||||
|
|
||||||
_lsl(x)
|
max(x)
|
||||||
|
Maximum of the values in the non-scalar (array or matrix) value x
|
||||||
|
|
||||||
|
min(x)
|
||||||
|
Minimum of the values in the non-scalar (array or matrix) value x
|
||||||
|
|
||||||
|
avg(x)
|
||||||
|
Average of the values in the non-scalar (array or matrix) value x
|
||||||
|
|
||||||
|
sum(x)
|
||||||
|
Sum of the values in the non-scalar (array or matrix) value x
|
||||||
|
|
||||||
|
len(x)
|
||||||
|
Number of values in the non-scalar (array or matrix) value x.
|
||||||
|
(This is different from the number of *bytes* in memory if the datatype isn't byte)
|
||||||
|
|
||||||
|
any(x)
|
||||||
|
1 ('true') if any of the values in the non-scalar (array or matrix) value x is 'true' (not zero), else 0 ('false')
|
||||||
|
|
||||||
|
all(x)
|
||||||
|
1 ('true') if all of the values in the non-scalar (array or matrix) value x are 'true' (not zero), else 0 ('false')
|
||||||
|
|
||||||
|
lsl(x)
|
||||||
Shift the bits in x (byte or word) one position to the left.
|
Shift the bits in x (byte or word) one position to the left.
|
||||||
Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)
|
Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)
|
||||||
Modifies in-place but also returns the new value.
|
Modifies in-place but also returns the new value.
|
||||||
|
|
||||||
_lsr(x)
|
lsr(x)
|
||||||
Shift the bits in x (byte or word) one position to the right.
|
Shift the bits in x (byte or word) one position to the right.
|
||||||
The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag)
|
The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag)
|
||||||
Modifies in-place but also returns the new value.
|
Modifies in-place but also returns the new value.
|
||||||
|
|
||||||
_rol(x)
|
rol(x)
|
||||||
Rotate the bits in x (byte or word) one position to the left.
|
Rotate the bits in x (byte or word) one position to the left.
|
||||||
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
||||||
while the highest bit will become the new Carry flag value.
|
while the highest bit will become the new Carry flag value.
|
||||||
(essentially, it is a 9-bit or 17-bit rotation)
|
(essentially, it is a 9-bit or 17-bit rotation)
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
_rol2(x)
|
rol2(x)
|
||||||
Like _rol but now as 8-bit or 16-bit rotation.
|
Like _rol but now as 8-bit or 16-bit rotation.
|
||||||
It uses some extra logic to not consider the carry flag as extra rotation bit.
|
It uses some extra logic to not consider the carry flag as extra rotation bit.
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
_ror(x)
|
ror(x)
|
||||||
Rotate the bits in x (byte or word) one position to the right.
|
Rotate the bits in x (byte or word) one position to the right.
|
||||||
This uses the CPU's rotate semantics: the highest bit will be set to the current value of the Carry flag,
|
This uses the CPU's rotate semantics: the highest bit will be set to the current value of the Carry flag,
|
||||||
while bit 0 will become the new Carry flag value.
|
while bit 0 will become the new Carry flag value.
|
||||||
(essentially, it is a 9-bit or 17-bit rotation)
|
(essentially, it is a 9-bit or 17-bit rotation)
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
_ror2(x)
|
ror2(x)
|
||||||
Like _ror but now as 8-bit or 16-bit rotation.
|
Like _ror but now as 8-bit or 16-bit rotation.
|
||||||
It uses some extra logic to not consider the carry flag as extra rotation bit.
|
It uses some extra logic to not consider the carry flag as extra rotation bit.
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
_P_carry(bit)
|
P_carry(bit)
|
||||||
Set (or clear) the CPU status register Carry flag. No result value.
|
Set (or clear) the CPU status register Carry flag. No result value.
|
||||||
(translated into ``SEC`` or ``CLC`` cpu instruction)
|
(translated into ``SEC`` or ``CLC`` cpu instruction)
|
||||||
|
|
||||||
_P_irqd(bit)
|
P_irqd(bit)
|
||||||
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
||||||
(translated into ``SEI`` or ``CLI`` cpu instruction)
|
(translated into ``SEI`` or ``CLI`` cpu instruction)
|
||||||
|
|
||||||
.. todo::
|
|
||||||
additional builtins such as: avg, sum, abs, round
|
|
||||||
|
@ -127,7 +127,7 @@ The following 6502 CPU hardware registers are directly usable in program code (a
|
|||||||
|
|
||||||
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits)
|
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits)
|
||||||
- ``AX``, ``AY``, ``XY`` surrogate 16-bit registers: LSB-order (lo/hi) combined register pairs
|
- ``AX``, ``AY``, ``XY`` surrogate 16-bit registers: LSB-order (lo/hi) combined register pairs
|
||||||
- the status register (P) carry flag and interrupt disable flag can be written via the ``_P_carry`` and ``_P_irqd`` builtin functions.
|
- the status register (P) carry flag and interrupt disable flag can be written via the ``P_carry`` and ``P_irqd`` builtin functions.
|
||||||
|
|
||||||
Subroutine Calling Conventions
|
Subroutine Calling Conventions
|
||||||
------------------------------
|
------------------------------
|
||||||
|
@ -5,6 +5,25 @@
|
|||||||
|
|
||||||
~ main $c003 {
|
~ main $c003 {
|
||||||
|
|
||||||
|
const word len1 = len([1,2,3,wa1, wa2, ws1, all1])
|
||||||
|
const word wa1 = abs(-999)
|
||||||
|
const byte wa2 = abs(-99)
|
||||||
|
const float wa3 = abs(-1.23456)
|
||||||
|
const float avg1 = avg([-1.23456, 99999])
|
||||||
|
const float sum1 = sum([-1.23456, 99999])
|
||||||
|
const word ws1 = floor(sum([1,2,3,4.9]))
|
||||||
|
const word ws2 = ceil(avg([1,2,3,4.9]))
|
||||||
|
const word ws3 = round(sum([1,2,3,4.9]))
|
||||||
|
const word any1 = any([0,0,0,0,0,22,0,0])
|
||||||
|
const word any2 = any([2+sin(2), 2])
|
||||||
|
const word all1 = all([0,0,0,0,0,22,0,0])
|
||||||
|
const word all2 = all([0.0])
|
||||||
|
const word all3 = all([wa1, wa2, ws1, all1])
|
||||||
|
|
||||||
|
const word max1 = max([-1,-2,3,99+22])
|
||||||
|
|
||||||
|
const word min1 = min([1,2,3,99+22])
|
||||||
|
|
||||||
|
|
||||||
A = X>2
|
A = X>2
|
||||||
X = Y>Y
|
X = Y>Y
|
||||||
@ -15,12 +34,12 @@
|
|||||||
str ascending5 = "z" to "z"
|
str ascending5 = "z" to "z"
|
||||||
const byte cc = 4 + (2==9)
|
const byte cc = 4 + (2==9)
|
||||||
byte cc2 = 4 - (2==10)
|
byte cc2 = 4 - (2==10)
|
||||||
memory byte derp = max($ffdd)
|
memory byte derp = max([$ffdd])
|
||||||
memory byte derpA = abs(-2.5-0.5)
|
memory byte derpA = abs(-2.5-0.5)
|
||||||
memory byte derpB = max(1, 2.2, 4.4, 100)
|
memory byte derpB = max([1, 2.2, 4.4, 100])
|
||||||
memory byte cderp = min($ffdd)+ (1/1)
|
memory byte cderp = min([$ffdd])+ (1/1)
|
||||||
memory byte cderpA = min($ffdd, 10, 20, 30)
|
memory byte cderpA = min([$ffdd, 10, 20, 30])
|
||||||
memory byte cderpB = min(1, 2.2, 4.4, 100)
|
memory byte cderpB = min([1, 2.2, 4.4, 100])
|
||||||
memory byte derp2 = 2+$ffdd+round(10*sin(3))
|
memory byte derp2 = 2+$ffdd+round(10*sin(3))
|
||||||
const byte hopla=55-33
|
const byte hopla=55-33
|
||||||
const byte hopla3=100+(-hopla)
|
const byte hopla3=100+(-hopla)
|
||||||
@ -47,8 +66,8 @@
|
|||||||
byte equalQQ = 4==4
|
byte equalQQ = 4==4
|
||||||
const byte equalQQ2 = (4+hopla)>0
|
const byte equalQQ2 = (4+hopla)>0
|
||||||
|
|
||||||
_P_carry(1)
|
P_carry(1)
|
||||||
_P_irqd(0)
|
P_irqd(0)
|
||||||
|
|
||||||
equalQQ = foo(33)
|
equalQQ = foo(33)
|
||||||
equalQQ = main.foo(33)
|
equalQQ = main.foo(33)
|
||||||
@ -73,16 +92,16 @@
|
|||||||
|
|
||||||
if(6==6) {
|
if(6==6) {
|
||||||
A=sin(X)
|
A=sin(X)
|
||||||
X=max(1,2,Y)
|
X=max([1,2,Y])
|
||||||
X=min(1,2,Y)
|
X=min([1,2,Y])
|
||||||
X=_lsl(1)
|
X=lsl(1)
|
||||||
X=_lsl(Y)
|
X=lsl(Y)
|
||||||
_P_carry(0)
|
P_carry(0)
|
||||||
_P_carry(Y) ; TODO error
|
P_carry(Y) ; TODO error
|
||||||
_P_carry(9.99) ; TODO error
|
P_carry(9.99) ; TODO error
|
||||||
_P_irqd(0)
|
P_irqd(0)
|
||||||
_P_irqd(Y) ; TODO error
|
P_irqd(Y) ; TODO error
|
||||||
_P_irqd(9.99) ; TODO error
|
P_irqd(9.99) ; TODO error
|
||||||
} else X=33
|
} else X=33
|
||||||
|
|
||||||
if(6>36) {
|
if(6>36) {
|
||||||
|
@ -21,11 +21,12 @@ fun main(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
val filepath = Paths.get(args[0]).normalize()
|
val filepath = Paths.get(args[0]).normalize()
|
||||||
val moduleAst = importModule(filepath)
|
val moduleAst = importModule(filepath)
|
||||||
moduleAst.linkParents()
|
moduleAst.linkParents()
|
||||||
val globalNamespace = moduleAst.namespace()
|
val globalNamespace = moduleAst.definingScope()
|
||||||
//globalNamespace.debugPrint()
|
// globalNamespace.debugPrint()
|
||||||
|
|
||||||
|
|
||||||
// perform syntax checks and optimizations
|
// perform syntax checks and optimizations
|
||||||
@ -35,7 +36,7 @@ fun main(args: Array<String>) {
|
|||||||
moduleAst.checkValid(globalNamespace) // check if tree is valid
|
moduleAst.checkValid(globalNamespace) // check if tree is valid
|
||||||
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers()
|
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers()
|
||||||
moduleAst.optimizeStatements(globalNamespace, allScopedSymbolDefinitions)
|
moduleAst.optimizeStatements(globalNamespace, allScopedSymbolDefinitions)
|
||||||
val globalNamespaceAfterOptimize = moduleAst.namespace() // it could have changed in the meantime
|
val globalNamespaceAfterOptimize = moduleAst.definingScope() // it could have changed in the meantime
|
||||||
moduleAst.checkValid(globalNamespaceAfterOptimize) // check if final tree is valid
|
moduleAst.checkValid(globalNamespaceAfterOptimize) // check if final tree is valid
|
||||||
moduleAst.checkRecursion() // check if there are recursive subroutine calls
|
moduleAst.checkRecursion() // check if there are recursive subroutine calls
|
||||||
|
|
||||||
@ -67,7 +68,10 @@ fun main(args: Array<String>) {
|
|||||||
// val assembler = intermediate.compileToAssembly()
|
// val assembler = intermediate.compileToAssembly()
|
||||||
// assembler.assemble(compilerOptions, "input", "output")
|
// assembler.assemble(compilerOptions, "input", "output")
|
||||||
// val monitorfile = assembler.generateBreakpointList()
|
// val monitorfile = assembler.generateBreakpointList()
|
||||||
//
|
|
||||||
|
val endTime = System.currentTimeMillis()
|
||||||
|
println("Compilation time: ${(endTime-startTime)/1000.0} sec.")
|
||||||
|
|
||||||
// // start the vice emulator
|
// // start the vice emulator
|
||||||
// val program = "foo"
|
// val program = "foo"
|
||||||
// val cmdline = listOf("x64", "-moncommands", monitorfile,
|
// val cmdline = listOf("x64", "-moncommands", monitorfile,
|
||||||
|
@ -161,6 +161,10 @@ interface IAstProcessor {
|
|||||||
fun process(label: Label): IStatement {
|
fun process(label: Label): IStatement {
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun process(literalValue: LiteralValue): LiteralValue {
|
||||||
|
return literalValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -173,7 +177,7 @@ interface Node {
|
|||||||
if(scope!=null) {
|
if(scope!=null) {
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
if(this is Label && this.name.startsWith("pseudo::")) {
|
if(this is Label && this.name.startsWith("builtin::")) {
|
||||||
return BuiltinFunctionScopePlaceholder
|
return BuiltinFunctionScopePlaceholder
|
||||||
}
|
}
|
||||||
throw FatalAstException("scope missing from $this")
|
throw FatalAstException("scope missing from $this")
|
||||||
@ -248,7 +252,7 @@ interface INameScope {
|
|||||||
} else {
|
} else {
|
||||||
// unqualified name, find the scope the statement is in, look in that first
|
// unqualified name, find the scope the statement is in, look in that first
|
||||||
var statementScope = statement
|
var statementScope = statement
|
||||||
while(true) {
|
while(statementScope !is ParentSentinel) {
|
||||||
val localScope = statementScope.definingScope()
|
val localScope = statementScope.definingScope()
|
||||||
val result = localScope.labelsAndVariables()[scopedName[0]]
|
val result = localScope.labelsAndVariables()[scopedName[0]]
|
||||||
if (result != null)
|
if (result != null)
|
||||||
@ -259,6 +263,7 @@ interface INameScope {
|
|||||||
// not found in this scope, look one higher up
|
// not found in this scope, look one higher up
|
||||||
statementScope = statementScope.parent
|
statementScope = statementScope.parent
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,6 +321,14 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
|||||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object BuiltinFunctionStatementPlaceholder : IStatement {
|
||||||
|
override var position: Position? = null
|
||||||
|
override var parent: Node = ParentSentinel
|
||||||
|
override fun linkParents(parent: Node) {}
|
||||||
|
override fun process(processor: IAstProcessor): IStatement = this
|
||||||
|
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
||||||
|
}
|
||||||
|
|
||||||
class Module(override val name: String,
|
class Module(override val name: String,
|
||||||
override var statements: MutableList<IStatement>) : Node, INameScope {
|
override var statements: MutableList<IStatement>) : Node, INameScope {
|
||||||
override var position: Position? = null
|
override var position: Position? = null
|
||||||
@ -334,47 +347,49 @@ class Module(override val name: String,
|
|||||||
processor.process(this)
|
processor.process(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun namespace(): INameScope {
|
private val theGlobalNamespace by lazy {
|
||||||
class GlobalNamespace(override val name: String,
|
GlobalNamespace("<<<global>>>", statements, position)
|
||||||
override var statements: MutableList<IStatement>,
|
|
||||||
override val position: Position?) : INameScope {
|
|
||||||
|
|
||||||
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used
|
|
||||||
|
|
||||||
override fun usedNames(): Set<String> = scopedNamesUsed
|
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
|
||||||
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
|
||||||
// pseudo functions always exist, return a dummy statement for them
|
|
||||||
val pseudo = Label("pseudo::${scopedName.last()}")
|
|
||||||
pseudo.position = statement.position
|
|
||||||
pseudo.parent = ParentSentinel
|
|
||||||
return pseudo
|
|
||||||
}
|
|
||||||
val stmt = super.lookup(scopedName, statement)
|
|
||||||
if(stmt!=null) {
|
|
||||||
val targetScopedName = when(stmt) {
|
|
||||||
is Label -> stmt.scopedname
|
|
||||||
is VarDecl -> stmt.scopedname
|
|
||||||
is Block -> stmt.scopedname
|
|
||||||
is Subroutine -> stmt.scopedname
|
|
||||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
|
||||||
}
|
|
||||||
registerUsedName(targetScopedName.joinToString("."))
|
|
||||||
}
|
|
||||||
return stmt
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun registerUsedName(name: String) {
|
|
||||||
scopedNamesUsed.add(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GlobalNamespace("<<<global>>>", statements, position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun definingScope(): INameScope = theGlobalNamespace
|
||||||
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||||
|
|
||||||
|
|
||||||
|
private class GlobalNamespace(override val name: String,
|
||||||
|
override var statements: MutableList<IStatement>,
|
||||||
|
override val position: Position?) : INameScope {
|
||||||
|
|
||||||
|
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used
|
||||||
|
|
||||||
|
override fun usedNames(): Set<String> = scopedNamesUsed
|
||||||
|
|
||||||
|
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
||||||
|
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
||||||
|
// builtin functions always exist, return a dummy statement for them
|
||||||
|
val builtinPlaceholder = Label("builtin::${scopedName.last()}")
|
||||||
|
builtinPlaceholder.position = statement.position
|
||||||
|
builtinPlaceholder.parent = ParentSentinel
|
||||||
|
return builtinPlaceholder
|
||||||
|
}
|
||||||
|
val stmt = super.lookup(scopedName, statement)
|
||||||
|
if(stmt!=null) {
|
||||||
|
val targetScopedName = when(stmt) {
|
||||||
|
is Label -> stmt.scopedname
|
||||||
|
is VarDecl -> stmt.scopedname
|
||||||
|
is Block -> stmt.scopedname
|
||||||
|
is Subroutine -> stmt.scopedname
|
||||||
|
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||||
|
}
|
||||||
|
registerUsedName(targetScopedName.joinToString("."))
|
||||||
|
}
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun registerUsedName(name: String) {
|
||||||
|
scopedNamesUsed.add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -575,7 +590,7 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(namespace: INameScope): LiteralValue? {
|
override fun constValue(namespace: INameScope): LiteralValue? {
|
||||||
throw ExpressionException("expression should have been optimized away into a single value, before const value was requested (this error is often caused by another)", position)
|
throw FatalAstException("binary expression should have been optimized away into a single value, before const value was requested (this error is often caused by another) pos=$position")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||||
@ -585,7 +600,7 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I
|
|||||||
class LiteralValue(val intvalue: Int? = null,
|
class LiteralValue(val intvalue: Int? = null,
|
||||||
val floatvalue: Double? = null,
|
val floatvalue: Double? = null,
|
||||||
val strvalue: String? = null,
|
val strvalue: String? = null,
|
||||||
val arrayvalue: List<IExpression>? = null) : IExpression {
|
val arrayvalue: MutableList<IExpression>? = null) : IExpression {
|
||||||
override var position: Position? = null
|
override var position: Position? = null
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
|
override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
|
||||||
@ -596,7 +611,7 @@ class LiteralValue(val intvalue: Int? = null,
|
|||||||
floatvalue!=null -> floatvalue.toInt()
|
floatvalue!=null -> floatvalue.toInt()
|
||||||
else -> {
|
else -> {
|
||||||
if((strvalue!=null || arrayvalue!=null) && errorIfNotNumeric)
|
if((strvalue!=null || arrayvalue!=null) && errorIfNotNumeric)
|
||||||
throw AstException("attempt to get int value from non-integer $this")
|
throw AstException("attempt to get int value from non-numeric $this")
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,7 +623,7 @@ class LiteralValue(val intvalue: Int? = null,
|
|||||||
intvalue!=null -> intvalue.toDouble()
|
intvalue!=null -> intvalue.toDouble()
|
||||||
else -> {
|
else -> {
|
||||||
if((strvalue!=null || arrayvalue!=null) && errorIfNotNumeric)
|
if((strvalue!=null || arrayvalue!=null) && errorIfNotNumeric)
|
||||||
throw AstException("attempt to get float value from non-integer $this")
|
throw AstException("attempt to get float value from non-numeric $this")
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -626,7 +641,11 @@ class LiteralValue(val intvalue: Int? = null,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(namespace: INameScope): LiteralValue? = this
|
override fun constValue(namespace: INameScope): LiteralValue? = this
|
||||||
override fun process(processor: IAstProcessor) = this
|
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "LiteralValue(int=$intvalue, float=$floatvalue, str=$strvalue, array=$arrayvalue pos=$position)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -751,14 +770,21 @@ class FunctionCall(override var target: IdentifierReference, override var arglis
|
|||||||
"round" -> builtinRound(arglist, position, namespace)
|
"round" -> builtinRound(arglist, position, namespace)
|
||||||
"rad" -> builtinRad(arglist, position, namespace)
|
"rad" -> builtinRad(arglist, position, namespace)
|
||||||
"deg" -> builtinDeg(arglist, position, namespace)
|
"deg" -> builtinDeg(arglist, position, namespace)
|
||||||
"_lsl" -> builtinLsl(arglist, position, namespace)
|
"sum" -> builtinSum(arglist, position, namespace)
|
||||||
"_lsr" -> builtinLsr(arglist, position, namespace)
|
"avg" -> builtinAvg(arglist, position, namespace)
|
||||||
"_rol" -> throw ExpressionException("builtin function _rol can't be used in expressions because it doesn't return a value", position)
|
"len" -> builtinLen(arglist, position, namespace)
|
||||||
"_rol2" -> throw ExpressionException("builtin function _rol2 can't be used in expressions because it doesn't return a value", position)
|
"any" -> builtinAny(arglist, position, namespace)
|
||||||
"_ror" -> throw ExpressionException("builtin function _ror can't be used in expressions because it doesn't return a value", position)
|
"all" -> builtinAll(arglist, position, namespace)
|
||||||
"_ror2" -> throw ExpressionException("builtin function _ror2 can't be used in expressions because it doesn't return a value", position)
|
"floor" -> builtinFloor(arglist, position, namespace)
|
||||||
"_P_carry" -> throw ExpressionException("builtin function _P_carry can't be used in expressions because it doesn't return a value", position)
|
"ceil" -> builtinCeil(arglist, position, namespace)
|
||||||
"_P_irqd" -> throw ExpressionException("builtin function _P_irqd can't be used in expressions because it doesn't return a value", position)
|
"lsl" -> builtinLsl(arglist, position, namespace)
|
||||||
|
"lsr" -> builtinLsr(arglist, position, namespace)
|
||||||
|
"rol" -> throw ExpressionException("builtin function _rol can't be used in expressions because it doesn't return a value", position)
|
||||||
|
"rol2" -> throw ExpressionException("builtin function _rol2 can't be used in expressions because it doesn't return a value", position)
|
||||||
|
"ror" -> throw ExpressionException("builtin function _ror can't be used in expressions because it doesn't return a value", position)
|
||||||
|
"ror2" -> throw ExpressionException("builtin function _ror2 can't be used in expressions because it doesn't return a value", position)
|
||||||
|
"P_carry" -> throw ExpressionException("builtin function _P_carry can't be used in expressions because it doesn't return a value", position)
|
||||||
|
"P_irqd" -> throw ExpressionException("builtin function _P_irqd can't be used in expressions because it doesn't return a value", position)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1252,7 +1278,7 @@ private fun il65Parser.BooleanliteralContext.toAst() = when(text) {
|
|||||||
|
|
||||||
|
|
||||||
private fun il65Parser.ArrayliteralContext.toAst(withPosition: Boolean) =
|
private fun il65Parser.ArrayliteralContext.toAst(withPosition: Boolean) =
|
||||||
expression().map { it.toAst(withPosition) }
|
expression().map { it.toAst(withPosition) }.toMutableList()
|
||||||
|
|
||||||
|
|
||||||
private fun il65Parser.If_stmtContext.toAst(withPosition: Boolean): IfStatement {
|
private fun il65Parser.If_stmtContext.toAst(withPosition: Boolean): IfStatement {
|
||||||
|
@ -20,8 +20,8 @@ fun Module.checkValid(globalNamespace: INameScope) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* todo check subroutine parameters against signature
|
* todo check subroutine call parameters against signature
|
||||||
* todo check subroutine return values against target assignment values
|
* todo check subroutine return values against the call's result assignments
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
||||||
@ -125,11 +125,12 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
|||||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||||
when {
|
when {
|
||||||
decl.value == null ->
|
decl.value == null ->
|
||||||
err("need a compile-time constant initializer value")
|
err("var/const declaration needs a compile-time constant initializer value")
|
||||||
decl.value !is LiteralValue ->
|
decl.value !is LiteralValue ->
|
||||||
err("need a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}")
|
err("var/const declaration needs a compile-time constant initializer value, found: ${decl.value!!::class.simpleName}")
|
||||||
decl.isScalar -> {
|
decl.isScalar -> {
|
||||||
checkConstInitializerValueScalar(decl)
|
checkConstInitializerValueScalar(decl)
|
||||||
|
checkValueType(decl, decl.value as LiteralValue, decl.position)
|
||||||
checkValueRange(decl.datatype, decl.value as LiteralValue, decl.position)
|
checkValueRange(decl.datatype, decl.value as LiteralValue, decl.position)
|
||||||
}
|
}
|
||||||
decl.isArray || decl.isMatrix -> {
|
decl.isArray || decl.isMatrix -> {
|
||||||
@ -298,10 +299,10 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
|||||||
|
|
||||||
override fun process(functionCall: FunctionCall): IExpression {
|
override fun process(functionCall: FunctionCall): IExpression {
|
||||||
// this function call is (part of) an expression, which should be in a statement somewhere.
|
// this function call is (part of) an expression, which should be in a statement somewhere.
|
||||||
val statementNode = findParentNode<IStatement>(functionCall)
|
val stmtOfExpression = findParentNode<IStatement>(functionCall)
|
||||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCall.position}")
|
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCall.position}")
|
||||||
|
|
||||||
val targetStatement = checkFunctionOrLabelExists(functionCall.target, statementNode)
|
val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression)
|
||||||
if(targetStatement!=null)
|
if(targetStatement!=null)
|
||||||
functionCall.targetStatement = targetStatement // link to the actual target statement
|
functionCall.targetStatement = targetStatement // link to the actual target statement
|
||||||
return super.process(functionCall)
|
return super.process(functionCall)
|
||||||
@ -315,6 +316,8 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? {
|
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? {
|
||||||
|
if(target.nameInSource.size==1 && BuiltinFunctionNames.contains(target.nameInSource[0]))
|
||||||
|
return BuiltinFunctionStatementPlaceholder
|
||||||
val targetStatement = globalNamespace.lookup(target.nameInSource, statement)
|
val targetStatement = globalNamespace.lookup(target.nameInSource, statement)
|
||||||
if(targetStatement is Label || targetStatement is Subroutine)
|
if(targetStatement is Label || targetStatement is Subroutine)
|
||||||
return targetStatement
|
return targetStatement
|
||||||
@ -331,17 +334,17 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
|||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
val number = value.asFloat(false)
|
val number = value.asFloat(false)
|
||||||
if (number!=null && (number > 1.7014118345e+38 || number < -1.7014118345e+38))
|
if (number!=null && (number > 1.7014118345e+38 || number < -1.7014118345e+38))
|
||||||
return err("floating point value out of range for MFLPT format")
|
return err("floating point value '$number' out of range for MFLPT format")
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
val number = value.asInt(false)
|
val number = value.asInt(false)
|
||||||
if (number!=null && (number < 0 || number > 255))
|
if (number!=null && (number < 0 || number > 255))
|
||||||
return err("value out of range for unsigned byte")
|
return err("value '$number' out of range for unsigned byte")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
val number = value.asInt(false)
|
val number = value.asInt(false)
|
||||||
if (number!=null && (number < 0 || number > 65535))
|
if (number!=null && (number < 0 || number > 65535))
|
||||||
return err("value out of range for unsigned word")
|
return err("value '$number' out of range for unsigned word")
|
||||||
}
|
}
|
||||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||||
val str = value.strvalue
|
val str = value.strvalue
|
||||||
@ -352,6 +355,37 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkValueType(vardecl: VarDecl, value: LiteralValue, position: Position?) : Boolean {
|
||||||
|
fun err(msg: String) : Boolean {
|
||||||
|
checkResult.add(SyntaxError(msg, position))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
when {
|
||||||
|
vardecl.isScalar -> when (vardecl.datatype) {
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
if (value.floatvalue == null)
|
||||||
|
return err("floating point value expected")
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if (value.intvalue == null)
|
||||||
|
return err("byte integer value expected")
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if (value.intvalue == null)
|
||||||
|
return err("word integer value expected")
|
||||||
|
}
|
||||||
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||||
|
if (value.strvalue == null)
|
||||||
|
return err("string value expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vardecl.isArray -> if(value.arrayvalue==null)
|
||||||
|
return err("array value expected")
|
||||||
|
vardecl.isMatrix -> TODO()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkConstInitializerValueScalar(decl: VarDecl) {
|
private fun checkConstInitializerValueScalar(decl: VarDecl) {
|
||||||
fun err(msg: String) {
|
fun err(msg: String) {
|
||||||
checkResult.add(SyntaxError(msg, decl.position))
|
checkResult.add(SyntaxError(msg, decl.position))
|
||||||
|
@ -21,10 +21,10 @@ fun Module.checkIdentifiers(): MutableMap<String, IStatement> {
|
|||||||
|
|
||||||
|
|
||||||
val BuiltinFunctionNames = setOf(
|
val BuiltinFunctionNames = setOf(
|
||||||
"_P_carry", "_P_irqd", "_rol", "_ror", "_rol2", "_ror2",
|
"P_carry", "P_irqd", "rol", "ror", "rol2", "ror2", "lsl", "lsr",
|
||||||
"_lsl", "_lsr", "sin", "cos", "abs", "acos",
|
"sin", "cos", "abs", "acos", "asin", "tan", "atan",
|
||||||
"asin", "tan", "atan", "log", "log10", "sqrt",
|
"log", "log10", "sqrt", "rad", "deg", "round", "floor", "ceil",
|
||||||
"max", "min", "round", "rad", "deg")
|
"max", "min", "avg", "sum", "len", "any", "all")
|
||||||
|
|
||||||
|
|
||||||
class AstIdentifiersChecker : IAstProcessor {
|
class AstIdentifiersChecker : IAstProcessor {
|
||||||
@ -65,7 +65,7 @@ class AstIdentifiersChecker : IAstProcessor {
|
|||||||
|
|
||||||
override fun process(subroutine: Subroutine): IStatement {
|
override fun process(subroutine: Subroutine): IStatement {
|
||||||
if(BuiltinFunctionNames.contains(subroutine.name)) {
|
if(BuiltinFunctionNames.contains(subroutine.name)) {
|
||||||
// the special pseudo-functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
||||||
} else {
|
} else {
|
||||||
val scopedName = subroutine.scopedname.joinToString(".")
|
val scopedName = subroutine.scopedname.joinToString(".")
|
||||||
@ -81,7 +81,7 @@ class AstIdentifiersChecker : IAstProcessor {
|
|||||||
|
|
||||||
override fun process(label: Label): IStatement {
|
override fun process(label: Label): IStatement {
|
||||||
if(BuiltinFunctionNames.contains(label.name)) {
|
if(BuiltinFunctionNames.contains(label.name)) {
|
||||||
// the special pseudo-functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
||||||
} else {
|
} else {
|
||||||
val scopedName = label.scopedname.joinToString(".")
|
val scopedName = label.scopedname.joinToString(".")
|
||||||
|
@ -3,22 +3,22 @@ package il65.functions
|
|||||||
import il65.ast.*
|
import il65.ast.*
|
||||||
|
|
||||||
|
|
||||||
val BuiltIns = listOf("sin", "cos", "abs", "acos", "asin", "tan", "atan", "log", "log10", "sqrt", "max", "min", "round", "rad", "deg")
|
val BuiltIns = listOf(
|
||||||
|
"sin", "cos", "abs", "acos", "asin", "tan", "atan", "log", "log10",
|
||||||
|
"sqrt", "max", "min", "round", "rad", "deg", "avg", "sum"
|
||||||
// @todo additional builtins such as: avg, sum, abs, round
|
)
|
||||||
|
|
||||||
|
|
||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||||
|
|
||||||
|
|
||||||
private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Double): LiteralValue {
|
private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
|
|
||||||
val float = args[0].constValue(namespace)?.asFloat()
|
val float = args[0].constValue(namespace)?.asFloat()
|
||||||
if(float!=null) {
|
if(float!=null) {
|
||||||
val result = LiteralValue(floatvalue = function(float))
|
val result = intOrFloatLiteral(function(float).toDouble(), args[0].position)
|
||||||
result.position = args[0].position
|
result.position = args[0].position
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -26,13 +26,13 @@ private fun oneDoubleArg(args: List<IExpression>, position: Position?, namespace
|
|||||||
throw NotConstArgumentException()
|
throw NotConstArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Int): LiteralValue {
|
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||||
|
|
||||||
val float = args[0].constValue(namespace)?.asFloat()
|
val float = args[0].constValue(namespace)?.asFloat()
|
||||||
if(float!=null) {
|
if(float!=null) {
|
||||||
val result = LiteralValue(intvalue = function(float))
|
val result = LiteralValue(function(float).toInt())
|
||||||
result.position = args[0].position
|
result.position = args[0].position
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -40,13 +40,13 @@ private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position?,
|
|||||||
throw NotConstArgumentException()
|
throw NotConstArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Int)->Int): LiteralValue {
|
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position?, namespace:INameScope, function: (arg: Int)->Number): LiteralValue {
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("built-in function requires one integer argument", position)
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
|
|
||||||
val integer = args[0].constValue(namespace)?.asInt()
|
val integer = args[0].constValue(namespace)?.asInt()
|
||||||
if(integer!=null) {
|
if(integer!=null) {
|
||||||
val result = LiteralValue(intvalue = function(integer))
|
val result = LiteralValue(function(integer).toInt())
|
||||||
result.position = args[0].position
|
result.position = args[0].position
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -54,8 +54,42 @@ private fun oneIntArgOutputInt(args: List<IExpression>, position: Position?, nam
|
|||||||
throw NotConstArgumentException()
|
throw NotConstArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun nonScalarArgOutputNumber(args: List<IExpression>, position: Position?, namespace:INameScope,
|
||||||
|
function: (arg: Collection<Double>)->Number): LiteralValue {
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||||
|
val iterable = args[0].constValue(namespace)
|
||||||
|
if(iterable?.arrayvalue == null)
|
||||||
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||||
|
val constants = iterable.arrayvalue.map { it.constValue(namespace) }
|
||||||
|
if(constants.contains(null))
|
||||||
|
throw NotConstArgumentException()
|
||||||
|
val result = function(constants.map { it?.asFloat()!! }).toDouble()
|
||||||
|
return intOrFloatLiteral(result, args[0].position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nonScalarArgOutputBoolean(args: List<IExpression>, position: Position?, namespace:INameScope,
|
||||||
|
function: (arg: Collection<Double>)->Boolean): LiteralValue {
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||||
|
val iterable = args[0].constValue(namespace)
|
||||||
|
if(iterable?.arrayvalue == null)
|
||||||
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||||
|
val constants = iterable.arrayvalue.map { it.constValue(namespace) }
|
||||||
|
if(constants.contains(null))
|
||||||
|
throw NotConstArgumentException()
|
||||||
|
val result = function(constants.map { it?.asFloat()!! })
|
||||||
|
return LiteralValue(if(result) 1 else 0)
|
||||||
|
}
|
||||||
|
|
||||||
fun builtinRound(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
fun builtinRound(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
= oneDoubleArgOutputInt(args, position, namespace) { it -> Math.round(it).toInt() }
|
= oneDoubleArgOutputInt(args, position, namespace, Math::round)
|
||||||
|
|
||||||
|
fun builtinFloor(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= oneDoubleArgOutputInt(args, position, namespace, Math::floor)
|
||||||
|
|
||||||
|
fun builtinCeil(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= oneDoubleArgOutputInt(args, position, namespace, Math::ceil)
|
||||||
|
|
||||||
fun builtinSin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
fun builtinSin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
= oneDoubleArg(args, position, namespace, Math::sin)
|
= oneDoubleArg(args, position, namespace, Math::sin)
|
||||||
@ -90,41 +124,35 @@ fun builtinRad(args: List<IExpression>, position: Position?, namespace:INameScop
|
|||||||
fun builtinDeg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
fun builtinDeg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
= oneDoubleArg(args, position, namespace, Math::toDegrees)
|
= oneDoubleArg(args, position, namespace, Math::toDegrees)
|
||||||
|
|
||||||
fun builtinAbs(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
|
fun builtinAbs(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
if(args.size!=1)
|
= oneDoubleArg(args, position, namespace, Math::abs)
|
||||||
throw SyntaxError("built-in function abs requires one numeric argument", position)
|
|
||||||
val float = args[0].constValue(namespace)?.asFloat()
|
|
||||||
if(float!=null)
|
|
||||||
return intOrFloatLiteral(Math.abs(float), args[0].position)
|
|
||||||
else
|
|
||||||
throw SyntaxError("built-in function abs requires floating point value as argument", position)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builtinMax(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
|
fun builtinLsl(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
if(args.isEmpty())
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
|
||||||
throw SyntaxError("max requires at least one argument", position)
|
|
||||||
val constants = args.map { it.constValue(namespace) }
|
|
||||||
if(constants.contains(null))
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
val result = constants.map { it?.asFloat()!! }.max()
|
|
||||||
return intOrFloatLiteral(result!!, args[0].position)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builtinMin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue {
|
fun builtinLsr(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
if(args.isEmpty())
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 1 }
|
||||||
throw SyntaxError("min requires at least one argument", position)
|
|
||||||
val constants = args.map { it.constValue(namespace) }
|
|
||||||
if(constants.contains(null))
|
|
||||||
throw NotConstArgumentException()
|
|
||||||
val result = constants.map { it?.asFloat()!! }.min()
|
|
||||||
return intOrFloatLiteral(result!!, args[0].position)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builtinLsl(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue =
|
fun builtinMin(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
|
= nonScalarArgOutputNumber(args, position, namespace) { it.min()!! }
|
||||||
|
|
||||||
fun builtinLsr(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue =
|
fun builtinMax(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 1 }
|
= nonScalarArgOutputNumber(args, position, namespace) { it.max()!! }
|
||||||
|
|
||||||
|
fun builtinSum(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= nonScalarArgOutputNumber(args, position, namespace) { it.sum() }
|
||||||
|
|
||||||
|
fun builtinAvg(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= nonScalarArgOutputNumber(args, position, namespace) { it.average() }
|
||||||
|
|
||||||
|
fun builtinLen(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= nonScalarArgOutputNumber(args, position, namespace) { it.size }
|
||||||
|
|
||||||
|
fun builtinAny(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= nonScalarArgOutputBoolean(args, position, namespace) { it.any { v -> v != 0.0} }
|
||||||
|
|
||||||
|
fun builtinAll(args: List<IExpression>, position: Position?, namespace:INameScope): LiteralValue
|
||||||
|
= nonScalarArgOutputBoolean(args, position, namespace) { it.all { v -> v != 0.0} }
|
||||||
|
|
||||||
|
|
||||||
private fun intOrFloatLiteral(value: Double, position: Position?): LiteralValue {
|
private fun intOrFloatLiteral(value: Double, position: Position?): LiteralValue {
|
||||||
|
@ -48,7 +48,7 @@ fun Module.optimizeExpressions(globalNamespace: INameScope) {
|
|||||||
todo expression optimization: reduce expression nesting / flattening of parenthesis
|
todo expression optimization: reduce expression nesting / flattening of parenthesis
|
||||||
todo expression optimization: simplify logical expression when a term makes it always true or false (1 or 0)
|
todo expression optimization: simplify logical expression when a term makes it always true or false (1 or 0)
|
||||||
todo expression optimization: optimize some simple multiplications into shifts (A*8 -> A<<3, A/4 -> A>>2)
|
todo expression optimization: optimize some simple multiplications into shifts (A*8 -> A<<3, A/4 -> A>>2)
|
||||||
to
|
todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcessor {
|
class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcessor {
|
||||||
@ -187,7 +187,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
v.position = range.position
|
v.position = range.position
|
||||||
v.parent = range.parent
|
v.parent = range.parent
|
||||||
v
|
v
|
||||||
})
|
}.toMutableList())
|
||||||
}
|
}
|
||||||
from.strvalue != null && to.strvalue != null -> {
|
from.strvalue != null && to.strvalue != null -> {
|
||||||
// char range
|
// char range
|
||||||
@ -208,6 +208,15 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
range
|
range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun process(literalValue: LiteralValue): LiteralValue {
|
||||||
|
if(literalValue.arrayvalue!=null) {
|
||||||
|
val newArray = literalValue.arrayvalue.map { it.process(this) }
|
||||||
|
literalValue.arrayvalue.clear()
|
||||||
|
literalValue.arrayvalue.addAll(newArray)
|
||||||
|
}
|
||||||
|
return super.process(literalValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ sub float_sub_SW1_from_XY (mflt: XY) -> (?) {
|
|||||||
sub clear_screen (char: A, color: Y) -> () {
|
sub clear_screen (char: A, color: Y) -> () {
|
||||||
; ---- clear the character screen with the given fill character and character color.
|
; ---- clear the character screen with the given fill character and character color.
|
||||||
; (assumes screen is at $0400, could be altered in the future with self-modifying code)
|
; (assumes screen is at $0400, could be altered in the future with self-modifying code)
|
||||||
; @todo X = SCREEN ADDR HI BYTE
|
; @todo some byte var to set the SCREEN ADDR HI BYTE
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _loop + 1 ; self-modifying
|
sta _loop + 1 ; self-modifying
|
||||||
|
Loading…
Reference in New Issue
Block a user