Skip to content

Commit 3addd18

Browse files
authored
Merge pull request #853 from jwest115/dictionary-specifier
Dictionary specifier syntax, semantics, and tests
2 parents 92f2f47 + c331f83 commit 3addd18

File tree

173 files changed

+2357
-904
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

173 files changed

+2357
-904
lines changed

compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
{
99
"name":"[Lio.circe.Encoder;"
1010
},
11+
{
12+
"name":"[Ljava.io.File;"
13+
},
1114
{
1215
"name":"[Ljava.lang.String;"
1316
},
@@ -1259,42 +1262,6 @@
12591262
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1909",
12601263
"fields":[{"name":"0bitmap$1769"}]
12611264
},
1262-
{
1263-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1910",
1264-
"fields":[{"name":"0bitmap$1768"}]
1265-
},
1266-
{
1267-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1911",
1268-
"fields":[{"name":"0bitmap$1760"}]
1269-
},
1270-
{
1271-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1912",
1272-
"fields":[{"name":"0bitmap$1761"}]
1273-
},
1274-
{
1275-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1913",
1276-
"fields":[{"name":"0bitmap$1762"}]
1277-
},
1278-
{
1279-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1914",
1280-
"fields":[{"name":"0bitmap$1763"}]
1281-
},
1282-
{
1283-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1915",
1284-
"fields":[{"name":"0bitmap$1764"}]
1285-
},
1286-
{
1287-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1916",
1288-
"fields":[{"name":"0bitmap$1765"}]
1289-
},
1290-
{
1291-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1917",
1292-
"fields":[{"name":"0bitmap$1766"}]
1293-
},
1294-
{
1295-
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$1918",
1296-
"fields":[{"name":"0bitmap$1767"}]
1297-
},
12981265
{
12991266
"name":"fpp.compiler.codegen.AnalysisJsonEncoder$$anon$192",
13001267
"fields":[{"name":"0bitmap$183"}]

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ case class Analysis(
2222
/** The set of files included when parsing input */
2323
includedFileSet: Set[File] = Set(),
2424
/** A map from pairs (spec loc kind, qualified name) to spec locs. */
25-
locationSpecifierMap: Map[(Ast.SpecLoc.Kind, Name.Qualified), Ast.SpecLoc] = Map(),
25+
locationSpecifierMap: Map[(Ast.SpecLoc.Kind, Name.Qualified), AstNode[Ast.SpecLoc]] = Map(),
2626
/** A list of unqualified names representing the enclosing scope names,
2727
* with the innermost name at the head of the list. For exapmle, inside
2828
* module B where B is inside A and A is at the top level, the module name
@@ -80,7 +80,9 @@ case class Analysis(
8080
/** Whether dictionary generation is required */
8181
dictionaryGeneration: Boolean = false,
8282
/** The mapping from nodes to implied uses */
83-
impliedUseMap: Map[AstNode.Id, ImpliedUse.Uses] = Map()
83+
impliedUseMap: Map[AstNode.Id, ImpliedUse.Uses] = Map(),
84+
/** The set of symbols defined with a dictionary specifier */
85+
dictionarySymbolSet: Set[Symbol] = Set()
8486
) {
8587

8688
/** Gets the qualified name of a symbol */
@@ -394,6 +396,9 @@ case class Analysis(
394396
s"\n\n${Locations.get(id)}\nbecause this type is not displayable$reason"
395397
}
396398
this.typeMap(id) match {
399+
case a: Type.AliasType =>
400+
val id = a.node._2.data.typeName.id
401+
getElementReason(id)
397402
case a: Type.Array =>
398403
val id = a.node._2.data.eltType.id
399404
getElementReason(id)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package fpp.compiler.analysis
2+
3+
import fpp.compiler.ast._
4+
import fpp.compiler.util._
5+
6+
/** Check dictionary definitions */
7+
object CheckDictionaryDefs
8+
extends Analyzer
9+
with ModuleAnalyzer
10+
{
11+
12+
def checkConstantDef(a: Analysis, s: Symbol.Constant) =
13+
if !s.isDictionaryDef
14+
then Right(a)
15+
else
16+
val id = s.getNodeId
17+
val t = a.typeMap(id)
18+
def result =
19+
val a1 = a.copy(dictionarySymbolSet = a.dictionarySymbolSet + s)
20+
Right(a1)
21+
def error =
22+
val loc = Locations.get(id)
23+
val msg = "dictionary constant must have a numeric, Boolean, string, or enum type"
24+
Left(SemanticError.InvalidType(loc, msg))
25+
t match
26+
case _: Type.String | Type.Boolean | _: Type.Enum => result
27+
case _ => if t.isNumeric then result else error
28+
29+
def checkTypeDef(a: Analysis, s: Symbol) =
30+
if s.isDictionaryDef
31+
then
32+
for {
33+
_ <- a.checkDisplayableType(
34+
s.getNodeId,
35+
"dictionary type is not displayable"
36+
)
37+
} yield a.copy(dictionarySymbolSet = a.dictionarySymbolSet + s)
38+
else Right(a)
39+
40+
override def defAliasTypeAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefAliasType]]) =
41+
checkTypeDef(a, Symbol.AliasType(aNode))
42+
43+
override def defArrayAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefArray]]) =
44+
checkTypeDef(a, Symbol.Array(aNode))
45+
46+
override def defEnumAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefEnum]]) =
47+
checkTypeDef(a, Symbol.Enum(aNode))
48+
49+
override def defStructAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefStruct]]) =
50+
checkTypeDef(a, Symbol.Struct(aNode))
51+
52+
override def defConstantAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefConstant]]) =
53+
checkConstantDef(a, Symbol.Constant(aNode))
54+
55+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ object CheckSemantics {
3333
_ <- CheckComponentInstanceDefs.checkIdRanges(a)
3434
a <- CheckStateMachineDefs.visitList(a, tul, CheckStateMachineDefs.transUnit)
3535
a <- CheckTopologyDefs.visitList(a, tul, CheckTopologyDefs.transUnit)
36-
a <- ConstructDictionaryMap.visitList(a, tul, ConstructDictionaryMap.transUnit)
3736
a <- BuildSpecLocMap.visitList(a, tul, BuildSpecLocMap.transUnit)
3837
a <- CheckSpecLocs.visitList(a, tul, CheckSpecLocs.transUnit)
38+
a <- CheckDictionaryDefs.visitList(a, tul, CheckDictionaryDefs.transUnit)
39+
a <- ConstructDictionaryMap.visitList(a, tul, ConstructDictionaryMap.transUnit)
3940
}
4041
yield a
4142
}

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

