mirror of
https://github.com/KarolS/millfork.git
synced 2024-10-12 09:23:45 +00:00
Unified syntax for indexing
This commit is contained in:
parent
85841c6395
commit
546c4d0f44
@ -65,7 +65,7 @@ Examples:
|
|||||||
p[0] // valid only if the type 't' is of size 1 or 2, accesses the pointed element
|
p[0] // valid only if the type 't' is of size 1 or 2, accesses the pointed element
|
||||||
p[i] // valid only if the type 't' is of size 1, equivalent to 't(p.raw[i])'
|
p[i] // valid only if the type 't' is of size 1, equivalent to 't(p.raw[i])'
|
||||||
p->x // valid only if the type 't' has a field called 'x', accesses the field 'x' of the pointed element
|
p->x // valid only if the type 't' has a field called 'x', accesses the field 'x' of the pointed element
|
||||||
p->x.y->z // you can stack it
|
p->x.y[0]->z[0][6] // you can stack it
|
||||||
|
|
||||||
## `nullptr`
|
## `nullptr`
|
||||||
|
|
||||||
|
@ -254,23 +254,51 @@ object AbstractExpressionCompiler {
|
|||||||
case DerefDebuggingExpression(_, 1) => b
|
case DerefDebuggingExpression(_, 1) => b
|
||||||
case DerefDebuggingExpression(_, 2) => w
|
case DerefDebuggingExpression(_, 2) => w
|
||||||
case DerefExpression(_, _, typ) => typ
|
case DerefExpression(_, _, typ) => typ
|
||||||
case IndirectFieldExpression(inner, fieldPath) =>
|
case IndirectFieldExpression(inner, firstIndices, fieldPath) =>
|
||||||
val firstPointerType = getExpressionType(env, log, inner)
|
var currentType = getExpressionType(env, log, inner)
|
||||||
fieldPath.foldLeft(firstPointerType) { (currentType, fieldName) =>
|
var ok = true
|
||||||
|
for(_ <- firstIndices) {
|
||||||
|
currentType match {
|
||||||
|
case PointerType(_, _, Some(targetType)) =>
|
||||||
|
currentType = targetType
|
||||||
|
case x if x.isPointy =>
|
||||||
|
currentType = b
|
||||||
|
case _ =>
|
||||||
|
log.error(s"Type `$currentType` is not a pointer type", expr.position)
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ((fieldName, indices) <- fieldPath) {
|
||||||
|
if (ok) {
|
||||||
currentType match {
|
currentType match {
|
||||||
case PointerType(_, _, Some(targetType)) =>
|
case PointerType(_, _, Some(targetType)) =>
|
||||||
val tuples = env.getSubvariables(targetType).filter(x => x._1 == "." + fieldName)
|
val tuples = env.getSubvariables(targetType).filter(x => x._1 == "." + fieldName)
|
||||||
if (tuples.isEmpty) {
|
if (tuples.isEmpty) {
|
||||||
log.error(s"Type `$targetType` doesn't have field named `$fieldName`", expr.position)
|
log.error(s"Type `$targetType` doesn't have field named `$fieldName`", expr.position)
|
||||||
b
|
ok = false
|
||||||
} else {
|
} else {
|
||||||
tuples.head._3
|
currentType = tuples.head._3
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
log.error(s"Type `$currentType` is not a pointer type", expr.position)
|
log.error(s"Type `$currentType` is not a pointer type", expr.position)
|
||||||
b
|
ok = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ok) {
|
||||||
|
for (_ <- indices) {
|
||||||
|
currentType match {
|
||||||
|
case PointerType(_, _, Some(targetType)) =>
|
||||||
|
currentType = targetType
|
||||||
|
case x if x.isPointy =>
|
||||||
|
currentType = b
|
||||||
|
case _ =>
|
||||||
|
log.error(s"Type `$currentType` is not a pointer type", expr.position)
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) currentType else b
|
||||||
case SeparateBytesExpression(hi, lo) =>
|
case SeparateBytesExpression(hi, lo) =>
|
||||||
if (getExpressionType(env, log, hi).size > 1) log.error("Hi byte too large", hi.position)
|
if (getExpressionType(env, log, hi).size > 1) log.error("Hi byte too large", hi.position)
|
||||||
if (getExpressionType(env, log, lo).size > 1) log.error("Lo byte too large", lo.position)
|
if (getExpressionType(env, log, lo).size > 1) log.error("Lo byte too large", lo.position)
|
||||||
|
@ -277,25 +277,60 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
|
|||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
expr match {
|
expr match {
|
||||||
case IndirectFieldExpression(root, fieldPath) if AbstractExpressionCompiler.getExpressionType(env, env.log, root).isInstanceOf[PointerType] =>
|
case IndirectFieldExpression(root, firstIndices, fieldPath) =>
|
||||||
fieldPath.foldLeft(root) { (pointer, fieldName) =>
|
val b = env.get[Type]("byte")
|
||||||
AbstractExpressionCompiler.getExpressionType(env, env.log, pointer) match {
|
var ok = true
|
||||||
|
var result = optimizeExpr(root, currentVarValues).pos(pos)
|
||||||
|
def applyIndex(result: Expression, index: Expression): Expression = {
|
||||||
|
AbstractExpressionCompiler.getExpressionType(env, env.log, result) match {
|
||||||
|
case pt@PointerType(_, _, Some(target)) =>
|
||||||
|
env.eval(index) match {
|
||||||
|
case Some(NumericConstant(0, _)) => //ok
|
||||||
|
case _ =>
|
||||||
|
env.log.error(s"Type `$pt` can be only indexed with 0")
|
||||||
|
}
|
||||||
|
DerefExpression(result, 0, target)
|
||||||
|
case x if x.isPointy =>
|
||||||
|
env.eval(index) match {
|
||||||
|
case Some(NumericConstant(n, _)) if n >= 0 && n <= 127 =>
|
||||||
|
DerefExpression(result, n.toInt, b)
|
||||||
|
case _ =>
|
||||||
|
DerefExpression(SumExpression(List(false -> result, false -> index), decimal = false), 0, b)
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
ctx.log.error("Not a pointer type on the left-hand side of `[`", pos)
|
||||||
|
ok = false
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index <- firstIndices) {
|
||||||
|
result = applyIndex(result, index)
|
||||||
|
}
|
||||||
|
for ((fieldName, indices) <- fieldPath) {
|
||||||
|
if (ok) {
|
||||||
|
result = AbstractExpressionCompiler.getExpressionType(env, env.log, result) match {
|
||||||
case PointerType(_, _, Some(target)) =>
|
case PointerType(_, _, Some(target)) =>
|
||||||
val subvariables = env.getSubvariables(target).filter(x => x._1 == "." + fieldName)
|
val subvariables = env.getSubvariables(target).filter(x => x._1 == "." + fieldName)
|
||||||
if (subvariables.isEmpty) {
|
if (subvariables.isEmpty) {
|
||||||
ctx.log.error(s"Type `${target.name}` does not contain field `$fieldName`", pointer.position)
|
ctx.log.error(s"Type `${target.name}` does not contain field `$fieldName`", result.position)
|
||||||
|
ok = false
|
||||||
LiteralExpression(0, 1)
|
LiteralExpression(0, 1)
|
||||||
} else {
|
} else {
|
||||||
DerefExpression(optimizeExpr(pointer, currentVarValues).pos(pos), subvariables.head._2, subvariables.head._3)
|
DerefExpression(optimizeExpr(result, currentVarValues).pos(pos), subvariables.head._2, subvariables.head._3)
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
ctx.log.error("Invalid pointer type on the left-hand side of `->`", pointer.position)
|
ctx.log.error("Invalid pointer type on the left-hand side of `->`", result.position)
|
||||||
LiteralExpression(0, 1)
|
LiteralExpression(0, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case IndirectFieldExpression(root, fieldPath) =>
|
if (ok) {
|
||||||
ctx.log.error("Invalid pointer type on the left-hand side of `->`", pos)
|
for (index <- indices) {
|
||||||
root
|
result = applyIndex(result, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
case DerefDebuggingExpression(inner, 1) =>
|
case DerefDebuggingExpression(inner, 1) =>
|
||||||
DerefExpression(optimizeExpr(inner, currentVarValues), 0, env.get[VariableType]("byte")).pos(pos)
|
DerefExpression(optimizeExpr(inner, currentVarValues), 0, env.get[VariableType]("byte")).pos(pos)
|
||||||
case DerefDebuggingExpression(inner, 2) =>
|
case DerefDebuggingExpression(inner, 2) =>
|
||||||
|
@ -1699,8 +1699,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||||||
nameCheck(inner)
|
nameCheck(inner)
|
||||||
case DerefExpression(inner, _, _) =>
|
case DerefExpression(inner, _, _) =>
|
||||||
nameCheck(inner)
|
nameCheck(inner)
|
||||||
case IndirectFieldExpression(inner, _) =>
|
case IndirectFieldExpression(inner, firstIndices, fields) =>
|
||||||
nameCheck(inner)
|
nameCheck(inner)
|
||||||
|
firstIndices.foreach(nameCheck)
|
||||||
|
fields.foreach(f => f._2.foreach(nameCheck))
|
||||||
case SeparateBytesExpression(h, l) =>
|
case SeparateBytesExpression(h, l) =>
|
||||||
nameCheck(h)
|
nameCheck(h)
|
||||||
nameCheck(l)
|
nameCheck(l)
|
||||||
|
@ -65,6 +65,12 @@ abstract class CallGraph(program: Program, log: Logger) {
|
|||||||
val varName = i.name.stripSuffix(".hi").stripSuffix(".lo").stripSuffix(".addr")
|
val varName = i.name.stripSuffix(".hi").stripSuffix(".lo").stripSuffix(".addr")
|
||||||
everCalledFunctions += varName
|
everCalledFunctions += varName
|
||||||
add(currentFunction, callingFunctions, i.index)
|
add(currentFunction, callingFunctions, i.index)
|
||||||
|
case i: DerefDebuggingExpression =>
|
||||||
|
add(currentFunction, callingFunctions, i.inner)
|
||||||
|
case IndirectFieldExpression(root, firstIndices, fields) =>
|
||||||
|
add(currentFunction, callingFunctions, root)
|
||||||
|
firstIndices.foreach(i => add(currentFunction, callingFunctions, i))
|
||||||
|
fields.foreach(f => f._2.foreach(i => add(currentFunction, callingFunctions, i)))
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,19 +202,26 @@ case class IndexedExpression(name: String, index: Expression) extends LhsExpress
|
|||||||
override def getAllIdentifiers: Set[String] = index.getAllIdentifiers + name
|
override def getAllIdentifiers: Set[String] = index.getAllIdentifiers + name
|
||||||
}
|
}
|
||||||
|
|
||||||
case class IndirectFieldExpression(root: Expression, fields: List[String]) extends LhsExpression {
|
case class IndirectFieldExpression(root: Expression, firstIndices: Seq[Expression], fields: Seq[(String, Seq[Expression])]) extends LhsExpression {
|
||||||
override def replaceVariable(variable: String, actualParam: Expression): Expression = IndirectFieldExpression(root.replaceVariable(variable, actualParam), fields)
|
override def replaceVariable(variable: String, actualParam: Expression): Expression =
|
||||||
|
IndirectFieldExpression(
|
||||||
|
root.replaceVariable(variable, actualParam),
|
||||||
|
firstIndices.map(_.replaceVariable(variable, actualParam)),
|
||||||
|
fields.map{case (f, i) => f -> i.map(_.replaceVariable(variable, actualParam))})
|
||||||
|
|
||||||
override def containsVariable(variable: String): Boolean = root.containsVariable(variable)
|
override def containsVariable(variable: String): Boolean =
|
||||||
|
root.containsVariable(variable) ||
|
||||||
|
firstIndices.exists(_.containsVariable(variable)) ||
|
||||||
|
fields.exists(_._2.exists(_.containsVariable(variable)))
|
||||||
|
|
||||||
override def getPointies: Seq[String] = root match {
|
override def getPointies: Seq[String] = (root match {
|
||||||
case VariableExpression(v) => List(v)
|
case VariableExpression(v) => List(v)
|
||||||
case _ => root.getPointies
|
case _ => root.getPointies
|
||||||
}
|
}) ++ firstIndices.flatMap(_.getPointies) ++ fields.flatMap(_._2.flatMap(_.getPointies))
|
||||||
|
|
||||||
override def isPure: Boolean = root.isPure
|
override def isPure: Boolean = root.isPure && firstIndices.forall(_.isPure) && fields.forall(_._2.forall(_.isPure))
|
||||||
|
|
||||||
override def getAllIdentifiers: Set[String] = root.getAllIdentifiers
|
override def getAllIdentifiers: Set[String] = root.getAllIdentifiers ++ firstIndices.flatMap(_.getAllIdentifiers) ++ fields.flatMap(_._2.flatMap(_.getAllIdentifiers))
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) extends LhsExpression {
|
case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) extends LhsExpression {
|
||||||
|
@ -92,7 +92,7 @@ object UnusedFunctions extends NodeOptimization {
|
|||||||
case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.toList)
|
case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.toList)
|
||||||
case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions)
|
case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions)
|
||||||
case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil))
|
case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil))
|
||||||
case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil)
|
case Assignment(target, expr) => getAllCalledFunctions(target :: expr :: Nil)
|
||||||
case s: ReturnDispatchStatement =>
|
case s: ReturnDispatchStatement =>
|
||||||
getAllCalledFunctions(s.getAllExpressions) ++ getAllCalledFunctions(s.branches.map(_.function))
|
getAllCalledFunctions(s.getAllExpressions) ++ getAllCalledFunctions(s.branches.map(_.function))
|
||||||
case s: Statement => getAllCalledFunctions(s.getAllExpressions)
|
case s: Statement => getAllCalledFunctions(s.getAllExpressions)
|
||||||
@ -115,6 +115,8 @@ object UnusedFunctions extends NodeOptimization {
|
|||||||
case FunctionCallExpression(name, xs) => name :: getAllCalledFunctions(xs)
|
case FunctionCallExpression(name, xs) => name :: getAllCalledFunctions(xs)
|
||||||
case IndexedExpression(arr, index) => arr :: getAllCalledFunctions(List(index))
|
case IndexedExpression(arr, index) => arr :: getAllCalledFunctions(List(index))
|
||||||
case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l))
|
case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l))
|
||||||
|
case DerefDebuggingExpression(inner, _) => getAllCalledFunctions(List(inner))
|
||||||
|
case IndirectFieldExpression(root, firstIndices, fieldPath) => getAllCalledFunctions(root :: firstIndices ++: fieldPath.flatMap(_._2).toList)
|
||||||
case _ => Nil
|
case _ => Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ object UnusedLocalVariables extends NodeOptimization {
|
|||||||
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
|
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
|
||||||
case DerefExpression(inner, _, _) => getAllReadVariables(List(inner))
|
case DerefExpression(inner, _, _) => getAllReadVariables(List(inner))
|
||||||
case DerefDebuggingExpression(inner, _) => getAllReadVariables(List(inner))
|
case DerefDebuggingExpression(inner, _) => getAllReadVariables(List(inner))
|
||||||
case IndirectFieldExpression(inner, _) => getAllReadVariables(List(inner))
|
case IndirectFieldExpression(inner, firstIndices, fields) => getAllReadVariables(List(inner) ++ firstIndices ++ fields.flatMap(_._2))
|
||||||
case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l))
|
case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l))
|
||||||
case _ => Nil
|
case _ => Nil
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
|
start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
|
||||||
pos <- position("loop direction")
|
pos <- position("loop direction")
|
||||||
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
|
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
|
||||||
end <- mfExpression(nonStatementLevel, false)
|
end <- mfExpression(nonStatementLevel, false, allowTopLevelIndexing = false)
|
||||||
body <- AWS ~ arrayContents
|
body <- AWS ~ arrayContents
|
||||||
} yield {
|
} yield {
|
||||||
val fixedDirection = direction match {
|
val fixedDirection = direction match {
|
||||||
@ -247,29 +247,29 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
|
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
|
||||||
} yield Seq(ArrayDeclarationStatement(name, bank, length, elementType.getOrElse("byte"), addr, contents, alignment).pos(p))
|
} yield Seq(ArrayDeclarationStatement(name, bank, length, elementType.getOrElse("byte"), addr, contents, alignment).pos(p))
|
||||||
|
|
||||||
def tightMfExpression(allowIntelHex: Boolean): P[Expression] = {
|
def tightMfExpression(allowIntelHex: Boolean, allowTopLevelIndexing: Boolean): P[Expression] = {
|
||||||
val a = if (allowIntelHex) atomWithIntel else atom
|
val a = if (allowIntelHex) atomWithIntel else atom
|
||||||
for {
|
if (allowTopLevelIndexing)
|
||||||
expression <- mfParenExpr(allowIntelHex) | derefExpression | functionCall(allowIntelHex) | mfIndexedExpression | a
|
mfExpressionWrapper[Expression](mfParenExpr(allowIntelHex) | derefExpression | functionCall(allowIntelHex) | a)
|
||||||
fieldPath <- ("->" ~/ AWS ~/ identifier).rep
|
else
|
||||||
} yield if (fieldPath.isEmpty) expression else IndirectFieldExpression(expression, fieldPath.toList)
|
mfParenExpr(allowIntelHex) | derefExpression | functionCall(allowIntelHex) | a
|
||||||
}
|
}
|
||||||
|
|
||||||
def tightMfExpressionButNotCall(allowIntelHex: Boolean): P[Expression] = {
|
def tightMfExpressionButNotCall(allowIntelHex: Boolean, allowTopLevelIndexing: Boolean): P[Expression] = {
|
||||||
val a = if (allowIntelHex) atomWithIntel else atom
|
val a = if (allowIntelHex) atomWithIntel else atom
|
||||||
for {
|
if (allowTopLevelIndexing)
|
||||||
expression <- mfParenExpr(allowIntelHex) | derefExpression | mfIndexedExpression | a
|
mfExpressionWrapper[Expression](mfParenExpr(allowIntelHex) | derefExpression | a)
|
||||||
fieldPath <- ("->" ~/ AWS ~/ identifier).rep
|
else
|
||||||
} yield if (fieldPath.isEmpty) expression else IndirectFieldExpression(expression, fieldPath.toList)
|
mfParenExpr(allowIntelHex) | derefExpression | a
|
||||||
}
|
}
|
||||||
|
|
||||||
def mfExpression(level: Int, allowIntelHex: Boolean): P[Expression] = {
|
def mfExpression(level: Int, allowIntelHex: Boolean, allowTopLevelIndexing: Boolean = true): P[Expression] = {
|
||||||
val allowedOperators = mfOperatorsDropFlatten(level)
|
val allowedOperators = mfOperatorsDropFlatten(level)
|
||||||
|
|
||||||
def inner: P[SeparatedList[Expression, String]] = {
|
def inner: P[SeparatedList[Expression, String]] = {
|
||||||
for {
|
for {
|
||||||
head <- tightMfExpression(allowIntelHex) ~/ HWS
|
head <- tightMfExpression(allowIntelHex, allowTopLevelIndexing) ~/ HWS
|
||||||
maybeOperator <- StringIn(allowedOperators: _*).!.?
|
maybeOperator <- (StringIn(allowedOperators: _*).! ~ !CharIn(Seq('/', '=', '-', '+', ':', '>', '<', '\''))).?
|
||||||
maybeTail <- maybeOperator.fold[P[Option[List[(String, Expression)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
|
maybeTail <- maybeOperator.fold[P[Option[List[(String, Expression)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
|
||||||
} yield {
|
} yield {
|
||||||
maybeTail.fold[SeparatedList[Expression, String]](SeparatedList.of(head))(t => SeparatedList(head, t))
|
maybeTail.fold[SeparatedList[Expression, String]](SeparatedList.of(head))(t => SeparatedList(head, t))
|
||||||
@ -296,6 +296,13 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
} else {
|
} else {
|
||||||
SeparateBytesExpression(p(xs.head, level + 1), p(xs.tail.head._2, level + 1))
|
SeparateBytesExpression(p(xs.head, level + 1), p(xs.tail.head._2, level + 1))
|
||||||
}
|
}
|
||||||
|
case List(eq) if level == 0 =>
|
||||||
|
if (xs.size != 2) {
|
||||||
|
log.error(s"The `$eq` operator can have only two arguments", xs.head.head.position)
|
||||||
|
LiteralExpression(0, 1)
|
||||||
|
} else {
|
||||||
|
FunctionCallExpression(eq, xs.items.map(value => p(value, level + 1)))
|
||||||
|
}
|
||||||
case List(op) =>
|
case List(op) =>
|
||||||
FunctionCallExpression(op, xs.items.map(value => p(value, level + 1)))
|
FunctionCallExpression(op, xs.items.map(value => p(value, level + 1)))
|
||||||
case _ =>
|
case _ =>
|
||||||
@ -307,25 +314,31 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
inner.map(x => p(x, 0))
|
inner.map(x => p(x, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
def mfLhsExpressionSimple: P[LhsExpression] = for {
|
def index: P[Expression] = HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~/ "]" ~/ Pass
|
||||||
expression <- mfIndexedExpression | derefExpression | (position() ~ identifier).map{case (p,n) => VariableExpression(n).pos(p)} ~ HWS
|
|
||||||
fieldPath <- ("->" ~/ AWS ~/ identifier).rep
|
|
||||||
} yield if (fieldPath.isEmpty) expression else IndirectFieldExpression(expression, fieldPath.toList)
|
|
||||||
|
|
||||||
def mfLhsExpression: P[LhsExpression] = for {
|
def mfExpressionWrapper[T <: Expression](inner: P[T]): P[T] = for {
|
||||||
(p, left) <- position() ~ mfLhsExpressionSimple
|
expr <- inner
|
||||||
rightOpt <- (HWS ~ ":" ~/ HWS ~ mfLhsExpressionSimple).?
|
firstIndices <- index.rep
|
||||||
} yield rightOpt.fold(left)(right => SeparateBytesExpression(left, right).pos(p))
|
fieldPath <- (HWS ~ "->" ~/ AWS ~/ identifier ~/ index.rep).rep
|
||||||
|
} yield (expr, firstIndices, fieldPath) match {
|
||||||
|
case (_, Seq(), Seq()) => expr
|
||||||
|
case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).asInstanceOf[T]
|
||||||
|
case _ => IndirectFieldExpression(expr, firstIndices, fieldPath).asInstanceOf[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// def mfLhsExpression: P[LhsExpression] = for {
|
||||||
|
// (p, left) <- position() ~ mfLhsExpressionSimple
|
||||||
|
// rightOpt <- (HWS ~ ":" ~/ HWS ~ mfLhsExpressionSimple).?
|
||||||
|
// } yield rightOpt.fold(left)(right => SeparateBytesExpression(left, right).pos(p))
|
||||||
|
|
||||||
|
def mfLhsExpressionSimple: P[LhsExpression] =
|
||||||
|
mfExpressionWrapper[LhsExpression](derefExpression | (position() ~ identifier).map{case (p,n) => VariableExpression(n).pos(p)} ~ HWS)
|
||||||
|
|
||||||
|
def mfLhsExpression: P[LhsExpression] =
|
||||||
|
mfExpression(nonStatementLevel, false).filter(_.isInstanceOf[LhsExpression]).map(_.asInstanceOf[LhsExpression])
|
||||||
|
|
||||||
def mfParenExpr(allowIntelHex: Boolean): P[Expression] = P("(" ~/ AWS ~/ mfExpression(nonStatementLevel, allowIntelHex) ~ AWS ~/ ")")
|
def mfParenExpr(allowIntelHex: Boolean): P[Expression] = P("(" ~/ AWS ~/ mfExpression(nonStatementLevel, allowIntelHex) ~ AWS ~/ ")")
|
||||||
|
|
||||||
def mfIndexedExpression: P[IndexedExpression] = for {
|
|
||||||
p <- position()
|
|
||||||
array <- identifier
|
|
||||||
index <- HWS ~ "[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~/ "]"
|
|
||||||
} yield IndexedExpression(array, index).pos(p)
|
|
||||||
|
|
||||||
def functionCall(allowIntelHex: Boolean): P[FunctionCallExpression] = for {
|
def functionCall(allowIntelHex: Boolean): P[FunctionCallExpression] = for {
|
||||||
p <- position()
|
p <- position()
|
||||||
name <- identifier
|
name <- identifier
|
||||||
@ -339,11 +352,14 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
inner <- mfParenExpr(false)
|
inner <- mfParenExpr(false)
|
||||||
} yield DerefDebuggingExpression(inner, yens.length).pos(p)
|
} yield DerefDebuggingExpression(inner, yens.length).pos(p)
|
||||||
|
|
||||||
val expressionStatement: P[Seq[ExecutableStatement]] = mfExpression(0, false).map(x => Seq(ExpressionStatement(x)))
|
val expressionStatement: P[Seq[ExecutableStatement]] = mfExpression(0, false).map {
|
||||||
|
case FunctionCallExpression("=", List(t: LhsExpression, s)) =>
|
||||||
val assignmentStatement: P[Seq[ExecutableStatement]] =
|
Seq(Assignment(t, s).pos(t.position))
|
||||||
(position() ~ mfLhsExpression ~ HWS ~ "=" ~/ HWS ~ mfExpression(1, false)).map {
|
case x@FunctionCallExpression("=", exprs) =>
|
||||||
case (p, l, r) => Seq(Assignment(l, r).pos(p))
|
log.error("Invalid left-hand-side of an assignment", x.position)
|
||||||
|
exprs.map(ExpressionStatement)
|
||||||
|
case x =>
|
||||||
|
Seq(ExpressionStatement(x).pos(x.position))
|
||||||
}
|
}
|
||||||
|
|
||||||
def keywordStatement: P[Seq[ExecutableStatement]] = P(
|
def keywordStatement: P[Seq[ExecutableStatement]] = P(
|
||||||
@ -355,8 +371,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
doWhileStatement |
|
doWhileStatement |
|
||||||
breakStatement |
|
breakStatement |
|
||||||
continueStatement |
|
continueStatement |
|
||||||
inlineAssembly |
|
inlineAssembly)
|
||||||
assignmentStatement)
|
|
||||||
|
|
||||||
def executableStatement: P[Seq[ExecutableStatement]] = (position() ~ P(keywordStatement | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
|
def executableStatement: P[Seq[ExecutableStatement]] = (position() ~ P(keywordStatement | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
|
||||||
|
|
||||||
@ -391,7 +406,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
val dispatchBranch: P[ReturnDispatchBranch] = for {
|
val dispatchBranch: P[ReturnDispatchBranch] = for {
|
||||||
pos <- position()
|
pos <- position()
|
||||||
l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS
|
l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS
|
||||||
f <- tightMfExpressionButNotCall(false) ~/ HWS
|
f <- tightMfExpressionButNotCall(false, allowTopLevelIndexing = false) ~/ HWS
|
||||||
parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mfExpression(nonStatementLevel, false).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
|
parameters <- ("(" ~/ position("dispatch actual parameters") ~ AWS ~/ mfExpression(nonStatementLevel, false).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ ")" ~/ "").?
|
||||||
} yield ReturnDispatchBranch(l, f, parameters.map(_._2.toList).getOrElse(Nil)).pos(pos)
|
} yield ReturnDispatchBranch(l, f, parameters.map(_._2.toList).getOrElse(Nil)).pos(pos)
|
||||||
|
|
||||||
@ -641,7 +656,7 @@ object MfParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mfOperators = List(
|
val mfOperators = List(
|
||||||
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'="),
|
List("+=", "-=", "+'=", "-'=", "^=", "&=", "|=", "*=", "*'=", "<<=", ">>=", "<<'=", ">>'=", "="),
|
||||||
List("||", "^^"),
|
List("||", "^^"),
|
||||||
List("&&"),
|
List("&&"),
|
||||||
List("==", "<=", ">=", "!=", "<", ">"),
|
List("==", "<=", ">=", "!=", "<", ">"),
|
||||||
@ -683,5 +698,5 @@ object MfParser {
|
|||||||
|
|
||||||
val functionFlags: P[Set[String]] = flags_("asm", "inline", "interrupt", "macro", "noinline", "reentrant", "kernal_interrupt")
|
val functionFlags: P[Set[String]] = flags_("asm", "inline", "interrupt", "macro", "noinline", "reentrant", "kernal_interrupt")
|
||||||
|
|
||||||
val InvalidReturnTypes = Set("enum", "alias", "array", "const", "stack", "register", "static", "volatile", "import")
|
val InvalidReturnTypes = Set("enum", "alias", "array", "const", "stack", "register", "static", "volatile", "import", "struct", "union")
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ class PreprocessorParser(options: CompilationOptions) {
|
|||||||
def inner: P[SeparatedList[Q, String]] = {
|
def inner: P[SeparatedList[Q, String]] = {
|
||||||
for {
|
for {
|
||||||
head <- tightMfExpression ~/ HWS
|
head <- tightMfExpression ~/ HWS
|
||||||
maybeOperator <- StringIn(allowedOperators: _*).!.?
|
maybeOperator <- (StringIn(allowedOperators: _*).! ~ !CharIn(Seq('-','+','/'))).?
|
||||||
maybeTail <- maybeOperator.fold[P[Option[List[(String, Q)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
|
maybeTail <- maybeOperator.fold[P[Option[List[(String, Q)]]]](Pass.map(_ => None))(o => (HWS ~/ inner ~/ HWS).map(x2 => Some((o -> x2.head) :: x2.tail)))
|
||||||
} yield {
|
} yield {
|
||||||
maybeTail.fold[SeparatedList[Q, String]](SeparatedList.of(head))(t => SeparatedList(head, t))
|
maybeTail.fold[SeparatedList[Q, String]](SeparatedList.of(head))(t => SeparatedList(head, t))
|
||||||
|
@ -2,12 +2,12 @@ package millfork.test
|
|||||||
|
|
||||||
import millfork.Cpu
|
import millfork.Cpu
|
||||||
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun}
|
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun}
|
||||||
import org.scalatest.{FunSuite, Matchers}
|
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karol Stasiak
|
* @author Karol Stasiak
|
||||||
*/
|
*/
|
||||||
class PointerSuite extends FunSuite with Matchers {
|
class PointerSuite extends FunSuite with Matchers with AppendedClues {
|
||||||
|
|
||||||
test("Pointers outside zeropage") {
|
test("Pointers outside zeropage") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Sixteen, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Sixteen, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
@ -168,4 +168,45 @@ class PointerSuite extends FunSuite with Matchers {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("Complex pointers") {
|
||||||
|
// TODO: optimize it when inlined
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
|
"""
|
||||||
|
| array output[3] @$c000
|
||||||
|
| struct s {
|
||||||
|
| pointer p
|
||||||
|
| }
|
||||||
|
| s tmp
|
||||||
|
| pointer.s tmpptr
|
||||||
|
| pointer.pointer.s get() {
|
||||||
|
| tmp.p = output.addr
|
||||||
|
| tmpptr = tmp.pointer
|
||||||
|
| return tmpptr.pointer
|
||||||
|
| }
|
||||||
|
| void main() {
|
||||||
|
| get()[0]->p[0] = 5
|
||||||
|
| }
|
||||||
|
""".stripMargin) { m =>
|
||||||
|
m.readByte(0xc000) should equal(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Indexing returned pointers") {
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
|
"""
|
||||||
|
| array output[10] @$c000
|
||||||
|
| pointer get() = output.addr
|
||||||
|
| void main() {
|
||||||
|
| byte i
|
||||||
|
| for i,0,paralleluntil,10 {
|
||||||
|
| get()[i] = 42
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin) { m =>
|
||||||
|
for(i <- 0xc000 until 0xc00a) {
|
||||||
|
m.readByte(i) should equal(42) withClue i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user