Skip to content

Commit

Permalink
Merge pull request #534 from nasa/feature/state-machine-phase-2
Browse files Browse the repository at this point in the history
State Machines Phase 2
  • Loading branch information
bocchino authored Dec 4, 2024
2 parents a54dc13 + 62c34ea commit 80f3c4a
Show file tree
Hide file tree
Showing 758 changed files with 51,762 additions and 4,009 deletions.
2,121 changes: 1,879 additions & 242 deletions compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json

Large diffs are not rendered by default.

50 changes: 40 additions & 10 deletions compiler/lib/src/main/scala/analysis/Analysis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,28 @@ case class Analysis(
topologyMap: Map[Symbol.Topology, Topology] = Map(),
/** The topology under construction */
topology: Option[Topology] = None,
/** The map from state machine symbols to state machines */
stateMachineMap: Map[Symbol.StateMachine, StateMachine] = Map(),
) {

/** Gets the qualified name of a symbol */
def getQualifiedName(s: Symbol): Name.Qualified = {
def getIdentList(so: Option[Symbol], out: List[Ast.Ident]): List[Ast.Ident] =
so match {
case Some(s) =>
val so1 = parentSymbolMap.get(s)
getIdentList(so1, s.getUnqualifiedName :: out)
case None => out
}
Name.Qualified.fromIdentList(getIdentList(Some(s), Nil))
val getQualifiedName = Analysis.getQualifiedNameFromMap (parentSymbolMap)

/** Gets the short name of a symbol
* When generating C++, we may need to keep the component prefix, because
* it is part of the symbol name */
def getShortName(
symbol: Symbol,
context: Symbol,
componentPrefix: Analysis.ComponentPrefix = Analysis.ComponentPrefix.Omit
): Name.Qualified = {
val name = getQualifiedName(symbol)
val enclosingPrefix = getEnclosingNames(context)
val prefix = (context, componentPrefix) match {
case (_: Symbol.Component, Analysis.ComponentPrefix.Keep) => enclosingPrefix
case _ => enclosingPrefix :+ context.getUnqualifiedName
}
name.shortName(prefix)
}

/** Gets the list of enclosing identifiers for a symbol */
Expand Down Expand Up @@ -389,7 +399,7 @@ case class Analysis(
checkDisplayableType(aNode._2.data.typeName.id, errorMsg)
)
}

}

object Analysis {
Expand Down Expand Up @@ -520,4 +530,24 @@ object Analysis {
s"($dec dec, $hex hex)"
}

/** Gets the qualified name of a symbol from a parent-symbol map */
def getQualifiedNameFromMap[S <: SymbolInterface]
(parentSymbolMap: Map[S,S]) (s: S):
Name.Qualified = {
def getIdentList(so: Option[S], out: List[Ast.Ident]): List[Ast.Ident] =
so match {
case Some(s) =>
val so1 = parentSymbolMap.get(s)
getIdentList(so1, s.getUnqualifiedName :: out)
case None => out
}
Name.Qualified.fromIdentList(getIdentList(Some(s), Nil))
}

sealed trait ComponentPrefix
object ComponentPrefix {
case object Keep extends ComponentPrefix
case object Omit extends ComponentPrefix
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package fpp.compiler.analysis

import fpp.compiler.ast._
import fpp.compiler.util._

/** State machine typed element analyzer */
trait SmTypedElementAnalyzer
extends StateMachineAnalysisVisitor
with StateAnalyzer
{

// ----------------------------------------------------------------------
// Interface methods to override
// Each of these methods is called when a corresponding typed element
// is visited
// ----------------------------------------------------------------------

def initialTransitionTypedElement(
sma: StateMachineAnalysis,
te: StateMachineTypedElement.InitialTransition
): Result = default(sma)

def choiceTypedElement(
sma: StateMachineAnalysis,
te: StateMachineTypedElement.Choice
): Result = default(sma)

def stateEntryTypedElement(
sma: StateMachineAnalysis,
te: StateMachineTypedElement.StateEntry
): Result = default(sma)

def stateExitTypedElement(
sma: StateMachineAnalysis,
te: StateMachineTypedElement.StateExit
): Result = default(sma)

def stateTransitionTypedElement(
sma: StateMachineAnalysis,
te: StateMachineTypedElement.StateTransition
): Result = default(sma)

// ----------------------------------------------------------------------
// Implementation using StateMachineAnalysisVisitor
// ----------------------------------------------------------------------

def visitTypedElement(
sma: StateMachineAnalysis,
te: StateMachineTypedElement
): Result = te match {
case it: StateMachineTypedElement.InitialTransition =>
initialTransitionTypedElement(sma, it)
case c: StateMachineTypedElement.Choice =>
choiceTypedElement(sma, c)
case se: StateMachineTypedElement.StateEntry =>
stateEntryTypedElement(sma, se)
case se: StateMachineTypedElement.StateExit =>
stateExitTypedElement(sma, se)
case st: StateMachineTypedElement.StateTransition =>
stateTransitionTypedElement(sma, st)
}

override def defChoiceAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.DefChoice]]
) = visitTypedElement(
sma,
StateMachineTypedElement.Choice(aNode)
)

override def specStateEntryAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecStateEntry]]
) = visitTypedElement(
sma,
StateMachineTypedElement.StateEntry(aNode)
)