Lines changed: 95 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,116 @@ object CheckSpecLocs
99
with ModuleAnalyzer
1010
{
1111

12-
override def defAbsTypeAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefAbsType]]) = {
13-
val (_, node, _) = aNode
14-
val name = node.data.name
15-
checkSpecLoc(a, Ast.SpecLoc.Type, name, node)
16-
}
12+
override def defAbsTypeAnnotatedNode(
13+
a: Analysis,
14+
aNode: Ast.Annotated[AstNode[Ast.DefAbsType]]
15+
) = checkSpecLoc(a, Ast.SpecLoc.Type, Symbol.AbsType(aNode))
1716

18-
override def defArrayAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefArray]]) = {
19-
val (_, node, _) = aNode
20-
val name = node.data.name
21-
checkSpecLoc(a, Ast.SpecLoc.Type, name, node)
22-
}
17+
override def defAliasTypeAnnotatedNode(
18+
a: Analysis,
19+
aNode: Ast.Annotated[AstNode[Ast.DefAliasType]]
20+
) = checkSpecLoc(a, Ast.SpecLoc.Type, Symbol.AliasType(aNode))
2321

24-
override def defConstantAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefConstant]]) = {
25-
val (_, node, _) = aNode
26-
val name = node.data.name
27-
checkSpecLoc(a, Ast.SpecLoc.Constant, name, node)
28-
}
22+
override def defArrayAnnotatedNode(
23+
a: Analysis,
24+
aNode: Ast.Annotated[AstNode[Ast.DefArray]]
25+
) = checkSpecLoc(a, Ast.SpecLoc.Type, Symbol.Array(aNode))
2926

30-
override def defEnumAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefEnum]]) = {
31-
val (_, node, _) = aNode
32-
val name = node.data.name
33-
checkSpecLoc(a, Ast.SpecLoc.Type, name, node)
34-
}
27+
override def defComponentAnnotatedNode(
28+
a: Analysis,
29+
aNode: Ast.Annotated[AstNode[Ast.DefComponent]]
30+
) = checkSpecLoc(a, Ast.SpecLoc.Component, Symbol.Component(aNode))
3531

