From 3aa4a2b58221a3e83b17d0c04c9a6e9c41e5500c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Nov 2022 21:39:00 +0100 Subject: refactor: separate primitive types from nodes --- subprojects/frontend/src/index.tsx | 21 +- subprojects/frontend/src/language/problem.grammar | 59 +- .../src/language/problemLanguageSupport.ts | 5 +- .../frontend/src/xtext/ContentAssistService.ts | 2 +- subprojects/language-model/problem.aird | 1407 ++++++++++++-------- .../src/main/resources/model/problem.ecore | 53 +- .../src/main/resources/model/problem.genmodel | 42 +- .../language/semantics/model/ModelInitializer.java | 9 +- .../java/tools/refinery/language/Problem.xtext | 102 +- .../refinery/language/ProblemRuntimeModule.java | 21 +- .../language/formatting2/ProblemFormatter.java | 32 +- .../language/resource/NodeNameCollector.java | 38 +- .../resource/ProblemDerivedStateComputer.java | 67 +- .../language/scoping/ProblemScopeProvider.java | 3 +- ...ferShortAssertionsProblemSemanticSequencer.java | 52 + .../refinery/language/utils/BuiltinSymbols.java | 12 +- .../tools/refinery/language/utils/NodeInfo.java | 10 +- .../refinery/language/utils/ProblemDesugarer.java | 50 +- .../refinery/language/utils/SymbolCollector.java | 134 +- .../tools/refinery/language/builtin.problem | 28 +- .../language/tests/ProblemParsingTest.java | 12 +- .../parser/antlr/TransitiveClosureParserTest.java | 4 +- .../language/tests/rules/RuleParsingTest.java | 14 +- .../language/tests/scoping/NodeScopingTest.java | 81 +- .../tests/serializer/ProblemSerializerTest.java | 36 +- .../language/tests/utils/SymbolCollectorTest.java | 73 +- .../tests/utils/WrappedAssertionArgument.java | 5 - .../model/tests/utils/WrappedClassDeclaration.java | 8 +- .../language/model/tests/utils/WrappedProblem.java | 23 +- 29 files changed, 1249 insertions(+), 1154 deletions(-) create mode 100644 subprojects/language/src/main/java/tools/refinery/language/serializer/PreferShortAssertionsProblemSemanticSequencer.java diff --git a/subprojects/frontend/src/index.tsx b/subprojects/frontend/src/index.tsx index b42b8062..0165d7c1 100644 --- a/subprojects/frontend/src/index.tsx +++ b/subprojects/frontend/src/index.tsx @@ -10,26 +10,23 @@ const initialValue = `class Family { } class Person { - Person[] children opposite parent - Person[0..1] parent opposite children + refers Person[] children opposite parent + refers Person[0..1] parent opposite children int age - TaxStatus taxStatus + refers TaxStatus taxStatus } enum TaxStatus { child, student, adult, retired } -int ageDifference(Person p, Person q) = - children(p, q), age(p, pAge), age(q, qAge) -> qAge - pAge. - -error invalidAgeDifference(Person p, Person q) <-> - children(p, q), ageDifference(p, q) <= 0. - % A child cannot have any dependents. pred invalidTaxStatus(Person p) <-> taxStatus(p, child), children(p, _q) +; + parent(p, q), + age(q) < age(p) ; taxStatus(p, retired), parent(p, q), @@ -44,10 +41,8 @@ children(anne, ciri). ?children(bob, ciri). default children(ciri, *): false. taxStatus(anne, adult). -age(anne, 35). -bobAge: 27. -age(bob, bobAge). -!age(ciri, bobAge). +age(bob) in 21..35. +age(ciri) = 10. scope Family = 1, Person += 5..10. `; diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar index 4235c433..7c4098d5 100644 --- a/subprojects/frontend/src/language/problem.grammar +++ b/subprojects/frontend/src/language/problem.grammar @@ -3,11 +3,12 @@ @external prop implicitCompletion from './props' @precedence { - containment @cut, prefix, exponential @right, multiplicative @left, additive @left, + range @left, + lattice @left, comparison @left } @@ -20,7 +21,7 @@ statement { ClassDefinition { kw<"abstract">? kw<"class"> RelationName (kw<"extends"> sep<",", RelationName>)? - (ClassBody { "{" ReferenceDeclaration* "}" } | ".") + (ClassBody { "{" FeatureDeclaration* "}" } | ".") } | EnumDefinition { kw<"enum"> RelationName @@ -35,7 +36,7 @@ statement { PredicateBody { ("<->" sep)? "." } } | FunctionDefinition { - RelationName RelationName ParameterList? + PrimitiveType RelationName ParameterList? FunctionBody { ("=" sep)? "." } } | //RuleDefinition { @@ -45,10 +46,8 @@ statement { //} | Assertion { kw<"default">? (NotOp | UnknownOp)? RelationName - ParameterList (":" LogicValue)? "." - } | - NodeValueAssertion { - QualifiedName ":" Constant "." + ParameterList + (":" LogicValue | ("=" | kw<"in">) Expr)? "." } | IndividualDeclaration { kw<"individual"> sep<",", IndividualNodeName> "." @@ -58,15 +57,8 @@ statement { } } -ReferenceDeclaration { - ( - ExplicitContainmentReference { - !containment (kw<"refers"> | ckw<"contains"> | kw<"container">) RelationName - } | - ImplicitContainmentReference { - RelationName - } - ) +FeatureDeclaration { + (ReferenceKind | PrimitiveType | kw<"bool">) RelationName ("[" Multiplicity? "]")? RelationName (kw<"opposite"> RelationName)? @@ -75,7 +67,10 @@ ReferenceDeclaration { Parameter { Modality? RelationName? VariableName } -Conjunction { ("," | Expr)+ } +// Use @dynamicPrecedence to prevent a(b) from being parsed as Expr { a } Expr { b } +// instead of Atom { a(b) } +// Being looser with token sequencing enables more consistent syntactic highlighting. +Conjunction { ("," | NextConjunction[@dynamicPrecedence=-10] { Expr })+ } Case { Conjunction ("->" Expr)? } @@ -85,8 +80,10 @@ Expr { UnaryExpr | BinaryExpr | Aggregation | VariableName | Atom | Constant | "(" Expr ")" } -BinaryExpr[@dynamicPrecedence=1] { +BinaryExpr { Expr !comparison ComparisonOp Expr | + Expr !lattice (LatticeMeet | "\\/") Expr | + Expr !range ".." Expr | Expr !additive ("+" | "-") Expr | Expr !multiplicative (StarMult | Divide) Expr | Expr !exponential "**" Expr @@ -110,12 +107,20 @@ Atom { RelationName "+"? ParameterList } // Literal //} -AssertionArgument { NodeName | StarArgument | Constant } +AssertionArgument { NodeName | StarArgument } + +Constant { Real | String | StarMult } -Constant { Real | String } +ReferenceKind { + kw<"refers"> | ckw<"contains"> | kw<"container"> +} + +PrimitiveType { + kw<"int"> | kw<"real"> | kw<"string"> +} LogicValue { - ckw<"true"> | ckw<"false"> | kw<"unknown"> | kw<"error"> + kw<"true"> | kw<"false"> | kw<"unknown"> | kw<"error"> } Modality { @@ -126,10 +131,16 @@ AggregationOp { ckw<"sum"> | ckw<"prod"> | ckw<"min"> | ckw<"max"> } +ComparisonOp { SymbolicComparisonOp | kw<"in"> } + ScopeElement { RelationName ("=" | "+=") Multiplicity } Multiplicity { (IntMult "..")? (IntMult | StarMult)} +// The ~name handles the ambiguity between transitve closure a+(b, c) and addition a+(b) +// in the grammar. We prefer the addition interpretation by applying @dynamicPrecedence=1 +// to the VariableName rule, +// but will go with the transtive closure (and highlight `a` as a relation) if forced. RelationName { QualifiedName ~name } //RuleName { QualifiedName } @@ -167,7 +178,9 @@ sep1 { content (separator content)* } Divide { "/" } - @precedence { BlockComment, LineComment, Divide } + LatticeMeet { "/\\" } + + @precedence { BlockComment, LineComment, LatticeMeet, Divide } identifier { $[A-Za-z_] $[a-zA-Z0-9_]* } @@ -186,7 +199,7 @@ sep1 { content (separator content)* } "\"" (![\\"\n] | "\\" (![\n] | "\n"))* "\"" } - ComparisonOp { ">" | ">=" | "<" | "<=" | "==" | "!=" } + SymbolicComparisonOp { ">" | ">=" | "<" | "<=" | "==" | "!=" } NotOp { "!" } diff --git a/subprojects/frontend/src/language/problemLanguageSupport.ts b/subprojects/frontend/src/language/problemLanguageSupport.ts index cde8b157..2a973c93 100644 --- a/subprojects/frontend/src/language/problemLanguageSupport.ts +++ b/subprojects/frontend/src/language/problemLanguageSupport.ts @@ -24,9 +24,10 @@ const parserWithMetadata = parser.configure({ 'problem class enum pred individual scope': t.definitionKeyword, 'abstract extends refers contains container opposite': t.modifier, 'default error contained containment': t.modifier, - 'true false unknown error': t.operatorKeyword, + 'true false unknown error': t.keyword, + 'int real string bool': t.keyword, 'may must current': t.operatorKeyword, - 'sum prod min max': t.operatorKeyword, + 'sum prod min max in': t.operatorKeyword, // 'new delete': t.keyword, NotOp: t.operator, UnknownOp: t.operator, diff --git a/subprojects/frontend/src/xtext/ContentAssistService.ts b/subprojects/frontend/src/xtext/ContentAssistService.ts index 101990af..fa894e4d 100644 --- a/subprojects/frontend/src/xtext/ContentAssistService.ts +++ b/subprojects/frontend/src/xtext/ContentAssistService.ts @@ -17,7 +17,7 @@ const PROPOSALS_LIMIT = 1000; const IDENTIFIER_REGEXP_STR = '[a-zA-Z0-9_]*'; -const HIGH_PRIORITY_KEYWORDS = ['<->', '==>']; +const HIGH_PRIORITY_KEYWORDS = ['<->', '->', '==>']; const log = getLogger('xtext.ContentAssistService'); diff --git a/subprojects/language-model/problem.aird b/subprojects/language-model/problem.aird index ea236bac..71c35bf0 100644 --- a/subprojects/language-model/problem.aird +++ b/subprojects/language-model/problem.aird @@ -7,15 +7,15 @@ build/resources/main/model/problem.genmodel - + - + - + @@ -35,7 +35,7 @@ - + @@ -48,7 +48,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -87,7 +87,7 @@ - + @@ -100,7 +100,7 @@ - + @@ -109,7 +109,7 @@ - + @@ -118,7 +118,7 @@ - + @@ -127,7 +127,7 @@ - + @@ -136,7 +136,7 @@ - + @@ -149,7 +149,7 @@ - + @@ -158,7 +158,7 @@ - + @@ -175,7 +175,7 @@ - + @@ -188,7 +188,7 @@ - + @@ -197,7 +197,7 @@ - + @@ -206,7 +206,7 @@ - + @@ -215,7 +215,7 @@ - + @@ -224,7 +224,7 @@ - + @@ -233,7 +233,7 @@ - + @@ -242,7 +242,7 @@ - + @@ -251,7 +251,7 @@ - + @@ -260,7 +260,7 @@ - + @@ -285,15 +285,11 @@ - + - - - - @@ -310,7 +306,7 @@ - + @@ -335,16 +331,20 @@ - + + + + + - + @@ -353,7 +353,7 @@ - + @@ -362,7 +362,7 @@ - + @@ -371,7 +371,7 @@ - + @@ -380,7 +380,7 @@ - + @@ -389,7 +389,7 @@ - + @@ -398,7 +398,7 @@ - + @@ -407,7 +407,59 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -458,22 +510,6 @@ - - - - - - - - - - - - - - - - @@ -764,17 +800,17 @@ - + - + - + - + @@ -924,17 +960,17 @@ - + - + - + - + @@ -1066,22 +1102,6 @@ - - - - - - - - - - - - - - - - @@ -1164,49 +1184,33 @@ - + - + - + - + - - - - - - - - - - - - - - - - - + - + - + - + @@ -1241,6 +1245,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1286,7 +1369,7 @@ - + KEEP_LOCATION @@ -1296,7 +1379,7 @@ - + @@ -1305,9 +1388,9 @@ - - - + + + labelSize @@ -1992,14 +2075,6 @@ - - - - - - - - @@ -2068,26 +2143,34 @@ - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + + + + + + + + + - + - + italic - + @@ -2266,7 +2349,7 @@ - + KEEP_LOCATION @@ -2290,9 +2373,9 @@ - - - + + + @@ -2329,12 +2412,12 @@ - + - + italic - + @@ -2373,20 +2456,6 @@ - - - - - - - labelSize - - - labelSize - - - - @@ -2429,6 +2498,119 @@ + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + italic + + + + + + + + + + + italic + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + italic + + + + + + + + + + + + italic + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2473,7 +2655,7 @@ - + @@ -2524,11 +2706,19 @@ + + + + + + + + - + @@ -2599,15 +2789,6 @@ - - - - - - - - - @@ -2615,7 +2796,7 @@ - + @@ -2628,7 +2809,7 @@ - + @@ -2641,7 +2822,7 @@ - + @@ -2654,7 +2835,7 @@ - + @@ -2727,11 +2908,15 @@ + + + + - + @@ -2811,6 +2996,24 @@ + + + + + + + + + + + + + + + + + + @@ -2846,33 +3049,33 @@ - + - + - + - + - + - + - + - + @@ -2894,17 +3097,17 @@ - + - + - + - + @@ -3020,35 +3223,19 @@ - - - - - - - - - - - - - - - - - + - + - + @@ -3148,22 +3335,6 @@ - - - - - - - - - - - - - - - - @@ -3308,6 +3479,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3323,7 +3542,7 @@ - + KEEP_LOCATION @@ -3372,7 +3591,7 @@ - + KEEP_LOCATION @@ -3536,6 +3755,22 @@ + + + + + + + + + + + + + + + + @@ -3692,18 +3927,7 @@ - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - + KEEP_LOCATION @@ -3808,19 +4032,6 @@ - - - - - - labelSize - - - labelSize - - - - @@ -3930,18 +4141,6 @@ - - - - - - - italic - - - - - @@ -4050,6 +4249,14 @@ + + + + + + + + @@ -4265,6 +4472,64 @@ + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + italic + + + + + + + + + + + + italic + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + italic + + + + + @@ -4282,15 +4547,11 @@ - + - - - - @@ -4299,7 +4560,7 @@ - + @@ -4308,41 +4569,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -4351,20 +4578,7 @@ - - - - - - - - - - - - - - + @@ -4373,7 +4587,7 @@ - + @@ -4382,16 +4596,7 @@ - - - - - - - - - - + @@ -4400,22 +4605,91 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + @@ -4432,89 +4706,25 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - + + - - - - - - - - - - - - - - - - @@ -4528,7 +4738,7 @@ - + @@ -4547,54 +4757,86 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4610,7 +4852,7 @@ - + KEEP_LOCATION @@ -4622,24 +4864,16 @@ - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - - - - - - - - @@ -4649,7 +4883,7 @@ - + KEEP_LOCATION @@ -4677,82 +4911,16 @@ - + - + italic - + - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - - - - italic - - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + KEEP_LOCATION @@ -4763,39 +4931,6 @@ - - - - - - - labelSize - - - labelSize - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - - - - - @@ -4818,19 +4953,6 @@ - - - - - - labelSize - - - labelSize - - - - @@ -4845,18 +4967,6 @@ - - - - - - - italic - - - - - @@ -4881,70 +4991,187 @@ - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + italic - - - - - + + + + + + labelSize - + labelSize - - - - - + + + + + labelSize - + labelSize - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + italic - - - - - - + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + italic + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + + + + italic + + + + + + + + + + + + italic + + + + + + + + + + labelSize - + labelSize + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore b/subprojects/language-model/src/main/resources/model/problem.ecore index 3214da65..314639c1 100644 --- a/subprojects/language-model/src/main/resources/model/problem.ecore +++ b/subprojects/language-model/src/main/resources/model/problem.ecore @@ -9,14 +9,14 @@ - + - + @@ -49,12 +49,13 @@ eType="#//ImplicitVariable" transient="true" containment="true"/> - + @@ -91,7 +92,7 @@ eType="#//Node" containment="true"/> - + @@ -107,17 +108,6 @@ - - - - - - - - - @@ -168,9 +158,9 @@ + - @@ -205,6 +195,8 @@ + + @@ -225,16 +217,12 @@ - - - - + @@ -256,4 +244,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel b/subprojects/language-model/src/main/resources/model/problem.genmodel index 230f9919..c5d7c0ea 100644 --- a/subprojects/language-model/src/main/resources/model/problem.genmodel +++ b/subprojects/language-model/src/main/resources/model/problem.genmodel @@ -29,9 +29,9 @@ + - @@ -52,6 +52,8 @@ + + @@ -59,13 +61,18 @@ + + + + + - + @@ -97,10 +104,10 @@ - + @@ -139,15 +146,6 @@ - - - - - - - - - @@ -199,13 +197,10 @@ - - - - + @@ -224,5 +219,20 @@ + + + + + + + + + + + + + + + diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java index 233cc156..557e9752 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java @@ -75,8 +75,6 @@ public class ModelInitializer { var argument = arguments.get(i); if (argument instanceof NodeAssertionArgument nodeArgument) { nodes[i] = nodeTrace.getOrThrow(nodeArgument.getNode()); - } else if (argument instanceof ConstantAssertionArgument constantArgument) { - nodes[i] = nodeTrace.getOrThrow(constantArgument.getNode()); } else if (argument instanceof WildcardAssertionArgument) { nodes[i] = -1; } else { @@ -86,8 +84,11 @@ public class ModelInitializer { return Tuple.of(nodes); } - private static TruthValue getTruthValue(LogicValue value) { - return switch (value) { + private static TruthValue getTruthValue(AssertionValue value) { + if (!(value instanceof LogicAssertionValue logicAssertionValue)) { + return TruthValue.ERROR; + } + return switch (logicAssertionValue.getLogicValue()) { case TRUE -> TruthValue.TRUE; case FALSE -> TruthValue.FALSE; case UNKNOWN -> TruthValue.UNKNOWN; diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext index bc1ee465..8b13a693 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext +++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext @@ -10,14 +10,13 @@ Problem: Statement: ClassDeclaration | EnumDeclaration | PredicateDefinition | FunctionDefinition | /* RuleDefinition | */ - Assertion | NodeValueAssertion | - ScopeDeclaration | IndividualDeclaration; + Assertion | ScopeDeclaration | IndividualDeclaration; ClassDeclaration: abstract?="abstract"? "class" name=Identifier ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)? - ("{" (referenceDeclarations+=ReferenceDeclaration ";"?)* "}" | "."); + ("{" (featureDeclarations+=FeatureDeclaration ";"?)* "}" | "."); EnumDeclaration: "enum" @@ -27,16 +26,27 @@ EnumDeclaration: EnumLiteral returns Node: name=Identifier; +FeatureDeclaration: + ReferenceDeclaration | AttributeDeclaration | FlagDeclaration; + enum ReferenceKind: REFERENCE="refers" | CONTAINMENT="contains" | CONTAINER="container"; ReferenceDeclaration: - (kind=ReferenceKind referenceType=[Relation|QualifiedName] | - referenceType=[Relation|NonRelationKindQualifiedName]) + kind=ReferenceKind referenceType=[Relation|QualifiedName] ("[" multiplicity=Multiplicity "]")? name=Identifier ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?; +enum PrimitiveType: + INT="int" | REAL="real" | STRING="string"; + +AttributeDeclaration: + attributeType=PrimitiveType name=Identifier; + +FlagDeclaration: + "bool" name=Identifier; + enum ErrorKind returns PredicateKind: ERROR="error"; @@ -54,7 +64,7 @@ Conjunction: literals+=Expr ("," literals+=Expr)*; FunctionDefinition: - functionType=[Relation|QualifiedName] name=Identifier + functionType=PrimitiveType name=Identifier "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" ("=" cases+=Case (";" cases+=Case)*)? "."; @@ -96,11 +106,21 @@ Expr: ComparisonExpr; enum ComparisonOp: - LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!="; + LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!=" | IN="in"; ComparisonExpr returns Expr: - AdditiveExpr ({ComparisonExpr.left=current} - op=ComparisonOp right=AdditiveExpr)*; + LatticeExpr ({ComparisonExpr.left=current} + op=ComparisonOp right=LatticeExpr)*; + +enum LatticeOp returns BinaryOp: + MEET="/\\" | JOIN="\\/"; + +LatticeExpr returns Expr: + RangeExpr ({ArithmeticBinaryExpr.left=current} + op=LatticeOp right=RangeExpr)*; + +RangeExpr returns Expr: + AdditiveExpr ({RangeExpr.left=current} ".." right=AdditiveExpr)*; enum AdditiveOp returns BinaryOp: ADD="+" | SUB="-"; @@ -125,7 +145,7 @@ ExponentialExpr returns Expr: UnaryExpr returns Expr: ArithmeticUnaryExpr | ModalExpr | NegationExpr | CountExpr | AggregationExpr | - Atom | VariableOrNodeExpr | ConstantExpr | "(" Expr ")"; + Atom | VariableOrNodeExpr | Constant | "(" Expr ")"; enum UnaryOp: PLUS="+" | MINUS="-"; @@ -159,21 +179,33 @@ Atom: VariableOrNodeExpr: variableOrNode=[VariableOrNode|QualifiedName]; -ConstantExpr: - constant=Constant; +Constant: + RealConstant | IntConstant | InfConstant | StringConstant; + +IntConstant: + intValue=INT; + +RealConstant: + realValue=Real; + +InfConstant: + {InfConstant} "*"; + +StringConstant: + stringValue=STRING; Assertion: default?="default"? - (value=ShortLogicValue? - relation=[Relation|QualifiedName] - "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")" - | relation=[Relation|QualifiedName] + (relation=[Relation|QualifiedName] "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")" - ":" value=LogicValue) + value=AssertionValue | + value=ShortLogicAssertionValue + relation=[Relation|QualifiedName] + "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")") "."; AssertionArgument: - NodeAssertionArgument | WildcardAssertionArgument | ConstantAssertionArgument; + NodeAssertionArgument | WildcardAssertionArgument; NodeAssertionArgument: node=[Node|QualifiedName]; @@ -181,29 +213,23 @@ NodeAssertionArgument: WildcardAssertionArgument: {WildcardAssertionArgument} "*"; -ConstantAssertionArgument: - negative?="-"? constant=Constant; +AssertionValue: + LogicAssertionValue | ExprAssertionValue; enum LogicValue: TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; -enum ShortLogicValue returns LogicValue: - FALSE="!" | UNKNOWN="?"; +LogicAssertionValue: + ":" logicValue=LogicValue; -NodeValueAssertion: - node=[Node|QualifiedName] ":" value=Constant "."; - -Constant: - RealConstant | IntConstant | StringConstant; - -IntConstant: - intValue=INT; +ExprAssertionValue: + (range?="in" | "=") body=Expr; -RealConstant: - realValue=Real; +enum ShortLogicValue returns LogicValue: + FALSE="!" | UNKNOWN="?"; -StringConstant: - stringValue=STRING; +ShortLogicAssertionValue returns LogicAssertionValue: + {LogicAssertionValue} logicValue=ShortLogicValue?; ScopeDeclaration: "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* "."; @@ -234,17 +260,11 @@ IndividualDeclaration: UpperBound returns ecore::EInt: INT | "*"; -NonRelationKindQualifiedName hidden(): - NonRelationKindIdentifier ("::" Identifier)*; - QualifiedName hidden(): Identifier ("::" Identifier)*; -NonRelationKindIdentifier: - ID | "true" | "false" | "contained" | "sum" | "prod" | "min" | "max"; - Identifier: - NonRelationKindIdentifier | "contains"; + ID | "contains" | "contained" | "sum" | "prod" | "min" | "max"; Real returns ecore::EDouble: EXPONENTIAL | INT "." (INT | EXPONENTIAL); diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java index c0777038..5efcdc81 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java +++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java @@ -3,25 +3,18 @@ */ package tools.refinery.language; +import com.google.inject.Binder; +import com.google.inject.name.Names; import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.parser.IParser; -import org.eclipse.xtext.resource.DerivedStateAwareResource; -import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager; -import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; -import org.eclipse.xtext.resource.IDerivedStateComputer; -import org.eclipse.xtext.resource.ILocationInFileProvider; -import org.eclipse.xtext.resource.IResourceDescription; -import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.resource.*; import org.eclipse.xtext.scoping.IGlobalScopeProvider; import org.eclipse.xtext.scoping.IScopeProvider; import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; +import org.eclipse.xtext.serializer.sequencer.ISemanticSequencer; import org.eclipse.xtext.validation.IResourceValidator; import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; - -import com.google.inject.Binder; -import com.google.inject.name.Names; - import tools.refinery.language.conversion.ProblemValueConverterService; import tools.refinery.language.naming.ProblemQualifiedNameConverter; import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; @@ -30,6 +23,7 @@ import tools.refinery.language.resource.ProblemLocationInFileProvider; import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; import tools.refinery.language.scoping.ProblemGlobalScopeProvider; import tools.refinery.language.scoping.ProblemLocalScopeProvider; +import tools.refinery.language.serializer.PreferShortAssertionsProblemSemanticSequencer; /** * Use this class to register components to be used at runtime / without the @@ -88,4 +82,9 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { public Class bindILocationInFileProvider() { return ProblemLocationInFileProvider.class; } + + @Override + public Class bindISemanticSequencer() { + return PreferShortAssertionsProblemSemanticSequencer.class; + } } diff --git a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java index a65e0750..46870edb 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java +++ b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java @@ -3,6 +3,7 @@ */ package tools.refinery.language.formatting2; +import com.google.inject.Inject; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.formatting2.AbstractJavaFormatter; import org.eclipse.xtext.formatting2.IFormattableDocument; @@ -12,9 +13,12 @@ import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion; import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; import tools.refinery.language.model.problem.*; +import tools.refinery.language.services.ProblemGrammarAccess; @SuppressWarnings("UnstableApiUsage") public class ProblemFormatter extends AbstractJavaFormatter { + @Inject + private ProblemGrammarAccess problemGrammarAccess; protected void format(Problem problem, IFormattableDocument doc) { doc.prepend(problem, this::noSpace); @@ -31,17 +35,35 @@ public class ProblemFormatter extends AbstractJavaFormatter { surroundNewLines(doc, assertion, this::singleNewLine); var region = regionFor(assertion); doc.append(region.feature(ProblemPackage.Literals.ASSERTION__DEFAULT), this::oneSpace); - doc.append(region.feature(ProblemPackage.Literals.ASSERTION__VALUE), this::noSpace); doc.append(region.feature(ProblemPackage.Literals.ASSERTION__RELATION), this::noSpace); formatParenthesizedList(region, doc); - doc.prepend(region.keyword(":"), this::noSpace); - doc.append(region.keyword(":"), this::oneSpace); + var value = assertion.getValue(); + if (value != null) { + doc.append(value, this::noSpace); + doc.format(value); + } doc.prepend(region.keyword("."), this::noSpace); for (var argument : assertion.getArguments()) { doc.format(argument); } } + protected void format(LogicAssertionValue assertionValue, IFormattableDocument doc) { + var region = regionFor(assertionValue); + doc.prepend(region.keyword(":"), this::noSpace); + doc.append(region.keyword(":"), this::oneSpace); + } + + protected void format(ExprAssertionValue assertionValue, IFormattableDocument doc) { + var region = regionFor(assertionValue); + doc.surround(region.keyword("="), this::oneSpace); + doc.surround(region.keyword("in"), this::oneSpace); + var body = assertionValue.getBody(); + if (body != null) { + doc.format(body); + } + } + protected void format(ClassDeclaration classDeclaration, IFormattableDocument doc) { surroundNewLines(doc, classDeclaration, this::twoNewLines); var region = regionFor(classDeclaration); @@ -53,8 +75,8 @@ public class ProblemFormatter extends AbstractJavaFormatter { doc.append(region.keyword("{"), it -> it.setNewLines(1, 1, 2)); doc.prepend(region.keyword("}"), it -> it.setNewLines(1, 1, 2)); doc.prepend(region.keyword("."), this::noSpace); - for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) { - doc.format(referenceDeclaration); + for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { + doc.format(featureDeclaration); } } diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java b/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java index 99bf9b64..419be0d3 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java @@ -1,10 +1,9 @@ package tools.refinery.language.resource; -import java.util.List; -import java.util.Set; - +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.name.Named; import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xtext.linking.impl.LinkingHelper; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.nodemodel.INode; @@ -12,20 +11,12 @@ import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.IScopeProvider; import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; - -import com.google.common.collect.ImmutableSet; -import com.google.inject.Inject; -import com.google.inject.name.Named; - -import tools.refinery.language.model.problem.Assertion; -import tools.refinery.language.model.problem.AssertionArgument; -import tools.refinery.language.model.problem.NodeAssertionArgument; -import tools.refinery.language.model.problem.NodeValueAssertion; -import tools.refinery.language.model.problem.Problem; -import tools.refinery.language.model.problem.ProblemPackage; -import tools.refinery.language.model.problem.Statement; +import tools.refinery.language.model.problem.*; import tools.refinery.language.naming.NamingUtil; +import java.util.List; +import java.util.Set; + public class NodeNameCollector { @Inject private LinkingHelper linkingHelper; @@ -55,25 +46,20 @@ public class NodeNameCollector { protected void collectStatementNodeNames(Statement statement) { if (statement instanceof Assertion assertion) { collectAssertionNodeNames(assertion); - } else if (statement instanceof NodeValueAssertion nodeValueAssertion) { - collectNodeValueAssertionNodeNames(nodeValueAssertion); } } protected void collectAssertionNodeNames(Assertion assertion) { for (AssertionArgument argument : assertion.getArguments()) { if (argument instanceof NodeAssertionArgument) { - collectNodeNames(argument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); + collectNodeNames(argument); } } } - protected void collectNodeValueAssertionNodeNames(NodeValueAssertion nodeValueAssertion) { - collectNodeNames(nodeValueAssertion, ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE); - } - - private void collectNodeNames(EObject eObject, EStructuralFeature feature) { - List nodes = NodeModelUtils.findNodesForFeature(eObject, feature); + private void collectNodeNames(EObject eObject) { + List nodes = NodeModelUtils.findNodesForFeature(eObject, + ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); for (INode node : nodes) { var nodeName = linkingHelper.getCrossRefNodeAsString(node, true); if (!NamingUtil.isValidId(nodeName)) { @@ -85,4 +71,4 @@ public class NodeNameCollector { } } } -} \ No newline at end of file +} diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java index f28e1791..8d3a552a 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java @@ -1,13 +1,9 @@ package tools.refinery.language.resource; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.name.Named; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; @@ -16,36 +12,19 @@ import org.eclipse.xtext.Constants; import org.eclipse.xtext.resource.DerivedStateAwareResource; import org.eclipse.xtext.resource.IDerivedStateComputer; import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; -import com.google.inject.name.Named; +import tools.refinery.language.model.problem.*; -import tools.refinery.language.model.problem.Assertion; -import tools.refinery.language.model.problem.ClassDeclaration; -import tools.refinery.language.model.problem.ConstantAssertionArgument; -import tools.refinery.language.model.problem.Node; -import tools.refinery.language.model.problem.Problem; -import tools.refinery.language.model.problem.ProblemFactory; -import tools.refinery.language.model.problem.Statement; +import java.util.*; +import java.util.function.Function; @Singleton public class ProblemDerivedStateComputer implements IDerivedStateComputer { public static final String NEW_NODE = "new"; - public static final String CONSTANT_NODE = "constant"; - @Inject @Named(Constants.LANGUAGE_NAME) private String languageName; - @Inject - @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) - private IScopeProvider scopeProvider; - @Inject private Provider nodeNameCollectorProvider; @@ -88,15 +67,6 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { && declaration.getNewNode() == null) { var newNode = adapter.createNewNodeIfAbsent(declaration, key -> createNode(NEW_NODE)); declaration.setNewNode(newNode); - } else if (statement instanceof Assertion assertion) { - for (var argument : assertion.getArguments()) { - if (argument instanceof ConstantAssertionArgument constantAssertionArgument - && constantAssertionArgument.getNode() == null) { - var constantNode = adapter.createConstantNodeIfAbsent(constantAssertionArgument, - key -> createNode(CONSTANT_NODE)); - constantAssertionArgument.setNode(constantNode); - } - } } } } @@ -130,22 +100,14 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { protected void discardDerivedProblemState(Problem problem, Adapter adapter) { Set classDeclarations = new HashSet<>(); - Set constantAssertionArguments = new HashSet<>(); problem.getNodes().clear(); for (var statement : problem.getStatements()) { if (statement instanceof ClassDeclaration classDeclaration) { classDeclaration.setNewNode(null); classDeclarations.add(classDeclaration); - } else if (statement instanceof Assertion assertion) { - for (var argument : assertion.getArguments()) { - if (argument instanceof ConstantAssertionArgument constantAssertionArgument) { - constantAssertionArgument.setNode(null); - constantAssertionArguments.add(constantAssertionArgument); - } - } } } - adapter.retainAll(classDeclarations, constantAssertionArguments); + adapter.retainAll(classDeclarations); derivedVariableComputer.discardDerivedVariables(problem); } @@ -166,24 +128,15 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { } protected static class Adapter extends AdapterImpl { - private Map newNodes = new HashMap<>(); - - private Map constantNodes = new HashMap<>(); + private final Map newNodes = new HashMap<>(); public Node createNewNodeIfAbsent(ClassDeclaration classDeclaration, Function createNode) { return newNodes.computeIfAbsent(classDeclaration, createNode); } - public Node createConstantNodeIfAbsent(ConstantAssertionArgument constantAssertionArgument, - Function createNode) { - return constantNodes.computeIfAbsent(constantAssertionArgument, createNode); - } - - public void retainAll(Collection classDeclarations, - Collection constantAssertionArguments) { + public void retainAll(Collection classDeclarations) { newNodes.keySet().retainAll(classDeclarations); - constantNodes.keySet().retainAll(constantAssertionArguments); } @Override diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java index c2045aea..3ab07496 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java @@ -29,8 +29,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { @Override public IScope getScope(EObject context, EReference reference) { var scope = super.getScope(context, reference); - if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE - || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) { + if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE) { return getNodesScope(context, scope); } if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE diff --git a/subprojects/language/src/main/java/tools/refinery/language/serializer/PreferShortAssertionsProblemSemanticSequencer.java b/subprojects/language/src/main/java/tools/refinery/language/serializer/PreferShortAssertionsProblemSemanticSequencer.java new file mode 100644 index 00000000..c51a5e28 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/serializer/PreferShortAssertionsProblemSemanticSequencer.java @@ -0,0 +1,52 @@ +package tools.refinery.language.serializer; + +import com.google.inject.Inject; +import org.eclipse.xtext.serializer.ISerializationContext; +import org.eclipse.xtext.serializer.sequencer.ITransientValueService.ListTransient; +import org.eclipse.xtext.serializer.sequencer.ITransientValueService.ValueTransient; +import tools.refinery.language.model.problem.Assertion; +import tools.refinery.language.model.problem.LogicAssertionValue; +import tools.refinery.language.model.problem.LogicValue; +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.services.ProblemGrammarAccess; + +public class PreferShortAssertionsProblemSemanticSequencer extends ProblemSemanticSequencer { + @Inject + private ProblemGrammarAccess grammarAccess; + + @Override + protected void sequence_Assertion(ISerializationContext context, Assertion semanticObject) { + if (semanticObject.isDefault() || + !(semanticObject.getValue() instanceof LogicAssertionValue logicAssertionValue) || + logicAssertionValue.getLogicValue() == LogicValue.ERROR) { + super.sequence_Assertion(context, semanticObject); + return; + } + if (errorAcceptor != null) { + if (transientValues.isValueTransient(semanticObject, ProblemPackage.Literals.ASSERTION__RELATION) == ValueTransient.YES) { + errorAcceptor.accept(diagnosticProvider.createFeatureValueMissing(semanticObject, + ProblemPackage.Literals.ASSERTION__RELATION)); + } + if (transientValues.isListTransient(semanticObject, ProblemPackage.Literals.ASSERTION__ARGUMENTS) == ListTransient.YES) { + errorAcceptor.accept(diagnosticProvider.createFeatureValueMissing(semanticObject, + ProblemPackage.Literals.ASSERTION__ARGUMENTS)); + } + } + var feeder = createSequencerFeeder(context, semanticObject); + var access = grammarAccess.getAssertionAccess(); + feeder.accept(access.getValueShortLogicAssertionValueParserRuleCall_1_1_0_0(), logicAssertionValue); + feeder.accept(access.getRelationRelationQualifiedNameParserRuleCall_1_1_1_0_1(), semanticObject.getRelation()); + var iterator = semanticObject.getArguments().iterator(); + if (iterator.hasNext()) { + var firstArgument = iterator.next(); + feeder.accept(access.getArgumentsAssertionArgumentParserRuleCall_1_1_3_0_0(), firstArgument, 0); + int index = 1; + while (iterator.hasNext()) { + var argument = iterator.next(); + feeder.accept(access.getArgumentsAssertionArgumentParserRuleCall_1_1_3_1_1_0(), argument, index); + index++; + } + } + feeder.finish(); + } +} diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java index 7e43cecf..d3777cd3 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java @@ -1,14 +1,8 @@ package tools.refinery.language.utils; -import tools.refinery.language.model.problem.ClassDeclaration; -import tools.refinery.language.model.problem.EnumDeclaration; -import tools.refinery.language.model.problem.Node; -import tools.refinery.language.model.problem.PredicateDefinition; -import tools.refinery.language.model.problem.Problem; -import tools.refinery.language.model.problem.ReferenceDeclaration; +import tools.refinery.language.model.problem.*; public record BuiltinSymbols(Problem problem, ClassDeclaration node, ReferenceDeclaration equals, - PredicateDefinition exists, ClassDeclaration domain, ClassDeclaration data, EnumDeclaration bool, Node boolTrue, - Node boolFalse, ClassDeclaration intClass, ClassDeclaration real, ClassDeclaration string, - PredicateDefinition contained, PredicateDefinition contains, PredicateDefinition root) { + PredicateDefinition exists, PredicateDefinition contained, PredicateDefinition contains, + PredicateDefinition root) { } diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java b/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java index 38db0e29..c8f47653 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/NodeInfo.java @@ -1,12 +1,4 @@ package tools.refinery.language.utils; -import java.util.ArrayList; -import java.util.Collection; - -import tools.refinery.language.model.problem.NodeValueAssertion; - -public record NodeInfo(String name, boolean individual, Collection valueAssertions) { - public NodeInfo(String name, boolean individual) { - this(name, individual, new ArrayList<>()); - } +public record NodeInfo(String name, boolean individual) { } diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java index 23fd8982..b8200919 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java @@ -40,19 +40,10 @@ public class ProblemDesugarer { var node = doGetDeclaration(builtin, ClassDeclaration.class, "node"); var equals = doGetEqualsReference(node); var exists = doGetDeclaration(builtin, PredicateDefinition.class, "exists"); - var domain = doGetDeclaration(builtin, ClassDeclaration.class, "domain"); - var data = doGetDeclaration(builtin, ClassDeclaration.class, "data"); - var bool = doGetDeclaration(builtin, EnumDeclaration.class, "bool"); - var boolTrue = doGetLiteral(bool, "true"); - var boolFalse = doGetLiteral(bool, "false"); - var intClass = doGetDeclaration(builtin, ClassDeclaration.class, "int"); - var real = doGetDeclaration(builtin, ClassDeclaration.class, "real"); - var string = doGetDeclaration(builtin, ClassDeclaration.class, "string"); var contained = doGetDeclaration(builtin, PredicateDefinition.class, "contained"); var contains = doGetDeclaration(builtin, PredicateDefinition.class, "contains"); var root = doGetDeclaration(builtin, PredicateDefinition.class, "root"); - return new BuiltinSymbols(builtin, node, equals, exists, domain, data, bool, boolTrue, boolFalse, intClass, - real, string, contained, contains, root); + return new BuiltinSymbols(builtin, node, equals, exists, contained, contains, root); } private T doGetDeclaration(Problem builtin, Class type, String name) { @@ -62,16 +53,12 @@ public class ProblemDesugarer { } private ReferenceDeclaration doGetEqualsReference(ClassDeclaration nodeClassDeclaration) { - return nodeClassDeclaration.getReferenceDeclarations().stream() - .filter(reference -> "equals".equals(reference.getName())).findFirst() + return (ReferenceDeclaration) nodeClassDeclaration.getFeatureDeclarations().stream() + .filter(reference -> reference instanceof ReferenceDeclaration && + "equals".equals(reference.getName())).findFirst() .orElseThrow(() -> new IllegalArgumentException("Reference " + "equals" + " not found")); } - private Node doGetLiteral(EnumDeclaration enumDeclaration, String name) { - return enumDeclaration.getLiterals().stream().filter(literal -> name.equals(literal.getName())).findFirst() - .orElseThrow(() -> new IllegalArgumentException("Enum literal " + name + " not found")); - } - public Collection getSuperclassesAndSelf(ClassDeclaration classDeclaration) { return cache.get(Tuples.create(classDeclaration, "superclassesAndSelf"), classDeclaration.eResource(), () -> doGetSuperclassesAndSelf(classDeclaration)); @@ -94,9 +81,6 @@ public class ProblemDesugarer { } } } - if (builtinSymbols.isPresent() && !found.contains(builtinSymbols.get().data())) { - found.add(builtinSymbols.get().domain()); - } return found; } @@ -108,31 +92,17 @@ public class ProblemDesugarer { private Collection doGetAllReferenceDeclarations(ClassDeclaration classDeclaration) { Set referenceDeclarations = new HashSet<>(); for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) { - referenceDeclarations.addAll(superclass.getReferenceDeclarations()); + for (FeatureDeclaration featureDeclaration : superclass.getFeatureDeclarations()) { + if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { + referenceDeclarations.add(referenceDeclaration); + } + } } return referenceDeclarations; } public boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) { - switch (referenceDeclaration.getKind()) { - case REFERENCE, CONTAINER: - return false; - case CONTAINMENT: - return true; - case DEFAULT: - return isDataClass(referenceDeclaration.getReferenceType()); - default: - throw new IllegalArgumentException("Unknown reference kind " + referenceDeclaration.getKind()); - } - } - - public boolean isDataClass(Relation relation) { - if (relation instanceof ClassDeclaration classDeclaration) { - var supertypes = getSuperclassesAndSelf(classDeclaration); - var builtinSymbols = getBuiltinSymbols(relation); - return builtinSymbols.isPresent() && supertypes.contains(builtinSymbols.get().data()); - } - return false; + return referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT; } public CollectedSymbols collectSymbols(Problem problem) { diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java b/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java index 210e96ab..5412f620 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java @@ -2,7 +2,6 @@ package tools.refinery.language.utils; import com.google.inject.Inject; import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.IQualifiedNameProvider; import tools.refinery.language.model.problem.*; @@ -64,43 +63,65 @@ class SymbolCollector { } private void collectClass(ClassDeclaration classDeclaration) { - // node and domain classes are not contained by default, but every other type is - // contained, including data types. - var contained = - classDeclaration != builtinSymbols.node() && classDeclaration != builtinSymbols.domain(); + var contained = classDeclaration != builtinSymbols.node(); var containmentRole = contained ? ContainmentRole.CONTAINED : ContainmentRole.NONE; var instanceParameter = ProblemFactory.eINSTANCE.createParameter(); instanceParameter.setName("instance"); var classInfo = new RelationInfo(getQualifiedNameString(classDeclaration), containmentRole, List.of(instanceParameter), null, null, List.of()); relations.put(classDeclaration, classInfo); - collectReferences(classDeclaration); + collectFeatures(classDeclaration); } - private void collectReferences(ClassDeclaration classDeclaration) { - for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) { - var referenceRole = desugarer.isContainmentReference(referenceDeclaration) ? - ContainmentRole.CONTAINMENT : - ContainmentRole.NONE; - var sourceParameter = ProblemFactory.eINSTANCE.createParameter(); - sourceParameter.setName("source"); - sourceParameter.setParameterType(classDeclaration); - var targetParameter = ProblemFactory.eINSTANCE.createParameter(); - targetParameter.setName("target"); - var multiplicity = referenceDeclaration.getMultiplicity(); - if (multiplicity == null) { - var exactMultiplicity = ProblemFactory.eINSTANCE.createExactMultiplicity(); - exactMultiplicity.setExactValue(1); - multiplicity = exactMultiplicity; + private void collectFeatures(ClassDeclaration classDeclaration) { + for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { + if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { + collectReference(classDeclaration, referenceDeclaration); + } else if (featureDeclaration instanceof AttributeDeclaration attributeDeclaration) { + collectAttribute(classDeclaration, attributeDeclaration); + } else if (featureDeclaration instanceof FlagDeclaration flagDeclaration) { + collectFlag(classDeclaration, flagDeclaration); + } else { + throw new IllegalArgumentException("Unknown FeatureDeclaration: " + featureDeclaration); } - targetParameter.setParameterType(referenceDeclaration.getReferenceType()); - var referenceInfo = new RelationInfo(getQualifiedNameString(referenceDeclaration), referenceRole, - List.of(sourceParameter, targetParameter), multiplicity, referenceDeclaration.getOpposite(), - List.of()); - this.relations.put(referenceDeclaration, referenceInfo); } } + private void collectReference(ClassDeclaration classDeclaration, ReferenceDeclaration referenceDeclaration) { + var referenceRole = desugarer.isContainmentReference(referenceDeclaration) ? + ContainmentRole.CONTAINMENT : + ContainmentRole.NONE; + var sourceParameter = ProblemFactory.eINSTANCE.createParameter(); + sourceParameter.setName("source"); + sourceParameter.setParameterType(classDeclaration); + var targetParameter = ProblemFactory.eINSTANCE.createParameter(); + targetParameter.setName("target"); + var multiplicity = referenceDeclaration.getMultiplicity(); + if (multiplicity == null) { + var exactMultiplicity = ProblemFactory.eINSTANCE.createExactMultiplicity(); + exactMultiplicity.setExactValue(1); + multiplicity = exactMultiplicity; + } + targetParameter.setParameterType(referenceDeclaration.getReferenceType()); + var referenceInfo = new RelationInfo(getQualifiedNameString(referenceDeclaration), referenceRole, + List.of(sourceParameter, targetParameter), multiplicity, referenceDeclaration.getOpposite(), + List.of()); + this.relations.put(referenceDeclaration, referenceInfo); + } + + private void collectAttribute(ClassDeclaration classDeclaration, AttributeDeclaration attributeDeclaration) { + // TODO Implement attribute handling. + } + + private void collectFlag(ClassDeclaration classDeclaration, FlagDeclaration flagDeclaration) { + var parameter = ProblemFactory.eINSTANCE.createParameter(); + parameter.setName("object"); + parameter.setParameterType(classDeclaration); + var referenceInfo = new RelationInfo(getQualifiedNameString(flagDeclaration), ContainmentRole.NONE, + List.of(parameter), null, null, List.of()); + this.relations.put(flagDeclaration, referenceInfo); + } + private void collectEnum(EnumDeclaration enumDeclaration) { var instanceParameter = ProblemFactory.eINSTANCE.createParameter(); instanceParameter.setName("instance"); @@ -117,8 +138,6 @@ class SymbolCollector { collectNewNode(classDeclaration); } else if (statement instanceof EnumDeclaration enumDeclaration) { collectEnumLiterals(enumDeclaration); - } else if (statement instanceof Assertion assertion) { - collectConstantNodes(assertion); } } for (var node : problem.getNodes()) { @@ -145,17 +164,6 @@ class SymbolCollector { } } - private void collectConstantNodes(Assertion assertion) { - for (var argument : assertion.getArguments()) { - if (argument instanceof ConstantAssertionArgument constantAssertionArgument) { - var constantNode = constantAssertionArgument.getNode(); - if (constantNode != null) { - addNode(constantNode, false); - } - } - } - } - private void addNode(Node node, boolean individual) { var info = new NodeInfo(getQualifiedNameString(node), individual); this.nodes.put(node, info); @@ -173,8 +181,6 @@ class SymbolCollector { for (var statement : problem.getStatements()) { if (statement instanceof Assertion assertion) { collectAssertion(assertion); - } else if (statement instanceof NodeValueAssertion nodeValueAssertion) { - collectNodeValueAssertion(nodeValueAssertion); } else if (statement instanceof PredicateDefinition predicateDefinition) { collectPredicateAssertion(predicateDefinition); } else if (statement instanceof ClassDeclaration classDeclaration) { @@ -196,48 +202,6 @@ class SymbolCollector { return; } relationInfo.assertions().add(assertion); - for (var argument : assertion.getArguments()) { - if (argument instanceof ConstantAssertionArgument constantAssertionArgument) { - var constantNode = constantAssertionArgument.getNode(); - if (constantNode != null) { - var valueAssertion = ProblemFactory.eINSTANCE.createNodeValueAssertion(); - valueAssertion.setNode(constantNode); - valueAssertion.setValue(EcoreUtil.copy(constantAssertionArgument.getConstant())); - collectNodeValueAssertion(valueAssertion); - var logicValue = assertion.getValue(); - if (logicValue != LogicValue.TRUE) { - addAssertion(builtinSymbols.exists(), logicValue, constantNode); - } - } - } - } - } - - private void collectNodeValueAssertion(NodeValueAssertion nodeValueAssertion) { - var node = nodeValueAssertion.getNode(); - if (node == null) { - return; - } - var nodeInfo = this.nodes.get(node); - if (nodeInfo == null) { - throw new IllegalStateException("Node value assertion refers to unknown node"); - } - nodeInfo.valueAssertions().add(nodeValueAssertion); - var constant = nodeValueAssertion.getValue(); - if (constant == null) { - return; - } - Relation dataType; - if (constant instanceof IntConstant) { - dataType = builtinSymbols.intClass(); - } else if (constant instanceof RealConstant) { - dataType = builtinSymbols.real(); - } else if (constant instanceof StringConstant) { - dataType = builtinSymbols.string(); - } else { - throw new IllegalArgumentException("Unknown constant type"); - } - addAssertion(dataType, LogicValue.TRUE, node); } private void collectPredicateAssertion(PredicateDefinition predicateDefinition) { @@ -278,7 +242,9 @@ class SymbolCollector { } assertion.getArguments().add(argument); } - assertion.setValue(logicValue); + var value = ProblemFactory.eINSTANCE.createLogicAssertionValue(); + value.setLogicValue(logicValue); + assertion.setValue(value); collectAssertion(assertion); } } diff --git a/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem b/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem index 38e77237..06b6da1d 100644 --- a/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem +++ b/subprojects/language/src/main/resources/tools/refinery/language/builtin.problem @@ -6,19 +6,21 @@ abstract class node { pred exists(node node). -abstract class domain extends node. - -abstract class data extends node. - -enum bool { - true, false -} - -class int extends data. - -class real extends data. - -class string extends data. +% class Integer { +% int intValue +% } +% +% class Real { +% real realValue +% } +% +% class String { +% string stringValue +% } +% +% enum Boolean { +% TRUE, FALSE +% } pred contained(node node). diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java index 3a6e015f..e0dee496 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java @@ -24,10 +24,10 @@ class ProblemParsingTest { } class Person { - Person[0..*] children opposite parent - Person[0..1] parent opposite children + refers Person[0..*] children opposite parent + refers Person[0..1] parent opposite children int age - TaxStatus taxStatus + refers TaxStatus taxStatus } enum TaxStatus { @@ -46,10 +46,8 @@ class ProblemParsingTest { children(anne, ciri). ?children(bob, ciri). taxStatus(anne, adult). - age(anne, 35). - bobAge: 27. - age(bob, bobAge). - !age(ciri, bobAge). + age(bob) in 21..35. + age(ciri) = 10. """); assertThat(problem.errors(), empty()); } diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/TransitiveClosureParserTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/TransitiveClosureParserTest.java index 96d12edf..6e702720 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/TransitiveClosureParserTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/TransitiveClosureParserTest.java @@ -24,7 +24,9 @@ class TransitiveClosureParserTest { @Test void binaryAddOperatorTest() { var problem = parseHelper.parse(""" - pred foo(int a, int b) <-> a + (b) > 10. + int a(). + int b(). + pred foo() <-> a() + (b()) > 10. """); assertThat(problem.errors(), empty()); var literal = problem.pred("foo").conj(0).lit(0).get(); diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/rules/RuleParsingTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/rules/RuleParsingTest.java index 4b42f4aa..72e5e18a 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/rules/RuleParsingTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/rules/RuleParsingTest.java @@ -1,10 +1,6 @@ package tools.refinery.language.tests.rules; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; - +import com.google.inject.Inject; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; import org.junit.jupiter.api.Disabled; @@ -12,13 +8,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; - -import com.google.inject.Inject; - import tools.refinery.language.model.tests.utils.ProblemParseHelper; import tools.refinery.language.tests.ProblemInjectorProvider; -@Disabled +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@Disabled("TODO: Rework transformation rules") @ExtendWith(InjectionExtension.class) @InjectWith(ProblemInjectorProvider.class) class RuleParsingTest { diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java index 9049b8ec..2fd647e5 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java @@ -3,6 +3,7 @@ package tools.refinery.language.tests.scoping; import com.google.inject.Inject; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -28,15 +29,11 @@ class NodeScopingTest { @ValueSource(strings = { "", "builtin::" }) void builtInArgumentTypeTest(String qualifiedNamePrefix) { var problem = parse(""" - pred predicate({PARAM}node a, {PARAM}data b, {PARAM}int c). + pred predicate({PARAM}node a). """, qualifiedNamePrefix); assertThat(problem.errors(), empty()); assertThat(problem.pred("predicate").param(0).getParameterType(), equalTo(problem.builtin().findClass("node").get())); - assertThat(problem.pred("predicate").param(1).getParameterType(), - equalTo(problem.builtin().findClass("data").get())); - assertThat(problem.pred("predicate").param(2).getParameterType(), - equalTo(problem.builtin().findClass("int").get())); } @Test @@ -54,16 +51,6 @@ class NodeScopingTest { assertThat(problem.assertion(1).arg(1).node(), equalTo(problem.node("b"))); } - @Test - void implicitNodeInNodeValueAssertionTest() { - var problem = parseHelper.parse(""" - a: 16. - """); - assertThat(problem.errors(), empty()); - assertThat(problem.nodeNames(), hasItems("a")); - assertThat(problem.nodeValueAssertion(0).getNode(), equalTo(problem.node("a"))); - } - @Test void implicitNodeInPredicateTest() { var problem = parse(""" @@ -93,18 +80,6 @@ class NodeScopingTest { assertThat(problem.assertion(1).arg(1).node(), equalTo(problem.individualNode("b"))); } - @ParameterizedTest - @MethodSource("individualNodeReferenceSource") - void individualNodeInNodeValueAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { - var problem = parse(""" - individual a. - {PARAM}a: 16. - """, qualifiedNamePrefix, namedProblem); - assertThat(problem.errors(), empty()); - assertThat(problem.nodeNames(), empty()); - assertThat(problem.nodeValueAssertion(0).getNode(), equalTo(problem.individualNode("a"))); - } - @ParameterizedTest @MethodSource("individualNodeReferenceSource") void individualNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) { @@ -121,6 +96,7 @@ class NodeScopingTest { return Stream.of(Arguments.of("", false), Arguments.of("", true), Arguments.of("test::", true)); } + @Disabled("No nodes are present in builtin.problem currently") @ParameterizedTest @MethodSource("builtInNodeReferencesSource") void builtInNodeTest(String qualifiedName) { @@ -133,18 +109,7 @@ class NodeScopingTest { assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.builtin().findClass("int").get().getNewNode())); } - @ParameterizedTest - @MethodSource("builtInNodeReferencesSource") - void builtInNodeInNodeValueAssertionTest(String qualifiedName) { - var problem = parse(""" - {PARAM}: 16. - """, qualifiedName); - assertThat(problem.errors(), empty()); - assertThat(problem.nodeNames(), empty()); - assertThat(problem.nodeValueAssertion(0).getNode(), - equalTo(problem.builtin().findClass("int").get().getNewNode())); - } - + @Disabled("No nodes are present in builtin.problem currently") @ParameterizedTest @MethodSource("builtInNodeReferencesSource") void builtInNodeInPredicateTest(String qualifiedName) { @@ -174,18 +139,6 @@ class NodeScopingTest { assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.findClass("Foo").get().getNewNode())); } - @ParameterizedTest - @MethodSource("classNewNodeReferencesSource") - void classNewNodeInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) { - var problem = parse(""" - class Foo. - {PARAM}: 16. - """, qualifiedName, namedProblem); - assertThat(problem.errors(), empty()); - assertThat(problem.nodeNames(), empty()); - assertThat(problem.nodeValueAssertion(0).getNode(), equalTo(problem.findClass("Foo").get().getNewNode())); - } - @ParameterizedTest @MethodSource("classNewNodeReferencesSource") void classNewNodeInPredicateTest(String qualifiedName, boolean namedProblem) { @@ -229,18 +182,6 @@ class NodeScopingTest { assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.findEnum("Foo").literal("alpha"))); } - @ParameterizedTest - @MethodSource("enumLiteralReferencesSource") - void enumLiteralInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) { - var problem = parse(""" - enum Foo { alpha, beta } - {PARAM}: 16. - """, qualifiedName, namedProblem); - assertThat(problem.errors(), empty()); - assertThat(problem.nodeNames(), empty()); - assertThat(problem.nodeValueAssertion(0).getNode(), equalTo(problem.findEnum("Foo").literal("alpha"))); - } - @ParameterizedTest @MethodSource("enumLiteralReferencesSource") void enumLiteralInPredicateTest(String qualifiedName, boolean namedProblem) { @@ -260,6 +201,7 @@ class NodeScopingTest { Arguments.of("test::Foo::alpha", true)); } + @Disabled("No enum literals are present in builtin.problem currently") @ParameterizedTest @MethodSource("builtInEnumLiteralReferencesSource") void builtInEnumLiteralTest(String qualifiedName) { @@ -272,18 +214,7 @@ class NodeScopingTest { assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.builtin().findEnum("bool").literal("true"))); } - @ParameterizedTest - @MethodSource("builtInEnumLiteralReferencesSource") - void builtInEnumLiteralInNodeValueAssertionTest(String qualifiedName) { - var problem = parse(""" - {PARAM}: 16. - """, qualifiedName); - assertThat(problem.errors(), empty()); - assertThat(problem.nodeNames(), empty()); - assertThat(problem.nodeValueAssertion(0).getNode(), - equalTo(problem.builtin().findEnum("bool").literal("true"))); - } - + @Disabled("No enum literals are present in builtin.problem currently") @ParameterizedTest @MethodSource("builtInEnumLiteralReferencesSource") void bultInEnumLiteralInPredicateTest(String qualifiedName) { diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java index 150e47a4..5024fb45 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java @@ -68,6 +68,29 @@ class ProblemSerializerTest { Arguments.of(LogicValue.UNKNOWN, "?foo(a)."), Arguments.of(LogicValue.ERROR, "foo(a): error.")); } + @ParameterizedTest + @MethodSource + void defaultAssertionTest(LogicValue value, String valueAsString) { + var pred = createPred(); + var node = ProblemFactory.eINSTANCE.createNode(); + node.setName("a"); + var individualDeclaration = ProblemFactory.eINSTANCE.createIndividualDeclaration(); + individualDeclaration.getNodes().add(node); + problem.getStatements().add(individualDeclaration); + createAssertion(pred, node, value, true); + + assertSerializedResult(""" + pred foo(node p). + + individual a. + default foo(a):\040""" + valueAsString + ".\n"); + } + + static Stream defaultAssertionTest() { + return Stream.of(Arguments.of(LogicValue.TRUE, "true"), Arguments.of(LogicValue.FALSE, "false"), + Arguments.of(LogicValue.UNKNOWN, "unknown"), Arguments.of(LogicValue.ERROR, "error")); + } + @Test void implicitNodeTest() { var pred = createPred(); @@ -116,13 +139,20 @@ class ProblemSerializerTest { createAssertion(relation, node, LogicValue.TRUE); } - private void createAssertion(Relation relation, Node node, LogicValue value) { + private void createAssertion(Relation relation, Node node, LogicValue logicValue) { + createAssertion(relation, node, logicValue, false); + } + + private void createAssertion(Relation relation, Node node, LogicValue logicValue, boolean isDefault) { var assertion = ProblemFactory.eINSTANCE.createAssertion(); assertion.setRelation(relation); var argument = ProblemFactory.eINSTANCE.createNodeAssertionArgument(); argument.setNode(node); assertion.getArguments().add(argument); + var value = ProblemFactory.eINSTANCE.createLogicAssertionValue(); + value.setLogicValue(logicValue); assertion.setValue(value); + assertion.setDefault(isDefault); problem.getStatements().add(assertion); } @@ -143,7 +173,7 @@ class ProblemSerializerTest { var variable = ProblemFactory.eINSTANCE.createImplicitVariable(); variable.setName("q"); conjunction.getImplicitVariables().add(variable); - var equals = nodeType.reference("equals"); + var equals = nodeType.feature("equals"); conjunction.getLiterals().add(createAtom(equals, parameter1, variable)); conjunction.getLiterals().add(createAtom(equals, variable, parameter2)); pred.getBodies().add(conjunction); @@ -177,7 +207,7 @@ class ProblemSerializerTest { pred.getParameters().add(parameter); var conjunction = ProblemFactory.eINSTANCE.createConjunction(); var atom = ProblemFactory.eINSTANCE.createAtom(); - var equals = nodeType.reference("equals"); + var equals = nodeType.feature("equals"); atom.setRelation(equals); var arg1 = ProblemFactory.eINSTANCE.createVariableOrNodeExpr(); arg1.setVariableOrNode(parameter); diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java index 98c16352..66b7f1ab 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java @@ -4,6 +4,7 @@ import com.google.inject.Inject; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; import org.hamcrest.Matcher; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -87,15 +88,15 @@ class SymbolCollectorTest { void referenceTest() { var problem = parseHelper.parse(""" class Foo { - Foo[] bar opposite quux - Foo quux opposite bar + refers Foo[] bar opposite quux + refers Foo quux opposite bar } """); var collectedSymbols = desugarer.collectSymbols(problem.get()); var fooClass = problem.findClass("Foo"); - var barReference = fooClass.reference("bar"); + var barReference = fooClass.feature("bar"); var barInfo = collectedSymbols.relations().get(barReference); - var quuxReference = fooClass.reference("quux"); + var quuxReference = fooClass.feature("quux"); var quuxInfo = collectedSymbols.relations().get(quuxReference); assertThat(barInfo.containmentRole(), is(ContainmentRole.NONE)); assertThat(barInfo.opposite(), is(quuxReference)); @@ -116,10 +117,11 @@ class SymbolCollectorTest { } """); var collectedSymbols = desugarer.collectSymbols(problem.get()); - assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).containmentRole(), + assertThat(collectedSymbols.relations().get(problem.findClass("Foo").feature("bar")).containmentRole(), is(ContainmentRole.CONTAINMENT)); } + @Disabled("TODO: Rework numerical references") @Test void dataReferenceTest() { var problem = parseHelper.parse(""" @@ -128,7 +130,7 @@ class SymbolCollectorTest { } """); var collectedSymbols = desugarer.collectSymbols(problem.get()); - assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).containmentRole(), + assertThat(collectedSymbols.relations().get(problem.findClass("Foo").feature("bar")).containmentRole(), is(ContainmentRole.CONTAINMENT)); } @@ -204,61 +206,6 @@ class SymbolCollectorTest { assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), hasSize(0)); } - @ParameterizedTest - @MethodSource("valueTypes") - void nodeValueAssertionTest(String value, String typeName) { - var problem = parseHelper.parse("a: %s.".formatted(value)); - var collectedSymbols = desugarer.collectSymbols(problem.get()); - var node = problem.node("a"); - var nodeInfo = collectedSymbols.nodes().get(node); - assertThat(nodeInfo.individual(), is(false)); - assertThat(nodeInfo.valueAssertions(), hasSize(1)); - assertThat(collectedSymbols.relations().get(problem.builtin().findClass(typeName).get()).assertions(), - assertsNode(node, LogicValue.TRUE)); - } - - @ParameterizedTest - @MethodSource("valueTypes") - void constantInAssertionTest(String value, String typeName) { - var problem = parseHelper.parse(""" - containment pred foo(node x, data y). - foo(a, %s). - """.formatted(value)); - var collectedSymbols = desugarer.collectSymbols(problem.get()); - var node = problem.assertion(0).arg(1).constantNode(); - var nodeInfo = collectedSymbols.nodes().get(node); - assertThat(nodeInfo.individual(), is(false)); - assertThat(nodeInfo.valueAssertions(), hasSize(1)); - assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), assertsNode(node, - LogicValue.TRUE)); - assertThat(collectedSymbols.relations().get(problem.builtin().findClass(typeName).get()).assertions(), - assertsNode(node, LogicValue.TRUE)); - } - - @ParameterizedTest - @MethodSource("valueTypes") - void constantInUnknownAssertionTest(String value, String typeName) { - var problem = parseHelper.parse(""" - containment pred foo(node x, data y). - foo(a, %s): unknown. - """.formatted(value)); - var collectedSymbols = desugarer.collectSymbols(problem.get()); - var node = problem.assertion(0).arg(1).constantNode(); - var nodeInfo = collectedSymbols.nodes().get(node); - assertThat(nodeInfo.individual(), is(false)); - assertThat(nodeInfo.valueAssertions(), hasSize(1)); - assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(), assertsNode(node, - LogicValue.UNKNOWN)); - assertThat(collectedSymbols.relations().get(problem.builtin().findClass(typeName).get()).assertions(), - assertsNode(node, LogicValue.TRUE)); - assertThat(collectedSymbols.relations().get(problem.builtinSymbols().exists()).assertions(), assertsNode(node, - LogicValue.UNKNOWN)); - } - - static Stream valueTypes() { - return Stream.of(Arguments.of("3", "int"), Arguments.of("3.14", "real"), Arguments.of("\"foo\"", "string")); - } - @Test void invalidProblemTest() { var problem = parseHelper.parse(""" @@ -279,13 +226,13 @@ class SymbolCollectorTest { var fooInfo = collectedSymbols.relations().get(problem.pred("foo").get()); assertThat(fooInfo.assertions(), hasSize(1)); var assertion = fooInfo.assertions().stream().findFirst().orElseThrow(); - assertThat(assertion.getValue(), is(LogicValue.FALSE)); + assertThat(assertion.getValue(), hasProperty("logicValue", is(LogicValue.FALSE))); assertThat(assertion.getArguments(), hasSize(2)); assertThat(assertion.getArguments(), everyItem(instanceOf(WildcardAssertionArgument.class))); } private static Matcher> assertsNode(Node node, LogicValue value) { return hasItem(allOf(hasProperty("arguments", hasItem(hasProperty("node", is(node)))), hasProperty("value", - is(value)))); + hasProperty("logicValue", is(value))))); } } diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java index b8b3a7de..840c1f74 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java @@ -1,7 +1,6 @@ package tools.refinery.language.model.tests.utils; import tools.refinery.language.model.problem.AssertionArgument; -import tools.refinery.language.model.problem.ConstantAssertionArgument; import tools.refinery.language.model.problem.Node; import tools.refinery.language.model.problem.NodeAssertionArgument; @@ -13,8 +12,4 @@ public record WrappedAssertionArgument(AssertionArgument assertionArgument) { public Node node() { return ((NodeAssertionArgument) assertionArgument).getNode(); } - - public Node constantNode() { - return ((ConstantAssertionArgument) assertionArgument).getNode(); - } } diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java index d8926c29..41b2ea62 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java @@ -1,14 +1,14 @@ package tools.refinery.language.model.tests.utils; import tools.refinery.language.model.problem.ClassDeclaration; -import tools.refinery.language.model.problem.ReferenceDeclaration; +import tools.refinery.language.model.problem.FeatureDeclaration; public record WrappedClassDeclaration(ClassDeclaration classDeclaration) { public ClassDeclaration get() { return classDeclaration; } - - public ReferenceDeclaration reference(String name) { - return ProblemNavigationUtil.named(classDeclaration.getReferenceDeclarations(), name); + + public FeatureDeclaration feature(String name) { + return ProblemNavigationUtil.named(classDeclaration.getFeatureDeclarations(), name); } } diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java index aef96b5b..78ca95c7 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java @@ -1,25 +1,14 @@ package tools.refinery.language.model.tests.utils; -import java.util.List; -import java.util.stream.Stream; - import org.eclipse.emf.ecore.resource.Resource.Diagnostic; import org.eclipse.emf.ecore.util.EcoreUtil; - -import tools.refinery.language.model.problem.Assertion; -import tools.refinery.language.model.problem.ClassDeclaration; -import tools.refinery.language.model.problem.EnumDeclaration; -import tools.refinery.language.model.problem.IndividualDeclaration; -import tools.refinery.language.model.problem.NamedElement; -import tools.refinery.language.model.problem.Node; -import tools.refinery.language.model.problem.NodeValueAssertion; -import tools.refinery.language.model.problem.PredicateDefinition; -import tools.refinery.language.model.problem.Problem; -import tools.refinery.language.model.problem.RuleDefinition; -import tools.refinery.language.model.problem.Statement; +import tools.refinery.language.model.problem.*; import tools.refinery.language.utils.BuiltinSymbols; import tools.refinery.language.utils.ProblemDesugarer; +import java.util.List; +import java.util.stream.Stream; + public record WrappedProblem(Problem problem) { public Problem get() { return problem; @@ -72,10 +61,6 @@ public record WrappedProblem(Problem problem) { return ProblemNavigationUtil.named(uniqueNodes, name); } - public NodeValueAssertion nodeValueAssertion(int i) { - return nthStatementOfType(NodeValueAssertion.class, i); - } - private Stream statementsOfType(Class type) { return problem.getStatements().stream().filter(type::isInstance).map(type::cast); } -- cgit v1.2.3-70-g09d2