override def specStateExitAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecStateExit]]
) = visitTypedElement(
sma,
StateMachineTypedElement.StateExit(aNode)
)

override def specInitialTransitionAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecInitialTransition]]
) = visitTypedElement(
sma,
StateMachineTypedElement.InitialTransition(aNode)
)

override def specStateTransitionAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecStateTransition]]
) = visitTypedElement(
sma,
StateMachineTypedElement.StateTransition(aNode)
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package fpp.compiler.analysis

import fpp.compiler.ast._

/** Analyze state definition members */
trait StateAnalyzer extends StateMachineAnalysisVisitor {

override def defStateAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.DefState]]
) = {
val (_, node, _) = aNode
val data = node.data
val sma1 = sma.copy(scopeNameList = data.name :: sma.scopeNameList)
for { sma2 <- visitList(sma1, data.members, matchStateMember) }
yield sma2.copy(scopeNameList = sma.scopeNameList)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fpp.compiler.analysis

import fpp.compiler.ast._

/** A generic analysis visitor for state machine semantics */
trait StateMachineAnalysisVisitor extends AstStateVisitor {

type State = StateMachineAnalysis

/** Apply an analysis to an option value */
final def opt[T] (f: (StateMachineAnalysis, T) => Result) (sma: StateMachineAnalysis, o: Option[T]): Result =
o.map(x => f(sma, x)).getOrElse(Right(sma))

override def defStateMachineAnnotatedNodeInternal(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.DefStateMachine]],
members: List[Ast.StateMachineMember]
) = visitList(sma, members, matchStateMachineMember)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package fpp.compiler.analysis

import fpp.compiler.ast._
import fpp.compiler.util._

/** Analyze state machine uses */
trait StateMachineUseAnalyzer
extends StateMachineAnalysisVisitor
with StateAnalyzer
{

// ----------------------------------------------------------------------
// Interface methods to override
// Each of these methods is called when a corresponding use occurs
// ----------------------------------------------------------------------

/** A use of an action definition */
def actionUse(
sma: StateMachineAnalysis,
node: AstNode[Ast.Ident],
use: Name.Unqualified
): Result = default(sma)

/** A use of a guard definition */
def guardUse(
sma: StateMachineAnalysis,
node: AstNode[Ast.Ident],
use: Name.Unqualified
): Result = default(sma)

/** A use of a signal definition */
def signalUse(
sma: StateMachineAnalysis,
node: AstNode[Ast.Ident],
use: Name.Unqualified
): Result = default(sma)

/** A use of a state definition or choice definition */
def stateOrChoiceUse(
sma: StateMachineAnalysis,
node: AstNode[Ast.QualIdent],
use: Name.Qualified
): Result = default(sma)

// ----------------------------------------------------------------------
// Implementation using StateMachineAnalysisVisitor
// ----------------------------------------------------------------------

override def defChoiceAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.DefChoice]]
) = {
val data = aNode._2.data
for {
sma <- identNode(guardUse)(sma, data.guard)
sma <- transitionExpr(sma, data.ifTransition.data)
sma <- transitionExpr(sma, data.elseTransition.data)
}
yield sma
}

override def specStateEntryAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecStateEntry]]
) = actions(sma, aNode._2.data.actions)

override def specStateExitAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecStateExit]]
) = actions(sma, aNode._2.data.actions)

override def specInitialTransitionAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecInitialTransition]]
) = transitionExpr(sma, aNode._2.data.transition.data)

override def specStateTransitionAnnotatedNode(
sma: StateMachineAnalysis,
aNode: Ast.Annotated[AstNode[Ast.SpecStateTransition]]
) = {
val data = aNode._2.data
for {
sma <- identNode(signalUse)(sma, data.signal)
sma <- opt(identNode(guardUse))(sma, data.guard)
sma <- transitionOrDo(sma, data.transitionOrDo)
}
yield sma
}

// ----------------------------------------------------------------------
// Private helper methods
// ----------------------------------------------------------------------

private def transitionExpr(
sma: StateMachineAnalysis,
e: Ast.TransitionExpr
): Result =
for {
sma <- actions(sma, e.actions)
sma <- qualIdentNode(stateOrChoiceUse)(sma, e.target)
}
yield sma

private def actions(sma: StateMachineAnalysis, actions: List[AstNode[Ast.Ident]]) =
Result.foldLeft (actions) (sma) (identNode(actionUse))

private def transitionOrDo(
sma: StateMachineAnalysis,
tod: Ast.TransitionOrDo
): Result = tod match {
case Ast.TransitionOrDo.Transition(e) => transitionExpr(sma, e.data)
case Ast.TransitionOrDo.Do(as) => actions(sma, as)
}

private def identNode
(f: (StateMachineAnalysis, AstNode[Ast.Ident], Name.Unqualified) => Result)
(sma: StateMachineAnalysis, ident: AstNode[Ast.Ident]): Result =
f(sma, ident, ident.data)

private def qualIdentNode
(f: (StateMachineAnalysis, AstNode[Ast.QualIdent], Name.Qualified) => Result)
(sma: StateMachineAnalysis, qualIdent: AstNode[Ast.QualIdent]): Result = {
val use = Name.Qualified.fromQualIdent(qualIdent.data)
f(sma, qualIdent, use)
}

}
Loading

0 comments on commit 80f3c4a

Please sign in to comment.