36-
override def defPortAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefPort]]) = {
37-
val (_, node, _) = aNode
38-
val name = node.data.name
39-
checkSpecLoc(a, Ast.SpecLoc.Port, name, node)
40-
}
32+
override def defComponentInstanceAnnotatedNode(
33+
a: Analysis,
34+
aNode: Ast.Annotated[AstNode[Ast.DefComponentInstance]]
35+
) = checkSpecLoc(a, Ast.SpecLoc.ComponentInstance, Symbol.ComponentInstance(aNode))
4136

42-
override def defStructAnnotatedNode(a: Analysis, aNode: Ast.Annotated[AstNode[Ast.DefStruct]]) = {
43-
val (_, node, _) = aNode
44-
val name = node.data.name
45-
checkSpecLoc(a, Ast.SpecLoc.Type, name, node)
46-
}
37+
override def defConstantAnnotatedNode(
38+
a: Analysis,
39+
aNode: Ast.Annotated[AstNode[Ast.DefConstant]]
40+
) = checkSpecLoc(a, Ast.SpecLoc.Constant, Symbol.Constant(aNode))
41+
42+
override def defEnumAnnotatedNode(
43+
a: Analysis,
44+
aNode: Ast.Annotated[AstNode[Ast.DefEnum]]
45+
) = checkSpecLoc(a, Ast.SpecLoc.Type, Symbol.Enum(aNode))
46+
47+
override def defInterfaceAnnotatedNode(
48+
a: Analysis,
49+
aNode: Ast.Annotated[AstNode[Ast.DefInterface]]
50+
) = checkSpecLoc(a, Ast.SpecLoc.Interface, Symbol.Interface(aNode))
4751

