Skip to content

Commit 4823b55

Browse files
authored
Merge pull request #740 from nasa/issue-738-implied-uses
Revise handling of implied uses in topologies
2 parents b9db462 + 5fbdbc7 commit 4823b55

33 files changed

+476
-55
lines changed

compiler/lib/src/main/scala/analysis/Analysis.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ case class Analysis(
7777
dictionary: Option[Dictionary] = None,
7878
/** The telemetry packet set under construction */
7979
tlmPacketSet: Option[TlmPacketSet] = None,
80-
/** Whether a dictionary is needed in code generation */
81-
dictionaryNeeded: Boolean = false,
82-
/** The set of type symbols used by the dictionary */
83-
dictionaryTypeSymbolSet: Set[Symbol] = Set()
80+
/** Whether dictionary generation is required */
81+
dictionaryGeneration: Boolean = false,
82+
/** The mapping from nodes to implied uses */
83+
impliedUseMap: Map[AstNode.Id, ImpliedUse.Uses] = Map()
8484
) {
8585

8686
/** Gets the qualified name of a symbol */
@@ -295,6 +295,10 @@ case class Analysis(
295295
v
296296
}
297297

298+
/** Gets the implied uses for an AST node */
299+
def getImpliedUses(kind: ImpliedUse.Kind, id: AstNode.Id): Set[ImpliedUse] =
300+
impliedUseMap(id).get(kind).getOrElse(Set())
301+
298302
/** Gets an int value from an AST node */
299303
def getIntValue(id: AstNode.Id): Result.Result[Int] = {
300304
val v = getBigIntValue(id)

compiler/lib/src/main/scala/analysis/Analyzers/UseAnalyzer.scala

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ trait UseAnalyzer extends TypeExpressionAnalyzer {
3939
} yield a
4040
}
4141

42+
override def defTopologyAnnotatedNode(a: Analysis, node: Ast.Annotated[AstNode[Ast.DefTopology]]) = {
43+
val id = node._2.id
44+
for {
45+
a <- visitImpliedUses(a, id)
46+
a <- super.defTopologyAnnotatedNode(a, node)
47+
} yield a
48+
}
49+
4250
override def exprDotNode(a: Analysis, node: AstNode[Ast.Expr], e: Ast.ExprDot) = {
4351
def nameOpt(e: Ast.Expr, qualifier: List[Name.Unqualified]): Option[Name.Qualified] = {
4452
e match {
@@ -126,9 +134,7 @@ trait UseAnalyzer extends TypeExpressionAnalyzer {
126134
case Ast.SpecPortInstance.TimeGet => "Time"
127135
}
128136
val identList = List("Fw", name)
129-
val nodeList = identList.map(AstNode.create(_, node.id))
130-
val qualIdent = Ast.QualIdent.fromNodeList(nodeList)
131-
val impliedUse = AstNode.create(qualIdent, node.id)
137+
val impliedUse = ImpliedUse.fromIdentListAndId(identList, node.id).asQualIdentNode
132138
for {
133139
a <- opt(exprNode)(a, special.priority)
134140
a <- qualIdentNode(portUse)(a, impliedUse)
@@ -192,4 +198,22 @@ trait UseAnalyzer extends TypeExpressionAnalyzer {
192198
tlmChannelIdentifierNode(a, node)
193199
}
194200

201+
private def visitImpliedConstantUses(a: Analysis, id: AstNode.Id) = {
202+
// TODO
203+
Right(a)
204+
}
205+
206+
private def visitImpliedTypeUses(a: Analysis, id: AstNode.Id) = {
207+
val uses = a.getImpliedUses(ImpliedUse.Kind.Type, id).toList
208+
def visit(a: Analysis, iu: ImpliedUse) = typeUse(a, iu.asTypeNameNode, iu.name)
209+
visitList(a, uses, visit)
210+
}
211+
212+
private def visitImpliedUses(a: Analysis, id: AstNode.Id) = {
213+
for {
214+
a <- visitImpliedConstantUses(a, id)
215+
a <- visitImpliedTypeUses(a, id)
216+
} yield a
217+
}
218+
195219
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package fpp.compiler.analysis
2+
3+
import fpp.compiler.ast._
4+
import fpp.compiler.util._
5+
6+
/** Check F Prime framework definitions */
7+
object CheckFrameworkDefs
8+
extends Analyzer
9+
with ModuleAnalyzer
10+
{
11+
12+
override def defAliasTypeAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefAliasType]]) = {
13+
val name = a.getQualifiedName (Symbol.AliasType(aNode)).toString
14+
val id = aNode._2.id
15+
name match {
16+
case "FwAssertArgType" => requireIntegerTypeAlias(a, name, id)
17+
case "FwChanIdType" => requireIntegerTypeAlias(a, name, id)
18+
case "FwDpIdType" => requireIntegerTypeAlias(a, name, id)
19+
case "FwDpPriorityType" => requireIntegerTypeAlias(a, name, id)
20+
case "FwEnumStoreType" => requireIntegerTypeAlias(a, name, id)
21+
case "FwEventIdType" => requireIntegerTypeAlias(a, name, id)
22+
case "FwIndexType" => requireSignedIntegerTypeAlias(a, name, id)
23+
case "FwOpcodeType" => requireIntegerTypeAlias(a, name, id)
24+
case "FwPacketDescriptorType" => requireIntegerTypeAlias(a, name, id)
25+
case "FwPrmIdType" => requireIntegerTypeAlias(a, name, id)
26+
case "FwQueuePriorityType" => requireIntegerTypeAlias(a, name, id)
27+
case "FwSignedSizeType" => requireSignedIntegerTypeAlias(a, name, id)
28+
case "FwSizeStoreType" => requireIntegerTypeAlias(a, name, id)
29+
case "FwSizeType" => requireUnsignedIntegerTypeAlias(a, name, id)
30+
case "FwPriorityType" => requireIntegerTypeAlias(a, name, id)
31+
case "FwTimeBaseStoreType" => requireIntegerTypeAlias(a, name, id)
32+
case "FwTimeContextStoreType" => requireIntegerTypeAlias(a, name, id)
33+
case "FwTlmPacketizeIdType" => requireIntegerTypeAlias(a, name, id)
34+
case "FwTraceIdType" => requireIntegerTypeAlias(a, name, id)
35+
case _ => Right(a)
36+
}
37+
}
38+
39+
private def requireIntegerTypeAlias(a: Analysis, name: String, id: AstNode.Id) =
40+
if a.typeMap(id).getUnderlyingType.isInt
41+
then Right(a)
42+
else Left(
43+
SemanticError.InvalidType(
44+
Locations.get(id),
45+
s"the F Prime framework type ${name} must be an alias of an integer type"
46+
)
47+
)
48+
49+
private def requireSignedIntegerTypeAlias(a: Analysis, name: String, id: AstNode.Id) = {
50+
val valid = a.typeMap(id).getUnderlyingType match {
51+
case pi: Type.PrimitiveInt => pi.signedness == Type.PrimitiveInt.Signed
52+
case _ => false
53+
}
54+
if (valid) then Right(a) else Left(
55+
SemanticError.InvalidType(
56+
Locations.get(id),
57+
s"the F Prime framework type ${name} must be an alias of a signed integer type"
58+
)
59+
)
60+
}
61+
62+
private def requireUnsignedIntegerTypeAlias(a: Analysis, name: String, id: AstNode.Id) = {
63+
val valid = a.typeMap(id).getUnderlyingType match {
64+
case pi: Type.PrimitiveInt => pi.signedness == Type.PrimitiveInt.Unsigned
65+
case _ => false
66+
}
67+
if (valid) then Right(a) else Left(
68+
SemanticError.InvalidType(
69+
Locations.get(id),
70+
s"the F Prime framework type ${name} must be an alias of an unsigned integer type"
71+
)
72+
)
73+
}
74+
75+
}

compiler/lib/src/main/scala/analysis/CheckSemantics/CheckSemantics.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ object CheckSemantics {
1717
a <- Right(a_tul._1)
1818
tul <- Right(a_tul._2)
1919
a <- EnterSymbols.visitList(a, tul, EnterSymbols.transUnit)
20+
a <- ConstructImpliedUseMap.visitList(a, tul, ConstructImpliedUseMap.transUnit)
2021
a <- CheckUses.visitList(a, tul, CheckUses.transUnit)
2122
_ <- CheckUseDefCycles.visitList(a, tul, CheckUseDefCycles.transUnit)
2223
a <- CheckTypeUses.visitList(a, tul, CheckTypeUses.transUnit)
2324
a <- CheckExprTypes.visitList(a, tul, CheckExprTypes.transUnit)
2425
a <- EvalImpliedEnumConsts.visitList(a, tul, EvalImpliedEnumConsts.transUnit)
2526
a <- EvalConstantExprs.visitList(a, tul, EvalConstantExprs.transUnit)
2627
a <- FinalizeTypeDefs.visitList(a, tul, FinalizeTypeDefs.transUnit)
28+
a <- CheckFrameworkDefs.visitList(a, tul, CheckFrameworkDefs.transUnit)
2729
a <- CheckPortDefs.visitList(a, tul, CheckPortDefs.transUnit)
2830
a <- CheckInterfaceDefs.visitList(a, tul, CheckInterfaceDefs.transUnit)
2931
a <- CheckComponentDefs.visitList(a, tul, CheckComponentDefs.transUnit)

compiler/lib/src/main/scala/analysis/CheckSemantics/CheckUses.scala

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -118,30 +118,18 @@ object CheckUses extends UseAnalyzer {
118118
}
119119

120120
override def defTopologyAnnotatedNode(a: Analysis, node: Ast.Annotated[AstNode[Ast.DefTopology]]) = {
121-
a.dictionaryNeeded match {
122-
case true => {
123-
val impliedTypeUses = List(
124-
"FwChanIdType",
125-
"FwEventIdType",
126-
"FwOpcodeType",
127-
"FwPacketDescriptorType",
128-
"FwTlmPacketizeIdType"
129-
)
130-
val (_, node1, _) = node
131-
val mapping = a.nestedScope.get (NameGroup.Type) _
121+
val impliedTypeUses = a.getImpliedUses(ImpliedUse.Kind.Type, node._2.id).toList
122+
for {
123+
_ <- Result.foldLeft (impliedTypeUses) (()) ((_, itu) => {
132124
for {
133-
a <- Result.foldLeft (impliedTypeUses) (a) ((a, t) => {
134-
for {
135-
symbol <- Result.annotateResult(
136-
helpers.getSymbolForName(mapping)(node1.id, t),
137-
s"when constructing a dictionary, the type $t must be defined")
138-
} yield a.copy(dictionaryTypeSymbolSet = a.dictionaryTypeSymbolSet + symbol)
139-
})
140-
a <- super.defTopologyAnnotatedNode(a, node)
141-
} yield a
142-
}
143-
case false => super.defTopologyAnnotatedNode(a, node)
144-
}
125+
_ <- Result.annotateResult(
126+
typeUse(a, itu.asTypeNameNode, itu.name),
127+
s"when constructing a dictionary, the type ${itu.name} must be defined"
128+
)
129+
} yield ()
130+
})
131+
a <- super.defTopologyAnnotatedNode(a, node)
132+
} yield a
145133
}
146134

147135
override def portUse(a: Analysis, node: AstNode[Ast.QualIdent], use: Name.Qualified) =

compiler/lib/src/main/scala/analysis/CheckSemantics/ConstructDictionaryMap.scala

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,11 @@ object ConstructDictionaryMap
2222
val d2 = DictionaryUsedSymbols(a, t).updateUsedSymbols(d1)
2323
DictionaryEntries(a, t).updateEntries(d2)
2424
}
25-
val a1 = a.copy(topology = Some(t), dictionary = Some(d))
2625
for {
27-
a <- Result.foldLeft (a.dictionaryTypeSymbolSet.toList) (a) ((a, s) => {
28-
val loc = Locations.get(s.getNodeId)
29-
if a.typeMap(s.getNodeId).getUnderlyingType.isInt
30-
then Right(a)
31-
else Left(
32-
SemanticError.InvalidType(
33-
loc,
34-
s"this F Prime framework type must be an alias of an integer type"
35-
)
36-
)
37-
})
38-
a <- super.defTopologyAnnotatedNode(a1, aNode)
26+
a <- super.defTopologyAnnotatedNode(
27+
a.copy(topology = Some(t), dictionary = Some(d)),
28+
aNode
29+
)
3930
}
4031
yield {
4132
val d = a.dictionary.get
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package fpp.compiler.analysis
2+
3+
import fpp.compiler.ast._
4+
import fpp.compiler.util._
5+
6+
/** Construct the implied use map */
7+
object ConstructImpliedUseMap
8+
extends Analyzer
9+
with ModuleAnalyzer
10+
{
11+
12+
override def defTopologyAnnotatedNode(
13+
a: Analysis,
14+
aNode: Ast.Annotated[AstNode[Ast.DefTopology]]
15+
) = {
16+
val id = aNode._2.id
17+
val typeNames = ImpliedUse.getTopologyTypes(a)
18+
val empty: ImpliedUse.Uses = Map()
19+
val map = typeNames.foldLeft (empty) ((m, tn) => {
20+
val id1 = ImpliedUse.replicateId(id)
21+
val impliedUse = ImpliedUse.fromIdentListAndId(tn, id1)
22+
val set = m.get(ImpliedUse.Kind.Type).getOrElse(Set())
23+
m + (ImpliedUse.Kind.Type -> (set + impliedUse))
24+
})
25+
Right(a.copy(impliedUseMap = a.impliedUseMap + (id -> map)))
26+
}
27+
28+
}

compiler/lib/src/main/scala/analysis/ComputeDependencies/ComputeDependencies.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ object ComputeDependencies {
2121
case _ => Right(a)
2222
}
2323
a <- BuildSpecLocMap.visitList(a, tul, BuildSpecLocMap.transUnit)
24+
a <- ConstructImpliedUseMap.visitList(a, tul, ConstructImpliedUseMap.transUnit)
2425
a <- MapUsesToLocs.visitList(a, tul, MapUsesToLocs.transUnit)
2526
}
2627
yield {

compiler/lib/src/main/scala/analysis/Semantics/ConstructDictionary/DictionaryUsedSymbols.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ final case class DictionaryUsedSymbols(a: Analysis, t: Topology) {
99
d.copy(usedSymbolSet = getUsedSymbolSet)
1010

1111
private def getUsedSymbolSet: Set[Symbol] =
12-
t.instanceMap.keys.toSet.flatMap(getUsedSymbolsForInstance) ++ a.dictionaryTypeSymbolSet
12+
t.instanceMap.keys.toSet.flatMap(getUsedSymbolsForInstance) ++
13+
a.getImpliedUses(ImpliedUse.Kind.Type, t.aNode._2.id).map(iu => a.useDefMap(iu.id))
1314

1415
private def getUsedSymbolsForInstance(ci: ComponentInstance) = {
1516
val component = ci.component
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package fpp.compiler.analysis
2+
3+
import fpp.compiler.ast.*
4+
import fpp.compiler.util.*
5+
6+
/** An implied use of an FPP symbol */
7+
case class ImpliedUse(
8+
/** The fully-qualified name of the implied use */
9+
name: Name.Qualified,
10+
/** The AST node id associated with the implied use */
11+
id: AstNode.Id
12+
) {
13+
14+
def asQualIdentNode: AstNode[Ast.QualIdent] = {
15+
val nodeList = name.toIdentList.map(AstNode.create(_, id))
16+
val qualIdent = Ast.QualIdent.fromNodeList(nodeList)
17+
AstNode.create(qualIdent, id)
18+
}
19+
20+
def asTypeNameNode: AstNode[Ast.TypeName] = {
21+
val typeName = Ast.TypeNameQualIdent(asQualIdentNode)
22+
AstNode.create(typeName, id)
23+
}
24+
25+
}
26+
27+
object ImpliedUse {
28+
29+
enum Kind:
30+
case Constant, Type
31+
32+
type Uses = Map[Kind, Set[ImpliedUse]]
33+
34+
/** The qualified names of implied type uses.
35+
* Each name is a list of identifiers */
36+
def getTopologyTypes(a: Analysis) =
37+
if (a.dictionaryGeneration) then List(
38+
"FwChanIdType",
39+
"FwEventIdType",
40+
"FwOpcodeType",
41+
"FwPacketDescriptorType",
42+
"FwTlmPacketizeIdType"
43+
).map(List(_))
44+
else Nil
45+
46+
def replicateId(id: AstNode.Id) = {
47+
val loc = Locations.get(id)
48+
val id1 = AstNode.getId
49+
Locations.put(id1, loc)
50+
id1
51+
}
52+
53+
def fromIdentListAndId(identList: List[Name.Unqualified], id: AstNode.Id) =
54+
ImpliedUse(Name.Qualified.fromIdentList(identList), id)
55+
56+
}

0 commit comments

Comments
 (0)