48-
private def checkSpecLoc[T](
52+
override def defPortAnnotatedNode(
53+
a: Analysis,
54+
aNode: Ast.Annotated[AstNode[Ast.DefPort]]
55+
) = checkSpecLoc(a, Ast.SpecLoc.Port, Symbol.Port(aNode))
56+
57+
override def defStructAnnotatedNode(
58+
a: Analysis,
59+
aNode: Ast.Annotated[AstNode[Ast.DefStruct]]
60+
) = checkSpecLoc(a, Ast.SpecLoc.Type, Symbol.Struct(aNode))
61+
62+
override def defTopologyAnnotatedNode(
63+
a: Analysis,
64+
aNode: Ast.Annotated[AstNode[Ast.DefTopology]]
65+
) = checkSpecLoc(a, Ast.SpecLoc.Topology, Symbol.Topology(aNode))
66+
67+
private def checkSpecLoc(
68+
a: Analysis,
69+
kind: Ast.SpecLoc.Kind,
70+
symbol: Symbol
71+
) = for {
72+
a <- checkPath(a, kind, symbol)
73+
a <- checkDictionarySpecifier(a, kind, symbol)
74+
} yield a
75+
76+
private def checkPath[T](
4977
a: Analysis,
5078
kind: Ast.SpecLoc.Kind,
51-
name: Name.Unqualified,
52-
node: AstNode[T]
79+
symbol: Symbol
5380
): Result = {
81+
val name = symbol.getUnqualifiedName
82+
val id = symbol.getNodeId
5483
val qualifiedName = Name.Qualified(a.scopeNameList.reverse, name)
55-
val actualLoc = Locations.get(node.id).tuLocation
84+
val actualLoc = Locations.get(id).tuLocation
5685
a.locationSpecifierMap.get((kind, qualifiedName)) match {
57-
case Some(specLoc) => {
86+
case Some(node) => {
87+
val specLoc = node.data
5888
val specifierLoc = Locations.get(specLoc.file.id)
59-
val specifiedJavaPath = specifierLoc.getRelativePath(specLoc.file.data)
89+
val specifiedJavaPath =
90+
specifierLoc.getRelativePath(specLoc.file.data)
6091
val specifiedPath = File.Path(specifiedJavaPath).toString
6192
val actualPath = actualLoc.file.toString
6293
if (specifiedPath == actualPath) Right(a)
63-
else Left(SemanticError.IncorrectSpecLoc(specifierLoc, specifiedPath, actualLoc))
94+
else Left(
95+
SemanticError.IncorrectLocationPath(
96+
specifierLoc,
97+
specifiedPath,
98+
actualLoc
99+
)
100+
)
101+
}
102+
case None => Right(a)
103+
}
104+
}
105+
106+
private def checkDictionarySpecifier(
107+
a: Analysis,
108+
kind: Ast.SpecLoc.Kind,
109+
symbol: Symbol
110+
) = {
111+
val name = a.getQualifiedName(symbol)
112+
val id = symbol.getNodeId
113+
val defLoc = Locations.get(id)
114+
a.locationSpecifierMap.get((kind, name)) match {
115+
case Some(node) => {
116+
val specifierLoc = Locations.get(node.id)
117+
if symbol.isDictionaryDef == node.data.isDictionaryDef
118+
then Right(a)
119+
else Left(
120+
SemanticError.IncorrectDictionarySpecifier(specifierLoc, defLoc)
121+
)
64122
}
65123
case None => Right(a)
66124
}

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,34 @@ object BuildSpecLocMap extends ModuleAnalyzer {
1717
val key = (spec.kind, qualifiedName)
1818
a.locationSpecifierMap.get(key) match {
1919
case None => {
20-
val map = a.locationSpecifierMap + (key -> spec)
20+
val map = a.locationSpecifierMap + (key -> specNode)
2121
Right(a.copy(locationSpecifierMap = map))
2222
}
23-
case Some(spec1) =>
24-
for { _ <- checkPathConsistency(spec, spec1) } yield a
23+
case Some(specNode1) =>
24+
for {
25+
_ <- checkPathConsistency(spec, specNode1.data)
26+
_ <- checkDictionarySpecifierConsistency(specNode, specNode1)
27+
} yield a
2528
}
2629
}
2730

31+
private def checkDictionarySpecifierConsistency(
32+
specNode1: AstNode[Ast.SpecLoc],
33+
specNode2: AstNode[Ast.SpecLoc]
34+
): Result.Result[Unit] = {
35+
val spec1 = specNode1.data
36+
val spec2 = specNode2.data
37+
if(spec1.isDictionaryDef == spec2.isDictionaryDef) then
38+
Right(())
39+
else
40+
Left(
41+
SemanticError.InconsistentDictionarySpecifier(
42+
Locations.get(specNode1.id),
43+
Locations.get(specNode2.id),
44+
)
45+
)
46+
}
47+
2848
private def checkPathConsistency(
2949
spec1: Ast.SpecLoc,
3050
spec2: Ast.SpecLoc
@@ -34,7 +54,7 @@ object BuildSpecLocMap extends ModuleAnalyzer {
3454
if (path1 == path2)
3555
Right(())
3656
else Left(
37-
SemanticError.InconsistentSpecLoc(
57+
SemanticError.InconsistentLocationPath(
3858
Locations.get(spec1.file.id),
3959
path1,
4060
Locations.get(spec2.file.id),

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ object ComputeDependencies {
2323
a <- BuildSpecLocMap.visitList(a, tul, BuildSpecLocMap.transUnit)
2424
a <- ConstructImpliedUseMap.visitList(a, tul, ConstructImpliedUseMap.transUnit)
2525
a <- MapUsesToLocs.visitList(a, tul, MapUsesToLocs.transUnit)
26+
a <- Right(addDictionaryDependencies(a))
2627
}
2728
yield {
2829
val includedFileSet = a.includedFileSet
@@ -31,4 +32,16 @@ object ComputeDependencies {
3132
}
3233
}
3334

35+
// Adds the dictionary dependencies to the dependency file set
36+
private def addDictionaryDependencies(a: Analysis) = {
37+
val dfs = a.locationSpecifierMap.foldLeft (a.dependencyFileSet) {
38+
case (s, (_, node)) => {
39+
if node.data.isDictionaryDef
40+
then s + Locations.get(node.data.file.id).file
41+
else s
42+
}
43+
}
44+
a.copy(dependencyFileSet = dfs)
45+
}
46+
3447
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ object MapUsesToLocs extends BasicUseAnalyzer {
6060
nameList match {
6161
case Nil => None
6262
case head :: tail => a.locationSpecifierMap.get((kind, head)) match {
63-
case specLoc @ Some(_) => specLoc
63+
case opt @ Some(_) => opt.map(_.data)
6464
case None => findLocation(tail)
6565
}
6666
}

0 commit comments

Comments
 (0)