diff options
Diffstat (limited to 'subprojects')
277 files changed, 9762 insertions, 949 deletions
diff --git a/subprojects/docs/build.gradle.kts b/subprojects/docs/build.gradle.kts index ab67128a..fcd6a651 100644 --- a/subprojects/docs/build.gradle.kts +++ b/subprojects/docs/build.gradle.kts | |||
@@ -22,24 +22,39 @@ val javadocs: Configuration by configurations.creating { | |||
22 | isCanBeResolved = true | 22 | isCanBeResolved = true |
23 | } | 23 | } |
24 | 24 | ||
25 | val releasedJavadocs: Configuration by configurations.creating { | ||
26 | isCanBeConsumed = false | ||
27 | isCanBeResolved = true | ||
28 | } | ||
29 | |||
30 | val interpreterGroup = property("tools.refinery.interpreter.group").toString() | ||
31 | val releasedVersion = property("tools.refinery.release").toString() | ||
32 | val releasedInterpreterVersion = property("tools.refinery.interpreter.release").toString() | ||
33 | |||
34 | repositories { | ||
35 | mavenCentral() | ||
36 | } | ||
37 | |||
25 | dependencies { | 38 | dependencies { |
26 | gradle.projectsEvaluated { | 39 | gradle.projectsEvaluated { |
27 | for (subproject in rootProject.subprojects) { | 40 | for (subproject in rootProject.subprojects) { |
28 | if (subproject.plugins.hasPlugin(JavaLibraryPlugin::class)) { | 41 | if (subproject.plugins.hasPlugin(JavaLibraryPlugin::class)) { |
29 | javadocs(project(subproject.path, "javadocElements")) | 42 | javadocs(project(subproject.path, "javadocElements")) |
43 | val releasedProjectVersion = if (subproject.group.toString() == interpreterGroup) | ||
44 | releasedInterpreterVersion else releasedVersion | ||
45 | releasedJavadocs("${subproject.group}:${subproject.name}:$releasedProjectVersion:javadoc@jar") | ||
30 | } | 46 | } |
31 | } | 47 | } |
32 | } | 48 | } |
49 | |||
50 | javadocs(project(":refinery-gradle-plugins", "javadocElements")) | ||
51 | releasedJavadocs("tools.refinery:refinery-gradle-plugins:$releasedVersion:javadoc@jar") | ||
33 | } | 52 | } |
34 | 53 | ||
35 | val srcDir = "src" | 54 | val srcDir = "src" |
36 | |||
37 | val docusaurusOutputDir = layout.buildDirectory.dir("docusaurus") | 55 | val docusaurusOutputDir = layout.buildDirectory.dir("docusaurus") |
38 | |||
39 | val javadocsDir = layout.buildDirectory.dir("javadocs") | 56 | val javadocsDir = layout.buildDirectory.dir("javadocs") |
40 | 57 | ||
41 | val javadocsDocsDir = javadocsDir.map { root -> root.dir("develop/javadoc") } | ||
42 | |||
43 | val configFiles: FileCollection = files( | 58 | val configFiles: FileCollection = files( |
44 | rootProject.file("yarn.lock"), | 59 | rootProject.file("yarn.lock"), |
45 | rootProject.file("package.json"), | 60 | rootProject.file("package.json"), |
@@ -54,25 +69,57 @@ val lintConfigFiles: FileCollection = configFiles + files( | |||
54 | rootProject.file(".eslintrc.cjs"), rootProject.file("prettier.config.cjs") | 69 | rootProject.file(".eslintrc.cjs"), rootProject.file("prettier.config.cjs") |
55 | ) | 70 | ) |
56 | 71 | ||
57 | tasks { | 72 | abstract class ExtractJavadocTask : DefaultTask() { |
58 | val extractJavadocs by registering { | 73 | @get:OutputDirectory |
59 | dependsOn(javadocs) | 74 | abstract val targetDir: DirectoryProperty |
60 | outputs.dir(javadocsDir) | 75 | |
61 | doFirst { | 76 | @get:Input |
62 | delete(javadocsDir) | 77 | abstract val resolvedJavadocArtifacts: MapProperty<String, File> |
78 | |||
79 | @get:Inject | ||
80 | abstract val fs: FileSystemOperations | ||
81 | |||
82 | @get:Inject | ||
83 | abstract val archive: ArchiveOperations | ||
84 | |||
85 | @TaskAction | ||
86 | fun action() { | ||
87 | fs.delete { | ||
88 | delete(targetDir) | ||
63 | } | 89 | } |
64 | doLast { | 90 | val javadocsDocsDir = targetDir.get() |
65 | javadocs.resolvedConfiguration.resolvedArtifacts.forEach { artifact -> | 91 | resolvedJavadocArtifacts.get().forEach { artifact -> |
66 | copy { | 92 | fs.copy { |
67 | from(zipTree(artifact.file)) | 93 | from(archive.zipTree(artifact.value)) |
68 | into(javadocsDocsDir.map { root -> root.dir(artifact.moduleVersion.id.name) }) | 94 | into(javadocsDocsDir.dir(artifact.key)) |
69 | } | ||
70 | } | 95 | } |
71 | } | 96 | } |
72 | } | 97 | } |
98 | } | ||
99 | |||
100 | fun resolveJavadocs(configuration: Configuration): Provider<Map<String, File>> { | ||
101 | return provider { | ||
102 | configuration.resolvedConfiguration.resolvedArtifacts.associate { artifact -> | ||
103 | artifact.moduleVersion.id.name to artifact.file | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | tasks { | ||
109 | val extractJavadocs by registering(ExtractJavadocTask::class) { | ||
110 | dependsOn(javadocs) | ||
111 | targetDir = javadocsDir.map { it.dir("snapshot/develop/javadoc" ) } | ||
112 | resolvedJavadocArtifacts = resolveJavadocs(javadocs) | ||
113 | } | ||
114 | |||
115 | val extractReleasedJavadocs by registering(ExtractJavadocTask::class) { | ||
116 | dependsOn(releasedJavadocs) | ||
117 | targetDir = javadocsDir.map { it.dir("develop/javadoc" ) } | ||
118 | resolvedJavadocArtifacts = resolveJavadocs(releasedJavadocs) | ||
119 | } | ||
73 | 120 | ||
74 | assembleFrontend { | 121 | assembleFrontend { |
75 | dependsOn(extractJavadocs) | 122 | dependsOn(extractJavadocs, extractReleasedJavadocs) |
76 | inputs.dir(srcDir) | 123 | inputs.dir(srcDir) |
77 | inputs.dir("static") | 124 | inputs.dir("static") |
78 | inputs.dir(javadocsDir) | 125 | inputs.dir(javadocsDir) |
diff --git a/subprojects/docs/docusaurus.config.ts b/subprojects/docs/docusaurus.config.ts index 7e76017b..5c32fcca 100644 --- a/subprojects/docs/docusaurus.config.ts +++ b/subprojects/docs/docusaurus.config.ts | |||
@@ -5,297 +5,323 @@ | |||
5 | * SPDX-License-Identifier: MIT AND EPL-2.0 | 5 | * SPDX-License-Identifier: MIT AND EPL-2.0 |
6 | */ | 6 | */ |
7 | 7 | ||
8 | import path from 'node:path'; | ||
9 | |||
8 | import type { MDXOptions } from '@docusaurus/mdx-loader'; | 10 | import type { MDXOptions } from '@docusaurus/mdx-loader'; |
11 | import type { Options as RedirectOptions } from '@docusaurus/plugin-client-redirects'; | ||
9 | import type { Options as DocsOptions } from '@docusaurus/plugin-content-docs'; | 12 | import type { Options as DocsOptions } from '@docusaurus/plugin-content-docs'; |
10 | import type { Options as PagesOptions } from '@docusaurus/plugin-content-pages'; | 13 | import type { Options as PagesOptions } from '@docusaurus/plugin-content-pages'; |
11 | import type { Options as ClassicThemeOptions } from '@docusaurus/theme-classic'; | 14 | import type { Options as ClassicThemeOptions } from '@docusaurus/theme-classic'; |
12 | import type { UserThemeConfig } from '@docusaurus/theme-common'; | 15 | import type { UserThemeConfig } from '@docusaurus/theme-common'; |
13 | import type { UserThemeConfig as AlgoliaConfig } from '@docusaurus/theme-search-algolia'; | 16 | import type { UserThemeConfig as AlgoliaConfig } from '@docusaurus/theme-search-algolia'; |
14 | import type { Config } from '@docusaurus/types'; | 17 | import type { Config } from '@docusaurus/types'; |
15 | import { Config as SwcConfig } from '@swc/core'; | 18 | import type { Config as SwcConfig } from '@swc/core'; |
19 | import { PropertiesFile } from 'java-properties'; | ||
16 | import { themes } from 'prism-react-renderer'; | 20 | import { themes } from 'prism-react-renderer'; |
17 | import smartypants from 'remark-smartypants'; | 21 | import smartypants from 'remark-smartypants'; |
18 | 22 | ||
23 | import remarkImage from './src/plugins/remarkImage'; | ||
19 | import remarkPosix2Windows from './src/plugins/remarkPosix2Windows'; | 24 | import remarkPosix2Windows from './src/plugins/remarkPosix2Windows'; |
25 | import remarkRefinery from './src/plugins/remarkRefinery'; | ||
26 | import remarkReplaceVariables from './src/plugins/remarkReplaceVariables'; | ||
20 | 27 | ||
21 | const markdownOptions: Partial<MDXOptions> = { | 28 | const properties = new PropertiesFile( |
22 | remarkPlugins: [[smartypants, { dashes: 'oldschool' }], remarkPosix2Windows], | 29 | path.join(__dirname, '../../gradle.properties'), |
23 | }; | 30 | ); |
24 | |||
25 | const docsOptions = { | ||
26 | ...markdownOptions, | ||
27 | sidebarPath: undefined, | ||
28 | editUrl: | ||
29 | 'https://github.com/graphs4value/refinery/edit/main/subprojects/docs', | ||
30 | } satisfies DocsOptions; | ||
31 | 31 | ||
32 | export default { | 32 | export default async function createConfigAsync() { |
33 | title: 'Refinery', | 33 | const markdownOptions: Partial<MDXOptions> = { |
34 | tagline: 'An efficient graph solver for generating well-formed models', | 34 | beforeDefaultRemarkPlugins: [remarkImage], |
35 | url: 'https://refinery.tools', | 35 | remarkPlugins: [ |
36 | baseUrl: '/', | 36 | [remarkReplaceVariables, { properties }], |
37 | baseUrlIssueBanner: false, | 37 | [smartypants, { dashes: 'oldschool' }], |
38 | trailingSlash: true, | 38 | remarkPosix2Windows, |
39 | staticDirectories: ['static', 'build/javadocs'], | 39 | await remarkRefinery(), |
40 | plugins: [ | ||
41 | [ | ||
42 | '@docusaurus/plugin-content-docs', | ||
43 | { | ||
44 | id: 'learn', | ||
45 | path: 'src/learn', | ||
46 | routeBasePath: '/learn', | ||
47 | ...docsOptions, | ||
48 | } satisfies DocsOptions, | ||
49 | ], | 40 | ], |
50 | [ | 41 | }; |
51 | '@docusaurus/plugin-content-docs', | 42 | |
52 | { | 43 | return { |
53 | id: 'develop', | 44 | title: 'Refinery', |
54 | path: 'src/develop', | 45 | tagline: 'An efficient graph solver for generating well-formed models', |
55 | routeBasePath: '/develop', | 46 | url: 'https://refinery.tools', |
56 | ...docsOptions, | 47 | baseUrl: '/', |
57 | } satisfies DocsOptions, | 48 | baseUrlIssueBanner: false, |
58 | ], | 49 | trailingSlash: true, |
59 | [ | 50 | staticDirectories: ['static', 'build/javadocs'], |
60 | '@docusaurus/plugin-content-pages', | 51 | plugins: [ |
61 | markdownOptions satisfies PagesOptions, | 52 | [ |
62 | ], | 53 | '@docusaurus/plugin-content-docs', |
63 | '@docusaurus/plugin-sitemap', | ||
64 | './src/plugins/loadersPlugin.ts', | ||
65 | './src/plugins/swcMinifyPlugin.ts', | ||
66 | ], | ||
67 | themes: [ | ||
68 | [ | ||
69 | '@docusaurus/theme-classic', | ||
70 | { | ||
71 | customCss: [require.resolve('./src/css/custom.css')], | ||
72 | } satisfies ClassicThemeOptions, | ||
73 | ], | ||
74 | '@docusaurus/theme-search-algolia', | ||
75 | ], | ||
76 | themeConfig: { | ||
77 | colorMode: { | ||
78 | respectPrefersColorScheme: true, | ||
79 | }, | ||
80 | prism: { | ||
81 | additionalLanguages: ['bash', 'java'], | ||
82 | theme: themes.oneLight, | ||
83 | darkTheme: themes.oneDark, | ||
84 | }, | ||
85 | navbar: { | ||
86 | title: 'Refinery', | ||
87 | logo: { | ||
88 | src: '/logo.svg', | ||
89 | srcDark: '/logo-dark.svg', | ||
90 | }, | ||
91 | hideOnScroll: true, | ||
92 | items: [ | ||
93 | { | ||
94 | label: 'Learn', | ||
95 | to: '/learn', | ||
96 | }, | ||
97 | { | ||
98 | label: 'Develop', | ||
99 | to: '/develop', | ||
100 | }, | ||
101 | { | ||
102 | label: 'GitHub', | ||
103 | position: 'right', | ||
104 | href: 'https://github.com/graphs4value/refinery', | ||
105 | }, | ||
106 | { | ||
107 | label: 'Try now', | ||
108 | position: 'right', | ||
109 | href: 'https://refinery.services/', | ||
110 | className: 'navbar__link--try-now', | ||
111 | }, | ||
112 | ], | ||
113 | }, | ||
114 | footer: { | ||
115 | links: [ | ||
116 | { | 54 | { |
117 | title: 'Learn', | 55 | path: 'src/docs', |
118 | items: [ | 56 | routeBasePath: '/', |
119 | { | 57 | sidebarPath: './sidebars.ts', |
120 | label: 'Introduction', | 58 | editUrl: |
121 | to: '/learn', | 59 | 'https://github.com/graphs4value/refinery/edit/main/subprojects/docs', |
60 | versions: { | ||
61 | current: { | ||
62 | path: 'snapshot', | ||
63 | label: `${String(properties.get('version'))} 🚧`, | ||
122 | }, | 64 | }, |
123 | { | 65 | }, |
124 | label: 'Tutorials', | 66 | ...markdownOptions, |
125 | to: '/learn/tutorials', | 67 | } satisfies DocsOptions, |
126 | }, | 68 | ], |
127 | { | 69 | [ |
128 | label: 'Langauge reference', | 70 | '@docusaurus/plugin-content-pages', |
129 | to: '/learn/language', | 71 | markdownOptions satisfies PagesOptions, |
130 | }, | 72 | ], |
131 | { | 73 | [ |
132 | label: 'Run in Docker', | 74 | '@docusaurus/plugin-client-redirects', |
133 | to: '/learn/docker', | ||
134 | }, | ||
135 | ], | ||
136 | }, | ||
137 | { | 75 | { |
138 | title: 'Develop', | 76 | redirects: [ |
139 | items: [ | ||
140 | { | ||
141 | label: 'Programming guide', | ||
142 | to: '/develop', | ||
143 | }, | ||
144 | { | 77 | { |
145 | label: 'Contributing', | 78 | to: '/develop/java/', |
146 | to: '/develop/contributing', | 79 | from: '/develop/', |
147 | }, | ||
148 | { | ||
149 | label: 'Javadoc', | ||
150 | to: '/develop/javadoc', | ||
151 | }, | 80 | }, |
152 | ], | 81 | ], |
153 | }, | 82 | } satisfies RedirectOptions, |
83 | ], | ||
84 | '@docusaurus/plugin-sitemap', | ||
85 | './src/plugins/loadersPlugin.ts', | ||
86 | './src/plugins/swcMinifyPlugin.ts', | ||
87 | ], | ||
88 | themes: [ | ||
89 | [ | ||
90 | '@docusaurus/theme-classic', | ||
154 | { | 91 | { |
155 | title: 'More', | 92 | customCss: [require.resolve('./src/css/custom.css')], |
156 | items: [ | 93 | } satisfies ClassicThemeOptions, |
157 | { | 94 | ], |
158 | label: 'Try now', | 95 | '@docusaurus/theme-search-algolia', |
159 | href: 'https://refinery.services/', | 96 | ], |
160 | }, | 97 | themeConfig: { |
161 | { | 98 | colorMode: { |
162 | label: 'GitHub', | 99 | respectPrefersColorScheme: true, |
163 | href: 'https://github.com/graphs4value/refinery', | 100 | }, |
164 | }, | 101 | prism: { |
165 | { | 102 | additionalLanguages: ['bash', 'groovy', 'ini', 'java', 'kotlin'], |
166 | label: 'License', | 103 | theme: themes.oneLight, |
167 | to: '/license', | 104 | darkTheme: themes.oneDark, |
168 | }, | 105 | }, |
169 | ], | 106 | navbar: { |
107 | title: 'Refinery', | ||
108 | logo: { | ||
109 | src: '/logo.svg', | ||
110 | srcDark: '/logo-dark.svg', | ||
170 | }, | 111 | }, |
171 | { | 112 | hideOnScroll: true, |
172 | title: 'Supporters', | 113 | items: [ |
173 | items: [ | 114 | { |
174 | { | 115 | type: 'doc', |
175 | label: 'BME MIT FTSRG', | 116 | label: 'Learn', |
176 | href: 'https://ftsrg.mit.bme.hu/en/', | 117 | docId: 'learn/index', |
177 | }, | 118 | }, |
178 | { | 119 | { |
179 | label: 'McGill ECE', | 120 | type: 'doc', |
180 | href: 'https://www.mcgill.ca/', | 121 | label: 'Develop', |
181 | }, | 122 | docId: 'develop/java', |
182 | { | 123 | }, |
183 | label: '2022 Amazon Research Awards', | 124 | { |
184 | href: 'https://www.amazon.science/research-awards/recipients/daniel-varro-fall-2021', | 125 | label: 'GitHub', |
185 | }, | 126 | position: 'right', |
186 | { | 127 | href: 'https://github.com/graphs4value/refinery', |
187 | label: 'LiU Software and Systems', | 128 | }, |
188 | href: 'https://liu.se/en/organisation/liu/ida/sas', | 129 | { |
130 | label: 'Try now', | ||
131 | position: 'right', | ||
132 | href: 'https://refinery.services/', | ||
133 | className: 'navbar__link--try-now', | ||
134 | }, | ||
135 | { | ||
136 | type: 'docsVersionDropdown', | ||
137 | position: 'right', | ||
138 | }, | ||
139 | ], | ||
140 | }, | ||
141 | footer: { | ||
142 | links: [ | ||
143 | { | ||
144 | title: 'Learn', | ||
145 | items: [ | ||
146 | { | ||
147 | label: 'Introduction', | ||
148 | to: '/learn', | ||
149 | }, | ||
150 | { | ||
151 | label: 'Tutorials', | ||
152 | to: '/learn/tutorials', | ||
153 | }, | ||
154 | { | ||
155 | label: 'Langauge reference', | ||
156 | to: '/learn/language', | ||
157 | }, | ||
158 | { | ||
159 | label: 'Run in Docker', | ||
160 | to: '/learn/docker', | ||
161 | }, | ||
162 | ], | ||
163 | }, | ||
164 | { | ||
165 | title: 'Develop', | ||
166 | items: [ | ||
167 | { | ||
168 | label: 'Programming guide', | ||
169 | to: '/develop/java', | ||
170 | }, | ||
171 | { | ||
172 | label: 'Contributing', | ||
173 | to: '/develop/contributing', | ||
174 | }, | ||
175 | { | ||
176 | label: 'Javadoc', | ||
177 | to: '/develop/javadoc', | ||
178 | }, | ||
179 | ], | ||
180 | }, | ||
181 | { | ||
182 | title: 'More', | ||
183 | items: [ | ||
184 | { | ||
185 | label: 'Try now', | ||
186 | href: 'https://refinery.services/', | ||
187 | }, | ||
188 | { | ||
189 | label: 'GitHub', | ||
190 | href: 'https://github.com/graphs4value/refinery', | ||
191 | }, | ||
192 | { | ||
193 | label: 'License', | ||
194 | to: '/license', | ||
195 | }, | ||
196 | ], | ||
197 | }, | ||
198 | { | ||
199 | title: 'Supporters', | ||
200 | items: [ | ||
201 | { | ||
202 | label: 'BME MIT FTSRG', | ||
203 | href: 'https://ftsrg.mit.bme.hu/en/', | ||
204 | }, | ||
205 | { | ||
206 | label: 'McGill ECE', | ||
207 | href: 'https://www.mcgill.ca/', | ||
208 | }, | ||
209 | { | ||
210 | label: '2022 Amazon Research Awards', | ||
211 | href: 'https://www.amazon.science/research-awards/recipients/daniel-varro-fall-2021', | ||
212 | }, | ||
213 | { | ||
214 | label: 'LiU Software and Systems', | ||
215 | href: 'https://liu.se/en/organisation/liu/ida/sas', | ||
216 | }, | ||
217 | { | ||
218 | label: 'WASP', | ||
219 | href: 'https://wasp-sweden.org/', | ||
220 | }, | ||
221 | ], | ||
222 | }, | ||
223 | ], | ||
224 | copyright: ` | ||
225 | Copyright © 2021-2024 | ||
226 | <a href="https://github.com/graphs4value/refinery/blob/main/CONTRIBUTORS.md#the-refinery-authors" target="_blank">The Refinery Authors</a>. | ||
227 | Available under the | ||
228 | <a href="/license/">Eclipse Public License - v 2.0</a>. | ||
229 | `, | ||
230 | }, | ||
231 | algolia: { | ||
232 | appId: 'KYHOYEO80F', | ||
233 | apiKey: '152acfb8d1ad9e10f29f083a6b017a69', | ||
234 | indexName: 'refinery', | ||
235 | // Javadoc doesn't use the Docusaurus router and has to be navigated to with `location.href` instead. | ||
236 | externalUrlRegex: '/([^/]+/)?develop/javadoc/.+', | ||
237 | }, | ||
238 | } satisfies UserThemeConfig & AlgoliaConfig, | ||
239 | webpack: { | ||
240 | // Speed up builds by using a native Javascript loader. | ||
241 | // See: https://github.com/facebook/docusaurus/issues/4765#issuecomment-841135926 | ||
242 | // But we follow the Docusaurus upstream from | ||
243 | // https://github.com/facebook/docusaurus/blob/791da2e4a1a53aa6309887059e3f112fcb35bec4/website/docusaurus.config.ts#L152-L171 | ||
244 | // and use swc instead of esbuild. | ||
245 | jsLoader: (isServer) => ({ | ||
246 | loader: require.resolve('swc-loader'), | ||
247 | options: { | ||
248 | jsc: { | ||
249 | parser: { | ||
250 | syntax: 'typescript', | ||
251 | tsx: true, | ||
189 | }, | 252 | }, |
190 | { | 253 | transform: { |
191 | label: 'WASP', | 254 | react: { |
192 | href: 'https://wasp-sweden.org/', | 255 | runtime: 'automatic', |
256 | }, | ||
193 | }, | 257 | }, |
194 | ], | 258 | target: 'es2022', |
195 | }, | ||
196 | ], | ||
197 | copyright: ` | ||
198 | Copyright © 2021-2024 | ||
199 | <a href="https://github.com/graphs4value/refinery/blob/main/CONTRIBUTORS.md#the-refinery-authors" target="_blank">The Refinery Authors</a>. | ||
200 | Available under the | ||
201 | <a href="/license/">Eclipse Public License - v 2.0</a>. | ||
202 | `, | ||
203 | }, | ||
204 | algolia: { | ||
205 | appId: 'KYHOYEO80F', | ||
206 | apiKey: '152acfb8d1ad9e10f29f083a6b017a69', | ||
207 | indexName: 'refinery', | ||
208 | // We don't have any context specified, so we need to disable this to return results. | ||
209 | contextualSearch: false, | ||
210 | // Javadoc doesn't use the Docusaurus router and has to be navigated to with `location.href` instead. | ||
211 | externalUrlRegex: '/develop/javadoc/.+', | ||
212 | }, | ||
213 | } satisfies UserThemeConfig & AlgoliaConfig, | ||
214 | webpack: { | ||
215 | // Speed up builds by using a native Javascript loader. | ||
216 | // See: https://github.com/facebook/docusaurus/issues/4765#issuecomment-841135926 | ||
217 | // But we follow the Docusaurus upstream from | ||
218 | // https://github.com/facebook/docusaurus/blob/791da2e4a1a53aa6309887059e3f112fcb35bec4/website/docusaurus.config.ts#L152-L171 | ||
219 | // and use swc instead of esbuild. | ||
220 | jsLoader: (isServer) => ({ | ||
221 | loader: require.resolve('swc-loader'), | ||
222 | options: { | ||
223 | jsc: { | ||
224 | parser: { | ||
225 | syntax: 'typescript', | ||
226 | tsx: true, | ||
227 | }, | 259 | }, |
228 | transform: { | 260 | module: { |
229 | react: { | 261 | type: isServer ? 'commonjs' : 'es6', |
230 | runtime: 'automatic', | ||
231 | }, | ||
232 | }, | 262 | }, |
233 | target: 'es2022', | 263 | } satisfies SwcConfig, |
234 | }, | 264 | }), |
235 | module: { | 265 | }, |
236 | type: isServer ? 'commonjs' : 'es6', | 266 | headTags: [ |
267 | { | ||
268 | tagName: 'link', | ||
269 | attributes: { | ||
270 | rel: 'icon', | ||
271 | href: '/favicon.svg', | ||
272 | type: 'image/svg+xml', | ||
237 | }, | 273 | }, |
238 | } satisfies SwcConfig, | ||
239 | }), | ||
240 | }, | ||
241 | headTags: [ | ||
242 | { | ||
243 | tagName: 'link', | ||
244 | attributes: { | ||
245 | rel: 'icon', | ||
246 | href: '/favicon.svg', | ||
247 | type: 'image/svg+xml', | ||
248 | }, | 274 | }, |
249 | }, | 275 | { |
250 | { | 276 | tagName: 'link', |
251 | tagName: 'link', | 277 | attributes: { |
252 | attributes: { | 278 | rel: 'icon', |
253 | rel: 'icon', | 279 | href: '/favicon.png', |
254 | href: '/favicon.png', | 280 | type: 'image/png', |
255 | type: 'image/png', | 281 | sizes: '32x32', |
256 | sizes: '32x32', | 282 | }, |
257 | }, | 283 | }, |
258 | }, | 284 | { |
259 | { | 285 | tagName: 'link', |
260 | tagName: 'link', | 286 | attributes: { |
261 | attributes: { | 287 | rel: 'icon', |
262 | rel: 'icon', | 288 | href: '/favicon-96x96.png', |
263 | href: '/favicon-96x96.png', | 289 | type: 'image/png', |
264 | type: 'image/png', | 290 | sizes: '96x96', |
265 | sizes: '96x96', | 291 | }, |
266 | }, | 292 | }, |
267 | }, | 293 | { |
268 | { | 294 | tagName: 'link', |
269 | tagName: 'link', | 295 | attributes: { |
270 | attributes: { | 296 | rel: 'apple-touch-icon', |
271 | rel: 'apple-touch-icon', | 297 | href: '/apple-touch-icon.png', |
272 | href: '/apple-touch-icon.png', | 298 | type: 'image/png', |
273 | type: 'image/png', | 299 | sizes: '180x180', |
274 | sizes: '180x180', | 300 | }, |
275 | }, | 301 | }, |
276 | }, | 302 | { |
277 | { | 303 | tagName: 'link', |
278 | tagName: 'link', | 304 | attributes: { |
279 | attributes: { | 305 | rel: 'manifest', |
280 | rel: 'manifest', | 306 | href: '/manifest.webmanifest', |
281 | href: '/manifest.webmanifest', | 307 | }, |
282 | }, | 308 | }, |
283 | }, | 309 | { |
284 | { | 310 | tagName: 'meta', |
285 | tagName: 'meta', | 311 | attributes: { |
286 | attributes: { | 312 | name: 'theme-color', |
287 | name: 'theme-color', | 313 | media: '(prefers-color-scheme:light)', |
288 | media: '(prefers-color-scheme:light)', | 314 | content: '#f5f5f5', |
289 | content: '#f5f5f5', | 315 | }, |
290 | }, | 316 | }, |
291 | }, | 317 | { |
292 | { | 318 | tagName: 'meta', |
293 | tagName: 'meta', | 319 | attributes: { |
294 | attributes: { | 320 | name: 'theme-color', |
295 | name: 'theme-color', | 321 | media: '(prefers-color-scheme:dark)', |
296 | media: '(prefers-color-scheme:dark)', | 322 | content: '#282c34', |
297 | content: '#282c34', | 323 | }, |
298 | }, | 324 | }, |
299 | }, | 325 | ], |
300 | ], | 326 | } satisfies Config; |
301 | } satisfies Config; | 327 | } |
diff --git a/subprojects/docs/package.json b/subprojects/docs/package.json index 31df0339..f28042b1 100644 --- a/subprojects/docs/package.json +++ b/subprojects/docs/package.json | |||
@@ -30,45 +30,48 @@ | |||
30 | }, | 30 | }, |
31 | "homepage": "https://refinery.tools", | 31 | "homepage": "https://refinery.tools", |
32 | "dependencies": { | 32 | "dependencies": { |
33 | "@algolia/client-search": "^4.24.0", | 33 | "@algolia/client-search": "^5.2.3", |
34 | "@docusaurus/core": "^3.4.0", | 34 | "@docusaurus/core": "^3.5.2", |
35 | "@docusaurus/plugin-content-docs": "^3.4.0", | 35 | "@docusaurus/plugin-client-redirects": "^3.5.2", |
36 | "@docusaurus/plugin-content-pages": "^3.4.0", | 36 | "@docusaurus/plugin-content-docs": "^3.5.2", |
37 | "@docusaurus/plugin-sitemap": "^3.4.0", | 37 | "@docusaurus/plugin-content-pages": "^3.5.2", |
38 | "@docusaurus/theme-classic": "^3.4.0", | 38 | "@docusaurus/plugin-sitemap": "^3.5.2", |
39 | "@docusaurus/theme-common": "^3.4.0", | 39 | "@docusaurus/theme-classic": "^3.5.2", |
40 | "@docusaurus/theme-search-algolia": "^3.4.0", | 40 | "@docusaurus/theme-common": "^3.5.2", |
41 | "@fontsource-variable/jetbrains-mono": "^5.0.21", | 41 | "@docusaurus/theme-search-algolia": "^3.5.2", |
42 | "@fontsource-variable/open-sans": "^5.0.29", | 42 | "@fontsource-variable/jetbrains-mono": "^5.0.22", |
43 | "@fontsource/open-sans": "^5.0.28", | 43 | "@fontsource-variable/open-sans": "^5.0.30", |
44 | "@fontsource/open-sans": "^5.0.29", | ||
45 | "@hpcc-js/wasm-zstd": "^1.2.0", | ||
44 | "@material-icons/svg": "^1.0.33", | 46 | "@material-icons/svg": "^1.0.33", |
45 | "@mdx-js/react": "^3.0.1", | 47 | "@mdx-js/react": "^3.0.1", |
46 | "@swc/core": "^1.6.13", | 48 | "@swc/core": "^1.7.22", |
47 | "clsx": "^2.1.1", | 49 | "clsx": "^2.1.1", |
50 | "java-properties": "^1.0.2", | ||
48 | "mdast-util-mdx": "^3.0.0", | 51 | "mdast-util-mdx": "^3.0.0", |
49 | "prism-react-renderer": "^2.3.1", | 52 | "prism-react-renderer": "^2.4.0", |
50 | "react": "^18.3.1", | 53 | "react": "^18.3.1", |
51 | "react-dom": "^18.3.1", | 54 | "react-dom": "^18.3.1", |
52 | "remark-smartypants": "^3.0.1", | 55 | "remark-smartypants": "^3.0.2", |
53 | "responsive-loader": "^3.1.2", | 56 | "responsive-loader": "^3.1.2", |
54 | "search-insights": "^2.14.0", | 57 | "search-insights": "^2.17.0", |
55 | "sharp": "^0.33.4", | 58 | "sharp": "^0.33.5", |
56 | "swc-loader": "^0.2.6", | 59 | "swc-loader": "^0.2.6", |
57 | "terser-webpack-plugin": "^5.3.10", | 60 | "terser-webpack-plugin": "^5.3.10", |
58 | "typescript": "5.5.3", | 61 | "typescript": "5.5.4", |
59 | "unified": "^11.0.5", | 62 | "unified": "^11.0.5", |
60 | "unist-util-visit": "^5.0.0", | 63 | "unist-util-visit": "^5.0.0", |
61 | "webpack": "^5.92.1" | 64 | "webpack": "^5.94.0" |
62 | }, | 65 | }, |
63 | "devDependencies": { | 66 | "devDependencies": { |
64 | "@docusaurus/mdx-loader": "^3.4.0", | 67 | "@docusaurus/mdx-loader": "^3.5.2", |
65 | "@docusaurus/module-type-aliases": "^3.4.0", | 68 | "@docusaurus/module-type-aliases": "^3.5.2", |
66 | "@docusaurus/types": "^3.4.0", | 69 | "@docusaurus/types": "^3.5.2", |
67 | "@types/babel__core": "^7.20.5", | 70 | "@types/babel__core": "^7.20.5", |
68 | "@types/mdast": "^4.0.4", | 71 | "@types/mdast": "^4.0.4", |
69 | "@types/node": "^20.14.10", | 72 | "@types/node": "^20.16.2", |
70 | "@types/react": "^18.3.3", | 73 | "@types/react": "^18.3.5", |
71 | "@types/react-dom": "^18.3.0", | 74 | "@types/react-dom": "^18.3.0", |
72 | "@types/unist": "^3.0.2" | 75 | "@types/unist": "^3.0.3" |
73 | } | 76 | } |
74 | } | 77 | } |
diff --git a/subprojects/docs/sidebars.ts b/subprojects/docs/sidebars.ts new file mode 100644 index 00000000..29348767 --- /dev/null +++ b/subprojects/docs/sidebars.ts | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import { SidebarsConfig } from '@docusaurus/plugin-content-docs'; | ||
8 | |||
9 | export default { | ||
10 | learnSidebar: [ | ||
11 | { | ||
12 | type: 'autogenerated', | ||
13 | dirName: 'learn', | ||
14 | }, | ||
15 | ], | ||
16 | developSidebar: [ | ||
17 | { | ||
18 | type: 'autogenerated', | ||
19 | dirName: 'develop', | ||
20 | }, | ||
21 | ], | ||
22 | } satisfies SidebarsConfig; | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/ImageTag.tsx b/subprojects/docs/src/components/ResponsiveImage/ImageTag.tsx new file mode 100644 index 00000000..b2002947 --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/ImageTag.tsx | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import styles from './index.module.css'; | ||
8 | |||
9 | export interface Props { | ||
10 | image: ResponsiveImageOutput; | ||
11 | alt?: string | undefined; | ||
12 | title?: string | undefined; | ||
13 | } | ||
14 | |||
15 | export default function ImageTag({ image, alt, title }: Props) { | ||
16 | return ( | ||
17 | <img | ||
18 | alt={alt} | ||
19 | title={title} | ||
20 | width={image.width} | ||
21 | height={image.height} | ||
22 | sizes="(min-width: 1440px) 1320px, (max-width: 996px) calc(100vw - 32px), calc(75vw - 257px)" | ||
23 | loading="lazy" | ||
24 | src={image.src} | ||
25 | srcSet={image.srcSet} | ||
26 | className={styles['image']} | ||
27 | /> | ||
28 | ); | ||
29 | } | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/SVG/index.module.css b/subprojects/docs/src/components/ResponsiveImage/SVG/index.module.css new file mode 100644 index 00000000..7738f412 --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/SVG/index.module.css | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | .container { | ||
8 | display: flex; | ||
9 | flex-direction: row; | ||
10 | justify-content: center; | ||
11 | } | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/SVG/index.tsx b/subprojects/docs/src/components/ResponsiveImage/SVG/index.tsx new file mode 100644 index 00000000..13567504 --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/SVG/index.tsx | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import { useId, type FunctionComponent, type SVGAttributes } from 'react'; | ||
8 | |||
9 | import styles from './index.module.css'; | ||
10 | |||
11 | export default function SVG({ | ||
12 | Component, | ||
13 | alt, | ||
14 | title, | ||
15 | }: { | ||
16 | Component: FunctionComponent<SVGAttributes<SVGElement> & { title?: string }>; | ||
17 | alt?: string; | ||
18 | title?: string; | ||
19 | }) { | ||
20 | const labelID = useId(); | ||
21 | |||
22 | return ( | ||
23 | <div className={styles['container']}> | ||
24 | <Component | ||
25 | role="img" | ||
26 | {...(title === undefined ? {} : { title })} | ||
27 | {...(alt === undefined ? {} : { 'aria-labelledby': labelID })} | ||
28 | /> | ||
29 | {alt !== undefined && ( | ||
30 | <div id={labelID} className="sr-only"> | ||
31 | {alt} | ||
32 | </div> | ||
33 | )} | ||
34 | </div> | ||
35 | ); | ||
36 | } | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/Themed.tsx b/subprojects/docs/src/components/ResponsiveImage/Themed.tsx new file mode 100644 index 00000000..5573685a --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/Themed.tsx | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import Link from '@docusaurus/Link'; | ||
8 | import { useColorMode } from '@docusaurus/theme-common'; | ||
9 | import useIsBrowser from '@docusaurus/useIsBrowser'; | ||
10 | import clsx from 'clsx'; | ||
11 | |||
12 | import ImageTag, { type Props as ImageTagProps } from './ImageTag'; | ||
13 | import styles from './index.module.css'; | ||
14 | import maxWidth from './maxWidth'; | ||
15 | |||
16 | interface Props extends Omit<ImageTagProps, 'image'> { | ||
17 | light: ResponsiveImageOutput; | ||
18 | dark: ResponsiveImageOutput; | ||
19 | originalLight: string; | ||
20 | originalDark: string; | ||
21 | } | ||
22 | |||
23 | export default function Themed({ | ||
24 | light, | ||
25 | dark, | ||
26 | originalLight, | ||
27 | originalDark, | ||
28 | ...props | ||
29 | }: Props) { | ||
30 | // Force re-render in browser. | ||
31 | // https://github.com/facebook/docusaurus/blob/e012e0315862b2ca02cad40c58d11d31c319ff75/packages/docusaurus-theme-classic/src/theme/CodeBlock/index.tsx#L32-L36 | ||
32 | const isBrowser = useIsBrowser(); | ||
33 | const { colorMode } = useColorMode(); | ||
34 | const isDark = colorMode === 'dark'; | ||
35 | const image = isDark ? dark : light; | ||
36 | const original = isDark ? originalDark : originalLight; | ||
37 | const imageTag = <ImageTag image={image} {...props} />; | ||
38 | const responsiveTag = isBrowser ? imageTag : <noscript>{imageTag}</noscript>; | ||
39 | |||
40 | if (light.width !== dark.width || light.height !== dark.height) { | ||
41 | throw new Error( | ||
42 | `Image size mismatch: ${light.src} is ${light.width}×${light.height}, but ${dark.src} is ${dark.width}×${dark.height}`, | ||
43 | ); | ||
44 | } | ||
45 | |||
46 | return ( | ||
47 | <div | ||
48 | style={{ | ||
49 | aspectRatio: `${light.width}/${light.height}`, | ||
50 | maxWidth: maxWidth(light), | ||
51 | }} | ||
52 | className={styles['container']} | ||
53 | > | ||
54 | <div | ||
55 | style={{ backgroundImage: `url(${light.placeholder})` }} | ||
56 | className={styles['placeholder']} | ||
57 | /> | ||
58 | <div | ||
59 | style={{ backgroundImage: `url(${dark.placeholder})` }} | ||
60 | className={clsx(styles['placeholder'], styles['placeholder--dark'])} | ||
61 | /> | ||
62 | <Link href={`pathname://${original}`} className={String(styles['link'])}> | ||
63 | {responsiveTag} | ||
64 | </Link> | ||
65 | </div> | ||
66 | ); | ||
67 | } | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/index.module.css b/subprojects/docs/src/components/ResponsiveImage/index.module.css new file mode 100644 index 00000000..8e868ac7 --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/index.module.css | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | .container { | ||
8 | position: relative; | ||
9 | width: 100%; | ||
10 | margin: 0 auto; | ||
11 | } | ||
12 | |||
13 | .placeholder, .link, .image { | ||
14 | position: absolute; | ||
15 | top: 0; | ||
16 | left: 0; | ||
17 | width: 100%; | ||
18 | height: 100%; | ||
19 | } | ||
20 | |||
21 | .placeholder { | ||
22 | z-index: 0; | ||
23 | background-size: cover; | ||
24 | } | ||
25 | |||
26 | .placeholder--dark { | ||
27 | display: none; | ||
28 | z-index: 10; | ||
29 | } | ||
30 | |||
31 | [data-theme='dark'] .placeholder--dark { | ||
32 | display: block; | ||
33 | } | ||
34 | |||
35 | .link, .image { | ||
36 | z-index: 100; | ||
37 | } | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/index.tsx b/subprojects/docs/src/components/ResponsiveImage/index.tsx new file mode 100644 index 00000000..7ce43853 --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/index.tsx | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import Link from '@docusaurus/Link'; | ||
8 | |||
9 | import ImageTag, { type Props as ImageTagProps } from './ImageTag'; | ||
10 | import styles from './index.module.css'; | ||
11 | |||
12 | interface Props extends ImageTagProps { | ||
13 | original: string; | ||
14 | } | ||
15 | |||
16 | export default function ThemedImage({ original, ...props }: Props) { | ||
17 | const { | ||
18 | image: { width, height, placeholder }, | ||
19 | } = props; | ||
20 | return ( | ||
21 | <div | ||
22 | style={{ aspectRatio: `${width}/${height}` }} | ||
23 | className={styles['container']} | ||
24 | > | ||
25 | <div | ||
26 | style={{ backgroundImage: `url(${placeholder})` }} | ||
27 | className={styles['placeholder']} | ||
28 | /> | ||
29 | <Link href={`pathname://${original}`} className={String(styles['link'])}> | ||
30 | <ImageTag {...props} /> | ||
31 | </Link> | ||
32 | </div> | ||
33 | ); | ||
34 | } | ||
diff --git a/subprojects/docs/src/components/ResponsiveImage/maxWidth.ts b/subprojects/docs/src/components/ResponsiveImage/maxWidth.ts new file mode 100644 index 00000000..bc9872d7 --- /dev/null +++ b/subprojects/docs/src/components/ResponsiveImage/maxWidth.ts | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | export default function maxWidth(image: ResponsiveImageOutput): number { | ||
8 | let result = 0; | ||
9 | image.images.forEach(({ width }) => { | ||
10 | if (width > result) { | ||
11 | result = width; | ||
12 | } | ||
13 | }); | ||
14 | return result; | ||
15 | } | ||
diff --git a/subprojects/docs/src/components/TryInRefinery.tsx b/subprojects/docs/src/components/TryInRefinery.tsx new file mode 100644 index 00000000..40766582 --- /dev/null +++ b/subprojects/docs/src/components/TryInRefinery.tsx | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import Link from '@docusaurus/Link'; | ||
8 | |||
9 | export default function TryInRefinery({ href }: { href: string }) { | ||
10 | return ( | ||
11 | <p> | ||
12 | <Link | ||
13 | href={href} | ||
14 | className="button button--primary button--outline button--play" | ||
15 | > | ||
16 | Try in Refinery | ||
17 | </Link> | ||
18 | </p> | ||
19 | ); | ||
20 | } | ||
diff --git a/subprojects/docs/src/components/UseCases/index.tsx b/subprojects/docs/src/components/UseCases/index.tsx index c9570cc6..a7332f3e 100644 --- a/subprojects/docs/src/components/UseCases/index.tsx +++ b/subprojects/docs/src/components/UseCases/index.tsx | |||
@@ -49,7 +49,7 @@ export default function UseCases() { | |||
49 | <b>Scenario generation</b> for testing autonomous vechicles | 49 | <b>Scenario generation</b> for testing autonomous vechicles |
50 | </> | 50 | </> |
51 | } | 51 | } |
52 | href="https://doi.org/10.1007/s10270-021-00884-z" | 52 | href="https://doi.org/10.1007/s10270-021-00918-6" |
53 | /> | 53 | /> |
54 | <UseCase | 54 | <UseCase |
55 | icon={<Uc2 />} | 55 | icon={<Uc2 />} |
diff --git a/subprojects/docs/src/css/custom.css b/subprojects/docs/src/css/custom.css index 146b45a0..3f0b5b35 100644 --- a/subprojects/docs/src/css/custom.css +++ b/subprojects/docs/src/css/custom.css | |||
@@ -80,14 +80,18 @@ code { | |||
80 | --ifm-navbar-shadow: var(--ifm-global-shadow-lw) !important; | 80 | --ifm-navbar-shadow: var(--ifm-global-shadow-lw) !important; |
81 | } | 81 | } |
82 | 82 | ||
83 | .button, .navbar__link, .footer__link-item, .DocSearch-Button-Placeholder { | 83 | .button, .navbar__link, .footer__link-item { |
84 | text-transform: uppercase; | 84 | text-transform: uppercase; |
85 | font-variation-settings: 'wdth' 87.5; | 85 | font-variation-settings: 'wdth' 87.5; |
86 | } | 86 | } |
87 | 87 | ||
88 | .DocSearch-Button-Placeholder { | 88 | .DocSearch-Button-Placeholder { |
89 | padding: 0 9px 0 6px !important; | 89 | font-family: var(--ifm-font-family-base) !important; |
90 | font-family: var(--ifm-font-family-base); | 90 | } |
91 | |||
92 | .button { | ||
93 | /* No decoration even in `::: info` boxes. */ | ||
94 | text-decoration: none; | ||
91 | } | 95 | } |
92 | 96 | ||
93 | .button--play::before { | 97 | .button--play::before { |
@@ -108,7 +112,7 @@ code { | |||
108 | } | 112 | } |
109 | 113 | ||
110 | .navbar__inner .navbar__link--try-now { | 114 | .navbar__inner .navbar__link--try-now { |
111 | margin: 0 0.75rem 0 0.5rem; | 115 | margin: 0 0.5rem; |
112 | padding: 0.25rem 1.25rem; | 116 | padding: 0.25rem 1.25rem; |
113 | border-radius: 50em; | 117 | border-radius: 50em; |
114 | } | 118 | } |
@@ -145,3 +149,19 @@ code { | |||
145 | .markdown svg { | 149 | .markdown svg { |
146 | max-width: 100%; | 150 | max-width: 100%; |
147 | } | 151 | } |
152 | |||
153 | .footnotes { | ||
154 | margin-top: 3rem; | ||
155 | padding-top: var(--ifm-hr-margin-vertical); | ||
156 | border-top: var(--ifm-hr-height) solid var(--ifm-hr-background-color); | ||
157 | } | ||
158 | |||
159 | .markdown > .footnotes ol, | ||
160 | .markdown > .footnotes li:last-of-type :last-of-type { | ||
161 | margin-bottom: 0; | ||
162 | } | ||
163 | |||
164 | .inline-icon { | ||
165 | fill: currentColor; | ||
166 | vertical-align: text-top; | ||
167 | } | ||
diff --git a/subprojects/docs/src/develop/index.md b/subprojects/docs/src/develop/index.md deleted file mode 100644 index 4537889e..00000000 --- a/subprojects/docs/src/develop/index.md +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 0 | ||
5 | --- | ||
6 | |||
7 | # Programming guide | ||
8 | |||
9 | :::warning | ||
10 | |||
11 | Under construction | ||
12 | |||
13 | ::: | ||
diff --git a/subprojects/docs/src/develop/javadoc.md b/subprojects/docs/src/develop/javadoc.md deleted file mode 100644 index e94e3eab..00000000 --- a/subprojects/docs/src/develop/javadoc.md +++ /dev/null | |||
@@ -1,40 +0,0 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: API documentation for Refinery components automatically generated by Javadoc | ||
5 | sidebar_position: 999 | ||
6 | --- | ||
7 | |||
8 | # Javadoc | ||
9 | |||
10 | Here you can find API documentation for Refinery components automatically generated by Javadoc. We recommend reading the [Programming guide](/develop) first to understand how to use these components. | ||
11 | |||
12 | # Refinery | ||
13 | |||
14 | * [`tools.refinery:refinery-generator`](pathname://refinery-generator) | ||
15 | * [`tools.refinery:refinery-language`](pathname://refinery-language) | ||
16 | * [`tools.refinery:refinery-language-ide`](pathname://refinery-language-ide) | ||
17 | * [`tools.refinery:refinery-language-model`](pathname://refinery-language-model) | ||
18 | * [`tools.refinery:refinery-language-semantics`](pathname://refinery-language-semantics) | ||
19 | * [`tools.refinery:refinery-logic`](pathname://refinery-logic) | ||
20 | * [`tools.refinery:refinery-store`](pathname://refinery-store) | ||
21 | * [`tools.refinery:refinery-store-dse`](pathname://refinery-store-dse) | ||
22 | * [`tools.refinery:refinery-store-dse-visualization`](pathname://refinery-store-dse-visualization) | ||
23 | * [`tools.refinery:refinery-store-query`](pathname://refinery-store-query) | ||
24 | * [`tools.refinery:refinery-store-query-interpreter`](pathname://refinery-store-query-interpreter) | ||
25 | * [`tools.refinery:refinery-store-reasoning`](pathname://refinery-store-reasoning) | ||
26 | * [`tools.refinery:refinery-store-reasoning-scope`](pathname://refinery-store-reasoning-scope) | ||
27 | * [`tools.refinery:refinery-store-reasoning-smt`](pathname://refinery-store-reasoning-smt) | ||
28 | |||
29 | # Interpreter | ||
30 | |||
31 | :::note | ||
32 | |||
33 | The _Refinery Interpreter_ is modified version of [VIATRA™](https://eclipse.dev/viatra/) specifically for use in Refinery. If you're interested in learning about [VIATRA™](https://eclipse.dev/viatra/), we recommend the [VIATRA™ documentation](https://eclipse.dev/viatra/documentation/index.html) and [source code](https://github.com/eclipse-viatra/org.eclipse.viatra) instead. Eclipse®, VIATRA™ and ‘Eclipse VIATRA™’ are trademarks of Eclipse Foundation, Inc. | ||
34 | |||
35 | ::: | ||
36 | |||
37 | * [`tools.refinery.interpreter:refinery-interpreter`](pathname://refinery-interpreter) | ||
38 | * [`tools.refinery.interpreter:refinery-interpreter-localsearch`](pathname://refinery-interpreter-localsearch) | ||
39 | * [`tools.refinery.interpreter:refinery-interpreter-rete`](pathname://refinery-interpreter-rete) | ||
40 | * [`tools.refinery.interpreter:refinery-interpreter-rete-recipes`](pathname://refinery-interpreter-rete-recipes) | ||
diff --git a/subprojects/docs/src/develop/contributing/commands.md b/subprojects/docs/src/docs/develop/contributing/commands.md index abfea704..7ffe2a6c 100644 --- a/subprojects/docs/src/develop/contributing/commands.md +++ b/subprojects/docs/src/docs/develop/contributing/commands.md | |||
@@ -28,28 +28,14 @@ This will also be run by GitHub Actions for each commit or pull requests. | |||
28 | 28 | ||
29 | ### `publishToMavenLocal` | 29 | ### `publishToMavenLocal` |
30 | 30 | ||
31 | |||
32 | ```bash posix2windows | 31 | ```bash posix2windows |
33 | ./gradlew publishToMavenLocal | 32 | ./gradlew publishToMavenLocal |
34 | ``` | 33 | ``` |
35 | 34 | ||
36 | Publishes the Refinery Java artifacts to the [Maven local repository](https://www.baeldung.com/maven-local-repository). | 35 | Publishes the Refinery Java artifacts to the [Maven local repository](https://www.baeldung.com/maven-local-repository). |
37 | 36 | ||
38 | Build tools, such as Gradle, will be able to consume such artifacts, which enables you to use the latest version of Refinery -- possibly including your own modification -- in other Java projects. | 37 | Build tools, such as Gradle, will be able to consume such artifacts, which enables you to use the latest version of Refinery -- possibly including your own modifications -- in other Java projects. |
39 | 38 | For more information, see our [programming guide](/snapshot/develop/java). | |
40 | For example, in Gradle, you may set | ||
41 | |||
42 | ```kotlin title="build.gradle.kts" | ||
43 | repositories { | ||
44 | mavenLocal() | ||
45 | } | ||
46 | |||
47 | dependencies { | ||
48 | implementation("tools.refinery:refinery-generator:0.0.0-SNAPSHOT") | ||
49 | } | ||
50 | ``` | ||
51 | |||
52 | to add a dependency on Refinery to your Java project. | ||
53 | 39 | ||
54 | ### `serve` | 40 | ### `serve` |
55 | 41 | ||
@@ -63,7 +49,7 @@ This task is ideal for running the Refinery backend if you don't intend to work | |||
63 | The Refinery frontend TypeScript projects is automatically built before the server starts. | 49 | The Refinery frontend TypeScript projects is automatically built before the server starts. |
64 | The server will use the latest build output of the frontend as static assets. | 50 | The server will use the latest build output of the frontend as static assets. |
65 | 51 | ||
66 | The behavior of this task is influenced by the same [environmental variables](/learn/docker#environmental-variables) as the Refinery [Docker container](/learn/docker). | 52 | The behavior of this task is influenced by the same [environmental variables](../../../learn/docker#environmental-variables) as the Refinery [Docker container](../../../learn/docker). |
67 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. | 53 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. |
68 | 54 | ||
69 | ### `serveBackend` | 55 | ### `serveBackend` |
@@ -78,18 +64,18 @@ This task is ideal for running the Refinery backend if you're working on the fro | |||
78 | No static assets will be build. | 64 | No static assets will be build. |
79 | You'll need to use [`yarnw frontend dev`](#frontend-dev) | 65 | You'll need to use [`yarnw frontend dev`](#frontend-dev) |
80 | 66 | ||
81 | Like [`gradlew serve`](#serve), the behavior of this task is influenced by the same [environmental variables](/learn/docker#environmental-variables) as the Refinery [Docker container](/learn/docker). | 67 | Like [`./gradlew serve`](#serve), the behavior of this task is influenced by the same [environmental variables](../../../learn/docker#environmental-variables) as the Refinery [Docker container](../../../learn/docker). |
82 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. | 68 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. |
83 | 69 | ||
84 | ## Yarn commands | 70 | ## Yarn commands |
85 | 71 | ||
86 | We provide a `yarnw` wrapper script to invoke the Yarn distribution installed by frontend-gradle-plugin directly. | 72 | We provide a `yarnw` wrapper script to invoke the Yarn distribution installed by frontend-gradle-plugin directly. |
87 | The following commands can only be run once [`gradlew build`](#build) has installed the necessary Node.js and Yarn packages. | 73 | The following commands can only be run once [`./gradlew build`](#build) has installed the necessary Node.js and Yarn packages. |
88 | 74 | ||
89 | ### `docs dev` | 75 | ### `docs dev` |
90 | 76 | ||
91 | ```bash posix2windows | 77 | ```bash posix2windows |
92 | ./yarn docs dev | 78 | ./yarnw docs dev |
93 | ``` | 79 | ``` |
94 | 80 | ||
95 | Builds and serves this documentation in development mode on port 3000. | 81 | Builds and serves this documentation in development mode on port 3000. |
@@ -98,7 +84,7 @@ Saved changes to most documentation sources are immediately reflected in the bro | |||
98 | You can set the port with the `-p` option, e.g. to use port 1313, use | 84 | You can set the port with the `-p` option, e.g. to use port 1313, use |
99 | 85 | ||
100 | ```bash posix2windows | 86 | ```bash posix2windows |
101 | ./yarn docs dev -p 1313 | 87 | ./yarnw docs dev -p 1313 |
102 | ``` | 88 | ``` |
103 | 89 | ||
104 | :::note | 90 | :::note |
@@ -114,13 +100,13 @@ which can be safely ignored. | |||
114 | ### `frontend dev` | 100 | ### `frontend dev` |
115 | 101 | ||
116 | ```bash posix2windows | 102 | ```bash posix2windows |
117 | ./yarn frontend dev | 103 | ./yarnw frontend dev |
118 | ``` | 104 | ``` |
119 | 105 | ||
120 | Builds and serves the refinery frontend on port 1313. | 106 | Builds and serves the refinery frontend on port 1313. |
121 | Saved changes to most source files are immediately reflected in the browser without reload. | 107 | Saved changes to most source files are immediately reflected in the browser without reload. |
122 | 108 | ||
123 | Before running this command, you need to start [`gradlew serveBackend`](#servebackend) to provide a backend for the frontend to connect to. | 109 | Before running this command, you need to start [`./gradlew serveBackend`](#servebackend) to provide a backend for the frontend to connect to. |
124 | The development server of the frontend will proxy all WebSocket connections to the backend. | 110 | The development server of the frontend will proxy all WebSocket connections to the backend. |
125 | 111 | ||
126 | The following environmental variables influence the behavior of this command: | 112 | The following environmental variables influence the behavior of this command: |
@@ -141,7 +127,7 @@ TCP port to listen at for incoming HTTP connections. | |||
141 | 127 | ||
142 | Hostname of the Refinery backend. | 128 | Hostname of the Refinery backend. |
143 | 129 | ||
144 | This should match the `REFINERY_LISTEN_HOST` passed to [`gradlew serveBackend`](#servebackend). | 130 | This should match the `REFINERY_LISTEN_HOST` passed to [`./gradlew serveBackend`](#servebackend). |
145 | 131 | ||
146 | **Default value:** `127.0.0.1` (connect to `localhost` over IPv4 only) | 132 | **Default value:** `127.0.0.1` (connect to `localhost` over IPv4 only) |
147 | 133 | ||
@@ -149,7 +135,7 @@ This should match the `REFINERY_LISTEN_HOST` passed to [`gradlew serveBackend`]( | |||
149 | 135 | ||
150 | TCP port of the Refinery backend. | 136 | TCP port of the Refinery backend. |
151 | 137 | ||
152 | This should match the `REFINERY_LISTEN_PORT` passed to [`gradlew serveBackend`](#servebackend). | 138 | This should match the `REFINERY_LISTEN_PORT` passed to [`./gradlew serveBackend`](#servebackend). |
153 | 139 | ||
154 | **Default value:** `1312` | 140 | **Default value:** `1312` |
155 | 141 | ||
diff --git a/subprojects/docs/src/develop/contributing/ide-setup.md b/subprojects/docs/src/docs/develop/contributing/ide-setup.md index 742035e0..d5dd4eb5 100644 --- a/subprojects/docs/src/develop/contributing/ide-setup.md +++ b/subprojects/docs/src/docs/develop/contributing/ide-setup.md | |||
@@ -12,7 +12,7 @@ title: IDE setup | |||
12 | We prefer [IntelliJ IDEA](https://www.jetbrains.com/idea/) as a Java development environment. | 12 | We prefer [IntelliJ IDEA](https://www.jetbrains.com/idea/) as a Java development environment. |
13 | No special preparations should be necessary for importing the project as a Gradle project into IDEA: | 13 | No special preparations should be necessary for importing the project as a Gradle project into IDEA: |
14 | 14 | ||
15 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later). | 15 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery on obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later). |
16 | 16 | ||
17 | 2. Clone the project git repository and open it in IntelliJ IDEA. Make sure to _open_ the project instead of creating a _new_ one in the same directory. | 17 | 2. Clone the project git repository and open it in IntelliJ IDEA. Make sure to _open_ the project instead of creating a _new_ one in the same directory. |
18 | 18 | ||
@@ -20,7 +20,7 @@ No special preparations should be necessary for importing the project as a Gradl | |||
20 | * In _Project Structure > Project settings > Project > SDK_, a Java 21 compatible JDK should be selected. | 20 | * In _Project Structure > Project settings > Project > SDK_, a Java 21 compatible JDK should be selected. |
21 | * In _Project Structure > Project settings > Project > Language level_, either _SDK default_ or _21_ should be selected. | 21 | * In _Project Structure > Project settings > Project > Language level_, either _SDK default_ or _21_ should be selected. |
22 | * Make sure that each module in _Project Structure > Project settings > Module_ uses the _Project default_ language level in _Sources > Language level_ and the _Project SDK_ in _Dependencies > Module SDK._ | 22 | * Make sure that each module in _Project Structure > Project settings > Module_ uses the _Project default_ language level in _Sources > Language level_ and the _Project SDK_ in _Dependencies > Module SDK._ |
23 | * In _Settings > Gradle settings > Gralde Projects > Gradle_, the _Distribution_ should be set to _Wrapper_ and the _Gradle JVM_ should be set to _Project SDK._ | 23 | * In _Settings > Gradle settings > Gradle Projects > Gradle_, the _Distribution_ should be set to _Wrapper_ and the _Gradle JVM_ should be set to _Project SDK._ |
24 | 24 | ||
25 | 4. We recommend installing the latest _SonarLint_ plugin in _Settings > Plugins_ to get real-time code quality analysis in your IDE. | 25 | 4. We recommend installing the latest _SonarLint_ plugin in _Settings > Plugins_ to get real-time code quality analysis in your IDE. |
26 | 26 | ||
@@ -35,7 +35,7 @@ You'll also need [VS Code](#vs-code) to edit the TypeScript code in Refinery. | |||
35 | 35 | ||
36 | ## Eclipse | 36 | ## Eclipse |
37 | 37 | ||
38 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. | 38 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery on obtaining the required JDK version. |
39 | 39 | ||
40 | 2. Download and extract the [Eclipse IDE for Java and DSL Developers 2023-12](https://www.eclipse.org/downloads/packages/release/2023-12/r/eclipse-ide-java-and-dsl-developers) package. | 40 | 2. Download and extract the [Eclipse IDE for Java and DSL Developers 2023-12](https://www.eclipse.org/downloads/packages/release/2023-12/r/eclipse-ide-java-and-dsl-developers) package. |
41 | 41 | ||
@@ -63,7 +63,7 @@ You'll also need [VS Code](#vs-code) to edit the TypeScript code in Refinery. | |||
63 | ``` | 63 | ``` |
64 | in the cloned repository. | 64 | in the cloned repository. |
65 | * This should complete without any compilation errors. | 65 | * This should complete without any compilation errors. |
66 | * To troubleshoot any error, see the [instructions about compiling Refinery](/develop/contributing#compiling). | 66 | * To troubleshoot any error, see the [instructions for compiling Refinery](/develop/contributing#compiling). |
67 | 67 | ||
68 | 8. Select _File > Import... > Gradle > Existing Gradle Project_ and import the cloned repository in Eclipse. | 68 | 8. Select _File > Import... > Gradle > Existing Gradle Project_ and import the cloned repository in Eclipse. |
69 | * Make sure to select the root of the repository (containing this file) as the _Project root directory_ and that the _Gradle distribution_ is _Gradle wrapper_. | 69 | * Make sure to select the root of the repository (containing this file) as the _Project root directory_ and that the _Gradle distribution_ is _Gradle wrapper_. |
@@ -73,13 +73,15 @@ You'll also need [VS Code](#vs-code) to edit the TypeScript code in Refinery. | |||
73 | 73 | ||
74 | We recommend [VSCodium](https://github.com/VSCodium/vscodium) or [Visual Studio Code](https://code.visualstudio.com/) to work with the parts of Refinery that are written is TypeScript. | 74 | We recommend [VSCodium](https://github.com/VSCodium/vscodium) or [Visual Studio Code](https://code.visualstudio.com/) to work with the parts of Refinery that are written is TypeScript. |
75 | 75 | ||
76 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later). | 76 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery on obtaining the required JDK version. |
77 | 77 | ||
78 | 2. Install the following VS Code extensions: | 78 | 2. Install the following VS Code extensions: |
79 | * _EditorConfig for VS Code_ [[Open VSX](https://open-vsx.org/extension/EditorConfig/EditorConfig)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)] | 79 | * _EditorConfig for VS Code_ [[Open VSX](https://open-vsx.org/extension/EditorConfig/EditorConfig)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)] |
80 | * _ZipFS - a zip file system_ [[Open VSX](https://open-vsx.org/extension/arcanis/vscode-zipfs)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs)] | ||
81 | * _ESLint_ [[Open VSX](https://open-vsx.org/extension/dbaeumer/vscode-eslint)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)] | 80 | * _ESLint_ [[Open VSX](https://open-vsx.org/extension/dbaeumer/vscode-eslint)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)] |
81 | * _MDX_ [[Open VSX](https://open-vsx.org/extension/unifiedjs/vscode-mdx)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx)] | ||
82 | * _Prettier - Code formatter_ [[Open VSX](https://open-vsx.org/extension/esbenp/prettier-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)] | ||
82 | * _XState VSCode_ [[Open VSX](https://open-vsx.org/extension/statelyai/stately-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)] | 83 | * _XState VSCode_ [[Open VSX](https://open-vsx.org/extension/statelyai/stately-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)] |
84 | * _ZipFS - a zip file system_ [[Open VSX](https://open-vsx.org/extension/arcanis/vscode-zipfs)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs)] | ||
83 | 85 | ||
84 | 3. Clone the project Git repository but _do not_ import it into VS Code yet. | 86 | 3. Clone the project Git repository but _do not_ import it into VS Code yet. |
85 | 87 | ||
diff --git a/subprojects/docs/src/develop/contributing/index.md b/subprojects/docs/src/docs/develop/contributing/index.md index aa0bdb2f..aa0bdb2f 100644 --- a/subprojects/docs/src/develop/contributing/index.md +++ b/subprojects/docs/src/docs/develop/contributing/index.md | |||
diff --git a/subprojects/docs/src/docs/develop/java.md b/subprojects/docs/src/docs/develop/java.md new file mode 100644 index 00000000..8bf796a5 --- /dev/null +++ b/subprojects/docs/src/docs/develop/java.md | |||
@@ -0,0 +1,460 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 0 | ||
5 | --- | ||
6 | |||
7 | # Programming guide | ||
8 | |||
9 | This guide is aimed at developers who wish to create Java applications that leverage Refinery as a library. | ||
10 | We also recommend browsing the [Javadoc documentation](../javadoc) associated with Refinery components. | ||
11 | See the [contributor's guide](../contributing) for information on building and modifying Refinery itself. | ||
12 | |||
13 | :::note | ||
14 | |||
15 | Refinery can run as a cloud-based [_Graph Solver as a Service_](https://refinery.services/) without local installation. | ||
16 | You can also run a compiled version as a [Docker container](../../learn/docker). | ||
17 | |||
18 | ::: | ||
19 | |||
20 | Below, you can find instructions on using [Gradle](#gradle) or [Apache Maven](#maven) to create applications that use Refinery as a Java library. | ||
21 | |||
22 | ## Working with Gradle {#gradle} | ||
23 | |||
24 | We recommend [Gradle](https://gradle.org/) as a build system for creating Java programs that use Refinery as a library. | ||
25 | We created a [Gradle plugin](pathname://../javadoc/refinery-gradle-plugins/) to simplify project configuration. | ||
26 | |||
27 | This tutorial explains how to use a **snapshot** or **local** pre-release version of Refinery with Gradle. | ||
28 | Released versions, such as the [**latest version**](/develop/java#gradle), are available from [Maven Central](https://central.sonatype.com/namespace/tools.refinery). | ||
29 | |||
30 | import Tabs from '@theme/Tabs'; | ||
31 | import TabItem from '@theme/TabItem'; | ||
32 | |||
33 | <Tabs groupId="version"> | ||
34 | <TabItem value="snapshot" label="Snapshot" default> | ||
35 | We always publish a [SNAPSHOT](https://maven.apache.org/guides/getting-started/index.html#what-is-a-snapshot-version) version of Refinery based on the latest commit in our [Git repository](https://github.com/graphs4value/refinery). This is the development version of our code and may change without warning at any time. | ||
36 | |||
37 | To find out the configuration for using our snapshot artifacts, select whether you use a Kotlin-based (`.gradle.kts`) or a Groovy-based (`.gradle`) configuration format for your Gradle build. You should add this code to your Gradle *settings* file, which is named `settings.gradle.kts` or `settings.gradle`. | ||
38 | |||
39 | <Tabs groupId="gradleLanguage"> | ||
40 | <TabItem value="kotlin" label="Kotlin"> | ||
41 | ```kotlin title="settings.gradle.kts" | ||
42 | pluginManagement { | ||
43 | repositories { | ||
44 | maven { | ||
45 | name = "refinery-snapshots" | ||
46 | url = uri("https://refinery.tools/maven/snapshots/") | ||
47 | } | ||
48 | gradlePluginPortal() | ||
49 | } | ||
50 | } | ||
51 | |||
52 | plugins { | ||
53 | id("tools.refinery.settings") version "@@@version@@@" | ||
54 | } | ||
55 | ``` | ||
56 | </TabItem> | ||
57 | <TabItem value="groovy" label="Groovy"> | ||
58 | ```groovy title="settings.gradle" | ||
59 | pluginManagement { | ||
60 | repositories { | ||
61 | maven { | ||
62 | name 'refinery-snapshots' | ||
63 | url 'https://refinery.tools/maven/snapshots/' | ||
64 | } | ||
65 | gradlePluginPortal() | ||
66 | } | ||
67 | } | ||
68 | |||
69 | plugins { | ||
70 | id 'tools.refinery.settings' version '@@@version@@@' | ||
71 | } | ||
72 | ``` | ||
73 | </TabItem> | ||
74 | </Tabs> | ||
75 | </TabItem> | ||
76 | <TabItem value="mavenLocal" label="Local"> | ||
77 | Running Refinery from a local build is an _advanced technique_ that you should only use if you want to [contribute to Refinery](../contributing) and have modified it yourself. | ||
78 | First you'll have to run the [`./gradlew publishToMavenLocal`](../contributing/commands#publishtomavenlocal) command in your local clone of the Refinery repository to install Refinery into your [local Maven repository](https://www.baeldung.com/maven-local-repository). | ||
79 | |||
80 | Next, to find out the configuration for using local artifacts, select whether you use a Kotlin-based (`.gradle.kts`) or a Groovy-based (`.gradle`) configuration format for your Gradle build. You should add this code to your Gradle *settings* file, which is named `settings.gradle.kts` or `settings.gradle`. | ||
81 | |||
82 | <Tabs groupId="gradleLanguage"> | ||
83 | <TabItem value="kotlin" label="Kotlin"> | ||
84 | ```kotlin title="settings.gradle.kts" | ||
85 | pluginManagement { | ||
86 | repositories { | ||
87 | mavenLocal() | ||
88 | gradlePluginPortal() | ||
89 | } | ||
90 | } | ||
91 | |||
92 | plugins { | ||
93 | id("tools.refinery.settings") version "@@@version@@@" | ||
94 | } | ||
95 | ``` | ||
96 | </TabItem> | ||
97 | <TabItem value="groovy" label="Groovy"> | ||
98 | ```groovy title="settings.gradle" | ||
99 | pluginManagement { | ||
100 | repositories { | ||
101 | mavenLocal() | ||
102 | gradlePluginPortal() | ||
103 | } | ||
104 | } | ||
105 | |||
106 | plugins { | ||
107 | id 'tools.refinery.settings' version '@@@version@@@' | ||
108 | } | ||
109 | ``` | ||
110 | </TabItem> | ||
111 | </Tabs> | ||
112 | </TabItem> | ||
113 | </Tabs> | ||
114 | |||
115 | This plugin will perform the following actions automatically: | ||
116 | * Add a [version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:sharing-catalogs) to your build to enable easy access to Refinery artifacts and their [dependencies](#declaring-dependencies). | ||
117 | * Lock refinery artifacts and their dependencies to a [platform](https://docs.gradle.org/current/userguide/platforms.html#sub:using-platform-to-control-transitive-deps) (Maven BOM) of tested versions. | ||
118 | * Configure a logger based on [Log4J over SLF4J](https://www.slf4j.org/legacy.html) and [SLF4J Simple](https://www.slf4j.org/apidocs/org/slf4j/simple/SimpleLogger.html) that is free from vulnerabilities and works out of the box for most use-cases. | ||
119 | * Generate [application](#building-applications) artifacts, if any, according to best practices used in the Refinery project. | ||
120 | * Add common dependencies for writing [unit tests](#writing-tests) for your Java code. | ||
121 | |||
122 | See the [multi-module projects](#multi-module-projects) section of this tutorial on how to disable some of these automated actions for a part of your build. | ||
123 | |||
124 | ### Declaring dependencies | ||
125 | |||
126 | The Refinery Gradle plugins adds a [version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:sharing-catalogs) named `refinery` that you can use to quickly access dependencies. | ||
127 | For example, to add a dependency to the [`tools.refinery:refinery-generator`](pathname://../javadoc/refinery-generator/) library, you add the following to your `build.gradle.kts` or `build.gradle` file: | ||
128 | |||
129 | <Tabs groupId="gradleLanguage"> | ||
130 | <TabItem value="kotlin" label="Kotlin"> | ||
131 | ```kotlin title="build.gradle.kts" | ||
132 | depndencies { | ||
133 | implementation(refinery.generator) | ||
134 | } | ||
135 | ``` | ||
136 | </TabItem> | ||
137 | <TabItem value="groovy" label="Groovy"> | ||
138 | ```groovy title="build.gradle" | ||
139 | dependencies { | ||
140 | implementation refinery.generator | ||
141 | } | ||
142 | ``` | ||
143 | </TabItem> | ||
144 | </Tabs> | ||
145 | |||
146 | The version catalog also contains the external dependencies used by the Refinery framework. | ||
147 | For example, you may add [GSON](https://google.github.io/gson/) for JSON parsing and [JCommander](https://jcommander.org/) for command-line argument parsing as follows: | ||
148 | |||
149 | <Tabs groupId="gradleLanguage"> | ||
150 | <TabItem value="kotlin" label="Kotlin"> | ||
151 | ```kotlin title="build.gradle.kts" | ||
152 | depndencies { | ||
153 | implementation(refinery.gson) | ||
154 | implementation(refinery.jcommander) | ||
155 | } | ||
156 | ``` | ||
157 | </TabItem> | ||
158 | <TabItem value="groovy" label="Groovy"> | ||
159 | ```groovy title="build.gradle" | ||
160 | dependencies { | ||
161 | implementation refinery.gson | ||
162 | implementation refinery.jcommander | ||
163 | } | ||
164 | ``` | ||
165 | </TabItem> | ||
166 | </Tabs> | ||
167 | |||
168 | ### Building applications | ||
169 | |||
170 | You can use the built-in [`application`](https://docs.gradle.org/current/userguide/application_plugin.html) to build stand-alone Java applications. | ||
171 | |||
172 | When developing you main application code in the `src/main/java` directory of you project, you can use the [`StandaloneRefinery`](pathname://../javadoc/refinery-generator/tools/refinery/generator/standalone/StandaloneRefinery.html) class from [`tools.refinery:refinery-generator`](pathname://../javadoc/refinery-generator/) to access Refinery generator components. See the tutorial on Xtext's [dependency injection](https://eclipse.dev/Xtext/documentation/302_configuration.html#dependency-injection) for more advanced use-cases. | ||
173 | |||
174 | ```java | ||
175 | package org.example; | ||
176 | |||
177 | import tools.refinery.generator.standalone.StandaloneRefinery; | ||
178 | |||
179 | import java.io.IOException; | ||
180 | |||
181 | public class ExampleMain { | ||
182 | public static void main(String[] args) throws IOException { | ||
183 | var problem = StandaloneRefinery.getProblemLoader().loadString(""" | ||
184 | class Filesystem { | ||
185 | contains Directory[1] root | ||
186 | } | ||
187 | |||
188 | class File. | ||
189 | |||
190 | class Directory extends File { | ||
191 | contains Directory[] children | ||
192 | } | ||
193 | |||
194 | scope Filesystem = 1, File = 20. | ||
195 | """); | ||
196 | var generator = StandaloneRefinery.getGeneratorFactory().createGenerator(problem); | ||
197 | generator.generate(); | ||
198 | var trace = generator.getProblemTrace(); | ||
199 | var childrenRelation = trace.getPartialRelation("Directory::children"); | ||
200 | var childrenInterpretation = generator.getPartialInterpretation(childrenRelation); | ||
201 | var cursor = childrenInterpretation.getAll(); | ||
202 | while (cursor.move()) { | ||
203 | System.out.printf("%s: %s%n", cursor.getKey(), cursor.getValue()); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | ``` | ||
208 | |||
209 | If you want to produce a "fat JAR" that embeds all dependencies (e.g., for invoking from the command line or from Python with a single command), you should also add the [shadow](https://github.com/Goooler/shadow) plugin. | ||
210 | The recommended version of the shadow plugin is set in our [version catalog](#declaring-dependencies). You can add it to your build script as follows: | ||
211 | |||
212 | <Tabs groupId="gradleLanguage"> | ||
213 | <TabItem value="kotlin" label="Kotlin"> | ||
214 | ```kotlin title="build.gradle.kts" | ||
215 | plugins { | ||
216 | application | ||
217 | alias(refinery.plugins.shadow) | ||
218 | } | ||
219 | |||
220 | application { | ||
221 | mainClass = "org.example.ExampleMain" | ||
222 | } | ||
223 | ``` | ||
224 | </TabItem> | ||
225 | <TabItem value="groovy" label="Groovy"> | ||
226 | ```groovy title="build.gradle" | ||
227 | plugins { | ||
228 | application | ||
229 | alias refinery.plugins.shadow | ||
230 | } | ||
231 | |||
232 | application { | ||
233 | mainClass 'org.example.ExampleMain' | ||
234 | } | ||
235 | ``` | ||
236 | </TabItem> | ||
237 | </Tabs> | ||
238 | |||
239 | After building your project with `./gradlew build`, you may find the produced "fat JAR" in the `build/libs` directory. | ||
240 | Its file name will be suffixed with `-all.jar`. | ||
241 | In you have Java 21 installed, you'll be able to run the application with the command | ||
242 | |||
243 | <Tabs groupId="posix2windows"> | ||
244 | <TabItem value="posix" label="Linux or macOS"> | ||
245 | ```bash | ||
246 | java -jar ./build/libs/example-0.0.0-SNAPSHOT-all.jar | ||
247 | ``` | ||
248 | </TabItem> | ||
249 | <TabItem value="windows" label="Windows (PowerShell)"> | ||
250 | ```bash | ||
251 | java -jar .\build\libs\example-0.0.0-SNAPSHOT-all.jar | ||
252 | ``` | ||
253 | </TabItem> | ||
254 | </Tabs> | ||
255 | |||
256 | Be sure to replace `example-0.0.0-SNAPSHOT` with the name and version of your project. | ||
257 | |||
258 | ### Writing tests | ||
259 | |||
260 | Our Gradle plugin automatically sets up [JUnit 5](https://junit.org/junit5/) for writing tests and [parameterized tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests). | ||
261 | It also sets up [Hamcrest](https://hamcrest.org/JavaHamcrest/) for writing assertions. | ||
262 | You should put your test files into the `src/test/java` directory in your projects. | ||
263 | You may run test with the commands `./gradlew test` or `./gradlew build`. | ||
264 | |||
265 | To ensure that your tests are properly isolated, you should *not* rely on the [`StandaloneRefinery`](pathname://../javadoc/refinery-generator/tools/refinery/generator/standalone/StandaloneRefinery.html) class from [`tools.refinery:refinery-generator`](pathname://../javadoc/refinery-generator/) when accessing Refinery generator components. | ||
266 | Instead, you should use Xtext's [dependency injection](https://eclipse.dev/Xtext/documentation/302_configuration.html#dependency-injection) and [unit testing](https://eclipse.dev/Xtext/documentation/103_domainmodelnextsteps.html#tutorial-unit-tests) support to instantiate the components. You'll need to add a dependency to Refinery's Xtext testing support library to your project. | ||
267 | |||
268 | <Tabs groupId="gradleLanguage"> | ||
269 | <TabItem value="kotlin" label="Kotlin"> | ||
270 | ```kotlin title="build.gradle.kts" | ||
271 | depndencies { | ||
272 | implementation(refinery.generator) | ||
273 | // highlight-next-line | ||
274 | testImplementation(testFixtures(refinery.language)) | ||
275 | } | ||
276 | ``` | ||
277 | </TabItem> | ||
278 | <TabItem value="groovy" label="Groovy"> | ||
279 | ```groovy title="build.gradle" | ||
280 | dependencies { | ||
281 | implementation refinery.generator | ||
282 | // highlight-next-line | ||
283 | testImplementation testFixtures(refinery.language) | ||
284 | } | ||
285 | ``` | ||
286 | </TabItem> | ||
287 | </Tabs> | ||
288 | |||
289 | The test fixtures for `refinery-language` include the `@InjectWithRefinery` [composed annotation](https://junit.org/junit5/docs/current/user-guide/#writing-tests-meta-annotations) to simplify Xtext injector configuration. | ||
290 | You can use this annotation in conjunction with `@Inject` annotations to set up your unit test. | ||
291 | |||
292 | ```java | ||
293 | package org.example; | ||
294 | |||
295 | import com.google.inject.Inject; | ||
296 | import org.junit.jupiter.api.Test; | ||
297 | import tools.refinery.generator.GeneratorResult; | ||
298 | import tools.refinery.generator.ModelGeneratorFactory; | ||
299 | import tools.refinery.generator.ProblemLoader; | ||
300 | import tools.refinery.language.tests.InjectWithRefinery; | ||
301 | |||
302 | import java.io.IOException; | ||
303 | |||
304 | import static org.hamcrest.MatcherAssert.assertThat; | ||
305 | import static org.hamcrest.Matchers.*; | ||
306 | |||
307 | // highlight-start | ||
308 | @InjectWithRefinery | ||
309 | // highlight-end | ||
310 | class ExampleTest { | ||
311 | // highlight-start | ||
312 | @Inject | ||
313 | private ProblemLoader problemLoader; | ||
314 | |||
315 | @Inject | ||
316 | private ModelGeneratorFactory generatorFactory; | ||
317 | // highlight-end | ||
318 | |||
319 | @Test | ||
320 | void testModelGeneration() throws IOException { | ||
321 | var problem = problemLoader.loadString(""" | ||
322 | class Filesystem { | ||
323 | contains Directory[1] root | ||
324 | } | ||
325 | |||
326 | class File. | ||
327 | |||
328 | class Directory extends File { | ||
329 | contains Directory[] children | ||
330 | } | ||
331 | |||
332 | scope Filesystem = 1, File = 20. | ||
333 | """); | ||
334 | var generator = generatorFactory.createGenerator(problem); | ||
335 | var result = generator.tryGenerate(); | ||
336 | assertThat(result, is(GeneratorResult.SUCCESS)); | ||
337 | } | ||
338 | } | ||
339 | ``` | ||
340 | |||
341 | ### Multi-module projects | ||
342 | |||
343 | By default, the `tools.refinery.settings` plugin will apply our `tools.refinery.java` plugin to all Java projects in your build and configure them for use with Refinery. This is sufficient for single-module Java projects, and multi-module projects where all of your Java modules use Refinery. | ||
344 | |||
345 | If you wish to use Refinery in only some modules in your multi-module project, you can disable this behavior by adding | ||
346 | |||
347 | ```ini title="gradle.properties" | ||
348 | tools.refinery.gradle.auto-apply=false | ||
349 | ``` | ||
350 | |||
351 | to the `gradle.properties` file in the root directory of your project. | ||
352 | |||
353 | If you use this setting, you'll need to add the `tools.refinery.java` plugin manually to any Java projects where you want to use Refinery like this: | ||
354 | |||
355 | <Tabs groupId="gradleLanguage"> | ||
356 | <TabItem value="kotlin" label="Kotlin"> | ||
357 | ```kotlin title="build.gradle.kts" | ||
358 | plugins { | ||
359 | id("tools.refinery.java") | ||
360 | } | ||
361 | ``` | ||
362 | </TabItem> | ||
363 | <TabItem value="groovy" label="Groovy"> | ||
364 | ```groovy title="build.gradle" | ||
365 | plugins { | ||
366 | id 'tools.refinery.java' | ||
367 | } | ||
368 | ``` | ||
369 | </TabItem> | ||
370 | </Tabs> | ||
371 | |||
372 | Do *not* attempt to set a `version` for this plugin, because versioning is already managed by the `tools.refinery.settings` plugin. Trying to set a version for the `tools.refinery.java` plugin separately will result in a Gradle error. | ||
373 | |||
374 | ## Working with Maven {#maven} | ||
375 | |||
376 | You may also develop applications based on Refiney using [Apache Maven](https://maven.apache.org/) as the build system. | ||
377 | Although we don't provide a Maven plugin for simplified configuration, you can still use our [platform](https://docs.gradle.org/current/userguide/platforms.html#sub:using-platform-to-control-transitive-deps) (Maven BOM) to lock the versions of Refinery and its dependencies to tested versions. | ||
378 | |||
379 | This tutorial explains how to use a **snapshot** or **local** pre-release version of Refinery with Maven. | ||
380 | Released versions, such as the [**latest version**](/develop/java#maven), are available from [Maven Central](https://central.sonatype.com/namespace/tools.refinery). | ||
381 | |||
382 | <Tabs groupId="version"> | ||
383 | <TabItem value="snapshot" label="Snapshot" default> | ||
384 | We always publish a [SNAPSHOT](https://maven.apache.org/guides/getting-started/index.html#what-is-a-snapshot-version) version of Refinery based on the latest commit in our [Git repository](https://github.com/graphs4value/refinery). This is the development version of our code and may change without warning at any time. | ||
385 | |||
386 | You should add the following configuration to your `pom.xml` file. If you use multi-module projects, we recommend that you add this to your parent POM. | ||
387 | |||
388 | ```xml title="pom.xml" | ||
389 | <project> | ||
390 | ... | ||
391 | <repositories> | ||
392 | <repository> | ||
393 | <id>refinery-snapshots</id> | ||
394 | <name>Refinery Snapshots</name> | ||
395 | <url>https://refinery.tools/maven/snapshots/</url> | ||
396 | <releases> | ||
397 | <enabled>false</enabled> | ||
398 | </releases> | ||
399 | <snapshots> | ||
400 | <enabled>true</enabled> | ||
401 | </snapshots> | ||
402 | </repository> | ||
403 | </repositories> | ||
404 | <dependencyManagement> | ||
405 | <dependencies> | ||
406 | <dependency> | ||
407 | <groupId>tools.refinery</groupId> | ||
408 | <artifactId>refinery-bom</artifactId> | ||
409 | <version>@@@version@@@</version> | ||
410 | <type>pom</type> | ||
411 | <scope>import</scope> | ||
412 | </dependency> | ||
413 | </dependencies> | ||
414 | </dependencyManagement> | ||
415 | ... | ||
416 | </project> | ||
417 | ``` | ||
418 | </TabItem> | ||
419 | <TabItem value="mavenLocal" label="Local"> | ||
420 | Running Refinery from a local build is an _advanced technique_ that you should only use if you want to [contribute to Refinery](../contributing) and have modified it yourself. | ||
421 | First you'll have to run the [`./gradlew publishToMavenLocal`](../contributing/commands#publishtomavenlocal) command in your local clone of the Refinery repository to install Refinery into your [local Maven repository](https://www.baeldung.com/maven-local-repository). | ||
422 | |||
423 | Next, you should add the following configuration to your `pom.xml` file. If you use multi-module projects, we recommend that you add this to your parent POM. | ||
424 | |||
425 | ```xml title="pom.xml" | ||
426 | <project> | ||
427 | ... | ||
428 | <dependencyManagement> | ||
429 | <dependencies> | ||
430 | <dependency> | ||
431 | <groupId>tools.refinery</groupId> | ||
432 | <artifactId>refinery-bom</artifactId> | ||
433 | <version>@@@version@@@</version> | ||
434 | <type>pom</type> | ||
435 | <scope>import</scope> | ||
436 | </dependency> | ||
437 | </dependencies> | ||
438 | </dependencyManagement> | ||
439 | ... | ||
440 | </project> | ||
441 | ``` | ||
442 | </TabItem> | ||
443 | </Tabs> | ||
444 | |||
445 | You'll be able to add dependencies to Refinery components without an explicit reference to the dependency version, since version numbers are managed by the BOM: | ||
446 | |||
447 | ```xml title="pom.xml" | ||
448 | <project> | ||
449 | ... | ||
450 | <dependencies> | ||
451 | <dependency> | ||
452 | <groupId>tools.refinery</groupId> | ||
453 | <artifactId>refinery-generator</artifactId> | ||
454 | </dependency> | ||
455 | </dependencies> | ||
456 | ... | ||
457 | </project> | ||
458 | ``` | ||
459 | |||
460 | However, since the Maven BOM doesn't offer additional configuration, you'll have to take care of tasks such as configuring logging and testing, as well as building applications yourself. | ||
diff --git a/subprojects/docs/src/docs/develop/javadoc.md b/subprojects/docs/src/docs/develop/javadoc.md new file mode 100644 index 00000000..b85825b5 --- /dev/null +++ b/subprojects/docs/src/docs/develop/javadoc.md | |||
@@ -0,0 +1,41 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: API documentation for Refinery components automatically generated by Javadoc | ||
5 | sidebar_position: 998 | ||
6 | --- | ||
7 | |||
8 | # Javadoc | ||
9 | |||
10 | Here you can find API documentation for Refinery components automatically generated by Javadoc. We recommend reading the [Programming guide](../java/) first to understand how to use these components. | ||
11 | |||
12 | # Refinery | ||
13 | |||
14 | * [`tools.refinery:refinery-generator:@@@version@@@`](pathname://refinery-generator/) | ||
15 | * [`tools.refinery:refinery-gradle-plugins:@@@version@@@`](pathname://refinery-gradle-plugins/) | ||
16 | * [`tools.refinery:refinery-language:@@@version@@@`](pathname://refinery-language/) | ||
17 | * [`tools.refinery:refinery-language-ide:@@@version@@@`](pathname://refinery-language-ide/) | ||
18 | * [`tools.refinery:refinery-language-model:@@@version@@@`](pathname://refinery-language-model/) | ||
19 | * [`tools.refinery:refinery-language-semantics:@@@version@@@`](pathname://refinery-language-semantics/) | ||
20 | * [`tools.refinery:refinery-logic:@@@version@@@`](pathname://refinery-logic/) | ||
21 | * [`tools.refinery:refinery-store:@@@version@@@`](pathname://refinery-store/) | ||
22 | * [`tools.refinery:refinery-store-dse:@@@version@@@`](pathname://refinery-store-dse/) | ||
23 | * [`tools.refinery:refinery-store-dse-visualization:@@@version@@@`](pathname://refinery-store-dse-visualization/) | ||
24 | * [`tools.refinery:refinery-store-query:@@@version@@@`](pathname://refinery-store-query/) | ||
25 | * [`tools.refinery:refinery-store-query-interpreter:@@@version@@@`](pathname://refinery-store-query-interpreter/) | ||
26 | * [`tools.refinery:refinery-store-reasoning:@@@version@@@`](pathname://refinery-store-reasoning/) | ||
27 | * [`tools.refinery:refinery-store-reasoning-scope:@@@version@@@`](pathname://refinery-store-reasoning-scope/) | ||
28 | * [`tools.refinery:refinery-store-reasoning-smt:@@@version@@@`](pathname://refinery-store-reasoning-smt/) | ||
29 | |||
30 | # Interpreter | ||
31 | |||
32 | :::note | ||
33 | |||
34 | The _Refinery Interpreter_ is modified version of [VIATRA™](https://eclipse.dev/viatra/) specifically for use in Refinery. If you're interested in learning about [VIATRA™](https://eclipse.dev/viatra/), we recommend the [VIATRA™ documentation](https://eclipse.dev/viatra/documentation/index.html) and [source code](https://github.com/eclipse-viatra/org.eclipse.viatra) instead. Eclipse®, VIATRA™ and ‘Eclipse VIATRA™’ are trademarks of Eclipse Foundation, Inc. | ||
35 | |||
36 | ::: | ||
37 | |||
38 | * [`tools.refinery.interpreter:refinery-interpreter:@@@tools.refinery.interpreter.version@@@`](pathname://refinery-interpreter/) | ||
39 | * [`tools.refinery.interpreter:refinery-interpreter-localsearch:@@@tools.refinery.interpreter.version@@@`](pathname://refinery-interpreter-localsearch/) | ||
40 | * [`tools.refinery.interpreter:refinery-interpreter-rete:@@@tools.refinery.interpreter.version@@@`](pathname://refinery-interpreter-rete/) | ||
41 | * [`tools.refinery.interpreter:refinery-interpreter-rete-recipes:@@@tools.refinery.interpreter.version@@@`](pathname://refinery-interpreter-rete-recipes/) | ||
diff --git a/subprojects/docs/src/docs/learn/docker/cli.md b/subprojects/docs/src/docs/learn/docker/cli.md new file mode 100644 index 00000000..0c3df088 --- /dev/null +++ b/subprojects/docs/src/docs/learn/docker/cli.md | |||
@@ -0,0 +1,145 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | sidebar_label: CLI | ||
6 | --- | ||
7 | |||
8 | # Command-line interface | ||
9 | |||
10 | You can run Refinery as a command-line applications via our [Docker container](https://github.com/graphs4value/refinery/pkgs/container/refinery-cli) on either `amd64` or `arm64` machines: | ||
11 | |||
12 | ```shell | ||
13 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:latest | ||
14 | ``` | ||
15 | |||
16 | This will let you read input files and generate models in the current directory (`${PWD}`) of your terminal session. | ||
17 | Module imports (e.g., `import some::module.` to import `some/module.refinery`) relative to the current directory are also supported. | ||
18 | |||
19 | For example, to generate a model based on the file named `input.problem` in the current directory and write the results into the file named `output.refinery`, you may run the [`generate` subcommand](#generate) with | ||
20 | |||
21 | ```shell | ||
22 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:latest generate -o output.refinery input.problem | ||
23 | ``` | ||
24 | |||
25 | If you want Refinery CLI to print its documentation, run | ||
26 | |||
27 | ```shell | ||
28 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:latest -help | ||
29 | ``` | ||
30 | |||
31 | ## The `generate` subcommand {#generate} | ||
32 | |||
33 | The `generate` subcommand generates a consistent concrete model from a partial model. | ||
34 | |||
35 | ```shell | ||
36 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:latest generate [options] input path | ||
37 | ``` | ||
38 | |||
39 | The `input path` should be a path to a `.problem` file relative to the current directory. | ||
40 | Due to Docker containerization, paths _outside_ the current directory (e.g., `../input.problem`) are not supported. | ||
41 | |||
42 | Passing `-` as the `input path` will read a partial model from the standard input. | ||
43 | |||
44 | By default, the generator is _deterministic_ and always outputs the same concrete model. See the [`-random-seed`](#generate-random-seed) option to customize this behavior. | ||
45 | |||
46 | See below for the list of supported `[options]`. | ||
47 | |||
48 | ### `-output`, `-o` {#generate-output} | ||
49 | |||
50 | The output path for the generated model. | ||
51 | Passing `-o -` will write the generated model to the standard output. | ||
52 | |||
53 | When generating multiple models with [`-solution-number`](#generate-solution-number), the value `-` is not supported and individual solutions will be saved to numbered files. | ||
54 | For example, if you pass `-o output.refinery -n 10`, solutions will be saved as `output_001.refinery`, `output_002.refinery`, ..., `output_010.refinery`. | ||
55 | |||
56 | **Default value:** `-`, i.e., the solution is written to the standard output. | ||
57 | |||
58 | ### `-random-seed`, `-r` {#generate-random-seed} | ||
59 | |||
60 | Random seed to control the behavior of model generation. | ||
61 | |||
62 | The same random seed value and Refinery release will produce the same output model for an input problem. | ||
63 | Models generated with different values of `-random-seed` are highly likely (but are not guaranteed) to be _substantially_ different. | ||
64 | |||
65 | **Default value:** `1` | ||
66 | |||
67 | ### `-scope`, `-s` {#generate-scope} | ||
68 | |||
69 | Add [scope constraints](../../language/logic#type-scopes) to the input problem. | ||
70 | |||
71 | This option is especially useful if you want to generate models of multiple sizes from the same partial model. | ||
72 | |||
73 | For example, the command | ||
74 | |||
75 | ```shell | ||
76 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:latest generate -s File=20..25 input.problem | ||
77 | ``` | ||
78 | |||
79 | is equivalent to appending | ||
80 | |||
81 | ```refinery title="input.problem" | ||
82 | scope File = 20..25. | ||
83 | ``` | ||
84 | |||
85 | to `input.problem`. | ||
86 | The syntax of the argument is equivalent to the [`scope`](../../language/logic#type-scopes) declaration, but you be careful with the handling of spaces in your shell. | ||
87 | Any number of `-s` arguments are supported. For example, the following argument lists are equivalent: | ||
88 | |||
89 | ```shell | ||
90 | -scope File=20..25,Directory=3 | ||
91 | -s File=20..25,Directory=3 | ||
92 | -s File=20..25 -s Directory=3 | ||
93 | -s "File = 20..25, Directory = 3" | ||
94 | -s "File = 20..25" -s "Directory = 3" | ||
95 | ``` | ||
96 | |||
97 | The `*` opeator also has to be quoted to avoid shell expansion: | ||
98 | |||
99 | ```shell | ||
100 | -s "File=20..*" | ||
101 | ``` | ||
102 | |||
103 | ### `-scope-override`, `-S` {#generate-scope-override} | ||
104 | |||
105 | Override [scope constraints](../../language/logic#type-scopes) to the input problem. | ||
106 | |||
107 | This argument is similar to [`-scope`](#generate-scope), but has higher precedence than the [`scope`](../../language/logic#type-scopes) declarations already present in the input file. | ||
108 | However, you can't override `scope` declarations in modules imported in the input file using the `import` statement. | ||
109 | |||
110 | For example, if we have | ||
111 | |||
112 | ```refinery title="input.problem" | ||
113 | scope File = 20..25, Directory = 3. | ||
114 | ``` | ||
115 | |||
116 | in the input file, the arguments `-s File=10..12 input.problem` will be interpreted as | ||
117 | |||
118 | ```refinery | ||
119 | scope File = 20..25, Directory = 3. | ||
120 | scope File = 10..12. | ||
121 | ``` | ||
122 | |||
123 | which results in an _unsatisfiable_ problem. If the use `-S File=10..12 input.problem` instead, the type scope for `File` is overridden as | ||
124 | |||
125 | ```refinery | ||
126 | scope Directory = 3. | ||
127 | scope File = 10..12. | ||
128 | ``` | ||
129 | |||
130 | and model generation can proceed as requested. Since we had specified no override for `Directory`, its type scope declared in `input.problem` was preserved. | ||
131 | |||
132 | Scope overrides do not override additional scopes, i.e., `-s File=20..30 -S File=10..25` is interpreted as `-S File=20..25`. | ||
133 | |||
134 | ### `-solution-number`, `-n` {#generate-solution-number} | ||
135 | |||
136 | The number of distinct solutions to generate. | ||
137 | |||
138 | Generated solutions are always different, but are frequently not _substantially_ different, i.e., the differences between generated solutions comprise only a few model elements. | ||
139 | You'll likely generate substantially different models by calling the generator multiple times with different [`-random-seed`](#generate-random-seed) values instead. | ||
140 | |||
141 | The generator will create [numbered output files](#generate-output) for each solution found. | ||
142 | The generation is considered successful if it finds at least one solution, but may find less than the requested number of solutions if no more exist. | ||
143 | In this case, there will be fewer output files than requested. | ||
144 | |||
145 | **Default value:** `1` | ||
diff --git a/subprojects/docs/src/docs/learn/docker/index.md b/subprojects/docs/src/docs/learn/docker/index.md new file mode 100644 index 00000000..1499bea3 --- /dev/null +++ b/subprojects/docs/src/docs/learn/docker/index.md | |||
@@ -0,0 +1,165 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | autogenerated_sidebar_hidden: true | ||
5 | sidebar_position: 100 | ||
6 | sidebar_label: Docker | ||
7 | --- | ||
8 | |||
9 | # Running in Docker | ||
10 | |||
11 | :::note | ||
12 | |||
13 | Refinery can run as a cloud-based _Graph Solver as a Service_ without local installation. | ||
14 | If you're just looking to try Refinery, our [online demo](https://refinery.services/) provides a seamless experience without installation. | ||
15 | |||
16 | ::: | ||
17 | |||
18 | :::info | ||
19 | |||
20 | Installing Refinery as a Docker container can support more advanced use cases, such as when generating models with more resources or a longer timeout. | ||
21 | |||
22 | ::: | ||
23 | |||
24 | To generate larger models with a longer timeout, you can use our [Docker container](https://github.com/graphs4value/refinery/pkgs/container/refinery) on either `amd64` or `arm64` machines: | ||
25 | |||
26 | ```shell | ||
27 | docker run --rm -it -p 8888:8888 ghcr.io/graphs4value/refinery:latest | ||
28 | ``` | ||
29 | |||
30 | Once Docker pulls and starts the container, you can navigate to http://localhost:8888 to open the model generation interface and start editing. | ||
31 | |||
32 | A [command-line interface (CLI)](cli) version of Refinery is also available as a Docker container. | ||
33 | |||
34 | Alternatively, you can follow the [instructions to set up a local development environment](/develop/contributing) and compile and run Refinery from source. | ||
35 | |||
36 | ## Environmental variables | ||
37 | |||
38 | The Docker container supports the following environmental variables to customize its behavior. | ||
39 | Customizing these variables should only be needed if you want to _increase resource limits_ or _expose your Refinery instance over the network_ for others. | ||
40 | |||
41 | Notes for **local-only instances** are highlighted with the :arrow_right: arrow emoji. | ||
42 | |||
43 | Important security notices for **public instances** are highlighted with the :warning: warning emoji. | ||
44 | |||
45 | ### Networking | ||
46 | |||
47 | #### `REFINERY_LISTEN_HOST` | ||
48 | |||
49 | Hostname to listen at for incoming HTTP connections. | ||
50 | |||
51 | **Default value:** `0.0.0.0` (accepts connections on any IP address) | ||
52 | |||
53 | #### `REFINERY_LISTEN_PORT` | ||
54 | |||
55 | TCP port to listen at for incoming HTTP connections. | ||
56 | |||
57 | Refinery doesn't support HTTPS connections out of the box, so there's no point in setting this to `443`. Use a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) instead if you wish to expose Refinery to encrypted connections. | ||
58 | |||
59 | If you change this value, don't forget to adjust the `-p 8888:8888` option of the `docker run` command to [expose](https://docs.docker.com/reference/cli/docker/container/run/#publish) the selected port. | ||
60 | |||
61 | **Default value:** `8888` | ||
62 | |||
63 | #### `REFINERY_PUBLIC_HOST` | ||
64 | |||
65 | Publicly visible hostname of the Refinery instance. | ||
66 | |||
67 | :arrow_right: For installations only accessed locally (i.e., `localhost:8888`) without any reverse proxy, you can safely leave this empty. | ||
68 | |||
69 | :warning: You should set this to the publicly visible hostname of your Refinery instance if you wish to expose Refinery over the network. Most likely, this will be the hostname of a reverse proxy that terminates TLS connections. Our online demo sets this to [refinery.services](https://refinery.services/). | ||
70 | |||
71 | **Default value:** _empty_ | ||
72 | |||
73 | #### `REFINERY_PUBLIC_PORT` | ||
74 | |||
75 | Publicly visible port of the Refinery instance. | ||
76 | |||
77 | :arrow_right: For installations only accessed locally (i.e., `localhost:8888`), this value is ignored because `REFINERY_PUBLC_HOST` is not set. | ||
78 | |||
79 | **Default value:** `443` | ||
80 | |||
81 | #### `REFINERY_ALLOWED_ORIGINS` | ||
82 | |||
83 | Comma-separated list of allowed origins for incoming WebSocket connections. If this variable is empty, all incoming WebSocket connections are accepted. | ||
84 | |||
85 | :arrow_right: For installations only accessed locally (i.e., `localhost:8888`) without any reverse proxy, you can safely leave this empty. | ||
86 | |||
87 | :warning: The value inferred from `REFINERY_PUBLIC_HOST` and `REFINERY_PUBLIC_PORT` should be suitable for instances exposed over the network. For security reasons, public instances should never leave this empty. | ||
88 | |||
89 | **Default value:** equal to `REFINERY_PUBLIC_HOST:REFINERY_PUBLIC_PORT` if they are both set, _empty_ otherwise | ||
90 | |||
91 | ### Timeouts | ||
92 | |||
93 | #### `REFINERY_SEMANTICS_TIMEOUT_MS` | ||
94 | |||
95 | Timeout for partial model semantics calculation in milliseconds. | ||
96 | |||
97 | :arrow_right: Increase this if you have a slower machine and the editor times out before showing a preview of your partial model in the _Graph_ or _Table_ views. | ||
98 | |||
99 | :warning: Increasing this timeout may increase server load. Excessively large timeout may allow users to overload you server by entering extremely complex partial models. | ||
100 | |||
101 | **Default value:** `1000` | ||
102 | |||
103 | #### `REFINERY_SEMANTICS_WARMUP_TIMEOUT_MS` | ||
104 | |||
105 | Timeout for partial model semantics calculation in milliseconds when the server first start. | ||
106 | |||
107 | Due to various initialization tasks, the first partial model semantics generation may take longer the `REFINERY_SEMANTICS_TIMEOUT_MS` and display a timeout error. This setting increases the timeout for the first generation, leading to seamless use even after server start (especially in auto-scaling setups). | ||
108 | |||
109 | **Default value:** equal to 2 × `REFINERY_SEMANTICS_TIMEOUT` | ||
110 | |||
111 | #### `REFINERY_MODEL_GENERATION_TIMEOUT_SEC` | ||
112 | |||
113 | Timeout for model generation in seconds. | ||
114 | |||
115 | :arrow_right: Adjust this value if you're generating very large models (> 10000 nodes) and need more time to complete a generation. Note that some _unsatisfiable_ model generation problems cannot be detected by Refinery and will result in model generation running for an arbitrarily long time without producing any solution. | ||
116 | |||
117 | :warning: Long running model generation will block a [_model generation thread_](#refinery_model_generation_thread_count). Try to balance the number of threads and the timeout to avoid exhausting system resources, but keep the wait time for a free model generation thread for users reasonably short. Auto-scaling to multiple instances may help with bursty demand. | ||
118 | |||
119 | **Default value:** `600` (10 minutes) | ||
120 | |||
121 | ### Threading | ||
122 | |||
123 | :arrow_right: If you only run a single model generation task at a time, you don't need to adjust these settings. | ||
124 | |||
125 | :warning: Excessively large thread counts may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. | ||
126 | |||
127 | #### `REFINERY_XTEXT_THREAD_COUNT` | ||
128 | |||
129 | Number of threads used for non-blocking text editing operations. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | ||
130 | |||
131 | **Default value:** `1` | ||
132 | |||
133 | #### `REFINERY_XTEXT_LOCKING_THREAD_COUNT` | ||
134 | |||
135 | Number of threads used for text editing operations that lock the document. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | ||
136 | |||
137 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` | ||
138 | |||
139 | #### `REFINERY_XTEXT_SEMANTICS_THREAD_COUNT` | ||
140 | |||
141 | Number of threads used for model semantics calculation. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | ||
142 | |||
143 | Must be at least as large as `REFINERY_XTEXT_THREAD_COUNT`. | ||
144 | |||
145 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` | ||
146 | |||
147 | #### `REFINERY_MODEL_GENERATION_THREAD_COUNT` | ||
148 | |||
149 | Number of threads used for model semantics calculation. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | ||
150 | |||
151 | :warning: Each model generation task may also demand a large amount of memory in addition to CPU time. | ||
152 | |||
153 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` | ||
154 | |||
155 | ### Libraries | ||
156 | |||
157 | #### `REFINERY_LIBRARY_PATH` | ||
158 | |||
159 | Modules (`.refinery` files) in this directory or colon-separated list of directories will be exposed to user via Refinery's `import` mechanism. | ||
160 | |||
161 | :arrow_right: Use this in conjunction with the [mount volume (-v)](https://docs.docker.com/reference/cli/docker/container/run/#volume) option of `docker run` to work with multi-file projects in Refinery. | ||
162 | |||
163 | :warning: Only expose files that you want to make public. It's best to expose a directory that contains nothing other than `.refinery` files to minimize potential information leaks. | ||
164 | |||
165 | **Default value:** _empty_ (no directories are exposed) | ||
diff --git a/subprojects/docs/src/docs/learn/index.md b/subprojects/docs/src/docs/learn/index.md new file mode 100644 index 00000000..7f67fd86 --- /dev/null +++ b/subprojects/docs/src/docs/learn/index.md | |||
@@ -0,0 +1,11 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 0 | ||
5 | --- | ||
6 | |||
7 | # Introduction | ||
8 | |||
9 | Various software and systems engineering scenarios rely on the systematic construction of consistent graph models. However, **automatically generating a diverse set of consistent graph models** for complex domain specifications is challenging. First, the graph generation problem must be specified with mathematical precision. Moreover, graph generation is a computationally complex task, which necessitates specialized logic solvers. | ||
10 | |||
11 | **Refinery is an open-source software framework** for the automated synthesis of a diverse set of consistent domain-specific graph models. The framework offers an expressive high-level specification language using partial models to succinctly formulate a wide range of graph generation challenges. It also provides a modern cloud-based architecture for a scalable _Graph Solver as a Service,_ which uses logic reasoning rules to efficiently synthesize a diverse set of solutions to graph generation problems by partial model refinement. Applications include system-level architecture synthesis, test generation for modeling tools or traffic scenario synthesis for autonomous vehicles. | ||
diff --git a/subprojects/docs/src/learn/language/_category_.yml b/subprojects/docs/src/docs/learn/language/_category_.yml index f5a6f896..a261ebf6 100644 --- a/subprojects/docs/src/learn/language/_category_.yml +++ b/subprojects/docs/src/docs/learn/language/_category_.yml | |||
@@ -6,5 +6,5 @@ position: 2 | |||
6 | label: Language reference | 6 | label: Language reference |
7 | link: | 7 | link: |
8 | type: generated-index | 8 | type: generated-index |
9 | slug: /language | 9 | slug: /learn/language |
10 | description: Learn more about the Refinery partial modeling language! | 10 | description: Learn more about the Refinery partial modeling language! |
diff --git a/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg b/subprojects/docs/src/docs/learn/language/classes/ContainmentInstance.svg index 197f4b48..197f4b48 100644 --- a/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg +++ b/subprojects/docs/src/docs/learn/language/classes/ContainmentInstance.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg.license b/subprojects/docs/src/docs/learn/language/classes/ContainmentInstance.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/ContainmentInstance.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/ContainmentInstance.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/InvalidInstance.svg b/subprojects/docs/src/docs/learn/language/classes/InvalidInstance.svg index fb9dd37d..fb9dd37d 100644 --- a/subprojects/docs/src/learn/language/classes/InvalidInstance.svg +++ b/subprojects/docs/src/docs/learn/language/classes/InvalidInstance.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/InvalidInstance.svg.license b/subprojects/docs/src/docs/learn/language/classes/InvalidInstance.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/InvalidInstance.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/InvalidInstance.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg b/subprojects/docs/src/docs/learn/language/classes/MultiplicityConstraintsInstance.svg index b28c295a..b28c295a 100644 --- a/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg +++ b/subprojects/docs/src/docs/learn/language/classes/MultiplicityConstraintsInstance.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg.license b/subprojects/docs/src/docs/learn/language/classes/MultiplicityConstraintsInstance.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/MultiplicityConstraintsInstance.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/MultiplicityConstraintsInstance.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg b/subprojects/docs/src/docs/learn/language/classes/NewObjectsSimple.svg index 95ba8def..95ba8def 100644 --- a/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg +++ b/subprojects/docs/src/docs/learn/language/classes/NewObjectsSimple.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg.license b/subprojects/docs/src/docs/learn/language/classes/NewObjectsSimple.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/NewObjectsSimple.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/NewObjectsSimple.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg b/subprojects/docs/src/docs/learn/language/classes/NewObjectsWithInheritance.svg index cdf365f0..cdf365f0 100644 --- a/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg +++ b/subprojects/docs/src/docs/learn/language/classes/NewObjectsWithInheritance.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg.license b/subprojects/docs/src/docs/learn/language/classes/NewObjectsWithInheritance.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/NewObjectsWithInheritance.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/NewObjectsWithInheritance.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeInstance.svg index 56a4d956..56a4d956 100644 --- a/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg +++ b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeInstance.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg.license b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeInstance.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/ReferencesOppositeInstance.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeInstance.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeSelf.svg index 81ab4a0c..81ab4a0c 100644 --- a/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg +++ b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeSelf.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg.license b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeSelf.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/ReferencesOppositeSelf.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/ReferencesOppositeSelf.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg b/subprojects/docs/src/docs/learn/language/classes/ReferencesSimple.svg index fac74815..fac74815 100644 --- a/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg +++ b/subprojects/docs/src/docs/learn/language/classes/ReferencesSimple.svg | |||
diff --git a/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg.license b/subprojects/docs/src/docs/learn/language/classes/ReferencesSimple.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/classes/ReferencesSimple.svg.license +++ b/subprojects/docs/src/docs/learn/language/classes/ReferencesSimple.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/classes/index.md b/subprojects/docs/src/docs/learn/language/classes/index.md index 73108039..18cbbf9f 100644 --- a/subprojects/docs/src/learn/language/classes/index.md +++ b/subprojects/docs/src/docs/learn/language/classes/index.md | |||
@@ -129,7 +129,7 @@ import ReferencesOppositeSelf from './ReferencesOppositeSelf.svg'; | |||
129 | 129 | ||
130 | ### Multiplicity | 130 | ### Multiplicity |
131 | 131 | ||
132 | _Multiplicity constrains_ can be provided after the reference type in square braces. | 132 | _Multiplicity constraints_ can be provided after the reference type in square braces. |
133 | They specify how many _outgoing_ references should exist for any given instance of the class. | 133 | They specify how many _outgoing_ references should exist for any given instance of the class. |
134 | 134 | ||
135 | :::info | 135 | :::info |
@@ -150,7 +150,7 @@ If the multiplicity constraint is omitted, the bound `[0..1]` is assumed. | |||
150 | 150 | ||
151 | In the following model, the node `v1` satisfies all multiplicity constraints of `outgoingTransition`. | 151 | In the following model, the node `v1` satisfies all multiplicity constraints of `outgoingTransition`. |
152 | The node `v2` violates the lower bound constraint, while `v3` violates the upper bound constraint. | 152 | The node `v2` violates the lower bound constraint, while `v3` violates the upper bound constraint. |
153 | All `Transition` instances satisfy the multiplicity constrains associated with `source`. | 153 | All `Transition` instances satisfy the multiplicity constraints associated with `source`. |
154 | 154 | ||
155 | ```refinery | 155 | ```refinery |
156 | class Vertex { | 156 | class Vertex { |
@@ -209,4 +209,5 @@ import ContainmentInstance from './ContainmentInstance.svg'; | |||
209 | 209 | ||
210 | <ContainmentInstance /> | 210 | <ContainmentInstance /> |
211 | 211 | ||
212 | Containment edges form trees, while non-containment references, such as `target`, may point across the containment hierarchy. | 212 | Containment edges form must form a forest. |
213 | In contrast, non-containment references, such as `target`, may cross the containment hierarchy. | ||
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsError.svg b/subprojects/docs/src/docs/learn/language/logic/AssertionsError.svg index 8ddc65f3..8ddc65f3 100644 --- a/subprojects/docs/src/learn/language/logic/AssertionsError.svg +++ b/subprojects/docs/src/docs/learn/language/logic/AssertionsError.svg | |||
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsError.svg.license b/subprojects/docs/src/docs/learn/language/logic/AssertionsError.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/logic/AssertionsError.svg.license +++ b/subprojects/docs/src/docs/learn/language/logic/AssertionsError.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsExample.svg b/subprojects/docs/src/docs/learn/language/logic/AssertionsExample.svg index 26b3d1ff..26b3d1ff 100644 --- a/subprojects/docs/src/learn/language/logic/AssertionsExample.svg +++ b/subprojects/docs/src/docs/learn/language/logic/AssertionsExample.svg | |||
diff --git a/subprojects/docs/src/learn/language/logic/AssertionsExample.svg.license b/subprojects/docs/src/docs/learn/language/logic/AssertionsExample.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/logic/AssertionsExample.svg.license +++ b/subprojects/docs/src/docs/learn/language/logic/AssertionsExample.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg b/subprojects/docs/src/docs/learn/language/logic/DefaultAssertions.svg index 2ab002bf..2ab002bf 100644 --- a/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg +++ b/subprojects/docs/src/docs/learn/language/logic/DefaultAssertions.svg | |||
diff --git a/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg.license b/subprojects/docs/src/docs/learn/language/logic/DefaultAssertions.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/logic/DefaultAssertions.svg.license +++ b/subprojects/docs/src/docs/learn/language/logic/DefaultAssertions.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/logic/MultiObjects.svg b/subprojects/docs/src/docs/learn/language/logic/MultiObjects.svg index a5232575..a5232575 100644 --- a/subprojects/docs/src/learn/language/logic/MultiObjects.svg +++ b/subprojects/docs/src/docs/learn/language/logic/MultiObjects.svg | |||
diff --git a/subprojects/docs/src/learn/language/logic/MultiObjects.svg.license b/subprojects/docs/src/docs/learn/language/logic/MultiObjects.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/logic/MultiObjects.svg.license +++ b/subprojects/docs/src/docs/learn/language/logic/MultiObjects.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/logic/ObjectScopes.svg b/subprojects/docs/src/docs/learn/language/logic/ObjectScopes.svg index 440dfb19..440dfb19 100644 --- a/subprojects/docs/src/learn/language/logic/ObjectScopes.svg +++ b/subprojects/docs/src/docs/learn/language/logic/ObjectScopes.svg | |||
diff --git a/subprojects/docs/src/learn/language/logic/ObjectScopes.svg.license b/subprojects/docs/src/docs/learn/language/logic/ObjectScopes.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/logic/ObjectScopes.svg.license +++ b/subprojects/docs/src/docs/learn/language/logic/ObjectScopes.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg b/subprojects/docs/src/docs/learn/language/logic/StrongerObjectScopes.svg index 6f988065..6f988065 100644 --- a/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg +++ b/subprojects/docs/src/docs/learn/language/logic/StrongerObjectScopes.svg | |||
diff --git a/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg.license b/subprojects/docs/src/docs/learn/language/logic/StrongerObjectScopes.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/logic/StrongerObjectScopes.svg.license +++ b/subprojects/docs/src/docs/learn/language/logic/StrongerObjectScopes.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/logic/index.md b/subprojects/docs/src/docs/learn/language/logic/index.md index e366e9b8..e366e9b8 100644 --- a/subprojects/docs/src/learn/language/logic/index.md +++ b/subprojects/docs/src/docs/learn/language/logic/index.md | |||
diff --git a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg b/subprojects/docs/src/docs/learn/language/predicates/DerivedFeature.svg index be9465b8..be9465b8 100644 --- a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg +++ b/subprojects/docs/src/docs/learn/language/predicates/DerivedFeature.svg | |||
diff --git a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg.license b/subprojects/docs/src/docs/learn/language/predicates/DerivedFeature.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg.license +++ b/subprojects/docs/src/docs/learn/language/predicates/DerivedFeature.svg.license | |||
diff --git a/subprojects/docs/src/learn/language/predicates/index.md b/subprojects/docs/src/docs/learn/language/predicates/index.md index 261054c1..261054c1 100644 --- a/subprojects/docs/src/learn/language/predicates/index.md +++ b/subprojects/docs/src/docs/learn/language/predicates/index.md | |||
diff --git a/subprojects/docs/src/learn/tutorials/_category_.yml b/subprojects/docs/src/docs/learn/tutorials/_category_.yml index adf8293f..fd563704 100644 --- a/subprojects/docs/src/learn/tutorials/_category_.yml +++ b/subprojects/docs/src/docs/learn/tutorials/_category_.yml | |||
@@ -6,6 +6,6 @@ position: 1 | |||
6 | label: Tutorials | 6 | label: Tutorials |
7 | link: | 7 | link: |
8 | type: generated-index | 8 | type: generated-index |
9 | slug: /tutorials | 9 | slug: /learn/tutorials |
10 | title: Tutorial overview | 10 | title: Tutorial overview |
11 | description: Try Refinery in practical partial modeling challenges! | 11 | description: Try Refinery in practical partial modeling challenges! |
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelDark.png b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelDark.png new file mode 100644 index 00000000..28d2b7b6 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelDark.png | |||
Binary files differ | |||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelDark.png.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig1.svg.license +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelDark.png.license | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelLight.png b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelLight.png new file mode 100644 index 00000000..fa7ae846 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelLight.png | |||
Binary files differ | |||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelLight.png.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig2.svg.license +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/filterPanelLight.png.license | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1.svg b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1.svg new file mode 100644 index 00000000..4d7376ac --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1.svg | |||
@@ -0,0 +1,254 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="399pt" height="358pt" viewBox="-6 -6 411 370" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-a18XnMFkI_RXh1dM6QtA1"><style>.refinery-a18XnMFkI_RXh1dM6QtA1 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node .node-outline{stroke:#19202b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node .node-header{fill:rgb(53, 161, 173);}.refinery-a18XnMFkI_RXh1dM6QtA1 .node .node-bg{fill:#fff;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-i .node-header{fill:#98c379;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge .edge-line{stroke:#19202b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge .edge-arrow{fill:#19202b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge-UNKNOWN text{fill:#696c77;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge-ERROR text{fill:#ca1243;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-a18XnMFkI_RXh1dM6QtA1 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-a18XnMFkI_RXh1dM6QtA1 .icon-TRUE{fill:#19202b;}.refinery-a18XnMFkI_RXh1dM6QtA1 .icon-UNKNOWN{fill:#696c77;}.refinery-a18XnMFkI_RXh1dM6QtA1 .icon-ERROR{fill:#ca1243;}.refinery-a18XnMFkI_RXh1dM6QtA1 text.label-UNKNOWN{fill:#696c77;}.refinery-a18XnMFkI_RXh1dM6QtA1 text.label-ERROR{fill:#ca1243;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE text:not(.label-ERROR){fill:#696c77;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE .node-outline{stroke:#696c77;stroke-dasharray:2 4;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE .node-header{fill:#fff;}.refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE .icon-TRUE{fill:#696c77;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 text.label-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE text:not(.label-ERROR){fill:#abb2bf;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE .node-outline{stroke:#abb2bf;stroke-dasharray:2 4;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE .node-header{fill:#282c34;}[data-theme="dark"] .refinery-a18XnMFkI_RXh1dM6QtA1 .node-exists-FALSE .icon-TRUE{fill:#abb2bf;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-a18XnMFkI_RXh1dM6QtA1-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-a18XnMFkI_RXh1dM6QtA1-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="scale(1 1) rotate(0) translate(4 354)"> | ||
4 | <!-- n4 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-g"> | ||
6 | |||
7 | <rect stroke="none" x="31.19" y="-350" width="86.62" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="27" y="-354" width="94" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="42.82" y="-334.2" font-size="12.00">filesystem1</text> | ||
10 | <use x="37.1885" y="-320.40000000000003" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="53.19" y="-310.8" font-size="12.00" class="label label-TRUE">Filesystem</text> | ||
12 | </g> | ||
13 | <polyline points="31.19,-326.6 117.81,-326.6" class="node-outline"/> | ||
14 | <rect fill="none" x="31.19" y="-350" width="86.62" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-0"><rect stroke="none" x="31.19" y="-350" width="86.62" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n8 --> | ||
17 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-n"> | ||
18 | |||
19 | <rect stroke="none" x="38" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="34" y="-269" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="63.43" y="-249.4" font-size="12.00">dir2</text> | ||
22 | <use x="44" y="-235.8" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="59.74" y="-227" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
24 | </g> | ||
25 | <use x="44" y="-219.8" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
26 | <g><text text-anchor="start" x="60" y="-210" font-size="12.00" class="label label-TRUE">Dir</text> | ||
27 | </g> | ||
28 | <polyline points="38,-241.8 111,-241.8" class="node-outline"/> | ||
29 | <rect fill="none" x="38" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-outline"/> | ||
30 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-1"><rect stroke="none" x="38" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
31 | <!-- n4->n8 --> | ||
32 | <g class="edge edge-TRUE"> | ||
33 | |||
34 | <path fill="none" stroke-width="2" d="M74.5,-301.27C74.5,-293.78 74.5,-285.25 74.5,-276.82" class="edge-line"/> | ||
35 | <polygon stroke-width="2" points="77.56,-276.89 74.5,-268.14 71.44,-276.89 77.56,-276.89" class="edge-line edge-arrow"/> | ||
36 | <text text-anchor="start" x="52.17" y="-287.69" font-weight="bold" font-size="10.50">root</text> | ||
37 | </g> | ||
38 | <!-- n5 --> | ||
39 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-g"> | ||
40 | |||
41 | <rect stroke="none" x="175.19" y="-350" width="86.62" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
42 | <rect stroke="none" x="171" y="-354" width="94" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-2)" class="node-header"/> | ||
43 | <text text-anchor="start" x="186.82" y="-334.2" font-size="12.00">filesystem2</text> | ||
44 | <use x="181.188" y="-320.40000000000003" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
45 | <g><text text-anchor="start" x="197.19" y="-310.8" font-size="12.00" class="label label-TRUE">Filesystem</text> | ||
46 | </g> | ||
47 | <polyline points="175.19,-326.6 261.81,-326.6" class="node-outline"/> | ||
48 | <rect fill="none" x="175.19" y="-350" width="86.62" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
49 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-2"><rect stroke="none" x="175.19" y="-350" width="86.62" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
50 | <!-- n6 --> | ||
51 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-n"> | ||
52 | |||
53 | <rect stroke="none" x="182" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-bg"/> | ||
54 | <rect stroke="none" x="178" y="-269" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-3)" class="node-header"/> | ||
55 | <text text-anchor="start" x="207.43" y="-249.4" font-size="12.00">dir1</text> | ||
56 | <use x="188" y="-235.8" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
57 | <g><text text-anchor="start" x="203.74" y="-227" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
58 | </g> | ||
59 | <use x="188" y="-219.8" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
60 | <g><text text-anchor="start" x="204" y="-210" font-size="12.00" class="label label-TRUE">Dir</text> | ||
61 | </g> | ||
62 | <polyline points="182,-241.8 255,-241.8" class="node-outline"/> | ||
63 | <rect fill="none" x="182" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-outline"/> | ||
64 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-3"><rect stroke="none" x="182" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
65 | <!-- n5->n6 --> | ||
66 | <g class="edge edge-TRUE"> | ||
67 | |||
68 | <path fill="none" stroke-width="2" d="M218.5,-301.27C218.5,-293.78 218.5,-285.25 218.5,-276.82" class="edge-line"/> | ||
69 | <polygon stroke-width="2" points="221.56,-276.89 218.5,-268.14 215.44,-276.89 221.56,-276.89" class="edge-line edge-arrow"/> | ||
70 | <text text-anchor="start" x="196.17" y="-287.69" font-weight="bold" font-size="10.50">root</text> | ||
71 | </g> | ||
72 | <!-- n7 --> | ||
73 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
74 | |||
75 | <rect stroke="none" x="182" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
76 | <rect stroke="none" x="178" y="-168" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-4)" class="node-header"/> | ||
77 | <text text-anchor="start" x="205.2" y="-149" font-size="12.00">link1</text> | ||
78 | <use x="188" y="-135.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
79 | <g><text text-anchor="start" x="203.74" y="-126.6" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
80 | </g> | ||
81 | <use x="188" y="-119.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
82 | <g><text text-anchor="start" x="204" y="-109.6" font-size="12.00" class="label label-TRUE">Link</text> | ||
83 | </g> | ||
84 | <polyline points="182,-141.4 255,-141.4" class="node-outline"/> | ||
85 | <rect fill="none" x="182" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
86 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-4"><rect stroke="none" x="182" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
87 | <!-- n6->n7 --> | ||
88 | <g class="edge edge-TRUE"> | ||
89 | |||
90 | <path fill="none" stroke-width="2" d="M205.84,-200.87C205.07,-192.98 204.83,-184.38 205.11,-176.04" class="edge-line"/> | ||
91 | <polygon stroke-width="2" points="208.15,-176.48 205.67,-167.55 202.04,-176.08 208.15,-176.48" class="edge-line edge-arrow"/> | ||
92 | <text text-anchor="start" x="158.76" y="-186.87" font-weight="bold" font-size="10.50">contents</text> | ||
93 | </g> | ||
94 | <!-- n9 --> | ||
95 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-n"> | ||
96 | |||
97 | <rect stroke="none" x="273" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
98 | <rect stroke="none" x="269" y="-168" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-5)" class="node-header"/> | ||
99 | <text text-anchor="start" x="298.43" y="-149" font-size="12.00">dir3</text> | ||
100 | <use x="279" y="-135.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
101 | <g><text text-anchor="start" x="294.74" y="-126.6" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
102 | </g> | ||
103 | <use x="279" y="-119.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
104 | <g><text text-anchor="start" x="295" y="-109.6" font-size="12.00" class="label label-TRUE">Dir</text> | ||
105 | </g> | ||
106 | <polyline points="273,-141.4 346,-141.4" class="node-outline"/> | ||
107 | <rect fill="none" x="273" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
108 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-5"><rect stroke="none" x="273" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
109 | <!-- n6->n9 --> | ||
110 | <g class="edge edge-TRUE"> | ||
111 | |||
112 | <path fill="none" stroke-width="2" d="M240.96,-200.87C248.74,-191.99 257.7,-182.19 266.53,-172.89" class="edge-line"/> | ||
113 | <polygon stroke-width="2" points="268.62,-175.14 272.48,-166.71 264.21,-170.9 268.62,-175.14" class="edge-line edge-arrow"/> | ||
114 | <text text-anchor="start" x="211.19" y="-186.75" font-weight="bold" font-size="10.50">contents</text> | ||
115 | </g> | ||
116 | <!-- n7->n6 --> | ||
117 | <g class="edge edge-TRUE"> | ||
118 | |||
119 | <path fill="none" d="M218.5,-164.53C218.5,-172.49 218.5,-181.2 218.5,-189.63" class="edge-line"/> | ||
120 | <polygon points="215,-189.35 218.5,-199.35 222,-189.35 215,-189.35" class="edge-line edge-arrow"/> | ||
121 | <text text-anchor="middle" x="202.2" y="-173.02" font-size="10.50">parent</text> | ||
122 | </g> | ||
123 | <!-- n7->n6 --> | ||
124 | <g class="edge edge-TRUE"> | ||
125 | |||
126 | <path fill="none" d="M231.14,-164.53C231.92,-172.49 232.17,-181.2 231.88,-189.63" class="edge-line"/> | ||
127 | <polygon points="228.41,-189.15 231.25,-199.36 235.39,-189.6 228.41,-189.15" class="edge-line edge-arrow"/> | ||
128 | <text text-anchor="middle" x="246.59" y="-173.02" font-size="10.50">target</text> | ||
129 | </g> | ||
130 | <!-- n12 --> | ||
131 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
132 | |||
133 | <rect stroke="none" x="0" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
134 | <rect stroke="none" x="-4" y="-168" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-6)" class="node-header"/> | ||
135 | <text text-anchor="start" x="24.63" y="-149" font-size="12.00">file3</text> | ||
136 | <use x="6" y="-135.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
137 | <g><text text-anchor="start" x="21.74" y="-126.6" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
138 | </g> | ||
139 | <use x="6" y="-119.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
140 | <g><text text-anchor="start" x="22" y="-109.6" font-size="12.00" class="label label-TRUE">File</text> | ||
141 | </g> | ||
142 | <polyline points="0,-141.4 73,-141.4" class="node-outline"/> | ||
143 | <rect fill="none" x="0" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
144 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-6"><rect stroke="none" x="0" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
145 | <!-- n8->n12 --> | ||
146 | <g class="edge edge-TRUE"> | ||
147 | |||
148 | <path fill="none" stroke-width="2" d="M56.15,-200.87C52.64,-192.8 49.12,-183.98 45.99,-175.47" class="edge-line"/> | ||
149 | <polygon stroke-width="2" points="48.93,-174.62 43.13,-167.39 43.16,-176.66 48.93,-174.62" class="edge-line edge-arrow"/> | ||
150 | <text text-anchor="start" x="2.47" y="-186.85" font-weight="bold" font-size="10.50">contents</text> | ||
151 | </g> | ||
152 | <!-- n13 --> | ||
153 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
154 | |||
155 | <rect stroke="none" x="91" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
156 | <rect stroke="none" x="87" y="-168" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-7)" class="node-header"/> | ||
157 | <text text-anchor="start" x="115.63" y="-149" font-size="12.00">file4</text> | ||
158 | <use x="97" y="-135.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
159 | <g><text text-anchor="start" x="112.74" y="-126.6" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
160 | </g> | ||
161 | <use x="97" y="-119.4" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
162 | <g><text text-anchor="start" x="113" y="-109.6" font-size="12.00" class="label label-TRUE">File</text> | ||
163 | </g> | ||
164 | <polyline points="91,-141.4 164,-141.4" class="node-outline"/> | ||
165 | <rect fill="none" x="91" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
166 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-7"><rect stroke="none" x="91" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
167 | <!-- n8->n13 --> | ||
168 | <g class="edge edge-TRUE"> | ||
169 | |||
170 | <path fill="none" stroke-width="2" d="M84.94,-200.87C89.03,-192.53 93.84,-183.39 98.75,-174.61" class="edge-line"/> | ||
171 | <polygon stroke-width="2" points="101.32,-176.27 103.02,-167.16 96.01,-173.23 101.32,-176.27" class="edge-line edge-arrow"/> | ||
172 | <text text-anchor="start" x="94.42" y="-174.04" font-weight="bold" font-size="10.50">contents</text> | ||
173 | </g> | ||
174 | <!-- n9->n6 --> | ||
175 | <g class="edge edge-TRUE"> | ||
176 | |||
177 | <path fill="none" d="M287.21,-164.53C279.37,-173.49 270.32,-183.39 261.4,-192.78" class="edge-line"/> | ||
178 | <polygon points="259.08,-190.14 254.67,-199.77 264.12,-194.99 259.08,-190.14" class="edge-line edge-arrow"/> | ||
179 | <text text-anchor="middle" x="286.89" y="-186.12" font-size="10.50">parent</text> | ||
180 | </g> | ||
181 | <!-- n10 --> | ||
182 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
183 | |||
184 | <rect stroke="none" x="227" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
185 | <rect stroke="none" x="223" y="-68" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-8)" class="node-header"/> | ||
186 | <text text-anchor="start" x="251.63" y="-48.6" font-size="12.00">file1</text> | ||
187 | <use x="233" y="-35" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
188 | <g><text text-anchor="start" x="248.74" y="-26.2" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
189 | </g> | ||
190 | <use x="233" y="-19" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
191 | <g><text text-anchor="start" x="249" y="-9.2" font-size="12.00" class="label label-TRUE">File</text> | ||
192 | </g> | ||
193 | <polyline points="227,-41 300,-41" class="node-outline"/> | ||
194 | <rect fill="none" x="227" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
195 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-8"><rect stroke="none" x="227" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
196 | <!-- n9->n10 --> | ||
197 | <g class="edge edge-TRUE"> | ||
198 | |||
199 | <path fill="none" stroke-width="2" d="M288.62,-100.47C284.41,-92.31 280.12,-83.39 276.26,-74.78" class="edge-line"/> | ||
200 | <polygon stroke-width="2" points="279.14,-73.72 272.85,-66.91 273.52,-76.16 279.14,-73.72" class="edge-line edge-arrow"/> | ||
201 | <text text-anchor="start" x="233.56" y="-86.65" font-weight="bold" font-size="10.50">contents</text> | ||
202 | </g> | ||
203 | <!-- n11 --> | ||
204 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
205 | |||
206 | <rect stroke="none" x="318" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
207 | <rect stroke="none" x="314" y="-68" width="81" height="27" clip-path="url(#refinery-a18XnMFkI_RXh1dM6QtA1-clip-9)" class="node-header"/> | ||
208 | <text text-anchor="start" x="342.63" y="-48.6" font-size="12.00">file2</text> | ||
209 | <use x="324" y="-35" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
210 | <g><text text-anchor="start" x="339.74" y="-26.2" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
211 | </g> | ||
212 | <use x="324" y="-19" width="12" height="12" href="#refinery-a18XnMFkI_RXh1dM6QtA1-icon-TRUE" class="icon icon-TRUE"/> | ||
213 | <g><text text-anchor="start" x="340" y="-9.2" font-size="12.00" class="label label-TRUE">File</text> | ||
214 | </g> | ||
215 | <polyline points="318,-41 391,-41" class="node-outline"/> | ||
216 | <rect fill="none" x="318" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
217 | <clipPath id="refinery-a18XnMFkI_RXh1dM6QtA1-clip-9"><rect stroke="none" x="318" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
218 | <!-- n9->n11 --> | ||
219 | <g class="edge edge-TRUE"> | ||
220 | |||
221 | <path fill="none" stroke-width="2" d="M317.41,-100.47C320.78,-92.22 324.79,-83.19 328.94,-74.49" class="edge-line"/> | ||
222 | <polygon stroke-width="2" points="331.59,-76.05 332.7,-66.85 326.09,-73.35 331.59,-76.05" class="edge-line edge-arrow"/> | ||
223 | <text text-anchor="start" x="325.3" y="-73.85" font-weight="bold" font-size="10.50">contents</text> | ||
224 | </g> | ||
225 | <!-- n10->n9 --> | ||
226 | <g class="edge edge-TRUE"> | ||
227 | |||
228 | <path fill="none" d="M284.28,-64.13C288.53,-72.36 292.87,-81.39 296.77,-90.09" class="edge-line"/> | ||
229 | <polygon points="293.48,-91.3 300.67,-99.08 299.9,-88.51 293.48,-91.3" class="edge-line edge-arrow"/> | ||
230 | <text text-anchor="middle" x="276.9" y="-72.84" font-size="10.50">parent</text> | ||
231 | </g> | ||
232 | <!-- n11->n9 --> | ||
233 | <g class="edge edge-TRUE"> | ||
234 | |||
235 | <path fill="none" d="M346.67,-64.13C343.28,-72.46 339.22,-81.59 335.02,-90.38" class="edge-line"/> | ||
236 | <polygon points="332,-88.59 330.73,-99.11 338.28,-91.67 332,-88.59" class="edge-line edge-arrow"/> | ||
237 | <text text-anchor="middle" x="322.41" y="-85.64" font-size="10.50">parent</text> | ||
238 | </g> | ||
239 | <!-- n12->n8 --> | ||
240 | <g class="edge edge-TRUE"> | ||
241 | |||
242 | <path fill="none" d="M54.76,-164.53C58.31,-172.67 61.88,-181.6 65.04,-190.21" class="edge-line"/> | ||
243 | <polygon points="61.67,-191.17 68.3,-199.44 68.27,-188.84 61.67,-191.17" class="edge-line edge-arrow"/> | ||
244 | <text text-anchor="middle" x="45.83" y="-173.03" font-size="10.50">parent</text> | ||
245 | </g> | ||
246 | <!-- n13->n8 --> | ||
247 | <g class="edge edge-TRUE"> | ||
248 | |||
249 | <path fill="none" d="M117.16,-164.53C113.04,-172.95 108.18,-182.19 103.21,-191.07" class="edge-line"/> | ||
250 | <polygon points="100.28,-189.14 98.35,-199.55 106.36,-192.62 100.28,-189.14" class="edge-line edge-arrow"/> | ||
251 | <text text-anchor="middle" x="91.29" y="-186.25" font-size="10.50">parent</text> | ||
252 | </g> | ||
253 | </g> | ||
254 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig3.svg.license +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1.svg.license | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1Simplified.svg b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1Simplified.svg new file mode 100644 index 00000000..13dab2a1 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1Simplified.svg | |||
@@ -0,0 +1,188 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="301pt" height="311pt" viewBox="-6 -6 313.2200012207031 323.20001220703125" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-83G8OR--ePj0Ssqa0Eja5"><style>.refinery-83G8OR--ePj0Ssqa0Eja5 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node .node-outline{stroke:#19202b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node .node-header{fill:rgb(53, 161, 173);}.refinery-83G8OR--ePj0Ssqa0Eja5 .node .node-bg{fill:#fff;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-i .node-header{fill:#98c379;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge .edge-line{stroke:#19202b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge .edge-arrow{fill:#19202b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge-UNKNOWN text{fill:#696c77;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge-ERROR text{fill:#ca1243;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-83G8OR--ePj0Ssqa0Eja5 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-83G8OR--ePj0Ssqa0Eja5 .icon-TRUE{fill:#19202b;}.refinery-83G8OR--ePj0Ssqa0Eja5 .icon-UNKNOWN{fill:#696c77;}.refinery-83G8OR--ePj0Ssqa0Eja5 .icon-ERROR{fill:#ca1243;}.refinery-83G8OR--ePj0Ssqa0Eja5 text.label-UNKNOWN{fill:#696c77;}.refinery-83G8OR--ePj0Ssqa0Eja5 text.label-ERROR{fill:#ca1243;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE text:not(.label-ERROR){fill:#696c77;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE .node-outline{stroke:#696c77;stroke-dasharray:2 4;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE .node-header{fill:#fff;}.refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE .icon-TRUE{fill:#696c77;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 text.label-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE text:not(.label-ERROR){fill:#abb2bf;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE .node-outline{stroke:#abb2bf;stroke-dasharray:2 4;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE .node-header{fill:#282c34;}[data-theme="dark"] .refinery-83G8OR--ePj0Ssqa0Eja5 .node-exists-FALSE .icon-TRUE{fill:#abb2bf;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-83G8OR--ePj0Ssqa0Eja5-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-83G8OR--ePj0Ssqa0Eja5-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 307.20001220703125)"> | ||
4 | <!-- n4 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-g"> | ||
6 | |||
7 | <rect stroke="none" x="9.41" y="-303.2" width="86.62" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="5" y="-307" width="94" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="21.04" y="-287.4" font-size="12.00">filesystem1</text> | ||
10 | <use x="15.4093" y="-273.6" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="31.41" y="-264" font-size="12.00" class="label label-TRUE">Filesystem</text> | ||
12 | </g> | ||
13 | <polyline points="9.41,-279.8 96.03,-279.8" class="node-outline"/> | ||
14 | <rect fill="none" x="9.41" y="-303.2" width="86.62" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-0"><rect stroke="none" x="9.41" y="-303.2" width="86.62" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n8 --> | ||
17 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-n"> | ||
18 | |||
19 | <rect stroke="none" x="30.38" y="-218.4" width="44.68000000000001" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="26" y="-222" width="52" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="41.65" y="-202.6" font-size="12.00">dir2</text> | ||
22 | <use x="36.38" y="-188.8" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="52.38" y="-179.2" font-size="12.00" class="label label-TRUE">Dir</text> | ||
24 | </g> | ||
25 | <polyline points="30.38,-195 75.06,-195" class="node-outline"/> | ||
26 | <rect fill="none" x="30.38" y="-218.4" width="44.68000000000001" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
27 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-1"><rect stroke="none" x="30.38" y="-218.4" width="44.68000000000001" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
28 | <!-- n4->n8 --> | ||
29 | <g class="edge edge-TRUE"> | ||
30 | |||
31 | <path fill="none" stroke-width="2" d="M52.72,-254.74C52.72,-246.99 52.72,-238.18 52.72,-229.75" class="edge-line"/> | ||
32 | <polygon stroke-width="2" points="55.78,-230 52.72,-221.25 49.66,-230.01 55.78,-230" class="edge-line edge-arrow"/> | ||
33 | <text text-anchor="start" x="30.39" y="-240.67" font-weight="bold" font-size="10.50">root</text> | ||
34 | </g> | ||
35 | <!-- n5 --> | ||
36 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-g"> | ||
37 | |||
38 | <rect stroke="none" x="128.41" y="-303.2" width="86.62" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
39 | <rect stroke="none" x="124" y="-307" width="94" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-2)" class="node-header"/> | ||
40 | <text text-anchor="start" x="140.04" y="-287.4" font-size="12.00">filesystem2</text> | ||
41 | <use x="134.409" y="-273.6" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
42 | <g><text text-anchor="start" x="150.41" y="-264" font-size="12.00" class="label label-TRUE">Filesystem</text> | ||
43 | </g> | ||
44 | <polyline points="128.41,-279.8 215.03,-279.8" class="node-outline"/> | ||
45 | <rect fill="none" x="128.41" y="-303.2" width="86.62" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
46 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-2"><rect stroke="none" x="128.41" y="-303.2" width="86.62" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
47 | <!-- n6 --> | ||
48 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-n"> | ||
49 | |||
50 | <rect stroke="none" x="149.38" y="-218.4" width="44.68000000000001" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
51 | <rect stroke="none" x="145" y="-222" width="52" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-3)" class="node-header"/> | ||
52 | <text text-anchor="start" x="160.65" y="-202.6" font-size="12.00">dir1</text> | ||
53 | <use x="155.38" y="-188.8" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
54 | <g><text text-anchor="start" x="171.38" y="-179.2" font-size="12.00" class="label label-TRUE">Dir</text> | ||
55 | </g> | ||
56 | <polyline points="149.38,-195 194.06,-195" class="node-outline"/> | ||
57 | <rect fill="none" x="149.38" y="-218.4" width="44.68000000000001" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
58 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-3"><rect stroke="none" x="149.38" y="-218.4" width="44.68000000000001" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
59 | <!-- n5->n6 --> | ||
60 | <g class="edge edge-TRUE"> | ||
61 | |||
62 | <path fill="none" stroke-width="2" d="M171.72,-254.74C171.72,-246.99 171.72,-238.18 171.72,-229.75" class="edge-line"/> | ||
63 | <polygon stroke-width="2" points="174.78,-230 171.72,-221.25 168.66,-230.01 174.78,-230" class="edge-line edge-arrow"/> | ||
64 | <text text-anchor="start" x="149.39" y="-240.67" font-weight="bold" font-size="10.50">root</text> | ||
65 | </g> | ||
66 | <!-- n7 --> | ||
67 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
68 | |||
69 | <rect stroke="none" x="146.26" y="-133.6" width="50.920000000000016" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
70 | <rect stroke="none" x="142" y="-137" width="58" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-4)" class="node-header"/> | ||
71 | <text text-anchor="start" x="158.42" y="-117.8" font-size="12.00">link1</text> | ||
72 | <use x="152.257" y="-104" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
73 | <g><text text-anchor="start" x="168.26" y="-94.4" font-size="12.00" class="label label-TRUE">Link</text> | ||
74 | </g> | ||
75 | <polyline points="146.26,-110.2 197.18,-110.2" class="node-outline"/> | ||
76 | <rect fill="none" x="146.26" y="-133.6" width="50.920000000000016" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
77 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-4"><rect stroke="none" x="146.26" y="-133.6" width="50.920000000000016" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
78 | <!-- n6->n7 --> | ||
79 | <g class="edge edge-TRUE"> | ||
80 | |||
81 | <path fill="none" stroke-width="2" d="M165.6,-169.94C165.03,-162.19 164.85,-153.38 165.05,-144.95" class="edge-line"/> | ||
82 | <polygon stroke-width="2" points="168.1,-145.33 165.44,-136.45 161.98,-145.05 168.1,-145.33" class="edge-line edge-arrow"/> | ||
83 | <text text-anchor="start" x="118.73" y="-155.87" font-weight="bold" font-size="10.50">contents</text> | ||
84 | </g> | ||
85 | <!-- n9 --> | ||
86 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-n"> | ||
87 | |||
88 | <rect stroke="none" x="215.38" y="-133.6" width="44.68000000000001" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
89 | <rect stroke="none" x="211" y="-137" width="52" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-5)" class="node-header"/> | ||
90 | <text text-anchor="start" x="226.65" y="-117.8" font-size="12.00">dir3</text> | ||
91 | <use x="221.38" y="-104" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
92 | <g><text text-anchor="start" x="237.38" y="-94.4" font-size="12.00" class="label label-TRUE">Dir</text> | ||
93 | </g> | ||
94 | <polyline points="215.38,-110.2 260.06,-110.2" class="node-outline"/> | ||
95 | <rect fill="none" x="215.38" y="-133.6" width="44.68000000000001" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
96 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-5"><rect stroke="none" x="215.38" y="-133.6" width="44.68000000000001" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
97 | <!-- n6->n9 --> | ||
98 | <g class="edge edge-TRUE"> | ||
99 | |||
100 | <path fill="none" stroke-width="2" d="M190.11,-169.94C196.84,-161.48 204.6,-151.75 211.87,-142.63" class="edge-line"/> | ||
101 | <polygon stroke-width="2" points="214.26,-144.55 217.32,-135.79 209.47,-140.73 214.26,-144.55" class="edge-line edge-arrow"/> | ||
102 | <text text-anchor="start" x="204.61" y="-155.89" font-weight="bold" font-size="10.50">contents</text> | ||
103 | </g> | ||
104 | <!-- n7->n6 --> | ||
105 | <g class="edge edge-TRUE"> | ||
106 | |||
107 | <path fill="none" d="M177.86,-133.43C178.42,-141.27 178.6,-150.2 178.38,-158.72" class="edge-line"/> | ||
108 | <polygon points="174.9,-158.27 177.92,-168.42 181.89,-158.6 174.9,-158.27" class="edge-line edge-arrow"/> | ||
109 | <text text-anchor="middle" x="163.88" y="-142.02" font-size="10.50">target</text> | ||
110 | </g> | ||
111 | <!-- n7->n6 --> | ||
112 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
113 | |||
114 | <rect stroke="none" x="16.22" y="-133.6" width="47" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
115 | <rect stroke="none" x="12" y="-137" width="55" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-6)" class="node-header"/> | ||
116 | <text text-anchor="start" x="27.86" y="-117.8" font-size="12.00">file3</text> | ||
117 | <use x="22.2228" y="-104" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
118 | <g><text text-anchor="start" x="38.22" y="-94.4" font-size="12.00" class="label label-TRUE">File</text> | ||
119 | </g> | ||
120 | <polyline points="16.22,-110.2 63.22,-110.2" class="node-outline"/> | ||
121 | <rect fill="none" x="16.22" y="-133.6" width="47" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
122 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-6"><rect stroke="none" x="16.22" y="-133.6" width="47" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
123 | <!-- n12 --> | ||
124 | <g class="edge edge-TRUE"> | ||
125 | |||
126 | <path fill="none" stroke-width="2" d="M49.1,-169.94C47.88,-162.19 46.5,-153.38 45.18,-144.95" class="edge-line"/> | ||
127 | <polygon stroke-width="2" points="48.22,-144.59 43.84,-136.42 42.17,-145.54 48.22,-144.59" class="edge-line edge-arrow"/> | ||
128 | <text text-anchor="start" x="0" y="-155.87" font-weight="bold" font-size="10.50">contents</text> | ||
129 | </g> | ||
130 | <!-- n8->n12 --> | ||
131 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
132 | |||
133 | <rect stroke="none" x="81.22" y="-133.6" width="47" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
134 | <rect stroke="none" x="77" y="-137" width="55" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-7)" class="node-header"/> | ||
135 | <text text-anchor="start" x="92.86" y="-117.8" font-size="12.00">file4</text> | ||
136 | <use x="87.2228" y="-104" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
137 | <g><text text-anchor="start" x="103.22" y="-94.4" font-size="12.00" class="label label-TRUE">File</text> | ||
138 | </g> | ||
139 | <polyline points="81.22,-110.2 128.22,-110.2" class="node-outline"/> | ||
140 | <rect fill="none" x="81.22" y="-133.6" width="47" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
141 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-7"><rect stroke="none" x="81.22" y="-133.6" width="47" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
142 | <!-- n13 --> | ||
143 | <g class="edge edge-TRUE"> | ||
144 | |||
145 | <path fill="none" stroke-width="2" d="M67.21,-169.94C72.4,-161.66 78.37,-152.16 83.99,-143.21" class="edge-line"/> | ||
146 | <polygon stroke-width="2" points="86.46,-145.03 88.52,-135.99 81.28,-141.77 86.46,-145.03" class="edge-line edge-arrow"/> | ||
147 | <text text-anchor="start" x="32.41" y="-143.26" font-weight="bold" font-size="10.50">contents</text> | ||
148 | </g> | ||
149 | <!-- n8->n13 --> | ||
150 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
151 | |||
152 | <rect stroke="none" x="181.22" y="-48.8" width="47" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
153 | <rect stroke="none" x="177" y="-52" width="55" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-8)" class="node-header"/> | ||
154 | <text text-anchor="start" x="192.86" y="-33" font-size="12.00">file1</text> | ||
155 | <use x="187.223" y="-19.2" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
156 | <g><text text-anchor="start" x="203.22" y="-9.6" font-size="12.00" class="label label-TRUE">File</text> | ||
157 | </g> | ||
158 | <polyline points="181.22,-25.4 228.22,-25.4" class="node-outline"/> | ||
159 | <rect fill="none" x="181.22" y="-48.8" width="47" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
160 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-8"><rect stroke="none" x="181.22" y="-48.8" width="47" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
161 | <!-- n9->n6 --> | ||
162 | <g class="edge edge-TRUE"> | ||
163 | |||
164 | <path fill="none" stroke-width="2" d="M228.53,-85.14C225.37,-77.22 221.77,-68.18 218.34,-59.57" class="edge-line"/> | ||
165 | <polygon stroke-width="2" points="221.18,-58.43 215.1,-51.44 215.49,-60.7 221.18,-58.43" class="edge-line edge-arrow"/> | ||
166 | <text text-anchor="start" x="175.02" y="-71.05" font-weight="bold" font-size="10.50">contents</text> | ||
167 | </g> | ||
168 | <!-- n10 --> | ||
169 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
170 | |||
171 | <rect stroke="none" x="246.22" y="-48.8" width="47.00000000000003" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
172 | <rect stroke="none" x="242" y="-52" width="55" height="27" clip-path="url(#refinery-83G8OR--ePj0Ssqa0Eja5-clip-9)" class="node-header"/> | ||
173 | <text text-anchor="start" x="257.86" y="-33" font-size="12.00">file2</text> | ||
174 | <use x="252.223" y="-19.2" width="12" height="12" href="#refinery-83G8OR--ePj0Ssqa0Eja5-icon-TRUE" class="icon icon-TRUE"/> | ||
175 | <g><text text-anchor="start" x="268.22" y="-9.6" font-size="12.00" class="label label-TRUE">File</text> | ||
176 | </g> | ||
177 | <polyline points="246.22,-25.4 293.22,-25.4" class="node-outline"/> | ||
178 | <rect fill="none" x="246.22" y="-48.8" width="47.00000000000003" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
179 | <clipPath id="refinery-83G8OR--ePj0Ssqa0Eja5-clip-9"><rect stroke="none" x="246.22" y="-48.8" width="47.00000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
180 | <!-- n9->n10 --> | ||
181 | <g class="edge edge-TRUE"> | ||
182 | |||
183 | <path fill="none" stroke-width="2" d="M246.63,-85.14C249.69,-77.22 253.19,-68.18 256.52,-59.57" class="edge-line"/> | ||
184 | <polygon stroke-width="2" points="259.35,-60.72 259.65,-51.45 253.64,-58.51 259.35,-60.72" class="edge-line edge-arrow"/> | ||
185 | <text text-anchor="start" x="207.44" y="-58.45" font-weight="bold" font-size="10.50">contents</text> | ||
186 | </g> | ||
187 | </g> | ||
188 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1Simplified.svg.license index b80566a0..b80566a0 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig4.svg.license +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/generatedModel1Simplified.svg.license | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/index.md b/subprojects/docs/src/docs/learn/tutorials/file-system/index.md new file mode 100644 index 00000000..2a17a21b --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/index.md | |||
@@ -0,0 +1,173 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2023-2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: Introduction to classes, references, and error predicates | ||
5 | sidebar_position: 0 | ||
6 | sidebar_label: File system | ||
7 | --- | ||
8 | |||
9 | # File system tutorial | ||
10 | |||
11 | This tutorial gives a brief overview of the partial modeling and model generation features of the Refinery framework. | ||
12 | It follows the development and analysis of a simple Refinery problem specification for modeling file systems. | ||
13 | We adapted the case study from [Chapter 1](https://alloytools.org/tutorials/online/frame-FS-1.html) of the [Alloy tutorial][alloy]. | ||
14 | |||
15 | [alloy]: https://alloytools.org/tutorials/online/index.html | ||
16 | |||
17 | ## Describing domain concepts | ||
18 | |||
19 | The Refinery partial modeling language supports [metamodeling](../../language/classes/index.md) to describe desired structure of generated models. | ||
20 | We may use [classes](../../language/classes/index.md#classes) and [references](../../language/classes/index.md#references) to describe domain concepts similarly to [object-oriented programming languages](https://en.wikipedia.org/wiki/Object-oriented_programming), such as C++ and Java. | ||
21 | |||
22 | ```refinery checkpoint=metamodel try | ||
23 | class Filesystem { | ||
24 | contains Dir[1] root | ||
25 | } | ||
26 | |||
27 | abstract class FSObject { | ||
28 | container Dir parent opposite contents | ||
29 | } | ||
30 | |||
31 | class Dir extends FSObject { | ||
32 | contains FSObject[] contents opposite parent | ||
33 | } | ||
34 | |||
35 | class File extends FSObject. | ||
36 | |||
37 | class Link extends FSObject { | ||
38 | FSObject[1] target | ||
39 | } | ||
40 | ``` | ||
41 | |||
42 | Throughout this website, the _Try in Refinery_ button will always denote an interactive example. | ||
43 | If you click it now, it'll take you to the [Refinery web UI](#refinery-web-ui). | ||
44 | |||
45 | ### Metamodel constraints | ||
46 | |||
47 | Our specification not only lists the concepts (classes and relations) of [file system](#describing-domain-concepts) the domain, but also prescribes a set of **metamodel constraints** concisely. | ||
48 | |||
49 | :::info | ||
50 | |||
51 | Metamodel constraints are often left implicit in programming. For example, the Java runtime environment will always prevent us from instantiating an `abstract` class. | ||
52 | However, in _logical_ languages like [Alloy][alloy], we have to [specify most of these constraints](https://alloytools.org/tutorials/online/frame-FS-1.html) manually. | ||
53 | |||
54 | While Refinery has a rigorous [logical background](../../language/logic/index.md), you'll see that it still lets us define domains in high-level terms. | ||
55 | |||
56 | ::: | ||
57 | |||
58 | Some constraints are about possible instances of classes: | ||
59 | |||
60 | * The classes `Dir`, `File`, and `Link` are marked as [**subclasses**](../../language/classes/index.md#inheritance) of `FSObject` with the `extends` keyword. Instances of the classes must also be instances of `FSObject`. | ||
61 | * Conversely, the class `FSObject` is marked as an **abstract class** with the `abstract` keyword. This means that any instance of `FSObject` must also be an instance of one of its subclasses. | ||
62 | * Classes that do not have a common superclass[^multiple-inheritance] are **disjoint,** i.e., they can't have any instances in common. | ||
63 | |||
64 | [^multiple-inheritance]: The Refinery language supports _multiple inheritance,_ where a class may extend multiple superclasses. However, in this tutorial, we'll rely on single inheritance only. | ||
65 | |||
66 | Other constraints are about references: | ||
67 | |||
68 | * References between classes must adhere to **[type constraints](../../language/classes/index.md#references).** For example, the source of a `parent` relationship must be an `FSObject` instance and its target must be a `Dir` instance. | ||
69 | * There is an [**opposite constraint**](../../language/classes/index.md#opposite-constraints) between `parent` and `contents`. Every occurrence of a `parent` relationship must have a `contents` relationship in the other direction and vice versa. | ||
70 | * All references must obey the corresponding **[multiplicity constrains](../../language/classes/index.md#multiplicity):** | ||
71 | * The notation `[]` means that multiple outgoing references are allowed, i.e., a `Dir` instance may have 0 or more `contents`. If we wanted to forbid empty `Dir`s, we could do so by writing `[1..*]` instead. | ||
72 | * The notation `[1]` means that there is _exactly_ one `root` for a `Filesystem` or `target` for a `Link`. | ||
73 | * If there is no specified multiplicity, such as in the case of `parent`, 0 or 1 outgoing references are assumed. This closely matches most object-oriented programming languages, where a reference by default may be `null`. | ||
74 | * The references `root` and `contents` are marked with the keyword `contains` as **containment references** that form a **[containment hierarchy](../../language/classes/index.md#containment-hierarchy):** | ||
75 | * Instances of classes that appear as the _reference type_ of a containment reference, such as the instances of `FSObject` (and its subclasses), _must_ have an incoming containment relationship. | ||
76 | * Conversely, the instances of `Filesystem` are the **roots** of the containment hierarchy. | ||
77 | |||
78 | Notice that we could use metamodel constraints to describe most of how our domain works. For example, we don't have to further elaborate that a single file system has a single root directory and forms a tree, or that a link points to exactly one target. | ||
79 | |||
80 | You can read more about else what you can express with metamodel constraints by clicking on the links in the lists above. They'll take you to the relevant parts of the [Refinery language reference](../../language/). | ||
81 | |||
82 | ## Model generation | ||
83 | |||
84 | Model generation automatically constructs possible instance models of your problem specification. | ||
85 | You can use it to get _examples_ for reasoning about a domain, _candidate designs_ for an engineering problem, or _test cases_ for data-driven software. | ||
86 | |||
87 | Before we can start generating models, we'll need to specify the desired model size[^model-size]. In this example, we'll generate an instance which has between 10 and 20 nodes (objects) by using the following [`scope`](../../language/logic/index.md#type-scopes) declaration: | ||
88 | |||
89 | [^model-size]: If you don't specify the model size at all, Refinery will often return an _empty_ model if it can satisfy the domain constraints that way. On the other hand, you shouldn't be very strict with the desired model size (e.g., generate _exactly_ 10 nodes) either, because some constraints may be unsatisfiable for specific model sizes. | ||
90 | |||
91 | ```refinery continue try | ||
92 | scope node = 10..20. | ||
93 | ``` | ||
94 | |||
95 | You should click the button labeled _Try in Refinery_ above to open this problem specification in Refinery. | ||
96 | |||
97 | ### Refinery web UI | ||
98 | |||
99 | Since you've just opened the user interface of Refinery, this is a great time to familiarize yourself with it! | ||
100 | We annotated the following screenshot to show your the various parts of the interface. | ||
101 | |||
102 | ![Screenshot of the Refinery interface with the file system problem specification opened. Parts of the user interface are annotated with numbered callouts.](./initialModelLight.png|./initialModelDark.png) | ||
103 | |||
104 | import CloudIcon from '@material-icons/svg/svg/cloud/baseline.svg'; | ||
105 | import CloudOffIcon from '@material-icons/svg/svg/cloud_off/baseline.svg'; | ||
106 | import CodeIcon from '@material-icons/svg/svg/code/baseline.svg'; | ||
107 | import PlayArrowIcon from '@material-icons/svg/svg/play_arrow/baseline.svg'; | ||
108 | import SaveAltIcon from '@material-icons/svg/svg/save_alt/baseline.svg'; | ||
109 | import SchemaIcon from '@material-icons/svg/svg/schema/round.svg'; | ||
110 | import SyncProblemIcon from '@material-icons/svg/svg/sync_problem/baseline.svg'; | ||
111 | import TableChartIcon from '@material-icons/svg/svg/table_chart/baseline.svg'; | ||
112 | import TuneIcon from '@material-icons/svg/svg/tune/baseline.svg'; | ||
113 | |||
114 | 1. The **code editor** appears on the left side of the window by default. It lets you edit your problem specification, and provides common helper functions like auto-complete (content assist), syntax highlighting, syntax checking, and semantic validation. | ||
115 | |||
116 | 2. The **toolbar** is located above the code editor, which includes buttons for common editing operations and settings. | ||
117 | |||
118 | Moreover, you can find the <CloudIcon className="inline-icon" aria-hidden="true" /> **connection** button here. If the button shows a <CloudOffIcon className="inline-icon" aria-hidden="true" /> **disconnected** state or an <SyncProblemIcon className="inline-icon" aria-hidden="true" /> **error,** you should check your internet connection. Clicking the button will attempt to reconnect. Clicking the button _again_ will disconnect from the server, but some code editing services and the model generator require an active connection[^refinery-server]. | ||
119 | |||
120 | 3. The **graph view** shows a visualization of your problem specification. The visualization of generated models will also appear here. We'll discuss the notation used in the visualization [later in this tutorial](#partial-models). | ||
121 | |||
122 | 4. The <TuneIcon className="inline-icon" aria-hidden="true" /> **filter panel** lets you customize what should appear in the visualization. Click the button to open the panel. | ||
123 | |||
124 | 5. The <SaveAltIcon className="inline-icon" aria-hidden="true" /> **export panel** lets you save the diagram as an SVG, PDF, or PNG file. Click the button to open the panel. | ||
125 | |||
126 | 6. **Zoom controls** lat you adjust the size of the visualization and make it automatically fit the screen. | ||
127 | |||
128 | 7. The **view selector** lets you toggle the <CodeIcon className="inline-icon" aria-hidden="true" /> **code,** <SchemaIcon className="inline-icon" aria-hidden="true" /> **graph,** and <TableChartIcon className="inline-icon" aria-hidden="true" /> **table views.** You can have all three views open at the same time, or even just a single one to take a deeper look at the model. | ||
129 | |||
130 | 8. Finally, the <PlayArrowIcon className="inline-icon" aria-hidden="true" /> **generate** button initiates model generation. You may only press this button of you problem specification is valid. Otherwise, it'll jump to the validation errors in your specification in the code view. Pressing the button while model generation is running will cancel the generation. | ||
131 | |||
132 | [^refinery-server]: This doesn't mean that you always need an internet connection to use Refinery! You can also download and run our [Docker container](../../docker/index.md) to host a Refinery server on your own machine for yourself or for your organization. | ||
133 | |||
134 | ### Running the generator | ||
135 | |||
136 | You can initiate model generation by clicking the <PlayArrowIcon className="inline-icon" aria-hidden="true" /> **generate** button. | ||
137 | It should return an instance model like this: | ||
138 | |||
139 | ![First model generated from the problem specification by Refinery](./generatedModel1.svg) | ||
140 | |||
141 | Take a moment to verify that this model indeed satisfies the [metamodel constraints](#metamodel-constraints) and contains between 10 and 20 nodes as we [previously requested](#model-generation). | ||
142 | |||
143 | You can use the <TuneIcon className="inline-icon" aria-hidden="true" /> **filter panel** to simplify this visualization. | ||
144 | It is readily apparent from the [metamodel constraints](#metamodel-constraints) that all non-`Filesystem` nodes are `FSObject` instances, and the `parent` relationships always appear in `opposite` to the `contents` relationships. Thus, we can hide `FSObject` and `parent` from the visualization entirely without losing any information with the following filter settings: | ||
145 | |||
146 | ![Filter panel settings for simplified visualization. Both checkboxes near FSObject and parent are unchecked.](./filterPanelLight.png|./filterPanelDark.png) | ||
147 | |||
148 | We end up with the following visualization: | ||
149 | |||
150 | ![Simplified visualization of the first generated model](./generatedModel1Simplified.svg) | ||
151 | |||
152 | Clicking the <PlayArrowIcon className="inline-icon" aria-hidden="true" /> **generate** button will yield different models by selecting a different _random seed_ to control the model generator. | ||
153 | This means that if you want to see multiple different instance models for your problem specification, you can just run the generation multiple times. | ||
154 | |||
155 | import CloseIcon from '@material-icons/svg/svg/close/baseline.svg'; | ||
156 | |||
157 | Generated models will appear as _tabs_ in the Refinery web UI. | ||
158 | You should also take the moment to explore the tabular representations of the generated model in the <TableChartIcon className="inline-icon" aria-hidden="true" /> **table view.** | ||
159 | You can remove generated models that you no longer need by clicking the <CloseIcon className="inline-icon" aria-hidden="true" /> **close** button. | ||
160 | |||
161 | At the end of this exercise, you should be looking at something like this in Refinery: | ||
162 | |||
163 | ![Refinery user interface with multiple generated models and the table view open](./modelGenerationLight.png|./modelGenerationDark.png) | ||
164 | |||
165 | ## Partial models | ||
166 | |||
167 | :::warning | ||
168 | |||
169 | This section of the tutorial is under construction. | ||
170 | |||
171 | ::: | ||
172 | |||
173 | ![Visualization corresponding to the file system metamodel](./metamodel.svg) | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.png b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.png new file mode 100644 index 00000000..4cfa0812 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.png | |||
Binary files differ | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.png.license b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.png.license new file mode 100644 index 00000000..4265fd6c --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.png.license | |||
@@ -0,0 +1,5 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | This file was automatically generated from initialModelDark.svg. | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.svg b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.svg new file mode 100644 index 00000000..ec5b1c93 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.svg | |||
@@ -0,0 +1,216 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||
3 | |||
4 | <svg | ||
5 | version="1.1" | ||
6 | id="svg1" | ||
7 | width="1920" | ||
8 | height="1080" | ||
9 | viewBox="0 0 1920 1080" | ||
10 | sodipodi:docname="initialModelDark.svg" | ||
11 | inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" | ||
12 | inkscape:export-filename="initialModelDark.png" | ||
13 | inkscape:export-xdpi="96" | ||
14 | inkscape:export-ydpi="96" | ||
15 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||
16 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||
17 | xmlns:xlink="http://www.w3.org/1999/xlink" | ||
18 | xmlns="http://www.w3.org/2000/svg" | ||
19 | xmlns:svg="http://www.w3.org/2000/svg"> | ||
20 | <defs | ||
21 | id="defs1" /> | ||
22 | <sodipodi:namedview | ||
23 | id="namedview1" | ||
24 | pagecolor="#ffffff" | ||
25 | bordercolor="#666666" | ||
26 | borderopacity="1.0" | ||
27 | inkscape:showpageshadow="2" | ||
28 | inkscape:pageopacity="0.0" | ||
29 | inkscape:pagecheckerboard="0" | ||
30 | inkscape:deskcolor="#d1d1d1" | ||
31 | showgrid="false" | ||
32 | inkscape:zoom="1.0733371" | ||
33 | inkscape:cx="932.60546" | ||
34 | inkscape:cy="334.93673" | ||
35 | inkscape:window-width="2560" | ||
36 | inkscape:window-height="1415" | ||
37 | inkscape:window-x="0" | ||
38 | inkscape:window-y="0" | ||
39 | inkscape:window-maximized="0" | ||
40 | inkscape:current-layer="layer1" | ||
41 | showguides="true" /> | ||
42 | <g | ||
43 | inkscape:groupmode="layer" | ||
44 | inkscape:label="Image" | ||
45 | id="g1" | ||
46 | sodipodi:insensitive="true"> | ||
47 | <image | ||
48 | width="1920" | ||
49 | height="1080" | ||
50 | preserveAspectRatio="none" | ||
51 | xlink:href="data:image/png;base64,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 PheN1SVsWfMvXlq4hNKw/6JHRERERERERERETpYxevw0vdETkXNL5CU88tZPuchYzY9v+jrvKHEx cBnxJN/7INl5HTT99WEqtp9sDaIFx2XfYMRlGfg/fJKSxWdiaHsREREZSJob60lISg13GCIiIp+K MzaW1NS0ruUDB+ppa20NY0QiIiLSn6kCWERERERERERERKQfc7W14WprC3cYIiIiMkBYTtxERERE REREREREREREREQGAiWARURERERERERERERERETOEZoDWERERERERM55mgNYREQGshEjR3HB3Iu6 lld/tJKSHcVhjEhERET6M80BLCIiIiIiIiIiItKPOZ2xDB2a17VcWLA5jNGIiIhIf6choEVERERE REREREREREREzhFKAIuIiIiIiIiIiIiIiIiInCM0BLSIiIiIiIiIiIhIP5CTO4jIyMge61PT0rot p6SmMnRYXo92gUCAivJ9Zyw+ERERGRiM0eOnmeEOQkRERERERORMam6sJyEpNdxhiIiIHNett93F hEn5n7r/tq1FvPTCc30YkYiIiAxEGgJaREREREREREREpB8oLNx8Wv23bNnUR5GIiIjIQKYEsIiI iIiIiIiIiEg/ULqzBI+n/VP19Xq9lJbs6OOIREREZCDSHMAiIiIiIiIiIiIi/UAwGGTr1kKmT5/Z tW737lLK9+7t0TZ38GCGDx/ZtVy8rYhAIHA2whQREZF+TglgERERERERERERkX6iaEtBtwSwgcHy ZUt7tPvM577UbblwS8EZj01EREQGBg0BLSIiIiIiIiIiItJP7N1ThsvV1rU8dFgezpjYbm2ioqIZ NnxE17LL1cae3aVnLUYRERHp386hCmCDmEm3c+/lgzG9rRyo2MHmNavZXufDDHdoIiIiIiIiIiIi IifBNE22FGxi9py5ABiGwYT8fNau/qirzcRJk7FaDtf2FBUWEDL1FlREREQ6nVMVwIY9lvjEZFIy hzJ6xpXcft9XuWSIAyPcgYmIiIiIiIiIiIicpMKC7sM5T5w0uftyfvdlDf8sIiIiRzqHEsAmrvXP 8KMH7uexn/+OxVubMCOzmX3FDBKVARYREREREREREZEBorq6koaGA13LOTm5JCcnAxAfH8+gQYO7 trW0tFBZUX7WYxQREZH+6xxKAB8Swte8h3VvrWBfyMCaNZisyHDHJCIiIiIiIiIiInLythRs6rY8 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 AAUhAAMAAAAAAAAUhAAMAAAAAAAAUBACMAAAAAAAAEBBCMAAAAAAAAAABSEAAwAAAAAAABSEAAwA AAAAAABQEAIwAAAAAAAAQEEIwAAAAAAAAAAFIQADAAAAAAAAFIQADAAAAAAAAFAQAjAAAAAAAABA QQjAAAAAAAAAAAUhAAMAAAAAAAAUhAAMAAAAAAAAUBACMAAAAAAAAEBBCMAAAAAAAAAABSEAAwAA AAAAABSEAAwAAAAAAABQEAIwAAAAAAAAQEEIwAAAAAAAAAAFIQADAAAAAAAAFIQADAAAAAAAAFAQ lUEvAAAAAAAAAPCquPU//9GXc976vT/oyzm9cgcwAAAAAAAAQEG4AxgAAAAAAADg/wzqzt1+cQcw AAAAAAAAQEEIwAAAAAAAAAAFIQADAAAAAAAAFIQADAAAAAAAAFAQAjAAAAAAAABAQQjAAAAAAAAA AAUhAAMAAAAAAAAUhAAMAAAAAAAAUBACMAAAAAAAAEBBCMAAAAAAAAAABSEAAwAAAAAAABSEAAwA AAAAAABQEAIwAAAAAAAAQEEIwAAAAAAAAAAFIQADAAAAAAAAFIQADAAAAAAAAFAQAjAAAAAAAABA QQjAAAAAAAAAAAUhAAMAAAAAAAAUhAAMAAAAAAAAUBACMAAAAAAAAEBBCMAAAAAAAAAABSEAAwAA AAAAABSEAAwAAAAAAABQEAIwAAAAAAAAQEFUBr0AAAAAAAAAwKtmemoqE5NTmRifyOjIWEbGRlKt VDI0VE6SdDrtNFutnB6f5uT0OAeHBzl4sJfdvb2B7i0AAwAAAAAAAN9qpVIpSTI7O5dLc5fynemL abfbOTk9Sb1ez8HBg+zu3U+r1Uq32308U6lUUilXUxuuZXZmNsuLyymXy/ly9342tzazvb2VJI9n Xsrf8ubNH728qwEAAMAA7O/u5ML0zKDXAAAA4BVTKpVSLpeztHg1CwsLSZLDw8McHR2m2Ww915nV aiXnzo1nfHw8SbK+vp7Vtbtpt9svJQS7AxgAAAAAAAD4VimVSimVSllavJrl5eXU6/Xs7Ozk9PTk zGc3m63s7+9lf38vIyOjmZ2dzeLiYlZWVrK6djfdbveFhmABGAAAAAAAAPjWGBoaytTUdK5feyPV SiVbW1t9Cb9Pc3p6ks3Nk4yMjGb+8nxmZmZz5/NfZ29vN51O54Vcc+iFnAoAAAAAAADwiimXy7m6 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 AAAAgIL4X5Ai0Ej9GSIDAAAAAElFTkSuQmCC " | ||
52 | id="image1" | ||
53 | x="0" | ||
54 | y="0" /> | ||
55 | </g> | ||
56 | <g | ||
57 | inkscape:groupmode="layer" | ||
58 | id="layer1" | ||
59 | inkscape:label="Annotations" | ||
60 | transform="translate(-461.15282,-418.07992)"> | ||
61 | <g | ||
62 | id="g13"> | ||
63 | <rect | ||
64 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
65 | id="rect1" | ||
66 | width="80" | ||
67 | height="80" | ||
68 | x="1041.1528" | ||
69 | y="1093.08" | ||
70 | rx="6.4000001" | ||
71 | ry="6.4000001" /> | ||
72 | <text | ||
73 | xml:space="preserve" | ||
74 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
75 | x="1081.1382" | ||
76 | y="1154.496" | ||
77 | id="text1"><tspan | ||
78 | sodipodi:role="line" | ||
79 | id="tspan1" | ||
80 | x="1081.1382" | ||
81 | y="1154.496" | ||
82 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">1</tspan></text> | ||
83 | </g> | ||
84 | <g | ||
85 | id="g14"> | ||
86 | <path | ||
87 | id="rect1-7" | ||
88 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
89 | d="m 1107.5532,528.07992 c -3.5456,0 -6.4004,2.85479 -6.4004,6.40039 v 6.38867 l -24,13.85547 24,13.85742 v 33.09766 c 0,3.5456 2.8548,6.40039 6.4004,6.40039 h 67.1992 c 3.5456,0 6.4004,-2.85479 6.4004,-6.40039 v -67.19922 c 0,-3.5456 -2.8548,-6.40039 -6.4004,-6.40039 z" /> | ||
90 | <text | ||
91 | xml:space="preserve" | ||
92 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
93 | x="1141.1382" | ||
94 | y="589.49591" | ||
95 | id="text1-0"><tspan | ||
96 | sodipodi:role="line" | ||
97 | id="tspan1-9" | ||
98 | x="1141.1382" | ||
99 | y="589.49591" | ||
100 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">2</tspan></text> | ||
101 | </g> | ||
102 | <g | ||
103 | id="g16"> | ||
104 | <path | ||
105 | id="rect1-7-6" | ||
106 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
107 | d="m 1526.5532,534.07992 c -3.5456,0 -6.4004,2.85479 -6.4004,6.40039 v 6.38867 l -24,13.85547 24,13.85742 v 33.09766 c 0,3.5456 2.8548,6.40039 6.4004,6.40039 h 67.1992 c 3.5456,0 6.4004,-2.85479 6.4004,-6.40039 v -67.19922 c 0,-3.5456 -2.8548,-6.40039 -6.4004,-6.40039 z" /> | ||
108 | <text | ||
109 | xml:space="preserve" | ||
110 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
111 | x="1560.1382" | ||
112 | y="595.49591" | ||
113 | id="text1-0-1"><tspan | ||
114 | sodipodi:role="line" | ||
115 | id="tspan1-9-8" | ||
116 | x="1560.1382" | ||
117 | y="595.49591" | ||
118 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">4</tspan></text> | ||
119 | </g> | ||
120 | <g | ||
121 | id="g19"> | ||
122 | <path | ||
123 | id="rect1-7-6-5" | ||
124 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
125 | d="m 1656.5532,443.07992 c -3.5456,0 -6.4004,2.85479 -6.4004,6.40039 v 6.38867 l -24,13.85547 24,13.85742 v 33.09766 c 0,3.5456 2.8548,6.40039 6.4004,6.40039 h 67.1992 c 3.5456,0 6.4004,-2.85479 6.4004,-6.40039 v -67.19922 c 0,-3.5456 -2.8548,-6.40039 -6.4004,-6.40039 z" /> | ||
126 | <text | ||
127 | xml:space="preserve" | ||
128 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
129 | x="1690.1382" | ||
130 | y="504.49591" | ||
131 | id="text1-0-1-0"><tspan | ||
132 | sodipodi:role="line" | ||
133 | id="tspan1-9-8-3" | ||
134 | x="1690.1382" | ||
135 | y="504.49591" | ||
136 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">7</tspan></text> | ||
137 | </g> | ||
138 | <g | ||
139 | id="g17"> | ||
140 | <path | ||
141 | id="rect1-7-6-3-9" | ||
142 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
143 | d="m 2279.7524,534.07992 c 3.5456,0 6.4004,2.85479 6.4004,6.40039 v 6.38867 l 24,13.85547 -24,13.85742 v 33.09766 c 0,3.5456 -2.8548,6.40039 -6.4004,6.40039 h -67.1992 c -3.5456,0 -6.4004,-2.85479 -6.4004,-6.40039 v -67.19922 c 0,-3.5456 2.8548,-6.40039 6.4004,-6.40039 z" /> | ||
144 | <text | ||
145 | xml:space="preserve" | ||
146 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
147 | x="2246.1382" | ||
148 | y="595.49591" | ||
149 | id="text1-0-1-7"><tspan | ||
150 | sodipodi:role="line" | ||
151 | id="tspan1-9-8-5" | ||
152 | x="2246.1382" | ||
153 | y="595.49591" | ||
154 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">5</tspan></text> | ||
155 | </g> | ||
156 | <g | ||
157 | id="g20"> | ||
158 | <path | ||
159 | id="rect1-7-6-3-9-2" | ||
160 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
161 | d="m 2174.1608,528.48032 c 0,-3.5456 -2.8548,-6.4004 -6.4004,-6.4004 h -6.3887 l -13.8554,-24 -13.8574,24 h -33.0977 c -3.5456,0 -6.4004,2.8548 -6.4004,6.4004 v 67.1992 c 0,3.5456 2.8548,6.4004 6.4004,6.4004 h 67.1992 c 3.5456,0 6.4004,-2.8548 6.4004,-6.4004 z" /> | ||
162 | <text | ||
163 | xml:space="preserve" | ||
164 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
165 | x="2134.1462" | ||
166 | y="583.49591" | ||
167 | id="text1-0-1-7-5"><tspan | ||
168 | sodipodi:role="line" | ||
169 | id="tspan1-9-8-5-69" | ||
170 | x="2134.1462" | ||
171 | y="583.49591" | ||
172 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">8</tspan></text> | ||
173 | </g> | ||
174 | <g | ||
175 | id="g18"> | ||
176 | <path | ||
177 | id="rect1-7-6-3-9-9" | ||
178 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
179 | d="m 2279.7524,1395.7426 c 3.5456,0 6.4004,2.8548 6.4004,6.4004 v 6.3887 l 24,13.8555 -24,13.8574 v 33.0976 c 0,3.5456 -2.8548,6.4004 -6.4004,6.4004 h -67.1992 c -3.5456,0 -6.4004,-2.8548 -6.4004,-6.4004 v -67.1992 c 0,-3.5456 2.8548,-6.4004 6.4004,-6.4004 z" /> | ||
180 | <text | ||
181 | xml:space="preserve" | ||
182 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
183 | x="2246.1382" | ||
184 | y="1457.1586" | ||
185 | id="text1-0-1-7-3"><tspan | ||
186 | sodipodi:role="line" | ||
187 | id="tspan1-9-8-5-1" | ||
188 | x="2246.1382" | ||
189 | y="1457.1586" | ||
190 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">6</tspan></text> | ||
191 | </g> | ||
192 | <g | ||
193 | id="g15"> | ||
194 | <rect | ||
195 | style="fill:#56b6c2;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
196 | id="rect1-3" | ||
197 | width="80" | ||
198 | height="80" | ||
199 | x="2111.1528" | ||
200 | y="918.0799" | ||
201 | rx="6.4000001" | ||
202 | ry="6.4000001" /> | ||
203 | <text | ||
204 | xml:space="preserve" | ||
205 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" | ||
206 | x="2151.1382" | ||
207 | y="979.49591" | ||
208 | id="text1-5"><tspan | ||
209 | sodipodi:role="line" | ||
210 | id="tspan1-6" | ||
211 | x="2151.1382" | ||
212 | y="979.49591" | ||
213 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1">3</tspan></text> | ||
214 | </g> | ||
215 | </g> | ||
216 | </svg> | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelDark.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.png b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.png new file mode 100644 index 00000000..b9cb638f --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.png | |||
Binary files differ | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.png.license b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.png.license new file mode 100644 index 00000000..e2da67f5 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.png.license | |||
@@ -0,0 +1,5 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | This file was automatically generated from initialModelLight.svg. | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.svg b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.svg new file mode 100644 index 00000000..25ced632 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.svg | |||
@@ -0,0 +1,224 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||
3 | |||
4 | <svg | ||
5 | version="1.1" | ||
6 | id="svg1" | ||
7 | width="1920" | ||
8 | height="1080" | ||
9 | viewBox="0 0 1920 1080" | ||
10 | sodipodi:docname="initialModelLight.svg" | ||
11 | inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" | ||
12 | inkscape:export-filename="initialModelLight.png" | ||
13 | inkscape:export-xdpi="96" | ||
14 | inkscape:export-ydpi="96" | ||
15 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||
16 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||
17 | xmlns:xlink="http://www.w3.org/1999/xlink" | ||
18 | xmlns="http://www.w3.org/2000/svg" | ||
19 | xmlns:svg="http://www.w3.org/2000/svg"> | ||
20 | <defs | ||
21 | id="defs1" /> | ||
22 | <sodipodi:namedview | ||
23 | id="namedview1" | ||
24 | pagecolor="#ffffff" | ||
25 | bordercolor="#666666" | ||
26 | borderopacity="1.0" | ||
27 | inkscape:showpageshadow="2" | ||
28 | inkscape:pageopacity="0.0" | ||
29 | inkscape:pagecheckerboard="0" | ||
30 | inkscape:deskcolor="#d1d1d1" | ||
31 | showgrid="false" | ||
32 | inkscape:zoom="0.53666853" | ||
33 | inkscape:cx="6.5217165" | ||
34 | inkscape:cy="630.74315" | ||
35 | inkscape:window-width="3840" | ||
36 | inkscape:window-height="2135" | ||
37 | inkscape:window-x="0" | ||
38 | inkscape:window-y="0" | ||
39 | inkscape:window-maximized="0" | ||
40 | inkscape:current-layer="layer1" | ||
41 | showguides="true" /> | ||
42 | <g | ||
43 | inkscape:groupmode="layer" | ||
44 | inkscape:label="Image" | ||
45 | id="g1" | ||
46 | sodipodi:insensitive="true"> | ||
47 | <image | ||
48 | width="1920" | ||
49 | height="1080" | ||
50 | preserveAspectRatio="none" | ||
51 | xlink:href="data:image/png;base64,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 AAAAAAAAAKAKAmgAAAAAAAAAgCoIoAEAAAAAAAAAqiCABgAAAAAAAACoggAaAAAAAAAAAKAKAmgA AAAAAAAAgCoIoAEAAAAAAAAAqiCABgAAAAAAAACoggAaAAAAAAAAAKAKAmgAAAAAAAAAgCoIoAEA AAAAAAAAqiCABgAAAAAAAACoggAaAAAAAAAAAKAKAmgAAAAAAAAAgCoIoAEAAAAAAAAAqiCABgAA AAAAAACoggAaAAAAAAAAAKAKAmgAAAAAAAAAgCoIoAEAAAAAAAAAqiCABgAAAAAAAACoggAaAAAA AAAAAKAKAmgAAAAAAAAAgCoIoAEAAAAAAAAAqiCABgAAAAAAAACoggAaAAAAAAAAAKAKAmgAAAAA AAAAgCoIoAEAAAAAAAAAqiCABgAAAAAAAACoggAaAAAAAAAAAKAKAmgAAAAAAAAAgCoIoAEAAAAA AAAAqiCABgAAAAAAAACoggAaAAAAAAAAAKAKAmgAAAAAAAAAgCoIoAEAAAAAAAAAqiCABgAAAAAA AACoggAaAAAAAAAAAKAKra8bAAAAAAAAAAA0jSlTppzDWS+++GKTd+LBDmgAAAAAAAAAgCokRVF8 3QMAAADQPGRkZKSnp/u6CwAAAKDZYAc0AAAAAAAAAEAVBNAAAAAAAAAAAFUQQAMAAAAAAAAAVEEA DQAAAAAAAABQBQE0AAAAAAAAAEAVBNAAAAAAAAAAAFUQQAMAAAAAAAAAVEEADQAAAAAAAABQBQE0 AAAAAAAAAEAVBNAAAAAAAAAAAFUQQAMAAAAAAAAAVEEADQAAAAAAAABQBQE0AAAAAAAAAEAVBNAA AAAAAAAAAFUQQAMAAAAAAAAAVEEADQAAAAAAAABQBQE0AAAAAAAAAEAVBNAAAAAAAAAAAFUQQAMA AAAAAAAAVEEADQAAAAAAAABQBQE0AAAAAAAAAEAVBNAAAAAAAAAAAFUQQAMAAAAAAAAAVEEADQAA AAAAAABQBQE0AAAAAAAAAEAVWl83AAAAAAAAAABoSiWlpeVl5WUVFZU2m8PpdLncLpdLCKHRaDQa WafVGgICTEZjoCnQHBSkaicE0AAAAAAAAADQ7CmKUlRUnF9YWFxcrNFoDQa9Qa+3Wq0arUYjy7Is CyGlTb7aAAAJxElEQVTcbrfL7XY5XQ6Ho6ikJDs3z+VyWiyW0OBgq9UiSVKTd0UADQAAAAAAAADN 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 /D+F2M33yoQJewAAAABJRU5ErkJggg== " | ||
52 | id="image1" | ||
53 | x="0" | ||
54 | y="0" /> | ||
55 | </g> | ||
56 | <g | ||
57 | inkscape:groupmode="layer" | ||
58 | id="layer1" | ||
59 | inkscape:label="Annotations" | ||
60 | transform="translate(-461.15282,-418.07992)"> | ||
61 | <g | ||
62 | id="g2" | ||
63 | transform="translate(136.86058,478.49221)"> | ||
64 | <rect | ||
65 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
66 | id="rect1" | ||
67 | width="80" | ||
68 | height="80" | ||
69 | x="904.29224" | ||
70 | y="614.58771" | ||
71 | rx="6.4000001" | ||
72 | ry="6.4000001" /> | ||
73 | <text | ||
74 | xml:space="preserve" | ||
75 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
76 | x="944.27759" | ||
77 | y="676.00372" | ||
78 | id="text1"><tspan | ||
79 | sodipodi:role="line" | ||
80 | id="tspan1" | ||
81 | x="944.27759" | ||
82 | y="676.00372" | ||
83 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">1</tspan></text> | ||
84 | </g> | ||
85 | <g | ||
86 | id="g4" | ||
87 | transform="translate(0,-10)"> | ||
88 | <path | ||
89 | id="rect1-7" | ||
90 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
91 | d="m 1107.5532,538.07992 c -3.5456,0 -6.4004,2.85479 -6.4004,6.40039 v 6.38867 l -24,13.85547 24,13.85742 v 33.09766 c 0,3.5456 2.8548,6.40039 6.4004,6.40039 h 67.1992 c 3.5456,0 6.4004,-2.85479 6.4004,-6.40039 v -67.19922 c 0,-3.5456 -2.8548,-6.40039 -6.4004,-6.40039 z" /> | ||
92 | <text | ||
93 | xml:space="preserve" | ||
94 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
95 | x="1141.1382" | ||
96 | y="599.49591" | ||
97 | id="text1-0"><tspan | ||
98 | sodipodi:role="line" | ||
99 | id="tspan1-9" | ||
100 | x="1141.1382" | ||
101 | y="599.49591" | ||
102 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">2</tspan></text> | ||
103 | </g> | ||
104 | <g | ||
105 | id="g4-2" | ||
106 | transform="translate(419.00002,-4)"> | ||
107 | <path | ||
108 | id="rect1-7-6" | ||
109 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
110 | d="m 1107.5532,538.07992 c -3.5456,0 -6.4004,2.85479 -6.4004,6.40039 v 6.38867 l -24,13.85547 24,13.85742 v 33.09766 c 0,3.5456 2.8548,6.40039 6.4004,6.40039 h 67.1992 c 3.5456,0 6.4004,-2.85479 6.4004,-6.40039 v -67.19922 c 0,-3.5456 -2.8548,-6.40039 -6.4004,-6.40039 z" /> | ||
111 | <text | ||
112 | xml:space="preserve" | ||
113 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
114 | x="1141.1382" | ||
115 | y="599.49591" | ||
116 | id="text1-0-1"><tspan | ||
117 | sodipodi:role="line" | ||
118 | id="tspan1-9-8" | ||
119 | x="1141.1382" | ||
120 | y="599.49591" | ||
121 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">4</tspan></text> | ||
122 | </g> | ||
123 | <g | ||
124 | id="g4-2-4" | ||
125 | transform="translate(549.00002,-95)"> | ||
126 | <path | ||
127 | id="rect1-7-6-5" | ||
128 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
129 | d="m 1107.5532,538.07992 c -3.5456,0 -6.4004,2.85479 -6.4004,6.40039 v 6.38867 l -24,13.85547 24,13.85742 v 33.09766 c 0,3.5456 2.8548,6.40039 6.4004,6.40039 h 67.1992 c 3.5456,0 6.4004,-2.85479 6.4004,-6.40039 v -67.19922 c 0,-3.5456 -2.8548,-6.40039 -6.4004,-6.40039 z" /> | ||
130 | <text | ||
131 | xml:space="preserve" | ||
132 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
133 | x="1141.1382" | ||
134 | y="599.49591" | ||
135 | id="text1-0-1-0"><tspan | ||
136 | sodipodi:role="line" | ||
137 | id="tspan1-9-8-3" | ||
138 | x="1141.1382" | ||
139 | y="599.49591" | ||
140 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">7</tspan></text> | ||
141 | </g> | ||
142 | <g | ||
143 | id="g5" | ||
144 | transform="translate(-13.99998,6)"> | ||
145 | <path | ||
146 | id="rect1-7-6-3-9" | ||
147 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
148 | d="m 2293.7524,528.07992 c 3.5456,0 6.4004,2.85479 6.4004,6.40039 v 6.38867 l 24,13.85547 -24,13.85742 v 33.09766 c 0,3.5456 -2.8548,6.40039 -6.4004,6.40039 h -67.1992 c -3.5456,0 -6.4004,-2.85479 -6.4004,-6.40039 v -67.19922 c 0,-3.5456 2.8548,-6.40039 6.4004,-6.40039 z" /> | ||
149 | <text | ||
150 | xml:space="preserve" | ||
151 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
152 | x="2260.1382" | ||
153 | y="589.49591" | ||
154 | id="text1-0-1-7"><tspan | ||
155 | sodipodi:role="line" | ||
156 | id="tspan1-9-8-5" | ||
157 | x="2260.1382" | ||
158 | y="589.49591" | ||
159 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">5</tspan></text> | ||
160 | </g> | ||
161 | <g | ||
162 | id="g6" | ||
163 | transform="translate(-82.508524,-188)"> | ||
164 | <path | ||
165 | id="rect1-7-6-3-9-2" | ||
166 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
167 | d="m 2256.6693,716.48032 c 0,-3.5456 -2.8548,-6.4004 -6.4004,-6.4004 h -6.3887 l -13.8554,-24 -13.8574,24 h -33.0977 c -3.5456,0 -6.4004,2.8548 -6.4004,6.4004 v 67.1992 c 0,3.5456 2.8548,6.4004 6.4004,6.4004 h 67.1992 c 3.5456,0 6.4004,-2.8548 6.4004,-6.4004 z" /> | ||
168 | <text | ||
169 | xml:space="preserve" | ||
170 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
171 | x="2216.6548" | ||
172 | y="771.49591" | ||
173 | id="text1-0-1-7-5"><tspan | ||
174 | sodipodi:role="line" | ||
175 | id="tspan1-9-8-5-69" | ||
176 | x="2216.6548" | ||
177 | y="771.49591" | ||
178 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">8</tspan></text> | ||
179 | </g> | ||
180 | <g | ||
181 | id="g5-2" | ||
182 | transform="translate(-13.99998,867.6627)"> | ||
183 | <path | ||
184 | id="rect1-7-6-3-9-9" | ||
185 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
186 | d="m 2293.7524,528.07992 c 3.5456,0 6.4004,2.85479 6.4004,6.40039 v 6.38867 l 24,13.85547 -24,13.85742 v 33.09766 c 0,3.5456 -2.8548,6.40039 -6.4004,6.40039 h -67.1992 c -3.5456,0 -6.4004,-2.85479 -6.4004,-6.40039 v -67.19922 c 0,-3.5456 2.8548,-6.40039 6.4004,-6.40039 z" /> | ||
187 | <text | ||
188 | xml:space="preserve" | ||
189 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
190 | x="2260.1382" | ||
191 | y="589.49591" | ||
192 | id="text1-0-1-7-3"><tspan | ||
193 | sodipodi:role="line" | ||
194 | id="tspan1-9-8-5-1" | ||
195 | x="2260.1382" | ||
196 | y="589.49591" | ||
197 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">6</tspan></text> | ||
198 | </g> | ||
199 | <g | ||
200 | id="g3" | ||
201 | transform="translate(47.332508)"> | ||
202 | <rect | ||
203 | style="fill:#038a99;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-miterlimit:1.45;paint-order:stroke fill markers" | ||
204 | id="rect1-3" | ||
205 | width="80" | ||
206 | height="80" | ||
207 | x="2063.8203" | ||
208 | y="918.0799" | ||
209 | rx="6.4000001" | ||
210 | ry="6.4000001" /> | ||
211 | <text | ||
212 | xml:space="preserve" | ||
213 | style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:60px;line-height:1.25;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:#f9f9f9;fill-opacity:1;stroke:none" | ||
214 | x="2103.8057" | ||
215 | y="979.49591" | ||
216 | id="text1-5"><tspan | ||
217 | sodipodi:role="line" | ||
218 | id="tspan1-6" | ||
219 | x="2103.8057" | ||
220 | y="979.49591" | ||
221 | style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle">3</tspan></text> | ||
222 | </g> | ||
223 | </g> | ||
224 | </svg> | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/initialModelLight.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/metamodel.svg b/subprojects/docs/src/docs/learn/tutorials/file-system/metamodel.svg new file mode 100644 index 00000000..0cf39cd3 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/metamodel.svg | |||
@@ -0,0 +1,132 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="202pt" height="358pt" viewBox="-6 -6 214.1699981689453 370" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-3QBf1eGyrSuIh9K81Q93C"><style>.refinery-3QBf1eGyrSuIh9K81Q93C .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-3QBf1eGyrSuIh9K81Q93C .node .node-outline{stroke:#19202b;}.refinery-3QBf1eGyrSuIh9K81Q93C .node .node-header{fill:rgb(53, 161, 173);}.refinery-3QBf1eGyrSuIh9K81Q93C .node .node-bg{fill:#fff;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-h .node-header{fill:#e06c75;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-i .node-header{fill:#98c379;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-j .node-header{fill:#c678dd;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-n .node-header{fill:#abcc94;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge .edge-line{stroke:#19202b;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge .edge-arrow{fill:#19202b;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge-UNKNOWN text{fill:#696c77;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge-ERROR text{fill:#ca1243;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-3QBf1eGyrSuIh9K81Q93C .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-3QBf1eGyrSuIh9K81Q93C .icon-TRUE{fill:#19202b;}.refinery-3QBf1eGyrSuIh9K81Q93C .icon-UNKNOWN{fill:#696c77;}.refinery-3QBf1eGyrSuIh9K81Q93C .icon-ERROR{fill:#ca1243;}.refinery-3QBf1eGyrSuIh9K81Q93C text.label-UNKNOWN{fill:#696c77;}.refinery-3QBf1eGyrSuIh9K81Q93C text.label-ERROR{fill:#ca1243;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE text:not(.label-ERROR){fill:#696c77;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE .node-outline{stroke:#696c77;stroke-dasharray:2 4;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE .node-header{fill:#fff;}.refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE .icon-TRUE{fill:#696c77;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C text.label-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE text:not(.label-ERROR){fill:#abb2bf;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE .node-outline{stroke:#abb2bf;stroke-dasharray:2 4;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE .node-header{fill:#282c34;}[data-theme="dark"] .refinery-3QBf1eGyrSuIh9K81Q93C .node-exists-FALSE .icon-TRUE{fill:#abb2bf;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-3QBf1eGyrSuIh9K81Q93C-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-3QBf1eGyrSuIh9K81Q93C-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="scale(1 1) rotate(0) translate(4 354)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-g"><rect stroke="none" x="29.5" y="-344.5" width="99" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="24.28" y="-350" width="98.44" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="20" y="-354" width="106" height="27" clip-path="url(#refinery-3QBf1eGyrSuIh9K81Q93C-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="29.28" y="-334.2" font-size="12.00">Filesystem::new</text> | ||
10 | <use x="30.2793" y="-320.40000000000003" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="46.28" y="-310.8" font-size="12.00" class="label label-TRUE">Filesystem</text> | ||
12 | </g> | ||
13 | <polyline points="24.28,-326.6 122.72,-326.6" class="node-outline"/> | ||
14 | <rect fill="none" x="24.28" y="-350" width="98.44" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-3QBf1eGyrSuIh9K81Q93C-clip-0"><rect stroke="none" x="24.28" y="-350" width="98.44" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n1 --> | ||
17 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-n"><rect stroke="none" x="42.5" y="-259.5" width="74" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
18 | |||
19 | <rect stroke="none" x="37" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="33" y="-269" width="81" height="27" clip-path="url(#refinery-3QBf1eGyrSuIh9K81Q93C-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="50.25" y="-249.4" font-size="12.00">Dir::new</text> | ||
22 | <use x="43" y="-235.8" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="58.74" y="-227" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
24 | </g> | ||
25 | <use x="43" y="-219.8" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
26 | <g><text text-anchor="start" x="59" y="-210" font-size="12.00" class="label label-TRUE">Dir</text> | ||
27 | </g> | ||
28 | <polyline points="37,-241.8 110,-241.8" class="node-outline"/> | ||
29 | <rect fill="none" x="37" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-outline"/> | ||
30 | <clipPath id="refinery-3QBf1eGyrSuIh9K81Q93C-clip-1"><rect stroke="none" x="37" y="-265.2" width="73" height="64.39999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
31 | <!-- n0->n1 --> | ||
32 | <g class="edge edge-UNKNOWN"> | ||
33 | |||
34 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M73.5,-301.27C73.5,-293.78 73.5,-285.25 73.5,-276.82" class="edge-line"/> | ||
35 | <polygon stroke-width="2" points="76.56,-276.89 73.5,-268.14 70.44,-276.89 76.56,-276.89" class="edge-line edge-arrow"/> | ||
36 | <text text-anchor="start" x="51.17" y="-287.69" font-weight="bold" font-size="10.50">root</text> | ||
37 | </g> | ||
38 | <!-- n1->n1 --> | ||
39 | <g class="edge edge-UNKNOWN"> | ||
40 | |||
41 | <path fill="none" stroke-dasharray="5,2" d="M109.92,-241.64C120.15,-241.26 128,-238.38 128,-233 128,-229.89 125.38,-227.61 121.19,-226.18" class="edge-line"/> | ||
42 | <polygon points="121.85,-222.74 111.42,-224.6 120.73,-229.65 121.85,-222.74" class="edge-line edge-arrow"/> | ||
43 | <text text-anchor="middle" x="126.7" y="-244.77" font-size="10.50">parent</text> | ||
44 | </g> | ||
45 | <!-- n1->n1 --> | ||
46 | <g class="edge edge-UNKNOWN"> | ||
47 | |||
48 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M109.68,-249.7C128.74,-252.4 146,-246.84 146,-233 146,-222.08 135.25,-216.31 121.36,-215.69" class="edge-line"/> | ||
49 | <polygon stroke-width="2" points="121.29,-212.63 112.71,-216.14 121.6,-218.75 121.29,-212.63" class="edge-line edge-arrow"/> | ||
50 | <text text-anchor="start" x="109.74" y="-253.86" font-weight="bold" font-size="10.50">contents</text> | ||
51 | </g> | ||
52 | <!-- n2 --> | ||
53 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="5.5" y="-58.5" width="74" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
54 | |||
55 | <rect stroke="none" x="0" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
56 | <rect stroke="none" x="-4" y="-68" width="81" height="27" clip-path="url(#refinery-3QBf1eGyrSuIh9K81Q93C-clip-2)" class="node-header"/> | ||
57 | <text text-anchor="start" x="12.09" y="-48.6" font-size="12.00">File::new</text> | ||
58 | <use x="6" y="-35" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
59 | <g><text text-anchor="start" x="21.74" y="-26.2" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
60 | </g> | ||
61 | <use x="6" y="-19" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
62 | <g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-TRUE">File</text> | ||
63 | </g> | ||
64 | <polyline points="0,-41 73,-41" class="node-outline"/> | ||
65 | <rect fill="none" x="0" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
66 | <clipPath id="refinery-3QBf1eGyrSuIh9K81Q93C-clip-2"><rect stroke="none" x="0" y="-64.4" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
67 | <!-- n1->n2 --> | ||
68 | <g class="edge edge-UNKNOWN"> | ||
69 | |||
70 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M62.66,-200.94C54.37,-167.34 44.16,-113.54 38.8,-75.96" class="edge-line"/> | ||
71 | <polygon stroke-width="2" points="41.84,-75.63 37.64,-67.37 35.77,-76.45 41.84,-75.63" class="edge-line edge-arrow"/> | ||
72 | <text text-anchor="start" x="2.11" y="-137.27" font-weight="bold" font-size="10.50">contents</text> | ||
73 | </g> | ||
74 | <!-- n3 --> | ||
75 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="79.5" y="-158.5" width="74" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
76 | |||
77 | <rect stroke="none" x="74" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
78 | <rect stroke="none" x="70" y="-168" width="81" height="27" clip-path="url(#refinery-3QBf1eGyrSuIh9K81Q93C-clip-3)" class="node-header"/> | ||
79 | <text text-anchor="start" x="84.13" y="-149" font-size="12.00">Link::new</text> | ||
80 | <use x="80" y="-135.4" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
81 | <g><text text-anchor="start" x="95.74" y="-126.6" font-style="italic" font-size="12.00" class="label label-TRUE">FSObject</text> | ||
82 | </g> | ||
83 | <use x="80" y="-119.4" width="12" height="12" href="#refinery-3QBf1eGyrSuIh9K81Q93C-icon-TRUE" class="icon icon-TRUE"/> | ||
84 | <g><text text-anchor="start" x="96" y="-109.6" font-size="12.00" class="label label-TRUE">Link</text> | ||
85 | </g> | ||
86 | <polyline points="74,-141.4 147,-141.4" class="node-outline"/> | ||
87 | <rect fill="none" x="74" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
88 | <clipPath id="refinery-3QBf1eGyrSuIh9K81Q93C-clip-3"><rect stroke="none" x="74" y="-164.8" width="73" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
89 | <!-- n1->n3 --> | ||
90 | <g class="edge edge-UNKNOWN"> | ||
91 | |||
92 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M72.55,-200.87C74.82,-192.71 77.94,-183.79 81.51,-175.18" class="edge-line"/> | ||
93 | <polygon stroke-width="2" points="84.26,-176.54 85.01,-167.3 78.66,-174.06 84.26,-176.54" class="edge-line edge-arrow"/> | ||
94 | <text text-anchor="start" x="32.38" y="-186.64" font-weight="bold" font-size="10.50">contents</text> | ||
95 | </g> | ||
96 | <!-- n2->n1 --> | ||
97 | <g class="edge edge-UNKNOWN"> | ||
98 | |||
99 | <path fill="none" stroke-dasharray="5,2" d="M47.37,-64.37C55.69,-98.16 65.95,-152.28 71.29,-189.85" class="edge-line"/> | ||
100 | <polygon points="67.78,-189.99 72.58,-199.44 74.72,-189.06 67.78,-189.99" class="edge-line edge-arrow"/> | ||
101 | <text text-anchor="middle" x="45.44" y="-123.1" font-size="10.50">parent</text> | ||
102 | </g> | ||
103 | <!-- n3->n1 --> | ||
104 | <g class="edge edge-UNKNOWN"> | ||
105 | |||
106 | <path fill="none" stroke-dasharray="5,2" d="M98.87,-164.53C95.81,-172.67 92.45,-181.6 89.22,-190.21" class="edge-line"/> | ||
107 | <polygon points="85.98,-188.86 85.74,-199.45 92.54,-191.32 85.98,-188.86" class="edge-line edge-arrow"/> | ||
108 | <text text-anchor="middle" x="108.27" y="-173.44" font-size="10.50">parent</text> | ||
109 | </g> | ||
110 | <!-- n3->n1 --> | ||
111 | <g class="edge edge-UNKNOWN"> | ||
112 | |||
113 | <path fill="none" stroke-dasharray="5,2" d="M111.51,-164.53C109.22,-172.76 106.07,-181.79 102.46,-190.49" class="edge-line"/> | ||
114 | <polygon points="99.33,-188.92 98.48,-199.48 105.73,-191.76 99.33,-188.92" class="edge-line edge-arrow"/> | ||
115 | <text text-anchor="middle" x="119.98" y="-186.25" font-size="10.50">target</text> | ||
116 | </g> | ||
117 | <!-- n3->n2 --> | ||
118 | <g class="edge edge-UNKNOWN"> | ||
119 | |||
120 | <path fill="none" stroke-dasharray="5,2" d="M87.09,-100.47C80.54,-91.77 73.34,-82.19 66.48,-73.06" class="edge-line"/> | ||
121 | <polygon points="69.48,-71.23 60.67,-65.34 63.88,-75.44 69.48,-71.23" class="edge-line edge-arrow"/> | ||
122 | <text text-anchor="middle" x="58.72" y="-85.3" font-size="10.50">target</text> | ||
123 | </g> | ||
124 | <!-- n3->n3 --> | ||
125 | <g class="edge edge-UNKNOWN"> | ||
126 | |||
127 | <path fill="none" stroke-dasharray="5,2" d="M146.92,-151.33C157.15,-150.51 165,-144.27 165,-132.6 165,-125.49 162.08,-120.39 157.49,-117.31" class="edge-line"/> | ||
128 | <polygon points="158.95,-114.11 148.36,-114.34 156.78,-120.76 158.95,-114.11" class="edge-line edge-arrow"/> | ||
129 | <text text-anchor="middle" x="179.58" y="-135.09" font-size="10.50">target</text> | ||
130 | </g> | ||
131 | </g> | ||
132 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/metamodel.svg.license b/subprojects/docs/src/docs/learn/tutorials/file-system/metamodel.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/metamodel.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationDark.png b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationDark.png new file mode 100644 index 00000000..18c16832 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationDark.png | |||
Binary files differ | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationDark.png.license b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationDark.png.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationDark.png.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationLight.png b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationLight.png new file mode 100644 index 00000000..4882c2a4 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationLight.png | |||
Binary files differ | |||
diff --git a/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationLight.png.license b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationLight.png.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/src/docs/learn/tutorials/file-system/modelGenerationLight.png.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/learn/index.md b/subprojects/docs/src/learn/index.md deleted file mode 100644 index bb28df57..00000000 --- a/subprojects/docs/src/learn/index.md +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 0 | ||
5 | --- | ||
6 | |||
7 | # Introduction | ||
8 | |||
9 | Various software and systems engineering scenarios rely on the systematic construction of consistent graph models. However, **automatically generating a diverse set of consistent graph models** for complex domain specifications is challenging. First, the graph generation problem must be specified with mathematical precision. Moreover, graph generation is a computationally complex task, which necessitates specialized logic solvers. | ||
10 | |||
11 | **Refinery is a novel open-source software framework** to automatically synthesize a diverse set of consistent domain-specific graph models. The framework offers an expressive high-level specification language using partial models to succinctly formulate a wide range of graph generation challenges. It also provides a modern cloud-based architecture for a scalable _Graph Solver as a Service,_ which uses logic reasoning rules to efficiently synthesize a diverse set of solutions to graph generation problems by partial model refinement. Applications include system-level architecture synthesis, test generation for modeling tools or traffic scenario synthesis for autonomous vehicles. | ||
diff --git a/subprojects/docs/src/plugins/remarkImage.ts b/subprojects/docs/src/plugins/remarkImage.ts new file mode 100644 index 00000000..49fa5305 --- /dev/null +++ b/subprojects/docs/src/plugins/remarkImage.ts | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import type { Image } from 'mdast'; | ||
8 | import type { | ||
9 | MdxjsEsm, | ||
10 | MdxJsxAttribute, | ||
11 | MdxJsxFlowElement, | ||
12 | } from 'mdast-util-mdx'; | ||
13 | import type { Transformer } from 'unified'; | ||
14 | import type { Node } from 'unist'; | ||
15 | import { visit } from 'unist-util-visit'; | ||
16 | |||
17 | import { | ||
18 | isImport, | ||
19 | isLiteral, | ||
20 | isParent, | ||
21 | replaceChildrenRecursively, | ||
22 | } from './remarkPluginUtils'; | ||
23 | |||
24 | function isImage(node: Node): node is Image { | ||
25 | return node.type === 'image'; | ||
26 | } | ||
27 | |||
28 | function createImport(componentName: string, url: string): MdxjsEsm { | ||
29 | const urlString = JSON.stringify(url); | ||
30 | return { | ||
31 | type: 'mdxjsEsm', | ||
32 | value: `import ${componentName} from ${urlString}`, | ||
33 | data: { | ||
34 | estree: { | ||
35 | type: 'Program', | ||
36 | body: [ | ||
37 | { | ||
38 | type: 'ImportDeclaration', | ||
39 | specifiers: [ | ||
40 | { | ||
41 | type: 'ImportDefaultSpecifier', | ||
42 | local: { type: 'Identifier', name: componentName }, | ||
43 | }, | ||
44 | ], | ||
45 | source: { | ||
46 | type: 'Literal', | ||
47 | value: url, | ||
48 | raw: urlString, | ||
49 | }, | ||
50 | }, | ||
51 | ], | ||
52 | sourceType: 'module', | ||
53 | }, | ||
54 | }, | ||
55 | }; | ||
56 | } | ||
57 | |||
58 | function addCommonAttributes( | ||
59 | image: Image, | ||
60 | attributes: MdxJsxAttribute[], | ||
61 | ): void { | ||
62 | const { alt, title } = image; | ||
63 | if (alt) { | ||
64 | attributes.push({ | ||
65 | type: 'mdxJsxAttribute', | ||
66 | name: 'alt', | ||
67 | value: alt, | ||
68 | }); | ||
69 | } | ||
70 | if (title) { | ||
71 | attributes.push({ | ||
72 | type: 'mdxJsxAttribute', | ||
73 | name: 'title', | ||
74 | value: title, | ||
75 | }); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | function getOriginalURL(url: string) { | ||
80 | const index = url.indexOf('?'); | ||
81 | if (index < 0) { | ||
82 | return url; | ||
83 | } | ||
84 | return url.substring(0, index); | ||
85 | } | ||
86 | |||
87 | function getResponsiveURL(url: string) { | ||
88 | const separator = url.indexOf('?') < 0 ? '?' : '&'; | ||
89 | const quality = /\.png$/.test(url) ? 100 : 85; | ||
90 | return `${url}${separator}quality=${quality}&sizes[]=2640,sizes[]=1928,sizes[]=1320,sizes[]=964,sizes[]=640,sizes[]=320&placeholder=true&rl`; | ||
91 | } | ||
92 | |||
93 | function getExpressionAttribute(name: string, value: string): MdxJsxAttribute { | ||
94 | return { | ||
95 | type: 'mdxJsxAttribute', | ||
96 | name, | ||
97 | value: { | ||
98 | type: 'mdxJsxAttributeValueExpression', | ||
99 | value, | ||
100 | data: { | ||
101 | estree: { | ||
102 | type: 'Program', | ||
103 | body: [ | ||
104 | { | ||
105 | type: 'ExpressionStatement', | ||
106 | expression: { | ||
107 | type: 'Identifier', | ||
108 | name: value, | ||
109 | }, | ||
110 | }, | ||
111 | ], | ||
112 | sourceType: 'module', | ||
113 | }, | ||
114 | }, | ||
115 | }, | ||
116 | }; | ||
117 | } | ||
118 | |||
119 | class ImageTransformer { | ||
120 | requiredImports = new Map<string, MdxjsEsm>(); | ||
121 | |||
122 | transformImage(image: Image, index: number): Node[] { | ||
123 | const { url } = image; | ||
124 | if (/\.svg$/.test(url)) { | ||
125 | return this.transformSVGImage(image, index); | ||
126 | } | ||
127 | if (url.indexOf('|') >= 0) { | ||
128 | return this.transformThemedImage(image, index); | ||
129 | } | ||
130 | return this.transformResponsiveImage(image, index); | ||
131 | } | ||
132 | |||
133 | private transformSVGImage(image: Image, index: number): Node[] { | ||
134 | const { url } = image; | ||
135 | const componentName = `Image__${index}`; | ||
136 | const attributes = [getExpressionAttribute('Component', componentName)]; | ||
137 | addCommonAttributes(image, attributes); | ||
138 | this.requireImport('SVGImage', '@site/src/components/ResponsiveImage/SVG'); | ||
139 | |||
140 | return [ | ||
141 | createImport(componentName, url), | ||
142 | { | ||
143 | type: 'mdxJsxFlowElement', | ||
144 | name: 'SVGImage', | ||
145 | attributes, | ||
146 | } as MdxJsxFlowElement, | ||
147 | ]; | ||
148 | } | ||
149 | |||
150 | private transformThemedImage(image: Image, index: number): Node[] { | ||
151 | const { url } = image; | ||
152 | const [urlLight, urlDark] = url.split('|'); | ||
153 | if (urlLight === undefined || urlDark === undefined) { | ||
154 | throw new Error(`Invalid themed image: ${url}`); | ||
155 | } | ||
156 | const componentNameLight = `ImageLight__${index}`; | ||
157 | const componentNameDark = `ImageDark__${index}`; | ||
158 | const originalLight = `originalImageLight__${index}`; | ||
159 | const originalDark = `originalImageDark${index}`; | ||
160 | this.requireImport( | ||
161 | 'ThemedImage', | ||
162 | '@site/src/components/ResponsiveImage/Themed', | ||
163 | ); | ||
164 | |||
165 | const attributes: MdxJsxAttribute[] = [ | ||
166 | getExpressionAttribute('light', componentNameLight), | ||
167 | getExpressionAttribute('dark', componentNameDark), | ||
168 | getExpressionAttribute('originalLight', originalLight), | ||
169 | getExpressionAttribute('originalDark', originalDark), | ||
170 | ]; | ||
171 | addCommonAttributes(image, attributes); | ||
172 | |||
173 | return [ | ||
174 | createImport(componentNameLight, getResponsiveURL(urlLight)), | ||
175 | createImport(componentNameDark, getResponsiveURL(urlDark)), | ||
176 | createImport(originalLight, getOriginalURL(urlLight)), | ||
177 | createImport(originalDark, getOriginalURL(urlDark)), | ||
178 | { | ||
179 | type: 'mdxJsxFlowElement', | ||
180 | name: 'ThemedImage', | ||
181 | attributes, | ||
182 | } as MdxJsxFlowElement, | ||
183 | ]; | ||
184 | } | ||
185 | |||
186 | private transformResponsiveImage(image: Image, index: number): Node[] { | ||
187 | const { url } = image; | ||
188 | const componentName = `Image__${index}`; | ||
189 | const original = `originalImage__${index}`; | ||
190 | this.requireImport( | ||
191 | 'ResponsiveImage', | ||
192 | '@site/src/components/ResponsiveImage', | ||
193 | ); | ||
194 | |||
195 | const attributes: MdxJsxAttribute[] = [ | ||
196 | getExpressionAttribute('image', componentName), | ||
197 | getExpressionAttribute('original', original), | ||
198 | ]; | ||
199 | addCommonAttributes(image, attributes); | ||
200 | |||
201 | return [ | ||
202 | createImport(componentName, getResponsiveURL(url)), | ||
203 | createImport(original, getOriginalURL(url)), | ||
204 | { | ||
205 | type: 'mdxJsxFlowElement', | ||
206 | name: 'ResponsiveImage', | ||
207 | attributes, | ||
208 | } as MdxJsxFlowElement, | ||
209 | ]; | ||
210 | } | ||
211 | |||
212 | private requireImport(componentName: string, url: string) { | ||
213 | if (!this.requiredImports.has(url)) { | ||
214 | this.requiredImports.set(url, createImport(componentName, url)); | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | |||
219 | export default function remarkImage(): Transformer { | ||
220 | return (root) => { | ||
221 | let counter = 0; | ||
222 | const transformer = new ImageTransformer(); | ||
223 | replaceChildrenRecursively(root, isImage, (image) => { | ||
224 | const result = transformer.transformImage(image, counter); | ||
225 | counter += 1; | ||
226 | return result; | ||
227 | }); | ||
228 | const { requiredImports } = transformer; | ||
229 | const imported = new Set<string>(); | ||
230 | visit(root, (node) => { | ||
231 | if (isLiteral(node)) { | ||
232 | requiredImports.forEach((_, key) => { | ||
233 | if (isImport(node, key)) { | ||
234 | imported.add(key); | ||
235 | } | ||
236 | }); | ||
237 | } | ||
238 | }); | ||
239 | const imports: MdxjsEsm[] = []; | ||
240 | requiredImports.forEach((node, key) => { | ||
241 | if (!imported.has(key)) { | ||
242 | imports.push(node); | ||
243 | } | ||
244 | }); | ||
245 | if (isParent(root)) { | ||
246 | root.children.unshift(...imports); | ||
247 | } | ||
248 | }; | ||
249 | } | ||
diff --git a/subprojects/docs/src/plugins/remarkPluginUtils.ts b/subprojects/docs/src/plugins/remarkPluginUtils.ts new file mode 100644 index 00000000..1dabcd11 --- /dev/null +++ b/subprojects/docs/src/plugins/remarkPluginUtils.ts | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * Copyright (c) Facebook, Inc. and its affiliates. | ||
3 | * Copyright (c) 2024 The Refinery Authors | ||
4 | * | ||
5 | * SPDX-License-Identifier: MIT AND EPL-2.0 | ||
6 | * | ||
7 | * This file is based on | ||
8 | * https://github.com/facebook/docusaurus/blob/e4ecffe41878728acff55a8370bd7440706c02f7/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts | ||
9 | */ | ||
10 | |||
11 | import type { Code, Literal } from 'mdast'; | ||
12 | import type { Node, Parent } from 'unist'; | ||
13 | |||
14 | export function isParent(node: Node): node is Parent { | ||
15 | return 'children' in node && Array.isArray(node.children); | ||
16 | } | ||
17 | |||
18 | export function replaceChildrenRecursively<T extends Node>( | ||
19 | node: Node, | ||
20 | shoulReplace: (node: Node) => node is T, | ||
21 | replacement: (node: T) => Node[], | ||
22 | ): boolean { | ||
23 | if (!isParent(node)) { | ||
24 | return false; | ||
25 | } | ||
26 | let didReplace = false; | ||
27 | let index = 0; | ||
28 | while (index < node.children.length) { | ||
29 | const child = node.children[index]; | ||
30 | if (child !== undefined && shoulReplace(child)) { | ||
31 | const result = replacement(child); | ||
32 | node.children.splice(index, 1, ...result); | ||
33 | index += result.length; | ||
34 | didReplace = true; | ||
35 | } else { | ||
36 | if ( | ||
37 | child !== undefined && | ||
38 | replaceChildrenRecursively(child, shoulReplace, replacement) | ||
39 | ) { | ||
40 | didReplace = true; | ||
41 | } | ||
42 | index += 1; | ||
43 | } | ||
44 | } | ||
45 | return didReplace; | ||
46 | } | ||
47 | |||
48 | export function isCode(node: Node): node is Code { | ||
49 | return node.type === 'code'; | ||
50 | } | ||
51 | |||
52 | export function isLiteral(node: Node): node is Literal { | ||
53 | return node.type === 'mdxjsEsm'; | ||
54 | } | ||
55 | |||
56 | export function isImport(node: Node, importedPath: string): boolean { | ||
57 | return isLiteral(node) && node.value.includes(importedPath); | ||
58 | } | ||
diff --git a/subprojects/docs/src/plugins/remarkPosix2Windows.ts b/subprojects/docs/src/plugins/remarkPosix2Windows.ts index 784802f2..418869f3 100644 --- a/subprojects/docs/src/plugins/remarkPosix2Windows.ts +++ b/subprojects/docs/src/plugins/remarkPosix2Windows.ts | |||
@@ -2,33 +2,27 @@ | |||
2 | * Copyright (c) Facebook, Inc. and its affiliates. | 2 | * Copyright (c) Facebook, Inc. and its affiliates. |
3 | * Copyright (c) 2024 The Refinery Authors | 3 | * Copyright (c) 2024 The Refinery Authors |
4 | * | 4 | * |
5 | * SPDX-License-Identifier: EPL-2.0 | 5 | * SPDX-License-Identifier: MIT AND EPL-2.0 |
6 | * | 6 | * |
7 | * This file is based on | 7 | * This file is based on |
8 | * https://github.com/facebook/docusaurus/blob/e4ecffe41878728acff55a8370bd7440706c02f7/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts | 8 | * https://github.com/facebook/docusaurus/blob/e4ecffe41878728acff55a8370bd7440706c02f7/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts |
9 | * but was changed to conver shell commands to POSIX to Windows syntax. | 9 | * but was changed to convert shell commands to POSIX to Windows syntax. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | import type { Code, Literal } from 'mdast'; | 12 | import type { Code } from 'mdast'; |
13 | import type { MdxjsEsm, MdxJsxFlowElement } from 'mdast-util-mdx'; | 13 | import type { MdxjsEsm, MdxJsxFlowElement } from 'mdast-util-mdx'; |
14 | import type { Transformer } from 'unified'; | 14 | import type { Transformer } from 'unified'; |
15 | import type { Node, Parent } from 'unist'; | 15 | import type { Node } from 'unist'; |
16 | import { visit } from 'unist-util-visit'; | ||
17 | 16 | ||
18 | function isLiteral(node: Node): node is Literal { | 17 | import { |
19 | return node.type === 'mdxjsEsm'; | 18 | isCode, |
20 | } | 19 | isImport, |
20 | isParent, | ||
21 | replaceChildrenRecursively, | ||
22 | } from './remarkPluginUtils'; | ||
21 | 23 | ||
22 | function isTabImport(node: Node): boolean { | 24 | function isTabImport(node: Node): boolean { |
23 | return isLiteral(node) && node.value.includes('@theme/Tabs'); | 25 | return isImport(node, '@theme/Tabs'); |
24 | } | ||
25 | |||
26 | function isParent(node: Node): node is Parent { | ||
27 | return 'children' in node && Array.isArray(node.children); | ||
28 | } | ||
29 | |||
30 | function isCode(node: Node): node is Code { | ||
31 | return node.type === 'code'; | ||
32 | } | 26 | } |
33 | 27 | ||
34 | function isPosix2Windows(node: Node): node is Code { | 28 | function isPosix2Windows(node: Node): node is Code { |
@@ -137,27 +131,17 @@ function createImportNode(): MdxjsEsm { | |||
137 | 131 | ||
138 | export default function remarkPosix2Windows(): Transformer { | 132 | export default function remarkPosix2Windows(): Transformer { |
139 | return (root) => { | 133 | return (root) => { |
140 | let transformed = false; | ||
141 | let alreadyImported = false; | 134 | let alreadyImported = false; |
142 | visit(root, (node) => { | 135 | const transformed = replaceChildrenRecursively( |
143 | if (isTabImport(node)) { | 136 | root, |
144 | alreadyImported = true; | 137 | (node) => { |
145 | } | 138 | if (isTabImport(node)) { |
146 | if (isParent(node)) { | 139 | alreadyImported = true; |
147 | let index = 0; | ||
148 | while (index < node.children.length) { | ||
149 | const child = node.children[index]; | ||
150 | if (child !== undefined && isPosix2Windows(child)) { | ||
151 | const result = transformNode(child); | ||
152 | node.children.splice(index, 1, ...result); | ||
153 | index += result.length; | ||
154 | transformed = true; | ||
155 | } else { | ||
156 | index += 1; | ||
157 | } | ||
158 | } | 140 | } |
159 | } | 141 | return isPosix2Windows(node); |
160 | }); | 142 | }, |
143 | transformNode, | ||
144 | ); | ||
161 | if (transformed && !alreadyImported) { | 145 | if (transformed && !alreadyImported) { |
162 | if (isParent(root)) { | 146 | if (isParent(root)) { |
163 | root.children.unshift(createImportNode()); | 147 | root.children.unshift(createImportNode()); |
diff --git a/subprojects/docs/src/plugins/remarkRefinery.ts b/subprojects/docs/src/plugins/remarkRefinery.ts new file mode 100644 index 00000000..c8668dc7 --- /dev/null +++ b/subprojects/docs/src/plugins/remarkRefinery.ts | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import { Zstd } from '@hpcc-js/wasm-zstd'; | ||
8 | import type { Code } from 'mdast'; | ||
9 | import type { MdxjsEsm, MdxJsxFlowElement } from 'mdast-util-mdx'; | ||
10 | import type { Transformer } from 'unified'; | ||
11 | import type { Node } from 'unist'; | ||
12 | |||
13 | import { | ||
14 | isCode, | ||
15 | isImport, | ||
16 | isParent, | ||
17 | replaceChildrenRecursively, | ||
18 | } from './remarkPluginUtils'; | ||
19 | |||
20 | const componentName = 'TryInRefinery'; | ||
21 | const componentPath = `@site/src/components/${componentName}`; | ||
22 | |||
23 | function isTryInRefineryImport(node: Node): boolean { | ||
24 | return isImport(node, componentPath); | ||
25 | } | ||
26 | |||
27 | function createImportNode(): MdxjsEsm { | ||
28 | const componentPathString = JSON.stringify(componentPath); | ||
29 | return { | ||
30 | type: 'mdxjsEsm', | ||
31 | value: `import ${componentName} from ${componentPathString}`, | ||
32 | data: { | ||
33 | estree: { | ||
34 | type: 'Program', | ||
35 | body: [ | ||
36 | { | ||
37 | type: 'ImportDeclaration', | ||
38 | specifiers: [ | ||
39 | { | ||
40 | type: 'ImportDefaultSpecifier', | ||
41 | local: { type: 'Identifier', name: componentName }, | ||
42 | }, | ||
43 | ], | ||
44 | source: { | ||
45 | type: 'Literal', | ||
46 | value: componentPath, | ||
47 | raw: componentPathString, | ||
48 | }, | ||
49 | }, | ||
50 | ], | ||
51 | sourceType: 'module', | ||
52 | }, | ||
53 | }, | ||
54 | }; | ||
55 | } | ||
56 | |||
57 | function isRefineryCode(node: Node): node is Code { | ||
58 | return isCode(node) && node.lang === 'refinery'; | ||
59 | } | ||
60 | |||
61 | class LiterateTransformer { | ||
62 | needsImport = false; | ||
63 | |||
64 | private saved = new Map<string, string>(); | ||
65 | |||
66 | private last: string | undefined; | ||
67 | |||
68 | constructor(private readonly zstd: Zstd) {} | ||
69 | |||
70 | transform(code: Code): Node[] { | ||
71 | const { meta } = code; | ||
72 | if (meta === undefined || meta === null) { | ||
73 | return [code]; | ||
74 | } | ||
75 | let value = `${code.value.trim()}\n`; | ||
76 | |||
77 | const continueMatch = meta.match(/\bcontinue(?:=(\w+))?\b/); | ||
78 | if (continueMatch !== null) { | ||
79 | let prefix: string; | ||
80 | const continueID = continueMatch[1]; | ||
81 | if (continueID === undefined) { | ||
82 | if (this.last === undefined) { | ||
83 | throw new Error('No code block to continue'); | ||
84 | } | ||
85 | prefix = this.last; | ||
86 | } else { | ||
87 | const savedPrefix = this.saved.get(continueID); | ||
88 | if (savedPrefix === undefined) { | ||
89 | throw new Error( | ||
90 | `Checkpoint to continue '${continueID}' was not found`, | ||
91 | ); | ||
92 | } | ||
93 | prefix = savedPrefix; | ||
94 | } | ||
95 | value = `${prefix}\n${value}`; | ||
96 | } | ||
97 | |||
98 | this.last = value; | ||
99 | |||
100 | const checkpointMatch = meta.match(/\bcheckpoint=(\w+)\b/); | ||
101 | if (checkpointMatch !== null && checkpointMatch[1] !== undefined) { | ||
102 | if (this.saved.has(checkpointMatch[1])) { | ||
103 | throw new Error(`Duplicate checkpoint ID ${checkpointMatch[1]}`); | ||
104 | } | ||
105 | this.saved.set(checkpointMatch[1], value); | ||
106 | } | ||
107 | |||
108 | const result: Node[] = []; | ||
109 | |||
110 | if (!/\bhidden\b/.test(meta)) { | ||
111 | result.push(code); | ||
112 | } | ||
113 | |||
114 | if (/\btry\b/.test(meta)) { | ||
115 | this.needsImport = true; | ||
116 | result.push({ | ||
117 | type: 'mdxJsxFlowElement', | ||
118 | name: componentName, | ||
119 | attributes: [ | ||
120 | { | ||
121 | type: 'mdxJsxAttribute', | ||
122 | name: 'href', | ||
123 | value: this.compressURL(value), | ||
124 | }, | ||
125 | ], | ||
126 | } as MdxJsxFlowElement); | ||
127 | } | ||
128 | |||
129 | return result; | ||
130 | } | ||
131 | |||
132 | private compressURL(value: string): string { | ||
133 | const encoder = new TextEncoder(); | ||
134 | const encodedBuffer = encoder.encode(value); | ||
135 | const compressedBuffer = this.zstd.compress(encodedBuffer, 20); | ||
136 | const base64 = Buffer.from(compressedBuffer).toString('base64url'); | ||
137 | return `https://refinery.services/#/1/${base64}`; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | export default async function loadRemarkRefineryPlugin(): Promise< | ||
142 | () => Transformer | ||
143 | > { | ||
144 | const zstd = await Zstd.load(); | ||
145 | return () => (root) => { | ||
146 | const transformer = new LiterateTransformer(zstd); | ||
147 | let alreadyImported = false; | ||
148 | replaceChildrenRecursively( | ||
149 | root, | ||
150 | (node) => { | ||
151 | if (isTryInRefineryImport(node)) { | ||
152 | alreadyImported = true; | ||
153 | } | ||
154 | return isRefineryCode(node); | ||
155 | }, | ||
156 | (node) => transformer.transform(node), | ||
157 | ); | ||
158 | if (transformer.needsImport && !alreadyImported) { | ||
159 | if (isParent(root)) { | ||
160 | root.children.unshift(createImportNode()); | ||
161 | } | ||
162 | } | ||
163 | }; | ||
164 | } | ||
diff --git a/subprojects/docs/src/plugins/remarkReplaceVariables.ts b/subprojects/docs/src/plugins/remarkReplaceVariables.ts new file mode 100644 index 00000000..5c0ae725 --- /dev/null +++ b/subprojects/docs/src/plugins/remarkReplaceVariables.ts | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | import type { PropertiesFile } from 'java-properties'; | ||
8 | import type { Transformer } from 'unified'; | ||
9 | import { visit } from 'unist-util-visit'; | ||
10 | |||
11 | const TEMPLATE_REGEXP = /@@@([a-zA-Z\d._-]*)@@@/g; | ||
12 | |||
13 | export default function remarkReplaceVariables({ | ||
14 | properties, | ||
15 | }: { | ||
16 | properties: PropertiesFile; | ||
17 | }): Transformer { | ||
18 | if (properties === undefined) { | ||
19 | throw new Error('propertiesPath is required'); | ||
20 | } | ||
21 | |||
22 | function substitution(substring: string, name: string): string { | ||
23 | if (name === '') { | ||
24 | // Escape sequence. | ||
25 | return '@@@'; | ||
26 | } | ||
27 | const value = properties.get(name); | ||
28 | if (value !== undefined) { | ||
29 | return String(value); | ||
30 | } | ||
31 | return substring; | ||
32 | } | ||
33 | |||
34 | return (root) => { | ||
35 | visit(root, (node) => { | ||
36 | if (!('value' in node)) { | ||
37 | return; | ||
38 | } | ||
39 | const { value } = node; | ||
40 | if (typeof value !== 'string') { | ||
41 | return; | ||
42 | } | ||
43 | /* eslint-disable-next-line no-param-reassign -- | ||
44 | * Here we are transforming the mdast `node` in-place. | ||
45 | */ | ||
46 | node.value = value.replace(TEMPLATE_REGEXP, substitution); | ||
47 | }); | ||
48 | }; | ||
49 | } | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/commands.md b/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/commands.md new file mode 100644 index 00000000..7ffe2a6c --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/commands.md | |||
@@ -0,0 +1,158 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | title: Build commands | ||
6 | --- | ||
7 | |||
8 | # Building from the command line | ||
9 | |||
10 | ## Gradle commands | ||
11 | |||
12 | We use [Gradle](https://gradle.org/) to manage the compilation and tests of Refinery. | ||
13 | |||
14 | Java code is built directly by Gradle. | ||
15 | We use the [frontend-gradle-plugin](https://siouan.github.io/frontend-gradle-plugin/) to manage a [Node.js](https://nodejs.org/en) and [Yarn](https://yarnpkg.com/) installation, which in turn is used to build TypeScript code (including this documentation website). | ||
16 | Typically, Yarn commands are issued by Gradle and you don't need to work with the TypeScript build system directly if you're only working on the Java parts of Refinery. | ||
17 | |||
18 | ### `build` | ||
19 | |||
20 | ```bash posix2windows | ||
21 | ./gradlew build | ||
22 | ``` | ||
23 | |||
24 | Compile all code, run all tests, and produce all build artifacts. | ||
25 | |||
26 | You should run this command before submitting a [Pull request](https://github.com/graphs4value/refinery/pulls) to make sure that all code builds and tests pass on your local machine. | ||
27 | This will also be run by GitHub Actions for each commit or pull requests. | ||
28 | |||
29 | ### `publishToMavenLocal` | ||
30 | |||
31 | ```bash posix2windows | ||
32 | ./gradlew publishToMavenLocal | ||
33 | ``` | ||
34 | |||
35 | Publishes the Refinery Java artifacts to the [Maven local repository](https://www.baeldung.com/maven-local-repository). | ||
36 | |||
37 | Build tools, such as Gradle, will be able to consume such artifacts, which enables you to use the latest version of Refinery -- possibly including your own modifications -- in other Java projects. | ||
38 | For more information, see our [programming guide](/snapshot/develop/java). | ||
39 | |||
40 | ### `serve` | ||
41 | |||
42 | ```bash posix2windows | ||
43 | ./gradlew serve | ||
44 | ``` | ||
45 | |||
46 | Starts the Refinery backend and web interface on port 1312. | ||
47 | |||
48 | This task is ideal for running the Refinery backend if you don't intend to work on the frontend. | ||
49 | The Refinery frontend TypeScript projects is automatically built before the server starts. | ||
50 | The server will use the latest build output of the frontend as static assets. | ||
51 | |||
52 | The behavior of this task is influenced by the same [environmental variables](../../../learn/docker#environmental-variables) as the Refinery [Docker container](../../../learn/docker). | ||
53 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. | ||
54 | |||
55 | ### `serveBackend` | ||
56 | |||
57 | ```bash posix2windows | ||
58 | ./gradlew serveBackend | ||
59 | ``` | ||
60 | |||
61 | Starts the Refinery backend on port 1312. | ||
62 | |||
63 | This task is ideal for running the Refinery backend if you're working on the frontend. | ||
64 | No static assets will be build. | ||
65 | You'll need to use [`yarnw frontend dev`](#frontend-dev) | ||
66 | |||
67 | Like [`./gradlew serve`](#serve), the behavior of this task is influenced by the same [environmental variables](../../../learn/docker#environmental-variables) as the Refinery [Docker container](../../../learn/docker). | ||
68 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. | ||
69 | |||
70 | ## Yarn commands | ||
71 | |||
72 | We provide a `yarnw` wrapper script to invoke the Yarn distribution installed by frontend-gradle-plugin directly. | ||
73 | The following commands can only be run once [`./gradlew build`](#build) has installed the necessary Node.js and Yarn packages. | ||
74 | |||
75 | ### `docs dev` | ||
76 | |||
77 | ```bash posix2windows | ||
78 | ./yarnw docs dev | ||
79 | ``` | ||
80 | |||
81 | Builds and serves this documentation in development mode on port 3000. | ||
82 | Saved changes to most documentation sources are immediately reflected in the browse without reloading. | ||
83 | |||
84 | You can set the port with the `-p` option, e.g. to use port 1313, use | ||
85 | |||
86 | ```bash posix2windows | ||
87 | ./yarnw docs dev -p 1313 | ||
88 | ``` | ||
89 | |||
90 | :::note | ||
91 | |||
92 | Running this command for the first time may generate error messages like | ||
93 | ``` | ||
94 | ERROR failed to read input source map: failed to parse inline source map url | ||
95 | ``` | ||
96 | which can be safely ignored. | ||
97 | |||
98 | ::: | ||
99 | |||
100 | ### `frontend dev` | ||
101 | |||
102 | ```bash posix2windows | ||
103 | ./yarnw frontend dev | ||
104 | ``` | ||
105 | |||
106 | Builds and serves the refinery frontend on port 1313. | ||
107 | Saved changes to most source files are immediately reflected in the browser without reload. | ||
108 | |||
109 | Before running this command, you need to start [`./gradlew serveBackend`](#servebackend) to provide a backend for the frontend to connect to. | ||
110 | The development server of the frontend will proxy all WebSocket connections to the backend. | ||
111 | |||
112 | The following environmental variables influence the behavior of this command: | ||
113 | |||
114 | #### `REFINERY_LISTEN_HOST` | ||
115 | |||
116 | Hostname to listen at for incoming HTTP connections. | ||
117 | |||
118 | **Default value:** `localhost` | ||
119 | |||
120 | #### `REFINERY_LISTEN_PORT` | ||
121 | |||
122 | TCP port to listen at for incoming HTTP connections. | ||
123 | |||
124 | **Default value:** `1313` | ||
125 | |||
126 | #### `REFINERY_BACKEND_HOST` | ||
127 | |||
128 | Hostname of the Refinery backend. | ||
129 | |||
130 | This should match the `REFINERY_LISTEN_HOST` passed to [`./gradlew serveBackend`](#servebackend). | ||
131 | |||
132 | **Default value:** `127.0.0.1` (connect to `localhost` over IPv4 only) | ||
133 | |||
134 | #### `REFINERY_LISTEN_PORT` | ||
135 | |||
136 | TCP port of the Refinery backend. | ||
137 | |||
138 | This should match the `REFINERY_LISTEN_PORT` passed to [`./gradlew serveBackend`](#servebackend). | ||
139 | |||
140 | **Default value:** `1312` | ||
141 | |||
142 | #### `REFINERY_PUBLIC_HOST` | ||
143 | |||
144 | Publicly visible hostname of the Refinery instance. | ||
145 | |||
146 | If you use a reverse proxy in front of the development server, you must set this variable. | ||
147 | Otherwise, connections to the development server will fail due to cross-origin protection. | ||
148 | |||
149 | **Default value:** equal to `REFINERY_LISTEN_HOST` | ||
150 | |||
151 | #### `REFINERY_PUBLIC_PORT` | ||
152 | |||
153 | Publicly visible port of the Refinery instance. | ||
154 | |||
155 | If you use a reverse proxy in front of the development server, you must set this variable. | ||
156 | Otherwise, connections to the development server will fail due to cross-origin protection. | ||
157 | |||
158 | **Default value:** equal to `REFINERY_LISTEN_PORT` | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/ide-setup.md b/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/ide-setup.md new file mode 100644 index 00000000..d5dd4eb5 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/ide-setup.md | |||
@@ -0,0 +1,96 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2021-2023 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 2 | ||
5 | title: IDE setup | ||
6 | --- | ||
7 | |||
8 | # Setting up the development environment | ||
9 | |||
10 | ## IntelliJ IDEA | ||
11 | |||
12 | We prefer [IntelliJ IDEA](https://www.jetbrains.com/idea/) as a Java development environment. | ||
13 | No special preparations should be necessary for importing the project as a Gradle project into IDEA: | ||
14 | |||
15 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery on obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later). | ||
16 | |||
17 | 2. Clone the project git repository and open it in IntelliJ IDEA. Make sure to _open_ the project instead of creating a _new_ one in the same directory. | ||
18 | |||
19 | 3. IntelliJ IDEA should build and index the project. If there are errors, it is likely that the `JAVA_HOME` was incorrectly set: | ||
20 | * In _Project Structure > Project settings > Project > SDK_, a Java 21 compatible JDK should be selected. | ||
21 | * In _Project Structure > Project settings > Project > Language level_, either _SDK default_ or _21_ should be selected. | ||
22 | * Make sure that each module in _Project Structure > Project settings > Module_ uses the _Project default_ language level in _Sources > Language level_ and the _Project SDK_ in _Dependencies > Module SDK._ | ||
23 | * In _Settings > Gradle settings > Gradle Projects > Gradle_, the _Distribution_ should be set to _Wrapper_ and the _Gradle JVM_ should be set to _Project SDK._ | ||
24 | |||
25 | 4. We recommend installing the latest _SonarLint_ plugin in _Settings > Plugins_ to get real-time code quality analysis in your IDE. | ||
26 | |||
27 | :::note | ||
28 | |||
29 | You'll need [Eclipse](#eclipse) to edit Xtext (`*.xtext`) and MWE2 (`*.mwe2`) files and Ecore class diagrams (`*.aird`, `*.ecore`, `*.genmodel`). | ||
30 | If you do not plan on making changes to such files, feel free to skip the Eclipse installation steps below. | ||
31 | |||
32 | You'll also need [VS Code](#vs-code) to edit the TypeScript code in Refinery. | ||
33 | |||
34 | ::: | ||
35 | |||
36 | ## Eclipse | ||
37 | |||
38 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery on obtaining the required JDK version. | ||
39 | |||
40 | 2. Download and extract the [Eclipse IDE for Java and DSL Developers 2023-12](https://www.eclipse.org/downloads/packages/release/2023-12/r/eclipse-ide-java-and-dsl-developers) package. | ||
41 | |||
42 | 3. Launch Eclipse and create a new workspace. | ||
43 | |||
44 | 4. Open _Help > Eclipse Marketplace_ and install the following software: | ||
45 | * _EclEmma Java Code Coverage_ | ||
46 | * _EcoreTools : Ecore Diagram Editor_ | ||
47 | * _Sirius_ (ignore the warning during installation about the solution _Sirius_ not being available) | ||
48 | * _SonarLint_ | ||
49 | |||
50 | 5. Open _Window > Preferences_ and set the following preferences: | ||
51 | * _General > Workspace > Text file encoding_ should be _UTF-8_. | ||
52 | * _General > Workspace > New text file line delimiter_ should be _Unix_. | ||
53 | * Add the JDK 21 to _Java > Installed JREs_. | ||
54 | * Make sure JDK 21 is selected for _JavaSE-21_ at _Java > Installed JREs > Execution Environments_. | ||
55 | * Set _Gradle > Java home_ to the `JAVA_HOME` directory (the directory which contains the `bin` directory) of JDK 21. Here, Buildship will show a yellow warning sign, which can be safely ignored. | ||
56 | * Set _Java > Compiler > JDK Compliance > Compiler compliance level_ to _21_. | ||
57 | |||
58 | 6. Clone the project Git repository but _do not_ import it into Eclipse yet. | ||
59 | |||
60 | 7. Open a new terminal and run | ||
61 | ```bash posix2windows | ||
62 | ./gradlew prepareEclipse | ||
63 | ``` | ||
64 | in the cloned repository. | ||
65 | * This should complete without any compilation errors. | ||
66 | * To troubleshoot any error, see the [instructions for compiling Refinery](/develop/contributing#compiling). | ||
67 | |||
68 | 8. Select _File > Import... > Gradle > Existing Gradle Project_ and import the cloned repository in Eclipse. | ||
69 | * Make sure to select the root of the repository (containing this file) as the _Project root directory_ and that the _Gradle distribution_ is _Gradle wrapper_. | ||
70 | * If you have previously imported the project into Eclipse, this step will likely fail. In that case, you should remove the projects from Eclipse, run `git clean -fxd` in the repository, and start over from step 8. | ||
71 | |||
72 | ## VS Code | ||
73 | |||
74 | We recommend [VSCodium](https://github.com/VSCodium/vscodium) or [Visual Studio Code](https://code.visualstudio.com/) to work with the parts of Refinery that are written is TypeScript. | ||
75 | |||
76 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery on obtaining the required JDK version. | ||
77 | |||
78 | 2. Install the following VS Code extensions: | ||
79 | * _EditorConfig for VS Code_ [[Open VSX](https://open-vsx.org/extension/EditorConfig/EditorConfig)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)] | ||
80 | * _ESLint_ [[Open VSX](https://open-vsx.org/extension/dbaeumer/vscode-eslint)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)] | ||
81 | * _MDX_ [[Open VSX](https://open-vsx.org/extension/unifiedjs/vscode-mdx)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx)] | ||
82 | * _Prettier - Code formatter_ [[Open VSX](https://open-vsx.org/extension/esbenp/prettier-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)] | ||
83 | * _XState VSCode_ [[Open VSX](https://open-vsx.org/extension/statelyai/stately-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)] | ||
84 | * _ZipFS - a zip file system_ [[Open VSX](https://open-vsx.org/extension/arcanis/vscode-zipfs)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs)] | ||
85 | |||
86 | 3. Clone the project Git repository but _do not_ import it into VS Code yet. | ||
87 | |||
88 | 4. Run | ||
89 | ```bash posix2windows | ||
90 | ./gradlew installFrontend | ||
91 | ``` | ||
92 | to install all required Node.js tooling. | ||
93 | |||
94 | 5. Open the repository with _Open Folder…_ in VS Code. | ||
95 | * When asked, select that you _Trust_ the folder. | ||
96 | * When asked, enable using the TypeScript and ESLint tooling specified in the repository. | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/index.md b/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/index.md new file mode 100644 index 00000000..aa0bdb2f --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/develop/contributing/index.md | |||
@@ -0,0 +1,59 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | title: Contributing | ||
6 | --- | ||
7 | |||
8 | import TabItem from '@theme/TabItem'; | ||
9 | import Tabs from '@theme/Tabs'; | ||
10 | |||
11 | # Contributing to Refinery | ||
12 | |||
13 | You can clone the refinery repository from GitHub at https://github.com/graphs4value/refinery. | ||
14 | If you want to contribute code, we recommend [forking](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the repository on GitHub so that you can submit a [pull request](https://github.com/graphs4value/refinery/pulls) later. | ||
15 | |||
16 | ## Required tools | ||
17 | |||
18 | Refinery is written in Java and TypeScript. To build Refinery, you'll need a **Java 21** compatible **Java Development Kit (JDK).** We recommend the [Adoptium Java 21 JDK](https://adoptium.net/) or the [Amazon Corretto Java 21 JDK](https://aws.amazon.com/corretto/). | ||
19 | |||
20 | ## Compiling Refinery {#compiling} | ||
21 | |||
22 | To build Refinery, run the command | ||
23 | ```bash posix2windows | ||
24 | ./gradlew build | ||
25 | ``` | ||
26 | in the cloned repository. | ||
27 | |||
28 | This should complete without any compilation errors. | ||
29 | |||
30 | If you get any errors about the JVM version, check whether the `JAVA_HOME` environment variable is set to the location of **JDK 21**. You can query the variable with | ||
31 | <Tabs groupId="posix2windows"> | ||
32 | <TabItem value="posix" label="Linux or macOS"> | ||
33 | ```bash | ||
34 | echo $JAVA_HOME | ||
35 | ``` | ||
36 | </TabItem> | ||
37 | <TabItem value="windows" label="Windows (PowerShell)"> | ||
38 | ```bash | ||
39 | echo $Env:JAVA_HOME | ||
40 | ``` | ||
41 | </TabItem> | ||
42 | </Tabs> | ||
43 | To set the `JAVA_HOME` environmental variable, use | ||
44 | <Tabs groupId="posix2windows"> | ||
45 | <TabItem value="posix" label="Linux or macOS"> | ||
46 | ```bash | ||
47 | export JAVA_HOME=/java/path/here | ||
48 | ``` | ||
49 | </TabItem> | ||
50 | <TabItem value="windows" label="Windows (PowerShell)"> | ||
51 | ```bash | ||
52 | $Env:JAVA_HOME="C:\java\path\here" | ||
53 | ``` | ||
54 | </TabItem> | ||
55 | </Tabs> | ||
56 | |||
57 | If the build fails with a `Host name must not be empty` error, you [might need to remove the empty proxy configuration from your global `gradle.properties` file](https://stackoverflow.com/a/62128323). | ||
58 | |||
59 | For further information, see the [supported build commands](/develop/contributing/commands) and the [instructions for setting up an IDE](/develop/contributing/ide-setup). | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/develop/java.md b/subprojects/docs/versioned_docs/version-0.1.0/develop/java.md new file mode 100644 index 00000000..26ba89f5 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/develop/java.md | |||
@@ -0,0 +1,351 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 0 | ||
5 | --- | ||
6 | |||
7 | # Programming guide | ||
8 | |||
9 | This guide is aimed at developers who wish to create Java applications that leverage Refinery as a library. | ||
10 | We also recommend browsing the [Javadoc documentation](../javadoc) associated with Refinery components. | ||
11 | See the [contributor's guide](../contributing) for information on building and modifying Refinery itself. | ||
12 | |||
13 | :::note | ||
14 | |||
15 | Refinery can run as a cloud-based [_Graph Solver as a Service_](https://refinery.services/) without local installation. | ||
16 | You can also run a compiled version as a [Docker container](../../learn/docker). | ||
17 | |||
18 | ::: | ||
19 | |||
20 | Below, you can find instructions on using [Gradle](#gradle) or [Apache Maven](#maven) to create applications that use Refinery as a Java library. | ||
21 | |||
22 | ## Working with Gradle {#gradle} | ||
23 | |||
24 | We recommend [Gradle](https://gradle.org/) as a build system for creating Java programs that use Refinery as a library. | ||
25 | We created a [Gradle plugin](pathname://../javadoc/refinery-gradle-plugins/) to simplify project configuration. | ||
26 | |||
27 | To find out the configuration for using our artifacts, select whether you use a Kotlin-based (`.gradle.kts`) or a Groovy-based (`.gradle`) configuration format for your Gradle build. You should add this code to your Gradle *settings* file, which is named `settings.gradle.kts` or `settings.gradle`. | ||
28 | |||
29 | import TabItem from '@theme/TabItem'; | ||
30 | import Tabs from '@theme/Tabs'; | ||
31 | |||
32 | <Tabs groupId="gradleLanguage"> | ||
33 | <TabItem value="kotlin" label="Kotlin"> | ||
34 | ```kotlin title="settings.gradle.kts" | ||
35 | plugins { | ||
36 | id("tools.refinery.settings") version "0.1.0" | ||
37 | } | ||
38 | ``` | ||
39 | </TabItem> | ||
40 | <TabItem value="groovy" label="Groovy"> | ||
41 | ```groovy title="settings.gradle" | ||
42 | plugins { | ||
43 | id 'tools.refinery.settings' version '0.1.0' | ||
44 | } | ||
45 | ``` | ||
46 | </TabItem> | ||
47 | </Tabs> | ||
48 | |||
49 | This plugin will perform the following actions automatically: | ||
50 | * Add a [version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:sharing-catalogs) to your build to enable easy access to Refinery artifacts and their [dependencies](#declaring-dependencies). | ||
51 | * Lock refinery artifacts and their dependencies to a [platform](https://docs.gradle.org/current/userguide/platforms.html#sub:using-platform-to-control-transitive-deps) (Maven BOM) of tested versions. | ||
52 | * Configure a logger based on [Log4J over SLF4J](https://www.slf4j.org/legacy.html) and [SLF4J Simple](https://www.slf4j.org/apidocs/org/slf4j/simple/SimpleLogger.html) that is free from vulnerabilities and works out of the box for most use-cases. | ||
53 | * Generate [application](#building-applications) artifacts, if any, according to best practices used in the Refinery project. | ||
54 | * Add common dependencies for writing [unit tests](#writing-tests) for your Java code. | ||
55 | |||
56 | See the [multi-module projects](#multi-module-projects) section of this tutorial on how to disable some of these automated actions for a part of your build. | ||
57 | |||
58 | ### Declaring dependencies | ||
59 | |||
60 | The Refinery Gradle plugins adds a [version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:sharing-catalogs) named `refinery` that you can use to quickly access dependencies. | ||
61 | For example, to add a dependency to the [`tools.refinery:refinery-generator`](pathname://../javadoc/refinery-generator/) library, you add the following to your `build.gradle.kts` or `build.gradle` file: | ||
62 | |||
63 | <Tabs groupId="gradleLanguage"> | ||
64 | <TabItem value="kotlin" label="Kotlin"> | ||
65 | ```kotlin title="build.gradle.kts" | ||
66 | depndencies { | ||
67 | implementation(refinery.generator) | ||
68 | } | ||
69 | ``` | ||
70 | </TabItem> | ||
71 | <TabItem value="groovy" label="Groovy"> | ||
72 | ```groovy title="build.gradle" | ||
73 | dependencies { | ||
74 | implementation refinery.generator | ||
75 | } | ||
76 | ``` | ||
77 | </TabItem> | ||
78 | </Tabs> | ||
79 | |||
80 | The version catalog also contains the external dependencies used by the Refinery framework. | ||
81 | For example, you may add [GSON](https://google.github.io/gson/) for JSON parsing and [JCommander](https://jcommander.org/) for command-line argument parsing as follows: | ||
82 | |||
83 | <Tabs groupId="gradleLanguage"> | ||
84 | <TabItem value="kotlin" label="Kotlin"> | ||
85 | ```kotlin title="build.gradle.kts" | ||
86 | depndencies { | ||
87 | implementation(refinery.gson) | ||
88 | implementation(refinery.jcommander) | ||
89 | } | ||
90 | ``` | ||
91 | </TabItem> | ||
92 | <TabItem value="groovy" label="Groovy"> | ||
93 | ```groovy title="build.gradle" | ||
94 | dependencies { | ||
95 | implementation refinery.gson | ||
96 | implementation refinery.jcommander | ||
97 | } | ||
98 | ``` | ||
99 | </TabItem> | ||
100 | </Tabs> | ||
101 | |||
102 | ### Building applications | ||
103 | |||
104 | You can use the built-in [`application`](https://docs.gradle.org/current/userguide/application_plugin.html) to build stand-alone Java applications. | ||
105 | |||
106 | When developing you main application code in the `src/main/java` directory of you project, you can use the [`StandaloneRefinery`](pathname://../javadoc/refinery-generator/tools/refinery/generator/standalone/StandaloneRefinery.html) class from [`tools.refinery:refinery-generator`](pathname://../javadoc/refinery-generator/) to access Refinery generator components. See the tutorial on Xtext's [dependency injection](https://eclipse.dev/Xtext/documentation/302_configuration.html#dependency-injection) for more advanced use-cases. | ||
107 | |||
108 | ```java | ||
109 | package org.example; | ||
110 | |||
111 | import tools.refinery.generator.standalone.StandaloneRefinery; | ||
112 | |||
113 | import java.io.IOException; | ||
114 | |||
115 | public class ExampleMain { | ||
116 | public static void main(String[] args) throws IOException { | ||
117 | var problem = StandaloneRefinery.getProblemLoader().loadString(""" | ||
118 | class Filesystem { | ||
119 | contains Directory[1] root | ||
120 | } | ||
121 | |||
122 | class File. | ||
123 | |||
124 | class Directory extends File { | ||
125 | contains Directory[] children | ||
126 | } | ||
127 | |||
128 | scope Filesystem = 1, File = 20. | ||
129 | """); | ||
130 | var generator = StandaloneRefinery.getGeneratorFactory().createGenerator(problem); | ||
131 | generator.generate(); | ||
132 | var trace = generator.getProblemTrace(); | ||
133 | var childrenRelation = trace.getPartialRelation("Directory::children"); | ||
134 | var childrenInterpretation = generator.getPartialInterpretation(childrenRelation); | ||
135 | var cursor = childrenInterpretation.getAll(); | ||
136 | while (cursor.move()) { | ||
137 | System.out.printf("%s: %s%n", cursor.getKey(), cursor.getValue()); | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | ``` | ||
142 | |||
143 | If you want to produce a "fat JAR" that embeds all dependencies (e.g., for invoking from the command line or from Python with a single command), you should also add the [shadow](https://github.com/Goooler/shadow) plugin. | ||
144 | The recommended version of the shadow plugin is set in our [version catalog](#declaring-dependencies). You can add it to your build script as follows: | ||
145 | |||
146 | <Tabs groupId="gradleLanguage"> | ||
147 | <TabItem value="kotlin" label="Kotlin"> | ||
148 | ```kotlin title="build.gradle.kts" | ||
149 | plugins { | ||
150 | application | ||
151 | alias(refinery.plugins.shadow) | ||
152 | } | ||
153 | |||
154 | application { | ||
155 | mainClass = "org.example.ExampleMain" | ||
156 | } | ||
157 | ``` | ||
158 | </TabItem> | ||
159 | <TabItem value="groovy" label="Groovy"> | ||
160 | ```groovy title="build.gradle" | ||
161 | plugins { | ||
162 | application | ||
163 | alias refinery.plugins.shadow | ||
164 | } | ||
165 | |||
166 | application { | ||
167 | mainClass 'org.example.ExampleMain' | ||
168 | } | ||
169 | ``` | ||
170 | </TabItem> | ||
171 | </Tabs> | ||
172 | |||
173 | After building your project with `./gradlew build`, you may find the produced "fat JAR" in the `build/libs` directory. | ||
174 | Its file name will be suffixed with `-all.jar`. | ||
175 | In you have Java 21 installed, you'll be able to run the application with the command | ||
176 | |||
177 | <Tabs groupId="posix2windows"> | ||
178 | <TabItem value="posix" label="Linux or macOS"> | ||
179 | ```bash | ||
180 | java -jar ./build/libs/example-0.0.0-SNAPSHOT-all.jar | ||
181 | ``` | ||
182 | </TabItem> | ||
183 | <TabItem value="windows" label="Windows (PowerShell)"> | ||
184 | ```bash | ||
185 | java -jar .\build\libs\example-0.0.0-SNAPSHOT-all.jar | ||
186 | ``` | ||
187 | </TabItem> | ||
188 | </Tabs> | ||
189 | |||
190 | Be sure to replace `example-0.0.0-SNAPSHOT` with the name and version of your project. | ||
191 | |||
192 | ### Writing tests | ||
193 | |||
194 | Our Gradle plugin automatically sets up [JUnit 5](https://junit.org/junit5/) for writing tests and [parameterized tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests). | ||
195 | It also sets up [Hamcrest](https://hamcrest.org/JavaHamcrest/) for writing assertions. | ||
196 | You should put your test files into the `src/test/java` directory in your projects. | ||
197 | You may run test with the commands `./gradlew test` or `./gradlew build`. | ||
198 | |||
199 | To ensure that your tests are properly isolated, you should *not* rely on the [`StandaloneRefinery`](pathname://../javadoc/refinery-generator/tools/refinery/generator/standalone/StandaloneRefinery.html) class from [`tools.refinery:refinery-generator`](pathname://../javadoc/refinery-generator/) when accessing Refinery generator components. | ||
200 | Instead, you should use Xtext's [dependency injection](https://eclipse.dev/Xtext/documentation/302_configuration.html#dependency-injection) and [unit testing](https://eclipse.dev/Xtext/documentation/103_domainmodelnextsteps.html#tutorial-unit-tests) support to instantiate the components. You'll need to add a dependency to Refinery's Xtext testing support library to your project. | ||
201 | |||
202 | <Tabs groupId="gradleLanguage"> | ||
203 | <TabItem value="kotlin" label="Kotlin"> | ||
204 | ```kotlin title="build.gradle.kts" | ||
205 | depndencies { | ||
206 | implementation(refinery.generator) | ||
207 | // highlight-next-line | ||
208 | testImplementation(testFixtures(refinery.language)) | ||
209 | } | ||
210 | ``` | ||
211 | </TabItem> | ||
212 | <TabItem value="groovy" label="Groovy"> | ||
213 | ```groovy title="build.gradle" | ||
214 | dependencies { | ||
215 | implementation refinery.generator | ||
216 | // highlight-next-line | ||
217 | testImplementation testFixtures(refinery.language) | ||
218 | } | ||
219 | ``` | ||
220 | </TabItem> | ||
221 | </Tabs> | ||
222 | |||
223 | Afterwards, you can use the `@ExtendWith`, `@InjectWith`, and `@Inject` annotations to set up your unit test. | ||
224 | |||
225 | ```java | ||
226 | package org.example; | ||
227 | |||
228 | import com.google.inject.Inject; | ||
229 | import org.eclipse.xtext.testing.InjectWith; | ||
230 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
231 | import org.junit.jupiter.api.Test; | ||
232 | import org.junit.jupiter.api.extension.ExtendWith; | ||
233 | import tools.refinery.generator.GeneratorResult; | ||
234 | import tools.refinery.generator.ModelGeneratorFactory; | ||
235 | import tools.refinery.generator.ProblemLoader; | ||
236 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
237 | |||
238 | import java.io.IOException; | ||
239 | |||
240 | import static org.hamcrest.MatcherAssert.assertThat; | ||
241 | import static org.hamcrest.Matchers.is; | ||
242 | |||
243 | // highlight-start | ||
244 | @ExtendWith(InjectionExtension.class) | ||
245 | @InjectWith(ProblemInjectorProvider.class) | ||
246 | // highlight-end | ||
247 | class ExampleTest { | ||
248 | // highlight-start | ||
249 | @Inject | ||
250 | private ProblemLoader problemLoader; | ||
251 | |||
252 | @Inject | ||
253 | private ModelGeneratorFactory generatorFactory; | ||
254 | // highlight-end | ||
255 | |||
256 | @Test | ||
257 | void testModelGeneration() throws IOException { | ||
258 | var problem = problemLoader.loadString(""" | ||
259 | class Filesystem { | ||
260 | contains Directory[1] root | ||
261 | } | ||
262 | |||
263 | class File. | ||
264 | |||
265 | class Directory extends File { | ||
266 | contains Directory[] children | ||
267 | } | ||
268 | |||
269 | scope Filesystem = 1, File = 20. | ||
270 | """); | ||
271 | var generator = generatorFactory.createGenerator(problem); | ||
272 | var result = generator.tryGenerate(); | ||
273 | assertThat(result, is(GeneratorResult.SUCCESS)); | ||
274 | } | ||
275 | } | ||
276 | ``` | ||
277 | |||
278 | ### Multi-module projects | ||
279 | |||
280 | By default, the `tools.refinery.settings` plugin will apply our `tools.refinery.java` plugin to all Java projects in your build and configure them for use with Refinery. This is sufficient for single-module Java projects, and multi-module projects where all of your Java modules use Refinery. | ||
281 | |||
282 | If you wish to use Refinery in only some modules in your multi-module project, you can disable this behavior by adding | ||
283 | |||
284 | ```ini title="gradle.properties" | ||
285 | tools.refinery.gradle.auto-apply=false | ||
286 | ``` | ||
287 | |||
288 | to the `gradle.properties` file in the root directory of your project. | ||
289 | |||
290 | If you use this setting, you'll need to add the `tools.refinery.java` plugin manually to any Java projects where you want to use Refinery like this: | ||
291 | |||
292 | <Tabs groupId="gradleLanguage"> | ||
293 | <TabItem value="kotlin" label="Kotlin"> | ||
294 | ```kotlin title="build.gradle.kts" | ||
295 | plugins { | ||
296 | id("tools.refinery.java") | ||
297 | } | ||
298 | ``` | ||
299 | </TabItem> | ||
300 | <TabItem value="groovy" label="Groovy"> | ||
301 | ```groovy title="build.gradle" | ||
302 | plugins { | ||
303 | id 'tools.refinery.java' | ||
304 | } | ||
305 | ``` | ||
306 | </TabItem> | ||
307 | </Tabs> | ||
308 | |||
309 | Do *not* attempt to set a `version` for this plugin, because versioning is already managed by the `tools.refinery.settings` plugin. Trying to set a version for the `tools.refinery.java` plugin separately will result in a Gradle error. | ||
310 | |||
311 | ## Working with Maven {#maven} | ||
312 | |||
313 | You may also develop applications based on Refiney using [Apache Maven](https://maven.apache.org/) as the build system. | ||
314 | Although we don't provide a Maven plugin for simplified configuration, you can still use our [platform](https://docs.gradle.org/current/userguide/platforms.html#sub:using-platform-to-control-transitive-deps) (Maven BOM) to lock the versions of Refinery and its dependencies to tested versions. | ||
315 | |||
316 | You should add the following configuration to your `pom.xml` file. If you use multi-module projects, we recommend that you add this to your parent POM. | ||
317 | |||
318 | ```xml title="pom.xml" | ||
319 | <project> | ||
320 | ... | ||
321 | <dependencyManagement> | ||
322 | <dependencies> | ||
323 | <dependency> | ||
324 | <groupId>tools.refinery</groupId> | ||
325 | <artifactId>refinery-bom</artifactId> | ||
326 | <version>0.1.0</version> | ||
327 | <type>pom</type> | ||
328 | <scope>import</scope> | ||
329 | </dependency> | ||
330 | </dependencies> | ||
331 | </dependencyManagement> | ||
332 | ... | ||
333 | </project> | ||
334 | ``` | ||
335 | |||
336 | You'll be able to add dependencies to Refinery components without an explicit reference to the dependency version, since version numbers are managed by the BOM: | ||
337 | |||
338 | ```xml title="pom.xml" | ||
339 | <project> | ||
340 | ... | ||
341 | <dependencies> | ||
342 | <dependency> | ||
343 | <groupId>tools.refinery</groupId> | ||
344 | <artifactId>refinery-generator</artifactId> | ||
345 | </dependency> | ||
346 | </dependencies> | ||
347 | ... | ||
348 | </project> | ||
349 | ``` | ||
350 | |||
351 | However, since the Maven BOM doesn't offer additional configuration, you'll have to take care of tasks such as configuring logging and testing, as well as building applications yourself. | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/develop/javadoc.md b/subprojects/docs/versioned_docs/version-0.1.0/develop/javadoc.md new file mode 100644 index 00000000..9a488bd7 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/develop/javadoc.md | |||
@@ -0,0 +1,41 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: API documentation for Refinery components automatically generated by Javadoc | ||
5 | sidebar_position: 998 | ||
6 | --- | ||
7 | |||
8 | # Javadoc | ||
9 | |||
10 | Here you can find API documentation for Refinery components automatically generated by Javadoc. We recommend reading the [Programming guide](../java/) first to understand how to use these components. | ||
11 | |||
12 | # Refinery | ||
13 | |||
14 | * [`tools.refinery:refinery-generator:0.1.0`](pathname://refinery-generator/) | ||
15 | * [`tools.refinery:refinery-gradle-plugins:0.1.0`](pathname://refinery-gradle-plugins/) | ||
16 | * [`tools.refinery:refinery-language:0.1.0`](pathname://refinery-language/) | ||
17 | * [`tools.refinery:refinery-language-ide:0.1.0`](pathname://refinery-language-ide/) | ||
18 | * [`tools.refinery:refinery-language-model:0.1.0`](pathname://refinery-language-model/) | ||
19 | * [`tools.refinery:refinery-language-semantics:0.1.0`](pathname://refinery-language-semantics/) | ||
20 | * [`tools.refinery:refinery-logic:0.1.0`](pathname://refinery-logic/) | ||
21 | * [`tools.refinery:refinery-store:0.1.0`](pathname://refinery-store/) | ||
22 | * [`tools.refinery:refinery-store-dse:0.1.0`](pathname://refinery-store-dse/) | ||
23 | * [`tools.refinery:refinery-store-dse-visualization:0.1.0`](pathname://refinery-store-dse-visualization/) | ||
24 | * [`tools.refinery:refinery-store-query:0.1.0`](pathname://refinery-store-query/) | ||
25 | * [`tools.refinery:refinery-store-query-interpreter:0.1.0`](pathname://refinery-store-query-interpreter/) | ||
26 | * [`tools.refinery:refinery-store-reasoning:0.1.0`](pathname://refinery-store-reasoning/) | ||
27 | * [`tools.refinery:refinery-store-reasoning-scope:0.1.0`](pathname://refinery-store-reasoning-scope/) | ||
28 | * [`tools.refinery:refinery-store-reasoning-smt:0.1.0`](pathname://refinery-store-reasoning-smt/) | ||
29 | |||
30 | # Interpreter | ||
31 | |||
32 | :::note | ||
33 | |||
34 | The _Refinery Interpreter_ is modified version of [VIATRA™](https://eclipse.dev/viatra/) specifically for use in Refinery. If you're interested in learning about [VIATRA™](https://eclipse.dev/viatra/), we recommend the [VIATRA™ documentation](https://eclipse.dev/viatra/documentation/index.html) and [source code](https://github.com/eclipse-viatra/org.eclipse.viatra) instead. Eclipse®, VIATRA™ and ‘Eclipse VIATRA™’ are trademarks of Eclipse Foundation, Inc. | ||
35 | |||
36 | ::: | ||
37 | |||
38 | * [`tools.refinery.interpreter:refinery-interpreter:0.28.1`](pathname://refinery-interpreter/) | ||
39 | * [`tools.refinery.interpreter:refinery-interpreter-localsearch:0.28.1`](pathname://refinery-interpreter-localsearch/) | ||
40 | * [`tools.refinery.interpreter:refinery-interpreter-rete:0.28.1`](pathname://refinery-interpreter-rete/) | ||
41 | * [`tools.refinery.interpreter:refinery-interpreter-rete-recipes:0.28.1`](pathname://refinery-interpreter-rete-recipes/) | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/docker/cli.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/docker/cli.md new file mode 100644 index 00000000..325ae7e7 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/docker/cli.md | |||
@@ -0,0 +1,145 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | sidebar_label: CLI | ||
6 | --- | ||
7 | |||
8 | # Command-line interface | ||
9 | |||
10 | You can run Refinery as a command-line applications via our [Docker container](https://github.com/graphs4value/refinery/pkgs/container/refinery-cli) on either `amd64` or `arm64` machines: | ||
11 | |||
12 | ```shell | ||
13 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:0.1.0 | ||
14 | ``` | ||
15 | |||
16 | This will let you read input files and generate models in the current directory (`${PWD}`) of your terminal session. | ||
17 | Module imports (e.g., `import some::module.` to import `some/module.refinery`) relative to the current directory are also supported. | ||
18 | |||
19 | For example, to generate a model based on the file named `input.problem` in the current directory and write the results into the file named `output.refinery`, you may run the [`generate` subcommand](#generate) with | ||
20 | |||
21 | ```shell | ||
22 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:0.1.0 generate -o output.refinery input.problem | ||
23 | ``` | ||
24 | |||
25 | If you want Refinery CLI to print its documentation, run | ||
26 | |||
27 | ```shell | ||
28 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:0.1.0 -help | ||
29 | ``` | ||
30 | |||
31 | ## The `generate` subcommand {#generate} | ||
32 | |||
33 | The `generate` subcommand generates a consistent concrete model from a partial model. | ||
34 | |||
35 | ```shell | ||
36 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:0.1.0 generate [options] input path | ||
37 | ``` | ||
38 | |||
39 | The `input path` should be a path to a `.problem` file relative to the current directory. | ||
40 | Due to Docker containerization, paths _outside_ the current directory (e.g., `../input.problem`) are not supported. | ||
41 | |||
42 | Passing `-` as the `input path` will read a partial model from the standard input. | ||
43 | |||
44 | By default, the generator is _deterministic_ and always outputs the same concrete model. See the [`-random-seed`](#generate-random-seed) option to customize this behavior. | ||
45 | |||
46 | See below for the list of supported `[options]`. | ||
47 | |||
48 | ### `-output`, `-o` {#generate-output} | ||
49 | |||
50 | The output path for the generated model. | ||
51 | Passing `-o -` will write the generated model to the standard output. | ||
52 | |||
53 | When generating multiple models with [`-solution-number`](#generate-solution-number), the value `-` is not supported and individual solutions will be saved to numbered files. | ||
54 | For example, if you pass `-o output.refinery -n 10`, solutions will be saved as `output_001.refinery`, `output_002.refinery`, ..., `output_010.refinery`. | ||
55 | |||
56 | **Default value:** `-`, i.e., the solution is written to the standard output. | ||
57 | |||
58 | ### `-random-seed`, `-r` {#generate-random-seed} | ||
59 | |||
60 | Random seed to control the behavior of model generation. | ||
61 | |||
62 | The same random seed value and Refinery release will produce the same output model for an input problem. | ||
63 | Models generated with different values of `-random-seed` are highly likely (but are not guaranteed) to be _substantially_ different. | ||
64 | |||
65 | **Default value:** `1` | ||
66 | |||
67 | ### `-scope`, `-s` {#generate-scope} | ||
68 | |||
69 | Add [scope constraints](../../language/logic#type-scopes) to the input problem. | ||
70 | |||
71 | This option is especially useful if you want to generate models of multiple sizes from the same partial model. | ||
72 | |||
73 | For example, the command | ||
74 | |||
75 | ```shell | ||
76 | docker run --rm -it -v ${PWD}:/data ghcr.io/graphs4value/refinery-cli:0.1.0 generate -s File=20..25 input.problem | ||
77 | ``` | ||
78 | |||
79 | is equivalent to appending | ||
80 | |||
81 | ```refinery title="input.problem" | ||
82 | scope File = 20..25. | ||
83 | ``` | ||
84 | |||
85 | to `input.problem`. | ||
86 | The syntax of the argument is equivalent to the [`scope`](../../language/logic#type-scopes) declaration, but you be careful with the handling of spaces in your shell. | ||
87 | Any number of `-s` arguments are supported. For example, the following argument lists are equivalent: | ||
88 | |||
89 | ```shell | ||
90 | -scope File=20..25,Directory=3 | ||
91 | -s File=20..25,Directory=3 | ||
92 | -s File=20..25 -s Directory=3 | ||
93 | -s "File = 20..25, Directory = 3" | ||
94 | -s "File = 20..25" -s "Directory = 3" | ||
95 | ``` | ||
96 | |||
97 | The `*` opeator also has to be quoted to avoid shell expansion: | ||
98 | |||
99 | ```shell | ||
100 | -s "File=20..*" | ||
101 | ``` | ||
102 | |||
103 | ### `-scope-override`, `-S` {#generate-scope-override} | ||
104 | |||
105 | Override [scope constraints](../../language/logic#type-scopes) to the input problem. | ||
106 | |||
107 | This argument is similar to [`-scope`](#generate-scope), but has higher precedence than the [`scope`](../../language/logic#type-scopes) declarations already present in the input file. | ||
108 | However, you can't override `scope` declarations in modules imported in the input file using the `import` statement. | ||
109 | |||
110 | For example, if we have | ||
111 | |||
112 | ```refinery title="input.problem" | ||
113 | scope File = 20..25, Directory = 3. | ||
114 | ``` | ||
115 | |||
116 | in the input file, the arguments `-s File=10..12 input.problem` will be interpreted as | ||
117 | |||
118 | ```refinery | ||
119 | scope File = 20..25, Directory = 3. | ||
120 | scope File = 10..12. | ||
121 | ``` | ||
122 | |||
123 | which results in an _unsatisfiable_ problem. If the use `-S File=10..12 input.problem` instead, the type scope for `File` is overridden as | ||
124 | |||
125 | ```refinery | ||
126 | scope Directory = 3. | ||
127 | scope File = 10..12. | ||
128 | ``` | ||
129 | |||
130 | and model generation can proceed as requested. Since we had specified no override for `Directory`, its type scope declared in `input.problem` was preserved. | ||
131 | |||
132 | Scope overrides do not override additional scopes, i.e., `-s File=20..30 -S File=10..25` is interpreted as `-S File=20..25`. | ||
133 | |||
134 | ### `-solution-number`, `-n` {#generate-solution-number} | ||
135 | |||
136 | The number of distinct solutions to generate. | ||
137 | |||
138 | Generated solutions are always different, but are frequently not _substantially_ different, i.e., the differences between generated solutions comprise only a few model elements. | ||
139 | You'll likely generate substantially different models by calling the generator multiple times with different [`-random-seed`](#generate-random-seed) values instead. | ||
140 | |||
141 | The generator will create [numbered output files](#generate-output) for each solution found. | ||
142 | The generation is considered successful if it finds at least one solution, but may find less than the requested number of solutions if no more exist. | ||
143 | In this case, there will be fewer output files than requested. | ||
144 | |||
145 | **Default value:** `1` | ||
diff --git a/subprojects/docs/src/learn/docker.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/docker/index.md index 0df87da8..5120095d 100644 --- a/subprojects/docs/src/learn/docker.md +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/docker/index.md | |||
@@ -1,6 +1,7 @@ | |||
1 | --- | 1 | --- |
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | 2 | SPDX-FileCopyrightText: 2024 The Refinery Authors |
3 | SPDX-License-Identifier: EPL-2.0 | 3 | SPDX-License-Identifier: EPL-2.0 |
4 | autogenerated_sidebar_hidden: true | ||
4 | sidebar_position: 100 | 5 | sidebar_position: 100 |
5 | sidebar_label: Docker | 6 | sidebar_label: Docker |
6 | --- | 7 | --- |
@@ -23,27 +24,19 @@ Installing Refinery as a Docker container can support more advanced use cases, s | |||
23 | To generate larger models with a longer timeout, you can use our [Docker container](https://github.com/graphs4value/refinery/pkgs/container/refinery) on either `amd64` or `arm64` machines: | 24 | To generate larger models with a longer timeout, you can use our [Docker container](https://github.com/graphs4value/refinery/pkgs/container/refinery) on either `amd64` or `arm64` machines: |
24 | 25 | ||
25 | ```shell | 26 | ```shell |
26 | docker run --rm -it -p 8888:8888 ghcr.io/graphs4value/refinery | 27 | docker run --rm -it -p 8888:8888 ghcr.io/graphs4value/refinery:0.1.0 |
27 | ``` | 28 | ``` |
28 | 29 | ||
29 | Once Docker pulls and starts the container, you can navigate to http://localhost:8888 to open the model generation interface and start editing. | 30 | Once Docker pulls and starts the container, you can navigate to http://localhost:8888 to open the model generation interface and start editing. |
30 | 31 | ||
31 | Alternatively, you can follow the [instructions to set up a local development environment](/develop/contributing) and compile and run Refinery from source. | 32 | A [command-line interface (CLI)](cli) version of Refinery is also available as a Docker container. |
32 | |||
33 | ## Updating | ||
34 | |||
35 | To take advantage of the latest updates, you can simply re-pull our Docker container from the GitHub Container Registry: | ||
36 | 33 | ||
37 | ```shell | 34 | Alternatively, you can follow the [instructions to set up a local development environment](/develop/contributing) and compile and run Refinery from source. |
38 | docker pull ghcr.io/graphs4value/refinery | ||
39 | ``` | ||
40 | |||
41 | Restart the container to make sure that you're running the last pulled version. | ||
42 | 35 | ||
43 | ## Environmental variables | 36 | ## Environmental variables |
44 | 37 | ||
45 | The Docker container supports the following environmental variables to customize its behavior. | 38 | The Docker container supports the following environmental variables to customize its behavior. |
46 | Customizing these variable should only be needed if you want to _increase resource limits_ or _expose you Refinery instance over the network_ for others. | 39 | Customizing these variables should only be needed if you want to _increase resource limits_ or _expose your Refinery instance over the network_ for others. |
47 | 40 | ||
48 | Notes for **local-only instances** are highlighted with the :arrow_right: arrow emoji. | 41 | Notes for **local-only instances** are highlighted with the :arrow_right: arrow emoji. |
49 | 42 | ||
@@ -127,21 +120,20 @@ Timeout for model generation in seconds. | |||
127 | 120 | ||
128 | ### Threading | 121 | ### Threading |
129 | 122 | ||
130 | :warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. | 123 | :arrow_right: If you only run a single model generation task at a time, you don't need to adjust these settings. |
124 | |||
125 | :warning: Excessively large thread counts may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. | ||
131 | 126 | ||
132 | #### `REFINERY_XTEXT_THREAD_COUNT` | 127 | #### `REFINERY_XTEXT_THREAD_COUNT` |
133 | 128 | ||
134 | Number of threads used for non-blocking text editing operations. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | 129 | Number of threads used for non-blocking text editing operations. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. |
135 | 130 | ||
136 | :warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. | ||
137 | |||
138 | **Default value:** `1` | 131 | **Default value:** `1` |
139 | 132 | ||
140 | #### `REFINERY_XTEXT_LOCKING_THREAD_COUNT` | 133 | #### `REFINERY_XTEXT_LOCKING_THREAD_COUNT` |
141 | 134 | ||
142 | Number of threads used for text editing operations that lock the document. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | 135 | Number of threads used for text editing operations that lock the document. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. |
143 | 136 | ||
144 | |||
145 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` | 137 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` |
146 | 138 | ||
147 | #### `REFINERY_XTEXT_SEMANTICS_THREAD_COUNT` | 139 | #### `REFINERY_XTEXT_SEMANTICS_THREAD_COUNT` |
@@ -150,15 +142,13 @@ Number of threads used for model semantics calculation. A value of `0` allows an | |||
150 | 142 | ||
151 | Must be at least as large as `REFINERY_XTEXT_THREAD_COUNT`. | 143 | Must be at least as large as `REFINERY_XTEXT_THREAD_COUNT`. |
152 | 144 | ||
153 | :warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. | ||
154 | |||
155 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` | 145 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` |
156 | 146 | ||
157 | #### `REFINERY_MODEL_GENERATION_THREAD_COUNT` | 147 | #### `REFINERY_MODEL_GENERATION_THREAD_COUNT` |
158 | 148 | ||
159 | Number of threads used for model semantics calculation. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. | 149 | Number of threads used for model semantics calculation. A value of `0` allows an _unlimited_ number of threads by running each semantics calculation in a new thread. |
160 | 150 | ||
161 | :warning: Excessively large values may overload the server. Make sure that _all_ Refinery threads can run at the same time to avoid thread starvation. Each model generation task may also demand a large amount of memory in addition to CPU time. | 151 | :warning: Each model generation task may also demand a large amount of memory in addition to CPU time. |
162 | 152 | ||
163 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` | 153 | **Default value:** equal to `REFINERY_XTEXT_THREAD_COUNT` |
164 | 154 | ||
@@ -170,6 +160,15 @@ Modules (`.refinery` files) in this directory or colon-separated list of directo | |||
170 | 160 | ||
171 | :arrow_right: Use this in conjunction with the [mount volume (-v)](https://docs.docker.com/reference/cli/docker/container/run/#volume) option of `docker run` to work with multi-file projects in Refinery. | 161 | :arrow_right: Use this in conjunction with the [mount volume (-v)](https://docs.docker.com/reference/cli/docker/container/run/#volume) option of `docker run` to work with multi-file projects in Refinery. |
172 | 162 | ||
173 | :warning: Make sure you only expose files that you want to make public. It's best to expose a directory that contains nothing other that `.refinery` files to minimize potential information leaks. | 163 | :warning: Only expose files that you want to make public. It's best to expose a directory that contains nothing other than `.refinery` files to minimize potential information leaks. |
174 | 164 | ||
175 | **Default value:** _empty_ (no directories are exposed) | 165 | **Default value:** _empty_ (no directories are exposed) |
166 | |||
167 | ## Pre-release versions | ||
168 | |||
169 | You can take advantage of the most recent code submitted to our repository by using the `latest` tag instead. | ||
170 | |||
171 | |||
172 | ```shell | ||
173 | docker run --pull always --rm -it -p 8888:8888 ghcr.io/graphs4value/refinery:latest | ||
174 | ``` | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/index.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/index.md new file mode 100644 index 00000000..7f67fd86 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/index.md | |||
@@ -0,0 +1,11 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 0 | ||
5 | --- | ||
6 | |||
7 | # Introduction | ||
8 | |||
9 | Various software and systems engineering scenarios rely on the systematic construction of consistent graph models. However, **automatically generating a diverse set of consistent graph models** for complex domain specifications is challenging. First, the graph generation problem must be specified with mathematical precision. Moreover, graph generation is a computationally complex task, which necessitates specialized logic solvers. | ||
10 | |||
11 | **Refinery is an open-source software framework** for the automated synthesis of a diverse set of consistent domain-specific graph models. The framework offers an expressive high-level specification language using partial models to succinctly formulate a wide range of graph generation challenges. It also provides a modern cloud-based architecture for a scalable _Graph Solver as a Service,_ which uses logic reasoning rules to efficiently synthesize a diverse set of solutions to graph generation problems by partial model refinement. Applications include system-level architecture synthesis, test generation for modeling tools or traffic scenario synthesis for autonomous vehicles. | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/_category_.yml b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/_category_.yml new file mode 100644 index 00000000..a261ebf6 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/_category_.yml | |||
@@ -0,0 +1,10 @@ | |||
1 | # SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
2 | # | ||
3 | # SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | position: 2 | ||
6 | label: Language reference | ||
7 | link: | ||
8 | type: generated-index | ||
9 | slug: /learn/language | ||
10 | description: Learn more about the Refinery partial modeling language! | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ContainmentInstance.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ContainmentInstance.svg new file mode 100644 index 00000000..197f4b48 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ContainmentInstance.svg | |||
@@ -0,0 +1,227 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="395pt" height="226pt" viewBox="-6 -6 407.4700012207031 238.39999389648438" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-sdYDAL2PsHukjJUpNyUhU"><style>.refinery-sdYDAL2PsHukjJUpNyUhU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .node .node-outline{stroke:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .node .node-header{fill:rgb(53, 161, 173);}.refinery-sdYDAL2PsHukjJUpNyUhU .node .node-bg{fill:#fff;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-h .node-header{fill:#e06c75;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-i .node-header{fill:#98c379;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-j .node-header{fill:#c678dd;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-n .node-header{fill:#abcc94;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-line{stroke:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-arrow{fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN text{fill:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR text{fill:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU .icon-TRUE{fill:#19202b;}.refinery-sdYDAL2PsHukjJUpNyUhU .icon-UNKNOWN{fill:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU .icon-ERROR{fill:#ca1243;}.refinery-sdYDAL2PsHukjJUpNyUhU text.label-UNKNOWN{fill:#696c77;}.refinery-sdYDAL2PsHukjJUpNyUhU text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-sdYDAL2PsHukjJUpNyUhU text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-sdYDAL2PsHukjJUpNyUhU-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-sdYDAL2PsHukjJUpNyUhU-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="scale(1 1) rotate(0) translate(4 222.4)"> | ||
4 | <!-- n3 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
6 | |||
7 | <rect stroke="none" x="155.95" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="151" y="-137" width="72" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="181.53" y="-117.8" font-size="12.00">v1</text> | ||
10 | <use x="161.952" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="177.95" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
12 | </g> | ||
13 | <polyline points="155.95,-110.2 219.98,-110.2" class="node-outline"/> | ||
14 | <rect fill="none" x="155.95" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-0"><rect stroke="none" x="155.95" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n4 --> | ||
17 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k"> | ||
18 | |||
19 | <rect stroke="none" x="113.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="109" y="-222" width="74" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="141.09" y="-202.6" font-size="12.00">r1</text> | ||
22 | <use x="119.78" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="135.78" y="-179.2" font-size="12.00" class="label label-TRUE">Region</text> | ||
24 | </g> | ||
25 | <polyline points="113.78,-195 180.15,-195" class="node-outline"/> | ||
26 | <rect fill="none" x="113.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
27 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-1"><rect stroke="none" x="113.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
28 | <!-- n3->n4 --> | ||
29 | <g class="edge edge-TRUE"> | ||
30 | |||
31 | <path fill="none" d="M182.6,-133.43C179.13,-141.63 174.65,-151.02 170,-159.88" class="edge-line"/> | ||
32 | <polygon points="166.95,-158.15 165.24,-168.61 173.1,-161.5 166.95,-158.15" class="edge-line edge-arrow"/> | ||
33 | <text text-anchor="middle" x="158.39" y="-155.04" font-size="10.50">region</text> | ||
34 | </g> | ||
35 | <!-- n8 --> | ||
36 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
37 | |||
38 | <rect stroke="none" x="53.86" y="-48.8" width="84.22000000000001" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
39 | <rect stroke="none" x="49" y="-52" width="92" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-2)" class="node-header"/> | ||
40 | <text text-anchor="start" x="90.42" y="-33" font-size="12.00">t1</text> | ||
41 | <use x="59.8559" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
42 | <g><text text-anchor="start" x="75.86" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
43 | </g> | ||
44 | <polyline points="53.86,-25.4 138.08,-25.4" class="node-outline"/> | ||
45 | <rect fill="none" x="53.86" y="-48.8" width="84.22000000000001" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
46 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-2"><rect stroke="none" x="53.86" y="-48.8" width="84.22000000000001" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
47 | <!-- n3->n8 --> | ||
48 | <g class="edge edge-TRUE"> | ||
49 | |||
50 | <path fill="none" stroke-width="2" d="M155.95,-84.92C145.63,-76.18 134.29,-66.11 124.25,-56.77" class="edge-line"/> | ||
51 | <polygon stroke-width="2" points="126.48,-54.66 118.02,-50.88 122.27,-59.11 126.48,-54.66" class="edge-line edge-arrow"/> | ||
52 | <text text-anchor="start" x="35.36" y="-71.18" font-weight="bold" font-size="10.50">outgoingTransition</text> | ||
53 | </g> | ||
54 | <!-- n3->n8 --> | ||
55 | |||
56 | <!-- n9 --> | ||
57 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
58 | |||
59 | <rect stroke="none" x="155.86" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
60 | <rect stroke="none" x="151" y="-52" width="92" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-3)" class="node-header"/> | ||
61 | <text text-anchor="start" x="192.42" y="-33" font-size="12.00">t2</text> | ||
62 | <use x="161.856" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
63 | <g><text text-anchor="start" x="177.86" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
64 | </g> | ||
65 | <polyline points="155.86,-25.4 240.08,-25.4" class="node-outline"/> | ||
66 | <rect fill="none" x="155.86" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
67 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-3"><rect stroke="none" x="155.86" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
68 | <!-- n3->n9 --> | ||
69 | <g class="edge edge-TRUE"> | ||
70 | |||
71 | <path fill="none" stroke-width="2" d="M172.38,-85.14C171.61,-77.31 172.15,-68.38 173.81,-59.86" class="edge-line"/> | ||
72 | <polygon stroke-width="2" points="176.73,-60.8 175.99,-51.56 170.81,-59.24 176.73,-60.8" class="edge-line edge-arrow"/> | ||
73 | <text text-anchor="start" x="72.62" y="-58.26" font-weight="bold" font-size="10.50">outgoingTransition</text> | ||
74 | </g><g class="edge edge-TRUE"> | ||
75 | |||
76 | <path fill="none" d="M184.63,-85.14C185,-77.31 185.9,-68.38 187.14,-59.86" class="edge-line"/> | ||
77 | <polygon points="190.57,-60.56 188.77,-50.12 183.67,-59.4 190.57,-60.56" class="edge-line edge-arrow"/> | ||
78 | <text text-anchor="middle" x="233.31" y="-70.26" font-size="10.50">incomingTransition</text> | ||
79 | </g> | ||
80 | <!-- n3->n9 --> | ||
81 | |||
82 | <!-- n10 --> | ||
83 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
84 | |||
85 | <rect stroke="none" x="257.86" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
86 | <rect stroke="none" x="253" y="-52" width="92" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-4)" class="node-header"/> | ||
87 | <text text-anchor="start" x="294.42" y="-33" font-size="12.00">t3</text> | ||
88 | <use x="263.856" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
89 | <g><text text-anchor="start" x="279.86" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
90 | </g> | ||
91 | <polyline points="257.86,-25.4 342.08,-25.4" class="node-outline"/> | ||
92 | <rect fill="none" x="257.86" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
93 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-4"><rect stroke="none" x="257.86" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
94 | <!-- n3->n10 --> | ||
95 | |||
96 | <!-- n4->n3 --> | ||
97 | <g class="edge edge-TRUE"> | ||
98 | |||
99 | <path fill="none" stroke-width="2" d="M152.26,-169.94C155.72,-161.75 160.2,-152.37 164.84,-143.5" class="edge-line"/> | ||
100 | <polygon stroke-width="2" points="167.39,-145.24 168.88,-136.09 162.01,-142.31 167.39,-145.24" class="edge-line edge-arrow"/> | ||
101 | <text text-anchor="start" x="119.24" y="-143.04" font-weight="bold" font-size="10.50">vertices</text> | ||
102 | </g> | ||
103 | <!-- n5 --> | ||
104 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
105 | |||
106 | <rect stroke="none" x="73.95" y="-133.6" width="64.02999999999999" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
107 | <rect stroke="none" x="69" y="-137" width="72" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-5)" class="node-header"/> | ||
108 | <text text-anchor="start" x="99.53" y="-117.8" font-size="12.00">v2</text> | ||
109 | <use x="79.9516" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
110 | <g><text text-anchor="start" x="95.95" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
111 | </g> | ||
112 | <polyline points="73.95,-110.2 137.98,-110.2" class="node-outline"/> | ||
113 | <rect fill="none" x="73.95" y="-133.6" width="64.02999999999999" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
114 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-5"><rect stroke="none" x="73.95" y="-133.6" width="64.02999999999999" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
115 | <!-- n4->n5 --> | ||
116 | <g class="edge edge-TRUE"> | ||
117 | |||
118 | <path fill="none" stroke-width="2" d="M129.42,-169.94C124.87,-161.93 120.16,-152.77 116.09,-144.08" class="edge-line"/> | ||
119 | <polygon stroke-width="2" points="118.93,-142.93 112.57,-136.19 113.34,-145.43 118.93,-142.93" class="edge-line edge-arrow"/> | ||
120 | <text text-anchor="start" x="78.34" y="-156.05" font-weight="bold" font-size="10.50">vertices</text> | ||
121 | </g> | ||
122 | <!-- n5->n4 --> | ||
123 | <g class="edge edge-TRUE"> | ||
124 | |||
125 | <path fill="none" d="M123.6,-133.43C128.21,-141.54 132.96,-150.81 137.06,-159.59" class="edge-line"/> | ||
126 | <polygon points="133.79,-160.84 141.05,-168.55 140.18,-157.99 133.79,-160.84" class="edge-line edge-arrow"/> | ||
127 | <text text-anchor="middle" x="117.57" y="-142.24" font-size="10.50">region</text> | ||
128 | </g> | ||
129 | <!-- n5->n8 --> | ||
130 | <g class="edge edge-TRUE"> | ||
131 | |||
132 | <path fill="none" d="M97.06,-85.14C95.54,-77.31 94.28,-68.38 93.46,-59.86" class="edge-line"/> | ||
133 | <polygon points="96.97,-59.85 92.75,-50.14 89.99,-60.37 96.97,-59.85" class="edge-line edge-arrow"/> | ||
134 | <text text-anchor="middle" x="47.13" y="-57.66" font-size="10.50">incomingTransition</text> | ||
135 | </g> | ||
136 | <!-- n5->n9 --> | ||
137 | |||
138 | <!-- n5->n10 --> | ||
139 | |||
140 | <!-- n6 --> | ||
141 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
142 | |||
143 | <rect stroke="none" x="267.95" y="-133.6" width="64.03000000000003" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
144 | <rect stroke="none" x="263" y="-137" width="72" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-6)" class="node-header"/> | ||
145 | <text text-anchor="start" x="293.53" y="-117.8" font-size="12.00">v3</text> | ||
146 | <use x="273.952" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
147 | <g><text text-anchor="start" x="289.95" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
148 | </g> | ||
149 | <polyline points="267.95,-110.2 331.98,-110.2" class="node-outline"/> | ||
150 | <rect fill="none" x="267.95" y="-133.6" width="64.03000000000003" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
151 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-6"><rect stroke="none" x="267.95" y="-133.6" width="64.03000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="edge edge-TRUE"> | ||
152 | |||
153 | <path fill="none" d="M293.84,-85.14C293.27,-77.31 293.09,-68.38 293.3,-59.86" class="edge-line"/> | ||
154 | <polygon points="296.78,-60.29 293.76,-50.14 289.79,-59.96 296.78,-60.29" class="edge-line edge-arrow"/> | ||
155 | <text text-anchor="middle" x="340.34" y="-70.26" font-size="10.50">incomingTransition</text> | ||
156 | </g> | ||
157 | <!-- n7 --> | ||
158 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k"> | ||
159 | |||
160 | <rect stroke="none" x="266.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
161 | <rect stroke="none" x="262" y="-222" width="74" height="27" clip-path="url(#refinery-sdYDAL2PsHukjJUpNyUhU-clip-7)" class="node-header"/> | ||
162 | <text text-anchor="start" x="294.09" y="-202.6" font-size="12.00">r2</text> | ||
163 | <use x="272.78" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-sdYDAL2PsHukjJUpNyUhU-icon-TRUE"/> | ||
164 | <g><text text-anchor="start" x="288.78" y="-179.2" font-size="12.00" class="label label-TRUE">Region</text> | ||
165 | </g> | ||
166 | <polyline points="266.78,-195 333.15,-195" class="node-outline"/> | ||
167 | <rect fill="none" x="266.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
168 | <clipPath id="refinery-sdYDAL2PsHukjJUpNyUhU-clip-7"><rect stroke="none" x="266.78" y="-218.4" width="66.37" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
169 | <!-- n6->n7 --> | ||
170 | <g class="edge edge-TRUE"> | ||
171 | |||
172 | <path fill="none" d="M306.1,-133.43C306.67,-141.27 306.84,-150.2 306.63,-158.72" class="edge-line"/> | ||
173 | <polygon points="303.14,-158.27 306.16,-168.42 310.14,-158.6 303.14,-158.27" class="edge-line edge-arrow"/> | ||
174 | <text text-anchor="middle" x="291.03" y="-142.02" font-size="10.50">region</text> | ||
175 | </g> | ||
176 | <!-- n6->n8 --> | ||
177 | |||
178 | <!-- n6->n9 --> | ||
179 | |||
180 | <!-- n6->n10 --> | ||
181 | <g class="edge edge-TRUE"> | ||
182 | |||
183 | <path fill="none" d="M213.57,-48.63C214.33,-56.56 213.75,-65.61 212.03,-74.21" class="edge-line"/> | ||
184 | <polygon points="208.7,-73.11 209.51,-83.67 215.47,-74.91 208.7,-73.11" class="edge-line edge-arrow"/> | ||
185 | <text text-anchor="middle" x="198.62" y="-70.03" font-size="10.50">target</text> | ||
186 | </g> | ||
187 | <!-- n6->n10 --> | ||
188 | |||
189 | <!-- n7->n6 --> | ||
190 | <g class="edge edge-TRUE"> | ||
191 | |||
192 | <path fill="none" stroke-width="2" d="M293.84,-169.94C293.28,-162.19 293.09,-153.38 293.29,-144.95" class="edge-line"/> | ||
193 | <polygon stroke-width="2" points="296.34,-145.33 293.69,-136.45 290.22,-145.05 296.34,-145.33" class="edge-line edge-arrow"/> | ||
194 | <text text-anchor="start" x="251.69" y="-155.87" font-weight="bold" font-size="10.50">vertices</text> | ||
195 | </g><g class="edge edge-TRUE"> | ||
196 | |||
197 | <path fill="none" stroke-width="2" d="M281.6,-85.14C279.9,-77.39 279.35,-68.58 279.95,-60.15" class="edge-line"/> | ||
198 | <polygon stroke-width="2" points="282.96,-60.72 281.14,-51.63 276.9,-59.87 282.96,-60.72" class="edge-line edge-arrow"/> | ||
199 | <text text-anchor="start" x="179.61" y="-58.47" font-weight="bold" font-size="10.50">outgoingTransition</text> | ||
200 | </g><g class="edge edge-TRUE"> | ||
201 | |||
202 | <path fill="none" d="M318.37,-48.63C320.07,-56.47 320.6,-65.4 319.95,-73.92" class="edge-line"/> | ||
203 | <polygon points="316.51,-73.24 318.55,-83.64 323.44,-74.24 316.51,-73.24" class="edge-line edge-arrow"/> | ||
204 | <text text-anchor="middle" x="305.62" y="-57.22" font-size="10.50">target</text> | ||
205 | </g> | ||
206 | <g class="edge edge-TRUE"> | ||
207 | |||
208 | <path fill="none" d="M127.91,-48.63C138.46,-57.54 150.06,-67.86 160.27,-77.38" class="edge-line"/> | ||
209 | <polygon points="157.7,-79.76 167.36,-84.09 162.51,-74.68 157.7,-79.76" class="edge-line edge-arrow"/> | ||
210 | <text text-anchor="middle" x="165.07" y="-69.83" font-size="10.50">source</text> | ||
211 | </g><g class="edge edge-TRUE"> | ||
212 | |||
213 | <path fill="none" d="M104.91,-48.63C106.42,-56.47 107.67,-65.4 108.49,-73.92" class="edge-line"/> | ||
214 | <polygon points="104.98,-73.91 109.19,-83.63 111.96,-73.4 104.98,-73.91" class="edge-line edge-arrow"/> | ||
215 | <text text-anchor="middle" x="122.29" y="-69.82" font-size="10.50">target</text> | ||
216 | </g><g class="edge edge-TRUE"> | ||
217 | |||
218 | <path fill="none" d="M201.3,-48.63C200.91,-56.47 200.01,-65.4 198.77,-73.92" class="edge-line"/> | ||
219 | <polygon points="195.34,-73.2 197.13,-83.64 202.24,-74.36 195.34,-73.2" class="edge-line edge-arrow"/> | ||
220 | <text text-anchor="middle" x="183.25" y="-57.22" font-size="10.50">source</text> | ||
221 | </g><g class="edge edge-TRUE"> | ||
222 | |||
223 | <path fill="none" d="M306.1,-48.63C306.67,-56.47 306.84,-65.4 306.63,-73.92" class="edge-line"/> | ||
224 | <polygon points="303.14,-73.47 306.16,-83.62 310.14,-73.8 303.14,-73.47" class="edge-line edge-arrow"/> | ||
225 | <text text-anchor="middle" x="323.2" y="-57.22" font-size="10.50">source</text> | ||
226 | </g></g> | ||
227 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ContainmentInstance.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ContainmentInstance.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ContainmentInstance.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/InvalidInstance.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/InvalidInstance.svg new file mode 100644 index 00000000..fb9dd37d --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/InvalidInstance.svg | |||
@@ -0,0 +1,20 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="74pt" height="72pt" viewBox="-6 -6 86 84.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-S3CluI8WDJspDI9OUqv4H"><style>.refinery-S3CluI8WDJspDI9OUqv4H .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .node .node-outline{stroke:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .node .node-header{fill:rgb(53, 161, 173);}.refinery-S3CluI8WDJspDI9OUqv4H .node .node-bg{fill:#fff;}.refinery-S3CluI8WDJspDI9OUqv4H .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-S3CluI8WDJspDI9OUqv4H .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-S3CluI8WDJspDI9OUqv4H .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-h .node-header{fill:#e06c75;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-i .node-header{fill:#98c379;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-j .node-header{fill:#c678dd;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-n .node-header{fill:#abcc94;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-S3CluI8WDJspDI9OUqv4H .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-line{stroke:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-arrow{fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN text{fill:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR text{fill:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H .icon-TRUE{fill:#19202b;}.refinery-S3CluI8WDJspDI9OUqv4H .icon-UNKNOWN{fill:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H .icon-ERROR{fill:#ca1243;}.refinery-S3CluI8WDJspDI9OUqv4H text.label-UNKNOWN{fill:#696c77;}.refinery-S3CluI8WDJspDI9OUqv4H text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-S3CluI8WDJspDI9OUqv4H text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-S3CluI8WDJspDI9OUqv4H-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-S3CluI8WDJspDI9OUqv4H-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-S3CluI8WDJspDI9OUqv4H-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 68.4000015258789)"> | ||
4 | <!-- n0 --> | ||
5 | |||
6 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE"> | ||
7 | |||
8 | <rect stroke="none" x="0" y="-64.4" width="66" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
9 | <rect stroke="none" x="-4" y="-68" width="74" height="27" clip-path="url(#refinery-S3CluI8WDJspDI9OUqv4H-clip-0)" class="node-header"/> | ||
10 | <text text-anchor="start" x="14.75" y="-48.6" font-size="12.00">invalid</text> | ||
11 | <use x="6" y="-35" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-S3CluI8WDJspDI9OUqv4H-icon-ERROR"/> | ||
12 | <g><text text-anchor="start" x="21.81" y="-25.2" font-size="12.00" class="label label-ERROR">Region</text> | ||
13 | </g> | ||
14 | <use x="6" y="-19" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-S3CluI8WDJspDI9OUqv4H-icon-ERROR"/> | ||
15 | <g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-ERROR">State</text> | ||
16 | </g> | ||
17 | <polyline points="0,-41 66,-41" class="node-outline"/> | ||
18 | <rect fill="none" x="0" y="-64.4" width="66" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
19 | <clipPath id="refinery-S3CluI8WDJspDI9OUqv4H-clip-0"><rect stroke="none" x="0" y="-64.4" width="66" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g></g> | ||
20 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/InvalidInstance.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/InvalidInstance.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/InvalidInstance.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/MultiplicityConstraintsInstance.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/MultiplicityConstraintsInstance.svg new file mode 100644 index 00000000..b28c295a --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/MultiplicityConstraintsInstance.svg | |||
@@ -0,0 +1,229 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="802pt" height="157pt" viewBox="-6 -6 814.3800048828125 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-MZ1i4PkeOsY_2x7-6CJ29"><style>.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-outline{stroke:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-header{fill:rgb(53, 161, 173);}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-bg{fill:#fff;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-i .node-header{fill:#98c379;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-line{stroke:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-arrow{fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN text{fill:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR text{fill:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-TRUE{fill:#19202b;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-UNKNOWN{fill:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-ERROR{fill:#ca1243;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-UNKNOWN{fill:#696c77;}.refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-MZ1i4PkeOsY_2x7-6CJ29 text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 153.1999969482422)"> | ||
4 | <!-- n0 --> | ||
5 | |||
6 | <!-- n0->n0 --> | ||
7 | |||
8 | <!-- n0->n1 --> | ||
9 | |||
10 | <!-- n0->n1 --> | ||
11 | |||
12 | <!-- n3 --> | ||
13 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
14 | |||
15 | <rect stroke="none" x="84.25" y="-141.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
16 | <rect stroke="none" x="80" y="-145" width="72" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-0)" class="node-header"/> | ||
17 | <text text-anchor="start" x="109.83" y="-125.6" font-size="12.00">v1</text> | ||
18 | <use x="90.2516" y="-111.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
19 | <g><text text-anchor="start" x="106.25" y="-102.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
20 | </g> | ||
21 | <polyline points="84.25,-118 148.28,-118" class="node-outline"/> | ||
22 | <rect fill="none" x="84.25" y="-141.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
23 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-0"><rect stroke="none" x="84.25" y="-141.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
24 | <!-- n1->n3 --> | ||
25 | <g class="edge edge-TRUE"> | ||
26 | |||
27 | <path fill="none" d="M127.79,-92.67C135,-81.99 144.45,-69.17 153.6,-57.6" class="edge-line"/> | ||
28 | <polygon points="156.32,-59.81 159.88,-49.83 150.87,-55.41 156.32,-59.81" class="edge-line edge-arrow"/> | ||
29 | <text text-anchor="middle" x="190.06" y="-60.95" font-size="10.50">outgoingTransition</text> | ||
30 | </g> | ||
31 | <!-- n5 --> | ||
32 | <g class="edge edge-TRUE"> | ||
33 | |||
34 | <path fill="none" d="M172.8,-48.65C165.6,-59.32 156.15,-72.13 147,-83.71" class="edge-line"/> | ||
35 | <polygon points="144.27,-81.51 140.72,-91.49 149.72,-85.91 144.27,-81.51" class="edge-line edge-arrow"/> | ||
36 | <text text-anchor="middle" x="140.38" y="-74.06" font-size="10.50">source</text> | ||
37 | </g> | ||
38 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
39 | |||
40 | <rect stroke="none" x="40.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
41 | <rect stroke="none" x="36" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-1)" class="node-header"/> | ||
42 | <text text-anchor="start" x="76.72" y="-33" font-size="12.00">t1</text> | ||
43 | <use x="46.1559" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
44 | <g><text text-anchor="start" x="62.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
45 | </g> | ||
46 | <polyline points="40.16,-25.4 124.38,-25.4" class="node-outline"/> | ||
47 | <rect fill="none" x="40.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
48 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-1"><rect stroke="none" x="40.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="edge edge-TRUE"> | ||
49 | |||
50 | <path fill="none" d="M96.97,-48.65C101.54,-58.79 106.27,-70.87 110.06,-81.98" class="edge-line"/> | ||
51 | <polygon points="106.64,-82.78 113.04,-91.23 113.3,-80.64 106.64,-82.78" class="edge-line edge-arrow"/> | ||
52 | <text text-anchor="middle" x="89.38" y="-60.85" font-size="10.50">source</text> | ||
53 | </g><g class="edge edge-TRUE"> | ||
54 | |||
55 | <path fill="none" d="M101.52,-92.67C96.95,-82.52 92.23,-70.44 88.44,-59.33" class="edge-line"/> | ||
56 | <polygon points="91.87,-58.54 85.47,-50.09 85.2,-60.68 91.87,-58.54" class="edge-line edge-arrow"/> | ||
57 | <text text-anchor="middle" x="46.32" y="-74.16" font-size="10.50">outgoingTransition</text> | ||
58 | </g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
59 | |||
60 | <rect stroke="none" x="142.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
61 | <rect stroke="none" x="138" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-2)" class="node-header"/> | ||
62 | <text text-anchor="start" x="178.72" y="-33" font-size="12.00">t2</text> | ||
63 | <use x="148.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
64 | <g><text text-anchor="start" x="164.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
65 | </g> | ||
66 | <polyline points="142.16,-25.4 226.38,-25.4" class="node-outline"/> | ||
67 | <rect fill="none" x="142.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
68 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-2"><rect stroke="none" x="142.16" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><!-- n5 --> | ||
69 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
70 | |||
71 | <rect stroke="none" x="200.77" y="-149.2" width="236.99999999999997" height="64.39999999999999" rx="12" ry="12" class="node-bg"/> | ||
72 | <rect stroke="none" x="196" y="-153" width="244" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-3)" class="node-header"/> | ||
73 | <text text-anchor="start" x="312.83" y="-133.4" font-size="12.00">v2</text> | ||
74 | <use x="206.766" y="-119.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
75 | <g><text text-anchor="start" x="222.77" y="-110" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
76 | </g> | ||
77 | <use x="206.766" y="-103.8" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-ERROR"/> | ||
78 | <g><text text-anchor="start" x="222.4" y="-94" font-size="12.00" class="label label-ERROR">outgoingTransition::invalidMultiplicity</text> | ||
79 | </g> | ||
80 | <polyline points="200.77,-125.8 437.77,-125.8" class="node-outline"/> | ||
81 | <rect fill="none" x="200.77" y="-149.2" width="236.99999999999997" height="64.39999999999999" rx="12" ry="12" class="node-outline"/> | ||
82 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-3"><rect stroke="none" x="200.77" y="-149.2" width="236.99999999999997" height="64.39999999999999" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
83 | |||
84 | <rect stroke="none" x="277.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
85 | <rect stroke="none" x="273" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-4)" class="node-header"/> | ||
86 | <text text-anchor="start" x="313.72" y="-33" font-size="12.00">t3</text> | ||
87 | <use x="283.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
88 | <g><text text-anchor="start" x="299.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
89 | </g> | ||
90 | <polyline points="277.16,-25.4 361.38,-25.4" class="node-outline"/> | ||
91 | <rect fill="none" x="277.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
92 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-4"><rect stroke="none" x="277.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
93 | <!-- n4->n5 --> | ||
94 | |||
95 | <!-- n5->n4 --> | ||
96 | <g class="edge edge-TRUE"> | ||
97 | |||
98 | <path fill="none" d="M325.25,-48.62C325.82,-56.19 326.07,-64.85 326.01,-73.38" class="edge-line"/> | ||
99 | <polygon points="322.51,-73.22 325.75,-83.31 329.51,-73.4 322.51,-73.22" class="edge-line edge-arrow"/> | ||
100 | <text text-anchor="middle" x="309.51" y="-57.13" font-size="10.50">source</text> | ||
101 | </g> | ||
102 | <!-- n6->n5 --> | ||
103 | <g class="edge edge-TRUE"> | ||
104 | |||
105 | <path fill="none" d="M312.82,-84.82C312.47,-76.84 312.42,-68.21 312.68,-60.07" class="edge-line"/> | ||
106 | <polygon points="316.18,-60.3 313.21,-50.13 309.19,-59.94 316.18,-60.3" class="edge-line edge-arrow"/> | ||
107 | <text text-anchor="middle" x="358.85" y="-70.18" font-size="10.50">outgoingTransition</text> | ||
108 | </g> | ||
109 | <!-- n7 --> | ||
110 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
111 | |||
112 | <rect stroke="none" x="404.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
113 | <rect stroke="none" x="400" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-5)" class="node-header"/> | ||
114 | <text text-anchor="start" x="440.72" y="-33" font-size="12.00">t4</text> | ||
115 | <use x="410.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
116 | <g><text text-anchor="start" x="426.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
117 | </g> | ||
118 | <polyline points="404.16,-25.4 488.38,-25.4" class="node-outline"/> | ||
119 | <rect fill="none" x="404.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
120 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-5"><rect stroke="none" x="404.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
121 | <!-- n8 --> | ||
122 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
123 | |||
124 | <rect stroke="none" x="493.77" y="-149.2" width="237" height="64.39999999999999" rx="12" ry="12" class="node-bg"/> | ||
125 | <rect stroke="none" x="489" y="-153" width="245" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-6)" class="node-header"/> | ||
126 | <text text-anchor="start" x="605.83" y="-133.4" font-size="12.00">v3</text> | ||
127 | <use x="499.766" y="-119.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
128 | <g><text text-anchor="start" x="515.77" y="-110" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
129 | </g> | ||
130 | <polyline points="493.77,-125.8 730.77,-125.8" class="node-outline"/><use x="499.766" y="-103.8" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-ERROR"/> | ||
131 | |||
132 | <rect fill="none" x="493.77" y="-149.2" width="237" height="64.39999999999999" rx="12" ry="12" class="node-outline"/><g><text text-anchor="start" x="515.4" y="-94" font-size="12.00" class="label label-ERROR">outgoingTransition::invalidMultiplicity</text> | ||
133 | </g> | ||
134 | |||
135 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-6"><rect stroke="none" x="493.77" y="-149.2" width="237" height="64.39999999999999" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
136 | <!-- n7->n8 --> | ||
137 | <g class="edge edge-TRUE"> | ||
138 | |||
139 | <path fill="none" d="M488.13,-45.15C507.42,-55.16 530.62,-67.65 551.73,-79.34" class="edge-line"/> | ||
140 | <polygon points="549.83,-82.28 560.27,-84.08 553.23,-76.16 549.83,-82.28" class="edge-line edge-arrow"/> | ||
141 | <text text-anchor="middle" x="508.37" y="-55.19" font-size="10.50">source</text> | ||
142 | </g> | ||
143 | <!-- n8->n7 --> | ||
144 | <g class="edge edge-TRUE"> | ||
145 | |||
146 | <path fill="none" d="M548.69,-84.82C530.15,-74.94 510.25,-64.06 492.94,-54.34" class="edge-line"/> | ||
147 | <polygon points="494.83,-51.38 484.4,-49.51 491.38,-57.48 494.83,-51.38" class="edge-line edge-arrow"/> | ||
148 | <text text-anchor="middle" x="469.61" y="-70.25" font-size="10.50">outgoingTransition</text> | ||
149 | </g> | ||
150 | <!-- n9 --> | ||
151 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
152 | |||
153 | <rect stroke="none" x="506.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
154 | <rect stroke="none" x="502" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-7)" class="node-header"/> | ||
155 | <text text-anchor="start" x="542.72" y="-33" font-size="12.00">t5</text> | ||
156 | <use x="512.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
157 | <g><text text-anchor="start" x="528.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
158 | </g> | ||
159 | <polyline points="506.16,-25.4 590.38,-25.4" class="node-outline"/> | ||
160 | <rect fill="none" x="506.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
161 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-7"><rect stroke="none" x="506.16" y="-48.8" width="84.21999999999997" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
162 | <!-- n8->n9 --> | ||
163 | <g class="edge edge-TRUE"> | ||
164 | |||
165 | <path fill="none" d="M583.79,-84.82C577.32,-76.19 570.67,-66.81 564.85,-58.09" class="edge-line"/> | ||
166 | <polygon points="567.9,-56.37 559.52,-49.89 562.03,-60.18 567.9,-56.37" class="edge-line edge-arrow"/> | ||
167 | <text text-anchor="middle" x="617.13" y="-69.99" font-size="10.50">outgoingTransition</text> | ||
168 | </g> | ||
169 | <!-- n10 --> | ||
170 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
171 | |||
172 | <rect stroke="none" x="608.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
173 | <rect stroke="none" x="604" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-8)" class="node-header"/> | ||
174 | <text text-anchor="start" x="644.72" y="-33" font-size="12.00">t6</text> | ||
175 | <use x="614.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
176 | <g><text text-anchor="start" x="630.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
177 | </g> | ||
178 | <polyline points="608.16,-25.4 692.38,-25.4" class="node-outline"/> | ||
179 | <rect fill="none" x="608.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
180 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-8"><rect stroke="none" x="608.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
181 | <!-- n8->n10 --> | ||
182 | <g class="edge edge-TRUE"> | ||
183 | |||
184 | <path fill="none" d="M618.89,-84.82C622.03,-76.47 625.8,-67.41 629.66,-58.94" class="edge-line"/> | ||
185 | <polygon points="632.78,-60.52 633.9,-49.99 626.45,-57.53 632.78,-60.52" class="edge-line edge-arrow"/> | ||
186 | <text text-anchor="middle" x="579.94" y="-57.16" font-size="10.50">outgoingTransition</text> | ||
187 | </g> | ||
188 | <!-- n11 --> | ||
189 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
190 | |||
191 | <rect stroke="none" x="710.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
192 | <rect stroke="none" x="706" y="-52" width="92" height="27" clip-path="url(#refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-9)" class="node-header"/> | ||
193 | <text text-anchor="start" x="746.72" y="-33" font-size="12.00">t7</text> | ||
194 | <use x="716.156" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-MZ1i4PkeOsY_2x7-6CJ29-icon-TRUE"/> | ||
195 | <g><text text-anchor="start" x="732.16" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
196 | </g> | ||
197 | <polyline points="710.16,-25.4 794.38,-25.4" class="node-outline"/> | ||
198 | <rect fill="none" x="710.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
199 | <clipPath id="refinery-MZ1i4PkeOsY_2x7-6CJ29-clip-9"><rect stroke="none" x="710.16" y="-48.8" width="84.22000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
200 | <!-- n8->n11 --> | ||
201 | <g class="edge edge-TRUE"> | ||
202 | |||
203 | <path fill="none" d="M654,-84.82C668.76,-74.99 685.52,-64.17 700.97,-54.48" class="edge-line"/> | ||
204 | <polygon points="702.47,-57.67 709.12,-49.42 698.78,-51.73 702.47,-57.67" class="edge-line edge-arrow"/> | ||
205 | <text text-anchor="middle" x="728.4" y="-57.01" font-size="10.50">outgoingTransition</text> | ||
206 | </g> | ||
207 | <!-- n9->n8 --> | ||
208 | <g class="edge edge-TRUE"> | ||
209 | |||
210 | <path fill="none" d="M570.65,-48.62C577.04,-56.79 583.96,-66.23 590.31,-75.41" class="edge-line"/> | ||
211 | <polygon points="587.33,-77.25 595.84,-83.57 593.13,-73.33 587.33,-77.25" class="edge-line edge-arrow"/> | ||
212 | <text text-anchor="middle" x="567.43" y="-69.5" font-size="10.50">source</text> | ||
213 | </g> | ||
214 | <!-- n10->n8 --> | ||
215 | <g class="edge edge-TRUE"> | ||
216 | |||
217 | <path fill="none" d="M646.51,-48.62C643.79,-56.54 640.22,-65.64 636.38,-74.54" class="edge-line"/> | ||
218 | <polygon points="633.29,-72.88 632.41,-83.44 639.68,-75.73 633.29,-72.88" class="edge-line edge-arrow"/> | ||
219 | <text text-anchor="middle" x="656.03" y="-57.56" font-size="10.50">source</text> | ||
220 | </g> | ||
221 | <!-- n11->n8 --> | ||
222 | <g class="edge edge-TRUE"> | ||
223 | |||
224 | <path fill="none" d="M722.36,-48.62C708.84,-57.83 692.35,-68.64 676.28,-78.88" class="edge-line"/> | ||
225 | <polygon points="674.75,-75.7 668.17,-84.01 678.5,-81.62 674.75,-75.7" class="edge-line edge-arrow"/> | ||
226 | <text text-anchor="middle" x="711.22" y="-70.14" font-size="10.50">source</text> | ||
227 | </g> | ||
228 | </g> | ||
229 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/MultiplicityConstraintsInstance.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/MultiplicityConstraintsInstance.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/MultiplicityConstraintsInstance.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsSimple.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsSimple.svg new file mode 100644 index 00000000..95ba8def --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsSimple.svg | |||
@@ -0,0 +1,29 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="172pt" height="57pt" viewBox="-6 -6 184.24000549316406 68.79999923706055" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-8UTvxM6Gq4184FIveUdov"><style>.refinery-8UTvxM6Gq4184FIveUdov .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .node .node-outline{stroke:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .node .node-header{fill:rgb(53, 161, 173);}.refinery-8UTvxM6Gq4184FIveUdov .node .node-bg{fill:#fff;}.refinery-8UTvxM6Gq4184FIveUdov .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-8UTvxM6Gq4184FIveUdov .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-8UTvxM6Gq4184FIveUdov .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-h .node-header{fill:#e06c75;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-i .node-header{fill:#98c379;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-j .node-header{fill:#c678dd;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-n .node-header{fill:#abcc94;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-8UTvxM6Gq4184FIveUdov .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .edge .edge-line{stroke:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .edge .edge-arrow{fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN text{fill:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR text{fill:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov .icon-TRUE{fill:#19202b;}.refinery-8UTvxM6Gq4184FIveUdov .icon-UNKNOWN{fill:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov .icon-ERROR{fill:#ca1243;}.refinery-8UTvxM6Gq4184FIveUdov text.label-UNKNOWN{fill:#696c77;}.refinery-8UTvxM6Gq4184FIveUdov text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-8UTvxM6Gq4184FIveUdov text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-8UTvxM6Gq4184FIveUdov-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-8UTvxM6Gq4184FIveUdov-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-8UTvxM6Gq4184FIveUdov-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 52.79999923706055)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="5.5" y="-42.5" width="79" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="0" y="-48.8" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="-4" y="-52" width="86" height="27" clip-path="url(#refinery-8UTvxM6Gq4184FIveUdov-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="5" y="-33" font-size="12.00">Region::new</text> | ||
10 | <use x="6" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-8UTvxM6Gq4184FIveUdov-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="22" y="-9.6" font-size="12.00" class="label label-TRUE">Region</text> | ||
12 | </g> | ||
13 | <polyline points="0,-25.4 78.19,-25.4" class="node-outline"/> | ||
14 | <rect fill="none" x="0" y="-48.8" width="78.19" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-8UTvxM6Gq4184FIveUdov-clip-0"><rect stroke="none" x="0" y="-48.8" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n2 --> | ||
17 | |||
18 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="100.5" y="-42.5" width="69" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
19 | |||
20 | <rect stroke="none" x="95.95" y="-48.8" width="68.29" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
21 | <rect stroke="none" x="91" y="-52" width="76" height="27" clip-path="url(#refinery-8UTvxM6Gq4184FIveUdov-clip-1)" class="node-header"/> | ||
22 | <text text-anchor="start" x="100.95" y="-33" font-size="12.00">State::new</text> | ||
23 | |||
24 | |||
25 | |||
26 | |||
27 | <use x="101.954" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-8UTvxM6Gq4184FIveUdov-icon-TRUE"/><g><text text-anchor="start" x="117.95" y="-9.6" font-size="12.00" class="label label-TRUE">State</text> | ||
28 | </g><polyline points="95.95,-25.4 164.24,-25.4" class="node-outline"/><rect fill="none" x="95.95" y="-48.8" width="68.29" height="48.8" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-8UTvxM6Gq4184FIveUdov-clip-1"><rect stroke="none" x="95.95" y="-48.8" width="68.29" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g></g> | ||
29 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsSimple.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsSimple.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsSimple.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsWithInheritance.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsWithInheritance.svg new file mode 100644 index 00000000..cdf365f0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsWithInheritance.svg | |||
@@ -0,0 +1,38 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="230pt" height="104pt" viewBox="-6 -6 242.10000610351562 116.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-snWM44tZVFiopoyqQzHDw"><style>.refinery-snWM44tZVFiopoyqQzHDw .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .node .node-outline{stroke:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .node .node-header{fill:rgb(53, 161, 173);}.refinery-snWM44tZVFiopoyqQzHDw .node .node-bg{fill:#fff;}.refinery-snWM44tZVFiopoyqQzHDw .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-snWM44tZVFiopoyqQzHDw .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-snWM44tZVFiopoyqQzHDw .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-h .node-header{fill:#e06c75;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-i .node-header{fill:#98c379;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-j .node-header{fill:#c678dd;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-n .node-header{fill:#abcc94;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-snWM44tZVFiopoyqQzHDw .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .edge .edge-line{stroke:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .edge .edge-arrow{fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN text{fill:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR text{fill:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw .icon-TRUE{fill:#19202b;}.refinery-snWM44tZVFiopoyqQzHDw .icon-UNKNOWN{fill:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw .icon-ERROR{fill:#ca1243;}.refinery-snWM44tZVFiopoyqQzHDw text.label-UNKNOWN{fill:#696c77;}.refinery-snWM44tZVFiopoyqQzHDw text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-snWM44tZVFiopoyqQzHDw text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-snWM44tZVFiopoyqQzHDw-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-snWM44tZVFiopoyqQzHDw-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 100.4000015258789)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="5.5" y="-66.5" width="79" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="0" y="-72.6" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="-4" y="-76" width="86" height="27" clip-path="url(#refinery-snWM44tZVFiopoyqQzHDw-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="5" y="-56.8" font-size="12.00">Region::new</text> | ||
10 | <use x="6" y="-43" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="22" y="-33.4" font-size="12.00" class="label label-TRUE">Region</text> | ||
12 | </g> | ||
13 | <polyline points="0,-49.2 78.19,-49.2" class="node-outline"/> | ||
14 | <rect fill="none" x="0" y="-72.6" width="78.19" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-snWM44tZVFiopoyqQzHDw-clip-0"><rect stroke="none" x="0" y="-72.6" width="78.19" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n1 --> | ||
17 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="101.5" y="-90.5" width="127" height="97" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
18 | |||
19 | <rect stroke="none" x="96.1" y="-96.4" width="126" height="96.4" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="92" y="-100" width="134" height="27" clip-path="url(#refinery-snWM44tZVFiopoyqQzHDw-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="129.95" y="-80.6" font-size="12.00">State::new</text> | ||
22 | <use x="102.096" y="-67" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="118.01" y="-58.2" font-style="italic" font-size="12.00" class="label label-TRUE">CompositeElement</text> | ||
24 | </g> | ||
25 | <use x="102.096" y="-51" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/> | ||
26 | <g><text text-anchor="start" x="118.1" y="-42.2" font-style="italic" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
27 | </g> | ||
28 | <use x="102.096" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/> | ||
29 | <g><text text-anchor="start" x="118.1" y="-26.2" font-style="italic" font-size="12.00" class="label label-TRUE">RegularState</text> | ||
30 | </g> | ||
31 | <use x="102.096" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-snWM44tZVFiopoyqQzHDw-icon-TRUE"/> | ||
32 | <g><text text-anchor="start" x="118.1" y="-9.2" font-size="12.00" class="label label-TRUE">State</text> | ||
33 | </g> | ||
34 | <polyline points="96.1,-73 222.1,-73" class="node-outline"/> | ||
35 | <rect fill="none" x="96.1" y="-96.4" width="126" height="96.4" rx="12" ry="12" class="node-outline"/> | ||
36 | <clipPath id="refinery-snWM44tZVFiopoyqQzHDw-clip-1"><rect stroke="none" x="96.1" y="-96.4" width="126" height="96.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
37 | </g> | ||
38 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsWithInheritance.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsWithInheritance.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/NewObjectsWithInheritance.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeInstance.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeInstance.svg new file mode 100644 index 00000000..56a4d956 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeInstance.svg | |||
@@ -0,0 +1,69 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="236pt" height="142pt" viewBox="-6 -6 247.8800048828125 153.60000610351562" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-aUMOWvqBGMJmEq5FBgfQD"><style>.refinery-aUMOWvqBGMJmEq5FBgfQD .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-outline{stroke:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-header{fill:rgb(53, 161, 173);}.refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-bg{fill:#fff;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-h .node-header{fill:#e06c75;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-i .node-header{fill:#98c379;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-j .node-header{fill:#c678dd;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-n .node-header{fill:#abcc94;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-line{stroke:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-arrow{fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN text{fill:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR text{fill:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD .icon-TRUE{fill:#19202b;}.refinery-aUMOWvqBGMJmEq5FBgfQD .icon-UNKNOWN{fill:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD .icon-ERROR{fill:#ca1243;}.refinery-aUMOWvqBGMJmEq5FBgfQD text.label-UNKNOWN{fill:#696c77;}.refinery-aUMOWvqBGMJmEq5FBgfQD text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-aUMOWvqBGMJmEq5FBgfQD text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-aUMOWvqBGMJmEq5FBgfQD-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-aUMOWvqBGMJmEq5FBgfQD-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 137.60000610351562)"> | ||
4 | <!-- n0 --> | ||
5 | |||
6 | <!-- n1 --> | ||
7 | |||
8 | <!-- n1->n0 --> | ||
9 | <!-- n1->n0 --> | ||
10 | |||
11 | <!-- n1->n0 --> | ||
12 | |||
13 | <!-- n1->n0 --> | ||
14 | |||
15 | |||
16 | <!-- n3 --> | ||
17 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
18 | |||
19 | <rect stroke="none" x="46.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="42" y="-137" width="72" height="27" clip-path="url(#refinery-aUMOWvqBGMJmEq5FBgfQD-clip-0)" class="node-header"/> | ||
21 | <text text-anchor="start" x="72.39" y="-117.8" font-size="12.00">v1</text> | ||
22 | <use x="52.8135" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="68.81" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
24 | </g> | ||
25 | <polyline points="46.81,-110.2 110.84,-110.2" class="node-outline"/> | ||
26 | <rect fill="none" x="46.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
27 | <clipPath id="refinery-aUMOWvqBGMJmEq5FBgfQD-clip-0"><rect stroke="none" x="46.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
28 | <g class="edge edge-TRUE"> | ||
29 | |||
30 | <path fill="none" d="M114.46,-48.63C110.99,-56.83 106.51,-66.22 101.86,-75.08" class="edge-line"/> | ||
31 | <polygon points="98.81,-73.35 97.1,-83.81 104.96,-76.7 98.81,-73.35" class="edge-line edge-arrow"/> | ||
32 | <text text-anchor="middle" x="89.45" y="-70.24" font-size="10.50">source</text> | ||
33 | </g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
34 | |||
35 | <rect stroke="none" x="128.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
36 | <rect stroke="none" x="124" y="-137" width="72" height="27" clip-path="url(#refinery-aUMOWvqBGMJmEq5FBgfQD-clip-1)" class="node-header"/> | ||
37 | <text text-anchor="start" x="154.39" y="-117.8" font-size="12.00">v2</text> | ||
38 | <use x="134.814" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE"/> | ||
39 | <g><text text-anchor="start" x="150.81" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
40 | </g> | ||
41 | <polyline points="128.81,-110.2 192.84,-110.2" class="node-outline"/> | ||
42 | <rect fill="none" x="128.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
43 | <clipPath id="refinery-aUMOWvqBGMJmEq5FBgfQD-clip-1"><rect stroke="none" x="128.81" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
44 | |||
45 | <rect stroke="none" x="77.72" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
46 | <rect stroke="none" x="73" y="-52" width="92" height="27" clip-path="url(#refinery-aUMOWvqBGMJmEq5FBgfQD-clip-2)" class="node-header"/> | ||
47 | <text text-anchor="start" x="114.28" y="-33" font-size="12.00">t1</text> | ||
48 | <use x="83.7178" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-aUMOWvqBGMJmEq5FBgfQD-icon-TRUE"/> | ||
49 | <g><text text-anchor="start" x="99.72" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
50 | </g> | ||
51 | <polyline points="77.72,-25.4 161.94,-25.4" class="node-outline"/> | ||
52 | <rect fill="none" x="77.72" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
53 | <clipPath id="refinery-aUMOWvqBGMJmEq5FBgfQD-clip-2"><rect stroke="none" x="77.72" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="edge edge-TRUE"> | ||
54 | |||
55 | <path fill="none" d="M84.13,-85.14C87.58,-76.95 92.06,-67.57 96.71,-58.7" class="edge-line"/> | ||
56 | <polygon points="99.76,-60.41 101.47,-49.96 93.61,-57.06 99.76,-60.41" class="edge-line edge-arrow"/> | ||
57 | <text text-anchor="middle" x="46.32" y="-57.24" font-size="10.50">outgoingTransition</text> | ||
58 | </g><g class="edge edge-TRUE"> | ||
59 | |||
60 | <path fill="none" d="M137.47,-48.63C142.07,-56.74 146.83,-66.01 150.92,-74.79" class="edge-line"/> | ||
61 | <polygon points="147.65,-76.04 154.91,-83.75 154.04,-73.19 147.65,-76.04" class="edge-line edge-arrow"/> | ||
62 | <text text-anchor="middle" x="132.53" y="-57.44" font-size="10.50">target</text> | ||
63 | </g><g class="edge edge-TRUE"> | ||
64 | |||
65 | <path fill="none" d="M143.28,-85.14C138.68,-77.04 133.92,-67.77 129.82,-58.99" class="edge-line"/> | ||
66 | <polygon points="133.08,-57.72 125.81,-50.01 126.69,-60.57 133.08,-57.72" class="edge-line edge-arrow"/> | ||
67 | <text text-anchor="middle" x="180.75" y="-70.04" font-size="10.50">incomingTransition</text> | ||
68 | </g></g> | ||
69 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeInstance.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeInstance.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeInstance.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeSelf.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeSelf.svg new file mode 100644 index 00000000..81ab4a0c --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeSelf.svg | |||
@@ -0,0 +1,24 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="117pt" height="57pt" viewBox="-6 -6 128.5 68.79999923706055" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-4mmABsPwhJURILrHpcKRU"><style>.refinery-4mmABsPwhJURILrHpcKRU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .node .node-outline{stroke:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .node .node-header{fill:rgb(53, 161, 173);}.refinery-4mmABsPwhJURILrHpcKRU .node .node-bg{fill:#fff;}.refinery-4mmABsPwhJURILrHpcKRU .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-4mmABsPwhJURILrHpcKRU .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-4mmABsPwhJURILrHpcKRU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-h .node-header{fill:#e06c75;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-i .node-header{fill:#98c379;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-j .node-header{fill:#c678dd;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-n .node-header{fill:#abcc94;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-4mmABsPwhJURILrHpcKRU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .edge .edge-line{stroke:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .edge .edge-arrow{fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN text{fill:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR text{fill:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU .icon-TRUE{fill:#19202b;}.refinery-4mmABsPwhJURILrHpcKRU .icon-UNKNOWN{fill:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU .icon-ERROR{fill:#ca1243;}.refinery-4mmABsPwhJURILrHpcKRU text.label-UNKNOWN{fill:#696c77;}.refinery-4mmABsPwhJURILrHpcKRU text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-4mmABsPwhJURILrHpcKRU text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-4mmABsPwhJURILrHpcKRU-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-4mmABsPwhJURILrHpcKRU-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-4mmABsPwhJURILrHpcKRU-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 52.79999923706055)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-p"><rect stroke="none" x="5.5" y="-42.5" width="80" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="0" y="-48.8" width="79.01" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="-4" y="-52" width="87" height="27" clip-path="url(#refinery-4mmABsPwhJURILrHpcKRU-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="5" y="-33" font-size="12.00">Person::new</text> | ||
10 | <use x="6" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-4mmABsPwhJURILrHpcKRU-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="22" y="-9.6" font-size="12.00" class="label label-TRUE">Person</text> | ||
12 | </g> | ||
13 | <polyline points="0,-25.4 79.01,-25.4" class="node-outline"/> | ||
14 | <rect fill="none" x="0" y="-48.8" width="79.01" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-4mmABsPwhJURILrHpcKRU-clip-0"><rect stroke="none" x="0" y="-48.8" width="79.01" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n0->n0 --> | ||
17 | <g class="edge edge-UNKNOWN"> | ||
18 | |||
19 | <path fill="none" stroke-dasharray="5,2" d="M78.75,-33.47C89.14,-32.95 97.01,-29.93 97.01,-24.4 97.01,-21.12 94.24,-18.72 89.81,-17.2" class="edge-line"/> | ||
20 | <polygon points="90.68,-13.8 80.24,-15.58 89.52,-20.7 90.68,-13.8" class="edge-line edge-arrow"/> | ||
21 | <text text-anchor="middle" x="93.87" y="-36.59" font-size="10.50">friend</text> | ||
22 | </g> | ||
23 | </g> | ||
24 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeSelf.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeSelf.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesOppositeSelf.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesSimple.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesSimple.svg new file mode 100644 index 00000000..fac74815 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesSimple.svg | |||
@@ -0,0 +1,43 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="104pt" height="142pt" viewBox="-6 -6 116.04000091552734 153.60000610351562" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-KS4S5srdaYqLoF1P1dM89"><style>.refinery-KS4S5srdaYqLoF1P1dM89 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .node .node-outline{stroke:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .node .node-header{fill:rgb(53, 161, 173);}.refinery-KS4S5srdaYqLoF1P1dM89 .node .node-bg{fill:#fff;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-i .node-header{fill:#98c379;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-line{stroke:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-arrow{fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN text{fill:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR text{fill:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 .icon-TRUE{fill:#19202b;}.refinery-KS4S5srdaYqLoF1P1dM89 .icon-UNKNOWN{fill:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 .icon-ERROR{fill:#ca1243;}.refinery-KS4S5srdaYqLoF1P1dM89 text.label-UNKNOWN{fill:#696c77;}.refinery-KS4S5srdaYqLoF1P1dM89 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-KS4S5srdaYqLoF1P1dM89 text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-KS4S5srdaYqLoF1P1dM89-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-KS4S5srdaYqLoF1P1dM89-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-KS4S5srdaYqLoF1P1dM89-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 137.60000610351562)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="15.5" y="-42.5" width="76" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="10.1" y="-48.8" width="75.84" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="6" y="-52" width="83" height="27" clip-path="url(#refinery-KS4S5srdaYqLoF1P1dM89-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="15.1" y="-33" font-size="12.00">Vertex::new</text> | ||
10 | <use x="16.0957" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KS4S5srdaYqLoF1P1dM89-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="32.1" y="-9.6" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
12 | </g> | ||
13 | <polyline points="10.1,-25.4 85.94,-25.4" class="node-outline"/> | ||
14 | <rect fill="none" x="10.1" y="-48.8" width="75.84" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-KS4S5srdaYqLoF1P1dM89-clip-0"><rect stroke="none" x="10.1" y="-48.8" width="75.84" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n1 --> | ||
17 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-i"><rect stroke="none" x="5.5" y="-127.5" width="97" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
18 | |||
19 | <rect stroke="none" x="0" y="-133.6" width="96.04" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="-4" y="-137" width="104" height="27" clip-path="url(#refinery-KS4S5srdaYqLoF1P1dM89-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="5" y="-117.8" font-size="12.00">Transition::new</text> | ||
22 | <use x="6" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-KS4S5srdaYqLoF1P1dM89-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="22" y="-94.4" font-size="12.00" class="label label-TRUE">Transition</text> | ||
24 | </g> | ||
25 | <polyline points="0,-110.2 96.04,-110.2" class="node-outline"/> | ||
26 | <rect fill="none" x="0" y="-133.6" width="96.04" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
27 | <clipPath id="refinery-KS4S5srdaYqLoF1P1dM89-clip-1"><rect stroke="none" x="0" y="-133.6" width="96.04" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
28 | <!-- n1->n0 --> | ||
29 | <g class="edge edge-UNKNOWN"> | ||
30 | |||
31 | <path fill="none" stroke-dasharray="5,2" d="M41.9,-85.14C41.32,-77.31 41.14,-68.38 41.35,-59.86" class="edge-line"/> | ||
32 | <polygon points="44.84,-60.29 41.81,-50.14 37.85,-59.96 44.84,-60.29" class="edge-line edge-arrow"/> | ||
33 | <text text-anchor="middle" x="24.78" y="-70.26" font-size="10.50">source</text> | ||
34 | </g> | ||
35 | <!-- n1->n0 --> | ||
36 | <g class="edge edge-UNKNOWN"> | ||
37 | |||
38 | <path fill="none" stroke-dasharray="5,2" d="M54.14,-85.14C54.71,-77.31 54.9,-68.38 54.68,-59.86" class="edge-line"/> | ||
39 | <polygon points="58.19,-59.96 54.23,-50.14 51.2,-60.29 58.19,-59.96" class="edge-line edge-arrow"/> | ||
40 | <text text-anchor="middle" x="40.18" y="-57.66" font-size="10.50">target</text> | ||
41 | </g> | ||
42 | </g> | ||
43 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesSimple.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesSimple.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/ReferencesSimple.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/index.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/index.md new file mode 100644 index 00000000..18cbbf9f --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/classes/index.md | |||
@@ -0,0 +1,213 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: Metamodeling in Refinery | ||
5 | sidebar_position: 0 | ||
6 | --- | ||
7 | |||
8 | # Classes and references | ||
9 | |||
10 | Refinery supports _metamodeling_ to describe the desired structure of generated models. | ||
11 | |||
12 | The metamodeling facilities are inspired by object-oriented software and the [Eclipse Modeling Foundation](https://eclipse.dev/modeling/emf/) (EMF) Core, a lightweight framework for data models. | ||
13 | The textual syntax in Refinery for defining metamodels is largely compatible with [Xcore](https://wiki.eclipse.org/Xcore), a textual syntax for EMF metamodels. | ||
14 | |||
15 | ## Classes | ||
16 | |||
17 | Classes are declared with the `class` keyword. | ||
18 | |||
19 | Like in many programming languages, class members are specified between curly braces `{}`. | ||
20 | If a class has no members, the declaration may be terminated with a `.` instead. | ||
21 | |||
22 | ```refinery | ||
23 | % Class with no members. | ||
24 | class Region {} | ||
25 | |||
26 | % Alternative syntax without curly braces. | ||
27 | class State. | ||
28 | ``` | ||
29 | |||
30 | By default, a _new object_ is added to the partial model to represent the instances of a class. | ||
31 | For example, the new objects `Region::new` and `State::new` represent potential instances of the classes `Region` and `State`, respectively: | ||
32 | |||
33 | import NewObjectsSimple from './NewObjectsSimple.svg'; | ||
34 | |||
35 | <NewObjectsSimple /> | ||
36 | |||
37 | As you can see, no new objects represent potential nodes that are instanceof of both `Region` and `State`. | ||
38 | In fact, such instances are not permitted at all. | ||
39 | Each node must the instance of a _single most-specific class:_ | ||
40 | |||
41 | import InvalidInstance from './InvalidInstance.svg'; | ||
42 | |||
43 | <InvalidInstance /> | ||
44 | |||
45 | ### Inheritance | ||
46 | |||
47 | Like in object-oriented programming languages, classes may declare _superclasses_ with the `extends` keyword. | ||
48 | The inheritance hierarchy may not contain any cycles (a class cannot be a superclass of itself), but _multiple inheritance_ is allowed. | ||
49 | |||
50 | Classes that can't be instantiated directly (i.e., a subclass must be instantiated instead) can be marked with the `abstract` keyword. | ||
51 | Such classes do not have a _new object,_ since there are no direct instances to represent. | ||
52 | |||
53 | ```refinery | ||
54 | abstract class CompositeElement. | ||
55 | class Region. | ||
56 | abstract class Vertex. | ||
57 | abstract class RegularState extends Vertex. | ||
58 | class State extends RegularState, CompositeElement. | ||
59 | ``` | ||
60 | |||
61 | Notice that the new object `State::new` is an instance of `CompositeElement`, `Vertex`, `RegularState`, and `State` as well. | ||
62 | |||
63 | import NewObjectsWithInheritance from './NewObjectsWithInheritance.svg'; | ||
64 | |||
65 | <NewObjectsWithInheritance /> | ||
66 | |||
67 | ## References | ||
68 | |||
69 | The graph structure of model generated by Refinery is determined by the _references_ of the metamodel, which will appear as labeled edges between nodes (class instances). | ||
70 | |||
71 | References are declared as class members by providing the _target type,_ and optional _multiplicity,_ and the name of the reference: | ||
72 | |||
73 | ```refinery | ||
74 | class Vertex. | ||
75 | class Transition { | ||
76 | Vertex[1] source | ||
77 | Vertex[1] target | ||
78 | } | ||
79 | ``` | ||
80 | |||
81 | import ReferencesSimple from './ReferencesSimple.svg'; | ||
82 | |||
83 | <ReferencesSimple /> | ||
84 | |||
85 | You may add the `refers` keyword for compatibility with [Xcore](https://wiki.eclipse.org/Xcore). The following specification is equivalent: | ||
86 | |||
87 | ```refinery | ||
88 | class Vertex. | ||
89 | class Transition { | ||
90 | refers Vertex[1] source | ||
91 | refers Vertex[1] target | ||
92 | } | ||
93 | ``` | ||
94 | |||
95 | ### Opposite constraints | ||
96 | |||
97 | The `opposite` keywords specifies that two references are in an _opposite_ relationship, i.e., if one reference is present in a direction, the other must be present between the same nodes in the opposite direction. | ||
98 | |||
99 | ``` | ||
100 | class Vertex { | ||
101 | Transition[] outgoingTransition opposite source | ||
102 | Transition[] incomingTransition opposite target | ||
103 | } | ||
104 | class Transition { | ||
105 | Vertex[1] source opposite outgoingTransition | ||
106 | Vertex[1] target opposite incomingTransition | ||
107 | } | ||
108 | ``` | ||
109 | |||
110 | import ReferencesOppositeInstance from './ReferencesOppositeInstance.svg'; | ||
111 | |||
112 | <ReferencesOppositeInstance /> | ||
113 | |||
114 | Opposites must be declared in pairs: it is a specification error to declare the `opposite` for one direction but not the other. | ||
115 | |||
116 | Unlike in EMF, references that are the `opposite` of themselves are also supported. | ||
117 | These must always be present in both directions between two nodes. | ||
118 | Thus, they correspond to undirected graph edges. | ||
119 | |||
120 | ```refinery | ||
121 | class Person { | ||
122 | Person[] friend opposite friend | ||
123 | } | ||
124 | ``` | ||
125 | |||
126 | import ReferencesOppositeSelf from './ReferencesOppositeSelf.svg'; | ||
127 | |||
128 | <ReferencesOppositeSelf /> | ||
129 | |||
130 | ### Multiplicity | ||
131 | |||
132 | _Multiplicity constraints_ can be provided after the reference type in square braces. | ||
133 | They specify how many _outgoing_ references should exist for any given instance of the class. | ||
134 | |||
135 | :::info | ||
136 | |||
137 | To control the number of _incoming_ references, add an `opposite` reference with multiplicity constraint. | ||
138 | |||
139 | ::: | ||
140 | |||
141 | A multiplicity constraint is of the form `[n..m]`, where the non-negative integer `n` is the _lower_ bound of outgoing references, | ||
142 | and `m` is a positive integer or `*` corresponding to the _upper_ bound of outgoing references. | ||
143 | The value of `*` represent a reference with _unbounded_ upper multiplicity. | ||
144 | |||
145 | If `n` = `m`, the shorter form `[n]` may be used. | ||
146 | The bound `[0..*]` may be abbreviated as `[]`. | ||
147 | If the multiplicity constraint is omitted, the bound `[0..1]` is assumed. | ||
148 | |||
149 | --- | ||
150 | |||
151 | In the following model, the node `v1` satisfies all multiplicity constraints of `outgoingTransition`. | ||
152 | The node `v2` violates the lower bound constraint, while `v3` violates the upper bound constraint. | ||
153 | All `Transition` instances satisfy the multiplicity constraints associated with `source`. | ||
154 | |||
155 | ```refinery | ||
156 | class Vertex { | ||
157 | Transition[2..3] outgoingTransition opposite source | ||
158 | } | ||
159 | class Transition { | ||
160 | Vertex[1] source opposite outgoingTransition | ||
161 | } | ||
162 | ``` | ||
163 | |||
164 | import MultiplicityConstraintsInstance from './MultiplicityConstraintsInstance.svg'; | ||
165 | |||
166 | <MultiplicityConstraintsInstance /> | ||
167 | |||
168 | ### Containment hierarchy | ||
169 | |||
170 | To structure models and ensure their connectedness, Refinery supports _containment_ constraints. | ||
171 | |||
172 | References may be marked as _containment_ references with the `contains` keyword. | ||
173 | |||
174 | Classes that are the _target type_ of at least one _containment_ reference are considered `contained`. | ||
175 | An instance of a `contained` class must have exactly 1 incoming containment reference. | ||
176 | Instances of classes that are not `contained` must _not_ have any incoming containment references. | ||
177 | |||
178 | Containment references have to form a _forest_, i.e., they must not contain any cycles. | ||
179 | The _roots_ of the forest are instances of classes that are not `contained`, while `contained` classes for the internal nodes and leaves of the trees. | ||
180 | |||
181 | Opposites of _containment_ references have to be marked with the `container` keyword. | ||
182 | They must not specify any multiplicity constraint, since the multiplicity is already implied by the containment hierarchy. | ||
183 | |||
184 | --- | ||
185 | |||
186 | In the following model, the instances of `Region` are the roots of the containment hierarchy. | ||
187 | The classes `Vertex` are `Transition` are both considered `contained`. | ||
188 | |||
189 | ```refinery | ||
190 | class Region { | ||
191 | contains Vertex[] vertices opposite region | ||
192 | } | ||
193 | |||
194 | class Vertex { | ||
195 | container Region region opposite vertices | ||
196 | contains Transition[] outgoingTransition opposite source | ||
197 | Transition[] incomingTransition opposite target | ||
198 | } | ||
199 | |||
200 | class Transition { | ||
201 | container Vertex source opposite outgoingTransition | ||
202 | Vertex[1] target opposite incomingTransition | ||
203 | } | ||
204 | ``` | ||
205 | |||
206 | Containment edges are show with **thick** lines: | ||
207 | |||
208 | import ContainmentInstance from './ContainmentInstance.svg'; | ||
209 | |||
210 | <ContainmentInstance /> | ||
211 | |||
212 | Containment edges form must form a forest. | ||
213 | In contrast, non-containment references, such as `target`, may cross the containment hierarchy. | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsError.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsError.svg new file mode 100644 index 00000000..8ddc65f3 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsError.svg | |||
@@ -0,0 +1,20 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="72pt" height="72pt" viewBox="-6 -6 84 84.4000015258789" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-t1ihtn3yDar9Rl2Fph8SJ"><style>.refinery-t1ihtn3yDar9Rl2Fph8SJ .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-outline{stroke:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-header{fill:rgb(53, 161, 173);}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-bg{fill:#fff;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-h .node-header{fill:#e06c75;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-i .node-header{fill:#98c379;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-j .node-header{fill:#c678dd;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-n .node-header{fill:#abcc94;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-line{stroke:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-arrow{fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN text{fill:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR text{fill:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-TRUE{fill:#19202b;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-UNKNOWN{fill:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-ERROR{fill:#ca1243;}.refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-UNKNOWN{fill:#696c77;}.refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-t1ihtn3yDar9Rl2Fph8SJ text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 68.4000015258789)"> | ||
4 | <!-- n3 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE"> | ||
6 | |||
7 | <rect stroke="none" x="0" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="-4" y="-68" width="72" height="27" clip-path="url(#refinery-t1ihtn3yDar9Rl2Fph8SJ-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="25.56" y="-48.6" font-size="12.00">v1</text> | ||
10 | |||
11 | |||
12 | <use x="6" y="-35" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-ERROR"/> | ||
13 | <g><text text-anchor="start" x="21.99" y="-25.2" font-size="12.00" class="label label-ERROR">Vertex</text> | ||
14 | </g> | ||
15 | <use x="6" y="-19" width="12" height="12" id="" class="icon icon-ERROR" href="#refinery-t1ihtn3yDar9Rl2Fph8SJ-icon-ERROR"/> | ||
16 | <g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-ERROR">State</text> | ||
17 | </g> | ||
18 | <polyline points="0,-41 64,-41" class="node-outline"/><rect fill="none" x="0" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-t1ihtn3yDar9Rl2Fph8SJ-clip-0"><rect stroke="none" x="0" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
19 | </g> | ||
20 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsError.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsError.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsError.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsExample.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsExample.svg new file mode 100644 index 00000000..26b3d1ff --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsExample.svg | |||
@@ -0,0 +1,99 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="250pt" height="157pt" viewBox="-6 -6 262.17999267578125 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-yow0X_ZN3HQDi30KE0Sac"><style>.refinery-yow0X_ZN3HQDi30KE0Sac .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .node .node-outline{stroke:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .node .node-header{fill:rgb(53, 161, 173);}.refinery-yow0X_ZN3HQDi30KE0Sac .node .node-bg{fill:#fff;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-h .node-header{fill:#e06c75;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-i .node-header{fill:#98c379;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-j .node-header{fill:#c678dd;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-n .node-header{fill:#abcc94;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-line{stroke:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-arrow{fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN text{fill:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR text{fill:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac .icon-TRUE{fill:#19202b;}.refinery-yow0X_ZN3HQDi30KE0Sac .icon-UNKNOWN{fill:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac .icon-ERROR{fill:#ca1243;}.refinery-yow0X_ZN3HQDi30KE0Sac text.label-UNKNOWN{fill:#696c77;}.refinery-yow0X_ZN3HQDi30KE0Sac text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-yow0X_ZN3HQDi30KE0Sac text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-yow0X_ZN3HQDi30KE0Sac-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-yow0X_ZN3HQDi30KE0Sac-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 153.1999969482422)"> | ||
4 | <!-- n3 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="6.5" y="-143.5" width="79" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="1.07" y="-149.2" width="78.19000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="-3" y="-153" width="86" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="6.07" y="-133.4" font-size="12.00">Region::new</text> | ||
10 | <use x="7.07291" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="23.07" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
12 | </g> | ||
13 | <polyline points="1.07,-125.8 79.26,-125.8" class="node-outline"/> | ||
14 | <rect fill="none" x="1.07" y="-149.2" width="78.19000000000001" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-0"><rect stroke="none" x="1.07" y="-149.2" width="78.19000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n4 --> | ||
17 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="14.5" y="-58.5" width="69" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
18 | |||
19 | <rect stroke="none" x="9.03" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="5" y="-68" width="76" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="14.03" y="-48.6" font-size="12.00">State::new</text> | ||
22 | <use x="15.027" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="31.01" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
24 | </g> | ||
25 | <use x="15.027" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/> | ||
26 | <g><text text-anchor="start" x="31.03" y="-9.2" font-size="12.00" class="label label-TRUE">State</text> | ||
27 | </g> | ||
28 | <polyline points="9.03,-41 77.31,-41" class="node-outline"/> | ||
29 | <rect fill="none" x="9.03" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
30 | <clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-1"><rect stroke="none" x="9.03" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
31 | <!-- n3->n4 --> | ||
32 | <g class="edge edge-UNKNOWN"> | ||
33 | |||
34 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M40.94,-100.47C41.19,-92.98 41.47,-84.45 41.75,-76.02" class="edge-line"/> | ||
35 | <polygon stroke-width="2" points="44.81,-76.18 42.04,-67.34 38.69,-75.98 44.81,-76.18" class="edge-line edge-arrow"/> | ||
36 | <text text-anchor="start" x="0" y="-86.89" font-weight="bold" font-size="10.50">vertices</text> | ||
37 | </g> | ||
38 | <!-- n8 --> | ||
39 | <!-- n4 --> | ||
40 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
41 | |||
42 | <rect stroke="none" x="96.17" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
43 | <rect stroke="none" x="92" y="-68" width="71" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-2)" class="node-header"/> | ||
44 | <text text-anchor="start" x="121.73" y="-48.6" font-size="12.00">v1</text> | ||
45 | <use x="102.169" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/> | ||
46 | <g><text text-anchor="start" x="118.15" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
47 | </g> | ||
48 | <use x="102.169" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-UNKNOWN"/> | ||
49 | <g><text text-anchor="start" x="118.17" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text> | ||
50 | </g> | ||
51 | <polyline points="96.17,-41 160.17,-41" class="node-outline"/> | ||
52 | <rect fill="none" x="96.17" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
53 | <clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-2"><rect stroke="none" x="96.17" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><!-- n0->n3 --> | ||
54 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k"> | ||
55 | |||
56 | <rect stroke="none" x="96.98" y="-149.2" width="66.38000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
57 | <rect stroke="none" x="92" y="-153" width="74" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-3)" class="node-header"/> | ||
58 | <text text-anchor="start" x="124.29" y="-133.4" font-size="12.00">r1</text> | ||
59 | <use x="102.982" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/> | ||
60 | <g><text text-anchor="start" x="118.98" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
61 | </g> | ||
62 | <polyline points="96.98,-125.8 163.36,-125.8" class="node-outline"/> | ||
63 | <rect fill="none" x="96.98" y="-149.2" width="66.38000000000001" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
64 | <clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-3"><rect stroke="none" x="96.98" y="-149.2" width="66.38000000000001" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
65 | |||
66 | <rect stroke="none" x="178.15" y="-56.6" width="64.03" height="48.800000000000004" rx="12" ry="12" class="node-bg"/> | ||
67 | <rect stroke="none" x="174" y="-60" width="72" height="27" clip-path="url(#refinery-yow0X_ZN3HQDi30KE0Sac-clip-4)" class="node-header"/> | ||
68 | <text text-anchor="start" x="203.73" y="-40.8" font-size="12.00">v2</text> | ||
69 | <use x="184.154" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-yow0X_ZN3HQDi30KE0Sac-icon-TRUE"/> | ||
70 | <g><text text-anchor="start" x="200.15" y="-17.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
71 | </g> | ||
72 | |||
73 | |||
74 | <polyline points="178.15,-33.2 242.18,-33.2" class="node-outline"/><rect fill="none" x="178.15" y="-56.6" width="64.03" height="48.800000000000004" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-yow0X_ZN3HQDi30KE0Sac-clip-4"><rect stroke="none" x="178.15" y="-56.6" width="64.03" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
75 | <g class="edge edge-UNKNOWN"> | ||
76 | |||
77 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M107.76,-100.47C99.59,-91.95 90.1,-82.07 80.95,-72.55" class="edge-line"/> | ||
78 | <polygon stroke-width="2" points="83.41,-70.68 75.14,-66.49 78.99,-74.93 83.41,-70.68" class="edge-line edge-arrow"/> | ||
79 | <text text-anchor="start" x="48.91" y="-86.57" font-weight="bold" font-size="10.50">vertices</text> | ||
80 | </g> | ||
81 | <!-- n2->n0 --> | ||
82 | |||
83 | <!-- n3->n0 --> | ||
84 | |||
85 | <!-- n3->n4 --> | ||
86 | |||
87 | <g class="edge edge-TRUE"> | ||
88 | |||
89 | <path fill="none" stroke-width="2" d="M129.65,-100.47C129.49,-92.98 129.3,-84.45 129.11,-76.02" class="edge-line"/> | ||
90 | <polygon stroke-width="2" points="132.18,-76.02 128.92,-67.34 126.05,-76.15 132.18,-76.02" class="edge-line edge-arrow"/> | ||
91 | <text text-anchor="start" x="129.26" y="-86.89" font-weight="bold" font-size="10.50">vertices</text> | ||
92 | </g> | ||
93 | <g class="edge edge-TRUE"> | ||
94 | |||
95 | <path fill="none" stroke-width="2" d="M150.77,-100.47C160.29,-89.69 171.74,-76.72 182.04,-65.06" class="edge-line"/> | ||
96 | <polygon stroke-width="2" points="184.14,-67.31 187.63,-58.72 179.55,-63.25 184.14,-67.31" class="edge-line edge-arrow"/> | ||
97 | <text text-anchor="start" x="128.85" y="-69.81" font-weight="bold" font-size="10.50">vertices</text> | ||
98 | </g></g> | ||
99 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsExample.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsExample.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/AssertionsExample.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/DefaultAssertions.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/DefaultAssertions.svg new file mode 100644 index 00000000..2ab002bf --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/DefaultAssertions.svg | |||
@@ -0,0 +1,129 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="327pt" height="157pt" viewBox="-6 -6 339.3299865722656 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-p_2c2ycZZXd-I3Xg-BBah"><style>.refinery-p_2c2ycZZXd-I3Xg-BBah .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-outline{stroke:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-header{fill:rgb(53, 161, 173);}.refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-bg{fill:#fff;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-h .node-header{fill:#e06c75;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-i .node-header{fill:#98c379;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-j .node-header{fill:#c678dd;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-n .node-header{fill:#abcc94;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-line{stroke:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-arrow{fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN text{fill:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR text{fill:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah .icon-TRUE{fill:#19202b;}.refinery-p_2c2ycZZXd-I3Xg-BBah .icon-UNKNOWN{fill:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah .icon-ERROR{fill:#ca1243;}.refinery-p_2c2ycZZXd-I3Xg-BBah text.label-UNKNOWN{fill:#696c77;}.refinery-p_2c2ycZZXd-I3Xg-BBah text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-p_2c2ycZZXd-I3Xg-BBah text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-p_2c2ycZZXd-I3Xg-BBah-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="scale(1 1) rotate(0) translate(4 153.2)"> | ||
4 | <!-- n0 --> | ||
5 | |||
6 | <!-- n2 --> | ||
7 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="5.5" y="-58.5" width="69" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
8 | |||
9 | <rect stroke="none" x="0" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
10 | <rect stroke="none" x="-4" y="-68" width="76" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-0)" class="node-header"/> | ||
11 | <text text-anchor="start" x="5" y="-48.6" font-size="12.00">State::new</text> | ||
12 | <use x="6" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
13 | <g><text text-anchor="start" x="21.99" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
14 | </g> | ||
15 | <use x="6" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
16 | <g><text text-anchor="start" x="22" y="-9.2" font-size="12.00" class="label label-TRUE">State</text> | ||
17 | </g> | ||
18 | <polyline points="0,-41 68.28,-41" class="node-outline"/> | ||
19 | <rect fill="none" x="0" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
20 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-0"><rect stroke="none" x="0" y="-64.4" width="68.28" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
21 | <!-- n0->n2 --> | ||
22 | |||
23 | <!-- n3 --> | ||
24 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k"> | ||
25 | |||
26 | <rect stroke="none" x="84.96" y="-149.2" width="66.37000000000002" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
27 | <rect stroke="none" x="80" y="-153" width="74" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-1)" class="node-header"/> | ||
28 | <text text-anchor="start" x="112.26" y="-133.4" font-size="12.00">r1</text> | ||
29 | <use x="90.9551" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
30 | <g><text text-anchor="start" x="106.96" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
31 | </g> | ||
32 | <polyline points="84.96,-125.8 151.33,-125.8" class="node-outline"/> | ||
33 | <rect fill="none" x="84.96" y="-149.2" width="66.37000000000002" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
34 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-1"><rect stroke="none" x="84.96" y="-149.2" width="66.37000000000002" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
35 | <!-- n3->n2 --> | ||
36 | <g class="edge edge-UNKNOWN"> | ||
37 | |||
38 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M96.51,-100.47C88.69,-92.04 79.64,-82.27 70.89,-72.83" class="edge-line"/> | ||
39 | <polygon stroke-width="2" points="73.24,-70.86 65.04,-66.53 68.75,-75.03 73.24,-70.86" class="edge-line edge-arrow"/> | ||
40 | <text text-anchor="start" x="38.02" y="-86.32" font-weight="bold" font-size="10.50">vertices</text> | ||
41 | </g> | ||
42 | <!-- n4 --> | ||
43 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
44 | |||
45 | <rect stroke="none" x="86.14" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
46 | <rect stroke="none" x="82" y="-68" width="71" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-2)" class="node-header"/> | ||
47 | <text text-anchor="start" x="111.71" y="-48.6" font-size="12.00">v1</text> | ||
48 | <use x="92.1416" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
49 | <g><text text-anchor="start" x="108.13" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
50 | </g> | ||
51 | <use x="92.1416" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN"/> | ||
52 | <g><text text-anchor="start" x="108.14" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text> | ||
53 | </g> | ||
54 | <polyline points="86.14,-41 150.14,-41" class="node-outline"/> | ||
55 | <rect fill="none" x="86.14" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
56 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-2"><rect stroke="none" x="86.14" y="-64.4" width="63.999999999999986" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
57 | <!-- n3->n4 --> | ||
58 | <g class="edge edge-TRUE"> | ||
59 | |||
60 | <path fill="none" stroke-width="2" d="M118.14,-100.47C118.14,-92.98 118.14,-84.45 118.14,-76.02" class="edge-line"/> | ||
61 | <polygon stroke-width="2" points="121.2,-76.09 118.14,-67.34 115.08,-76.09 121.2,-76.09" class="edge-line edge-arrow"/> | ||
62 | <text text-anchor="start" x="118.14" y="-86.89" font-weight="bold" font-size="10.50">vertices</text> | ||
63 | </g> | ||
64 | <!-- n5 --> | ||
65 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k"> | ||
66 | |||
67 | <rect stroke="none" x="168.96" y="-149.2" width="66.37" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
68 | <rect stroke="none" x="164" y="-153" width="74" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-3)" class="node-header"/> | ||
69 | <text text-anchor="start" x="196.26" y="-133.4" font-size="12.00">r2</text> | ||
70 | <use x="174.955" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
71 | <g><text text-anchor="start" x="190.96" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
72 | </g> | ||
73 | <polyline points="168.96,-125.8 235.33,-125.8" class="node-outline"/> | ||
74 | <rect fill="none" x="168.96" y="-149.2" width="66.37" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
75 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-3"><rect stroke="none" x="168.96" y="-149.2" width="66.37" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
76 | <!-- n6 --> | ||
77 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
78 | |||
79 | <rect stroke="none" x="170.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
80 | <rect stroke="none" x="166" y="-68" width="72" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-4)" class="node-header"/> | ||
81 | <text text-anchor="start" x="195.71" y="-48.6" font-size="12.00">v2</text> | ||
82 | <use x="176.142" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
83 | <g><text text-anchor="start" x="192.13" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
84 | </g> | ||
85 | <use x="176.142" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN"/> | ||
86 | <g><text text-anchor="start" x="192.14" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text> | ||
87 | </g> | ||
88 | <polyline points="170.14,-41 234.14,-41" class="node-outline"/> | ||
89 | <rect fill="none" x="170.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
90 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-4"><rect stroke="none" x="170.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
91 | <!-- n5->n6 --> | ||
92 | <g class="edge edge-TRUE"> | ||
93 | |||
94 | <path fill="none" stroke-width="2" d="M202.14,-100.47C202.14,-92.98 202.14,-84.45 202.14,-76.02" class="edge-line"/> | ||
95 | <polygon stroke-width="2" points="205.2,-76.09 202.14,-67.34 199.08,-76.09 205.2,-76.09" class="edge-line edge-arrow"/> | ||
96 | <text text-anchor="start" x="160.61" y="-86.89" font-weight="bold" font-size="10.50">vertices</text> | ||
97 | </g><g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
98 | |||
99 | <rect stroke="none" x="254.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
100 | <rect stroke="none" x="250" y="-68" width="72" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-5)" class="node-header"/> | ||
101 | <text text-anchor="start" x="279.71" y="-48.6" font-size="12.00">v3</text> | ||
102 | <use x="260.142" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
103 | <g><text text-anchor="start" x="276.13" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
104 | </g> | ||
105 | <use x="260.142" y="-19" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-UNKNOWN"/> | ||
106 | <g><text text-anchor="start" x="276.14" y="-9.2" font-size="12.00" class="label label-UNKNOWN">State</text> | ||
107 | </g> | ||
108 | <polyline points="254.14,-41 318.14,-41" class="node-outline"/> | ||
109 | <rect fill="none" x="254.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
110 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-5"><rect stroke="none" x="254.14" y="-64.4" width="64" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
111 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-k"> | ||
112 | |||
113 | <rect stroke="none" x="252.96" y="-149.2" width="66.36999999999998" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
114 | <rect stroke="none" x="248" y="-153" width="74" height="27" clip-path="url(#refinery-p_2c2ycZZXd-I3Xg-BBah-clip-6)" class="node-header"/> | ||
115 | <text text-anchor="start" x="280.26" y="-133.4" font-size="12.00">r3</text> | ||
116 | <use x="258.955" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-p_2c2ycZZXd-I3Xg-BBah-icon-TRUE"/> | ||
117 | <g><text text-anchor="start" x="274.96" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
118 | </g> | ||
119 | <polyline points="252.96,-125.8 319.33,-125.8" class="node-outline"/> | ||
120 | <rect fill="none" x="252.96" y="-149.2" width="66.36999999999998" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
121 | <clipPath id="refinery-p_2c2ycZZXd-I3Xg-BBah-clip-6"><rect stroke="none" x="252.96" y="-149.2" width="66.36999999999998" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g><!-- n7->n8 --> | ||
122 | <g class="edge edge-TRUE"> | ||
123 | |||
124 | <path fill="none" stroke-width="2" d="M286.14,-100.47C286.14,-92.98 286.14,-84.45 286.14,-76.02" class="edge-line"/> | ||
125 | <polygon stroke-width="2" points="289.2,-76.09 286.14,-67.34 283.08,-76.09 289.2,-76.09" class="edge-line edge-arrow"/> | ||
126 | <text text-anchor="start" x="244.61" y="-86.89" font-weight="bold" font-size="10.50">vertices</text> | ||
127 | </g> | ||
128 | </g> | ||
129 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/DefaultAssertions.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/DefaultAssertions.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/DefaultAssertions.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/MultiObjects.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/MultiObjects.svg new file mode 100644 index 00000000..a5232575 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/MultiObjects.svg | |||
@@ -0,0 +1,81 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="508pt" height="57pt" viewBox="-6 -6 519.5700073242188 68.79999923706055" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-meygjgDtG1kB8-FV4IATS"><style>.refinery-meygjgDtG1kB8-FV4IATS .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .node .node-outline{stroke:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .node .node-header{fill:rgb(53, 161, 173);}.refinery-meygjgDtG1kB8-FV4IATS .node .node-bg{fill:#fff;}.refinery-meygjgDtG1kB8-FV4IATS .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-meygjgDtG1kB8-FV4IATS .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-meygjgDtG1kB8-FV4IATS .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-h .node-header{fill:#e06c75;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-i .node-header{fill:#98c379;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-j .node-header{fill:#c678dd;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-n .node-header{fill:#abcc94;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-meygjgDtG1kB8-FV4IATS .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .edge .edge-line{stroke:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .edge .edge-arrow{fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN text{fill:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR text{fill:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS .icon-TRUE{fill:#19202b;}.refinery-meygjgDtG1kB8-FV4IATS .icon-UNKNOWN{fill:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS .icon-ERROR{fill:#ca1243;}.refinery-meygjgDtG1kB8-FV4IATS text.label-UNKNOWN{fill:#696c77;}.refinery-meygjgDtG1kB8-FV4IATS text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-meygjgDtG1kB8-FV4IATS text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-meygjgDtG1kB8-FV4IATS-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-meygjgDtG1kB8-FV4IATS-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-meygjgDtG1kB8-FV4IATS-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 52.79999923706055)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE"> | ||
6 | |||
7 | <rect stroke="none" x="0" y="-48.8" width="59.74" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="-4" y="-52" width="67" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="6.58" y="-33" font-size="12.00">node [1]</text> | ||
10 | |||
11 | |||
12 | <polyline points="0,-25.4 59.74,-25.4" class="node-outline"/><use x="6" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-TRUE"/> | ||
13 | <rect fill="none" x="0" y="-48.8" width="59.74" height="48.8" rx="12" ry="12" class="node-outline"/><g><text text-anchor="start" x="22" y="-9.6" font-size="12.00" class="label label-TRUE">exists</text> | ||
14 | </g> | ||
15 | <clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-0"><rect stroke="none" x="0" y="-48.8" width="59.74" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n0->n0 --> | ||
17 | <g class="edge edge-TRUE"> | ||
18 | |||
19 | <path fill="none" d="M59.32,-33.55C69.51,-33.64 77.74,-30.6 77.74,-24.4 77.74,-20.82 74.99,-18.29 70.7,-16.81" class="edge-line"/> | ||
20 | <polygon points="71.2,-13.35 60.82,-15.46 70.25,-20.28 71.2,-13.35" class="edge-line edge-arrow"/> | ||
21 | <text text-anchor="middle" x="75.93" y="-36.7" font-size="10.50">equals</text> | ||
22 | </g> | ||
23 | <!-- n1 --> | ||
24 | <g class="node node-IMPLICIT node-exists-UNKNOWN node-equalsSelf-TRUE"> | ||
25 | |||
26 | <rect stroke="none" x="95.38" y="-48.8" width="100.98000000000002" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
27 | <rect stroke="none" x="91" y="-52" width="108" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-1)" class="node-header"/> | ||
28 | <text text-anchor="start" x="100.38" y="-33" font-size="12.00">removable [0..1]</text> | ||
29 | |||
30 | |||
31 | <use x="101.384" y="-19.2" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-UNKNOWN"/> | ||
32 | <g><text text-anchor="start" x="117.38" y="-9.6" font-size="12.00" class="label label-UNKNOWN">exists</text> | ||
33 | </g> | ||
34 | <polyline points="95.38,-25.4 196.36,-25.4" class="node-outline"/><rect fill="none" x="95.38" y="-48.8" width="100.98000000000002" height="48.8" rx="12" ry="12" class="node-outline"/><clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-1"><rect stroke="none" x="95.38" y="-48.8" width="100.98000000000002" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
35 | <!-- n1->n1 --> | ||
36 | <g class="edge edge-TRUE"> | ||
37 | |||
38 | <path fill="none" d="M196.34,-33.23C206.82,-32.3 214.36,-29.36 214.36,-24.4 214.36,-21.46 211.7,-19.22 207.35,-17.7" class="edge-line"/> | ||
39 | <polygon points="208.31,-14.32 197.83,-15.86 206.98,-21.2 208.31,-14.32" class="edge-line edge-arrow"/> | ||
40 | <text text-anchor="middle" x="213.45" y="-36.29" font-size="10.50">equals</text> | ||
41 | </g> | ||
42 | <!-- n2 --> | ||
43 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-UNKNOWN"><rect stroke="none" x="237.5" y="-42.5" width="70" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
44 | |||
45 | <rect stroke="none" x="232.01" y="-48.8" width="69.72000000000003" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
46 | <rect stroke="none" x="228" y="-52" width="77" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-2)" class="node-header"/> | ||
47 | <text text-anchor="start" x="237.01" y="-33" font-size="12.00">multi [1..*]</text> | ||
48 | <use x="238.011" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-TRUE"/> | ||
49 | <g><text text-anchor="start" x="254.01" y="-9.6" font-size="12.00" class="label label-TRUE">exists</text> | ||
50 | </g> | ||
51 | <polyline points="232.01,-25.4 301.73,-25.4" class="node-outline"/> | ||
52 | <rect fill="none" x="232.01" y="-48.8" width="69.72000000000003" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
53 | <clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-2"><rect stroke="none" x="232.01" y="-48.8" width="69.72000000000003" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
54 | <!-- n2->n2 --> | ||
55 | <g class="edge edge-UNKNOWN"> | ||
56 | |||
57 | <path fill="none" d="M301.69,-33.52C311.86,-33.21 319.73,-30.17 319.73,-24.4 319.73,-21.07 317.1,-18.64 312.91,-17.13" class="edge-line" stroke-dasharray="5,2"/> | ||
58 | <polygon points="313.62,-13.7 303.19,-15.52 312.48,-20.61 313.62,-13.7" class="edge-line edge-arrow"/> | ||
59 | <text text-anchor="middle" x="318.3" y="-36.66" font-size="10.50">equals</text> | ||
60 | </g> | ||
61 | <!-- n3 --> | ||
62 | <g class="node node-IMPLICIT node-exists-UNKNOWN node-equalsSelf-UNKNOWN"><rect stroke="none" x="342.5" y="-42.5" width="130" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
63 | |||
64 | <rect stroke="none" x="337.25" y="-48.8" width="129.24" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
65 | <rect stroke="none" x="333" y="-52" width="137" height="27" clip-path="url(#refinery-meygjgDtG1kB8-FV4IATS-clip-3)" class="node-header"/> | ||
66 | <text text-anchor="start" x="342.25" y="-33" font-size="12.00">removableMulti [0..*]</text> | ||
67 | <use x="343.251" y="-19.2" width="12" height="12" id="" class="icon icon-UNKNOWN" href="#refinery-meygjgDtG1kB8-FV4IATS-icon-UNKNOWN"/> | ||
68 | <g><text text-anchor="start" x="359.25" y="-9.6" font-size="12.00" class="label label-UNKNOWN">exists</text> | ||
69 | </g> | ||
70 | <polyline points="337.25,-25.4 466.49,-25.4" class="node-outline"/> | ||
71 | <rect fill="none" x="337.25" y="-48.8" width="129.24" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
72 | <clipPath id="refinery-meygjgDtG1kB8-FV4IATS-clip-3"><rect stroke="none" x="337.25" y="-48.8" width="129.24" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
73 | <!-- n3->n3 --> | ||
74 | <g class="edge edge-UNKNOWN"> | ||
75 | |||
76 | <path fill="none" stroke-dasharray="5,2" d="M466.3,-32.9C477.06,-31.71 484.49,-28.88 484.49,-24.4 484.49,-21.74 481.87,-19.66 477.49,-18.16" class="edge-line"/> | ||
77 | <polygon points="478.28,-14.75 467.78,-16.2 476.89,-21.61 478.28,-14.75" class="edge-line edge-arrow"/> | ||
78 | <text text-anchor="middle" x="483.44" y="-35.94" font-size="10.50">equals</text> | ||
79 | </g> | ||
80 | </g> | ||
81 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/MultiObjects.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/MultiObjects.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/MultiObjects.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/ObjectScopes.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/ObjectScopes.svg new file mode 100644 index 00000000..440dfb19 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/ObjectScopes.svg | |||
@@ -0,0 +1,58 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="260pt" height="157pt" viewBox="-6 -6 271.9100036621094 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery--ID4ju8v8LPQmawWdGqBG"><style>.refinery--ID4ju8v8LPQmawWdGqBG .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .node .node-outline{stroke:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .node .node-header{fill:rgb(53, 161, 173);}.refinery--ID4ju8v8LPQmawWdGqBG .node .node-bg{fill:#fff;}.refinery--ID4ju8v8LPQmawWdGqBG .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery--ID4ju8v8LPQmawWdGqBG .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery--ID4ju8v8LPQmawWdGqBG .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-g .node-header{fill:#e5c07b;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-h .node-header{fill:#e06c75;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-i .node-header{fill:#98c379;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-j .node-header{fill:#c678dd;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-k .node-header{fill:#80a7f4;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-m .node-header{fill:#e78b8f;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-n .node-header{fill:#abcc94;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-p .node-header{fill:#92c0e9;}.refinery--ID4ju8v8LPQmawWdGqBG .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-line{stroke:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-arrow{fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN text{fill:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-arrow{fill:none;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR text{fill:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-line{stroke:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG .icon-TRUE{fill:#19202b;}.refinery--ID4ju8v8LPQmawWdGqBG .icon-UNKNOWN{fill:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG .icon-ERROR{fill:#ca1243;}.refinery--ID4ju8v8LPQmawWdGqBG text.label-UNKNOWN{fill:#696c77;}.refinery--ID4ju8v8LPQmawWdGqBG text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery--ID4ju8v8LPQmawWdGqBG text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery--ID4ju8v8LPQmawWdGqBG-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery--ID4ju8v8LPQmawWdGqBG-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="scale(1 1) rotate(0) translate(4 153.2)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="74.5" y="-143.5" width="117" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="69.26" y="-149.2" width="116.17999999999999" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="65" y="-153" width="124" height="27" clip-path="url(#refinery--ID4ju8v8LPQmawWdGqBG-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="74.26" y="-133.4" font-size="12.00">Region::new [0..70]</text> | ||
10 | <use x="75.2588" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="91.26" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
12 | </g> | ||
13 | <polyline points="69.26,-125.8 185.44,-125.8" class="node-outline"/> | ||
14 | <rect fill="none" x="69.26" y="-149.2" width="116.17999999999999" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery--ID4ju8v8LPQmawWdGqBG-clip-0"><rect stroke="none" x="69.26" y="-149.2" width="116.17999999999999" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n2 --> | ||
17 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="143.5" y="-58.5" width="114" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
18 | |||
19 | <rect stroke="none" x="138.78" y="-64.4" width="113.13" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="134" y="-68" width="121" height="27" clip-path="url(#refinery--ID4ju8v8LPQmawWdGqBG-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="143.78" y="-48.6" font-size="12.00">State::new [0..120]</text> | ||
22 | <use x="144.782" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="160.77" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
24 | </g> | ||
25 | <use x="144.782" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/> | ||
26 | <g><text text-anchor="start" x="160.78" y="-9.2" font-size="12.00" class="label label-TRUE">State</text> | ||
27 | </g> | ||
28 | <polyline points="138.78,-41 251.91,-41" class="node-outline"/> | ||
29 | <rect fill="none" x="138.78" y="-64.4" width="113.13" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
30 | <clipPath id="refinery--ID4ju8v8LPQmawWdGqBG-clip-1"><rect stroke="none" x="138.78" y="-64.4" width="113.13" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="5.5" y="-50.5" width="121" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
31 | |||
32 | <rect stroke="none" x="0" y="-56.6" width="120.7" height="48.800000000000004" rx="12" ry="12" class="node-bg"/> | ||
33 | <rect stroke="none" x="-4" y="-60" width="128" height="27" clip-path="url(#refinery--ID4ju8v8LPQmawWdGqBG-clip-2)" class="node-header"/> | ||
34 | <text text-anchor="start" x="5" y="-40.8" font-size="12.00">Vertex::new [0..120]</text> | ||
35 | <use x="6" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery--ID4ju8v8LPQmawWdGqBG-icon-TRUE"/> | ||
36 | <g><text text-anchor="start" x="22" y="-17.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
37 | </g> | ||
38 | <polyline points="0,-33.2 120.7,-33.2" class="node-outline"/> | ||
39 | <rect fill="none" x="0" y="-56.6" width="120.7" height="48.800000000000004" rx="12" ry="12" class="node-outline"/> | ||
40 | <clipPath id="refinery--ID4ju8v8LPQmawWdGqBG-clip-2"><rect stroke="none" x="0" y="-56.6" width="120.7" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
41 | <!-- n0->n2 --> | ||
42 | <g class="edge edge-UNKNOWN"> | ||
43 | |||
44 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M110.09,-100.47C102.28,-89.9 92.91,-77.23 84.42,-65.75" class="edge-line"/> | ||
45 | <polygon stroke-width="2" points="87.01,-64.1 79.34,-58.89 82.08,-67.74 87.01,-64.1" class="edge-line edge-arrow"/> | ||
46 | <text text-anchor="start" x="52.27" y="-82.58" font-weight="bold" font-size="10.50">vertices</text> | ||
47 | </g> | ||
48 | <!-- n3 --> | ||
49 | |||
50 | <!-- n3->n2 --> | ||
51 | <g class="edge edge-UNKNOWN"> | ||
52 | |||
53 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M144.86,-100.47C150.99,-92.3 158.07,-82.87 164.95,-73.7" class="edge-line"/> | ||
54 | <polygon stroke-width="2" points="167.38,-75.57 170.18,-66.73 162.48,-71.89 167.38,-75.57" class="edge-line edge-arrow"/> | ||
55 | <text text-anchor="start" x="116.95" y="-86.47" font-weight="bold" font-size="10.50">vertices</text> | ||
56 | </g> | ||
57 | </g> | ||
58 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/ObjectScopes.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/ObjectScopes.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/ObjectScopes.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/StrongerObjectScopes.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/StrongerObjectScopes.svg new file mode 100644 index 00000000..6f988065 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/StrongerObjectScopes.svg | |||
@@ -0,0 +1,58 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="246pt" height="157pt" viewBox="-6 -6 258.2899932861328 169.1999969482422" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-J5TgkNWMX1Aj-K6cbPdd9"><style>.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-outline{stroke:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-header{fill:rgb(53, 161, 173);}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-bg{fill:#fff;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-i .node-header{fill:#98c379;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-line{stroke:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-arrow{fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN text{fill:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR text{fill:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-TRUE{fill:#19202b;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-UNKNOWN{fill:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-ERROR{fill:#ca1243;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-UNKNOWN{fill:#696c77;}.refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-J5TgkNWMX1Aj-K6cbPdd9 text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="scale(1 1) rotate(0) translate(4 153.2)"> | ||
4 | <!-- n0 --> | ||
5 | <g class="node node-NEW node-exists-UNKNOWN node-equalsSelf-UNKNOWN node-typeHash-k"><rect stroke="none" x="74.5" y="-143.5" width="117" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
6 | |||
7 | <rect stroke="none" x="69.69" y="-149.2" width="116.18" height="48.79999999999998" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="65" y="-153" width="124" height="27" clip-path="url(#refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="74.69" y="-133.4" font-size="12.00">Region::new [0..70]</text> | ||
10 | <use x="75.6895" y="-119.6" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="91.69" y="-110" font-size="12.00" class="label label-TRUE">Region</text> | ||
12 | </g> | ||
13 | <polyline points="69.69,-125.8 185.87,-125.8" class="node-outline"/> | ||
14 | <rect fill="none" x="69.69" y="-149.2" width="116.18" height="48.79999999999998" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-0"><rect stroke="none" x="69.69" y="-149.2" width="116.18" height="48.79999999999998" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n2 --> | ||
17 | <g class="node node-NEW node-exists-TRUE node-equalsSelf-UNKNOWN node-typeHash-j"><rect stroke="none" x="150.5" y="-58.5" width="94" height="65" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
18 | |||
19 | <rect stroke="none" x="145.27" y="-64.4" width="93.01999999999998" height="64.4" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="141" y="-68" width="101" height="27" clip-path="url(#refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="150.27" y="-48.6" font-size="12.00">State::new [20]</text> | ||
22 | <use x="151.268" y="-35" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="167.25" y="-25.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
24 | </g> | ||
25 | <use x="151.268" y="-19" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/> | ||
26 | <g><text text-anchor="start" x="167.27" y="-9.2" font-size="12.00" class="label label-TRUE">State</text> | ||
27 | </g> | ||
28 | <polyline points="145.27,-41 238.29,-41" class="node-outline"/> | ||
29 | <rect fill="none" x="145.27" y="-64.4" width="93.01999999999998" height="64.4" rx="12" ry="12" class="node-outline"/> | ||
30 | <clipPath id="refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-1"><rect stroke="none" x="145.27" y="-64.4" width="93.01999999999998" height="64.4" rx="12" ry="12" class="node-bg"/></clipPath></g><g class="node node-NEW node-exists-TRUE node-equalsSelf-UNKNOWN node-typeHash-h"><rect stroke="none" x="5.5" y="-50.5" width="128" height="49" rx="12.5" ry="12.5" class="node-shadow node-bg"/> | ||
31 | |||
32 | <rect stroke="none" x="0" y="-56.6" width="127.56" height="48.800000000000004" rx="12" ry="12" class="node-bg"/> | ||
33 | <rect stroke="none" x="-4" y="-60" width="135" height="27" clip-path="url(#refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-2)" class="node-header"/> | ||
34 | <text text-anchor="start" x="5" y="-40.8" font-size="12.00">Vertex::new [30..100]</text> | ||
35 | <use x="6" y="-27" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-J5TgkNWMX1Aj-K6cbPdd9-icon-TRUE"/> | ||
36 | <g><text text-anchor="start" x="22" y="-17.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
37 | </g> | ||
38 | <polyline points="0,-33.2 127.56,-33.2" class="node-outline"/> | ||
39 | <rect fill="none" x="0" y="-56.6" width="127.56" height="48.800000000000004" rx="12" ry="12" class="node-outline"/> | ||
40 | <clipPath id="refinery-J5TgkNWMX1Aj-K6cbPdd9-clip-2"><rect stroke="none" x="0" y="-56.6" width="127.56" height="48.800000000000004" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
41 | <!-- n0->n2 --> | ||
42 | <g class="edge edge-UNKNOWN"> | ||
43 | |||
44 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M111.3,-100.47C103.83,-89.9 94.88,-77.23 86.77,-65.75" class="edge-line"/> | ||
45 | <polygon stroke-width="2" points="89.5,-64.3 81.95,-58.93 84.5,-67.84 89.5,-64.3" class="edge-line edge-arrow"/> | ||
46 | <text text-anchor="start" x="54.2" y="-82.58" font-weight="bold" font-size="10.50">vertices</text> | ||
47 | </g> | ||
48 | <!-- n3 --> | ||
49 | |||
50 | <!-- n3->n2 --> | ||
51 | <g class="edge edge-UNKNOWN"> | ||
52 | |||
53 | <path fill="none" stroke-width="2" stroke-dasharray="5,2" d="M144.26,-100.47C150.03,-92.3 156.69,-82.87 163.17,-73.7" class="edge-line"/> | ||
54 | <polygon stroke-width="2" points="165.51,-75.7 168.06,-66.78 160.51,-72.16 165.51,-75.7" class="edge-line edge-arrow"/> | ||
55 | <text text-anchor="start" x="115.55" y="-86.47" font-weight="bold" font-size="10.50">vertices</text> | ||
56 | </g> | ||
57 | </g> | ||
58 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/StrongerObjectScopes.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/StrongerObjectScopes.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/StrongerObjectScopes.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/index.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/index.md new file mode 100644 index 00000000..e366e9b8 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/logic/index.md | |||
@@ -0,0 +1,256 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: Four-valued logic abstraction | ||
5 | sidebar_position: 1 | ||
6 | --- | ||
7 | |||
8 | # Partial modeling | ||
9 | |||
10 | Refinery allow precisely expressing _unknown,_ _uncertain_ or even _contradictory_ information using [four-valued logic](https://en.wikipedia.org/wiki/Four-valued_logic#Belnap). | ||
11 | During model generation, unknown aspects of the partial model get _refined_ into concrete (true or false) facts until the generated model is completed, or a contradiction is reached. | ||
12 | |||
13 | The _Belnap--Dunn four-valued logic_ supports the following truth values: | ||
14 | |||
15 | * `true` values correspond to facts known about the model, e.g., that a node is the instance of a given class or there is a reference between two nodes. | ||
16 | * `false` values correspond to facts that are known not to hold, e.g., that a node is _not_ an instance of a given class or there is _no_ reference between two nodes. | ||
17 | * `unknown` values express uncertain properties and design decisions yet to be made. During model refinement, `unknown` values are gradually replaced with `true` and `false` values until a consistent and concrete model is derived. | ||
18 | * `error` values represent contradictions and validation failures in the model. One a model contains an error value, it can't be refined into a consistent model anymore. | ||
19 | |||
20 | ## Assertions | ||
21 | |||
22 | _Assertions_ express facts about a partial model. An assertion is formed by a _symbol_ and an _argument list_ of _nodes_ in parentheses. | ||
23 | Possible symbols include [classes](../classes/#classes), [references](../classes/#references), and [predicates](../predicates). | ||
24 | Nodes appearing in the argument list are automatically added to the model. | ||
25 | |||
26 | A _negative_ assertion with a `false` truth value is indicated by prefixing it with `!`. | ||
27 | |||
28 | --- | ||
29 | |||
30 | Consider the following metamodel: | ||
31 | |||
32 | ```refinery | ||
33 | class Region { | ||
34 | contains Vertex[] vertices | ||
35 | } | ||
36 | class Vertex. | ||
37 | class State extends Vertex. | ||
38 | ``` | ||
39 | |||
40 | Along with the following set of assertions: | ||
41 | |||
42 | ```refinery | ||
43 | Region(r1). | ||
44 | Vertex(v1). | ||
45 | Vertex(v2). | ||
46 | !State(v2). | ||
47 | vertices(r1, v1). | ||
48 | vertices(r1, v2). | ||
49 | !vertices(Region::new, v1). | ||
50 | !vertices(Region::new, v2). | ||
51 | ``` | ||
52 | |||
53 | import AssertionsExample from './AssertionsExample.svg'; | ||
54 | |||
55 | <AssertionsExample /> | ||
56 | |||
57 | It is `true` that `r1` is an instance of the class `Region`, while `v1` and `v2` are instances of the class `Vertex`. | ||
58 | We also assert that `v2` is _not_ an instance of the class `State`, but it is unknown whether `v1` is an instance of the class `State`. | ||
59 | Types that are `unknown` are shown in a lighter color and with an outlined icon. | ||
60 | |||
61 | It is `true` that there is a `vertices` reference between `r1` and `v1`, as well as `r1` and `v2`, but there is no such reference from `Region::new` to the same vertices. | ||
62 | As no information is provided, it is `unknown` whether `State::new` is a vertex of any `Region` instance. | ||
63 | References that are `unknown` are shown in a lighter color and with a dashed line. | ||
64 | |||
65 | ### Propagation | ||
66 | |||
67 | Refinery can automatically infer some facts about the partial model based on the provided assertions by information _propagation._ | ||
68 | The set of assertions in the [example above](#assertions) is equivalent to the following: | ||
69 | |||
70 | ```refinery | ||
71 | vertices(r1, v1). | ||
72 | vertices(r1, v2). | ||
73 | !State(v2). | ||
74 | ``` | ||
75 | |||
76 | By the type constraints of the `vertices` reference, Refinery can infer that `r1` is a `Region` instance and `v1` and `v2` are `Vertex` instances. | ||
77 | Since `State` is a subclass of `Vertex`, it is still unknown whether `v1` is a `State` instance, | ||
78 | but `v2` is explicitly forbidden from being such by the negative assertion `!State(v2)`. | ||
79 | We may omit `!vertices(Region::new, v1)` and `!vertices(Region::new, v2)`, since `v1` and `v2` may be a target of only one [containment](../classes/#containment-hierarchy) reference. | ||
80 | |||
81 | Contradictory assertions lead to `error` values in the partial model: | ||
82 | |||
83 | ```refinery | ||
84 | State(v1). | ||
85 | !Vertex(v1). | ||
86 | ``` | ||
87 | |||
88 | import AssertionsError from './AssertionsError.svg'; | ||
89 | |||
90 | <AssertionsError /> | ||
91 | |||
92 | ### Default assertions | ||
93 | |||
94 | Assertions marked with the `default` keyword have _lower priority_ that other assertions. | ||
95 | They may contain wildcard arguments `*` to specify information about _all_ nodes in the graph. | ||
96 | However, they can be overridden by more specific assertions that are not marked with the `default` keyword. | ||
97 | |||
98 | --- | ||
99 | |||
100 | To make sure that the reference `vertices` is `false` everywhere except where explicitly asserted, we may add a `default` assertion: | ||
101 | |||
102 | ```refinery | ||
103 | default !vertices(*, *). | ||
104 | vertices(r1, v1). | ||
105 | vertices(r2, v2). | ||
106 | vertices(r3, v3). | ||
107 | ?vertices(r1, State::new). | ||
108 | ``` | ||
109 | |||
110 | import DefaultAssertions from './DefaultAssertions.svg'; | ||
111 | |||
112 | <DefaultAssertions /> | ||
113 | |||
114 | We can prefix an assertion with `?` to explicitly assert that some fact about the partial model is `unknown`. | ||
115 | This is useful for overriding negative `default` assertions. | ||
116 | |||
117 | ## Multi-objects | ||
118 | |||
119 | The special symbols `exists` and `equals` control the _number of graph nodes_ represented by an object in a partial model. | ||
120 | |||
121 | By default, `exists` is `true` for all objects. | ||
122 | An object `o` with `?exists(o)` (i.e., `exists(o)` explicitly set to `unknown`) may be _removed_ from the partial model. | ||
123 | Therefore, it represents _at least 0_ graph nodes. | ||
124 | |||
125 | By default, `equals` is `true` for its _diagonal_, i.e., we have `equals(o, o)` for all object `o`. | ||
126 | For off-diagonal pairs, i.e., `(p, q)` with `p` not equal to `q`, we always have `!equals(p, q)`: distinct objects can never be _merged._ | ||
127 | If we set a _diagonal_ entry to `unknown` by writing `?equals(o, o)`, the object `o` becomes a **multi-object:** it can be freely _split_ into multiple graph nodes. | ||
128 | Therefore, multi-objects represent _possibly more than 1_ graph nodes. | ||
129 | |||
130 | | `exists(o)` | `equals(o, o)` | Number of nodes | Description | | ||
131 | |:-:|:-:|-:|:-| | ||
132 | | `true` | `true` | `1` | graph node | | ||
133 | | `unknown` | `true` | `0..1` | removable graph node | | ||
134 | | `true` | `unknown` | `1..*` | multi-object | | ||
135 | | `unknown` | `unknown` | `0..*` | removable multi-object | | ||
136 | |||
137 | In the Refinery web UI, `?exists(o)` is represented with a _dashed_ border, while `?equals(o, o)` | ||
138 | |||
139 | ```refinery | ||
140 | node(node). | ||
141 | |||
142 | node(removable). | ||
143 | ?exists(removable). | ||
144 | |||
145 | node(multi). | ||
146 | ?equals(multi, multi). | ||
147 | |||
148 | node(removableMulti). | ||
149 | ?exists(removableMulti). | ||
150 | ?equals(removableMulti, removableMulti). | ||
151 | ``` | ||
152 | |||
153 | import MultiObjects from './MultiObjects.svg'; | ||
154 | |||
155 | <MultiObjects /> | ||
156 | |||
157 | import TuneIcon from '@material-icons/svg/svg/tune/baseline.svg'; | ||
158 | import LabelIcon from '@material-icons/svg/svg/label/baseline.svg'; | ||
159 | import LabelOutlineIcon from '@material-icons/svg/svg/label/outline.svg'; | ||
160 | |||
161 | :::info | ||
162 | |||
163 | You may use the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" /> _filter panel_ icon in Refinery to toggle the visibility of special symbols like `exists` and `equals`. | ||
164 | You may either show <LabelOutlineIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Unknown value icon" /> _both true and unknown_ values or <LabelIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="True value icon" /> _just true_ values. | ||
165 | The _object scopes_ toggle will also show the number of graph nodes represented by an object in square brackets after its name, like in the figure above. | ||
166 | ::: | ||
167 | |||
168 | By default, a **new object** `C::new` is added for each non-`abstract` [class](../classes#classes) `C` with `?exists(C::new)` and `?equals(C::new, C::new)`. | ||
169 | This multi-object represents all potential instances of the class. | ||
170 | To assert that no new instances of `C` should be added to the generated model, you may write `!exists(C::new)`. | ||
171 | |||
172 | You may use the `multi` keyword to quickly defined a (removable) multi-object: | ||
173 | |||
174 | ```refinery | ||
175 | multi removableMulti. | ||
176 | % This is equivalent to: | ||
177 | % ?exists(removableMulti). | ||
178 | % ?equals(removableMulti, removableMulti). | ||
179 | ``` | ||
180 | |||
181 | ## Type scopes | ||
182 | |||
183 | _Type scopes_ offer finer-grained control over the number of graph nodes in the generated model (as represented by the multi-objects) that `exists` or `equals` assertions. | ||
184 | |||
185 | A _type scope constraint_ is formed by a unary symbol (a [class](../classes/#classes) or a [predicate](../predicates) with a single parameter) and _scope range._ | ||
186 | Ranges have a form similar to [multiplicity constraints](../classes#multiplicity): a range `n..m` indicates a lower bound of `n` and an upper bound of `m`. | ||
187 | While an upper bound of `*` indicates a possibly unbounded number of objects, generated models will always be finite. | ||
188 | Like for multiplicity constraints, the case `n..n` can be abbreviated as `n`. | ||
189 | |||
190 | The number of nodes in the generated model can be controlled using the `node` special symbol. | ||
191 | For example, we may write the following to generate a model with at least 100 at and most 120 nodes: | ||
192 | |||
193 | ```refinery | ||
194 | scope node = 100..200. | ||
195 | ``` | ||
196 | |||
197 | A `scope` declaration may prescribe type scope constraint for any number of symbols, separated by `,`. | ||
198 | Multiple `scope` declarations are also permitted. | ||
199 | Multiple ranges provided for the same symbol will be intersected, i.e., they influence the generated model simultaneously. | ||
200 | |||
201 | In other words, | ||
202 | ``` | ||
203 | scope Region = 10, State = 80..120. | ||
204 | scope State = 100..150. | ||
205 | % Equivalent to: | ||
206 | scope Region = 10, State = 100..120. | ||
207 | ``` | ||
208 | |||
209 | The _object scopes_ option in the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" /> _filter panel_ may help in exploring the effects of object scopes. | ||
210 | |||
211 | --- | ||
212 | |||
213 | Consider the example | ||
214 | |||
215 | ```refinery | ||
216 | class Region { | ||
217 | contains Vertex[] vertices | ||
218 | } | ||
219 | class Vertex. | ||
220 | class State extends Vertex. | ||
221 | scope node = 100..120, Vertex = 50..*. | ||
222 | ``` | ||
223 | |||
224 | import ObjectScopes from './ObjectScopes.svg'; | ||
225 | |||
226 | <ObjectScopes /> | ||
227 | |||
228 | Notice that Refinery could determine that there can be no more than 70 `Region` instances in the generated model, since at least 50 of the `100..120` nodes in the model must be `Vertex` instances. | ||
229 | However, since `State` is a subclass of `Vertex` (i.e., `State::new` is also an instance of `Vertex`), the range `50..*` is shared between both `Vertex::new` and `State::new`, resulting in both representing `0..120` nodes. | ||
230 | Nevertheless, every generated model will obey the scope constraint exactly, i.e., will have between 100 and 120 node, at least 50 of which are `Vertex` instances. | ||
231 | |||
232 | By providing more information, Refinery can determine more precise ranges for multi-objects. | ||
233 | For example, we may strengthen the scope constraints as follows: | ||
234 | |||
235 | ```refinery | ||
236 | scope node = 100..120, Vertex = 50..*, State = 20. | ||
237 | ``` | ||
238 | |||
239 | import StrongerObjectScopes from './StrongerObjectScopes.svg'; | ||
240 | |||
241 | <StrongerObjectScopes /> | ||
242 | |||
243 | ### Incremental scopes | ||
244 | |||
245 | We may specify an _incremental_ object scope with the `+=` operator to determine the number of new instances to be added to the model. | ||
246 | This is only allowed for symbol that are classes with no subclasses, as it directly influences the number of nodes represented by the corresponding `::new` object. | ||
247 | |||
248 | For example, to ensure that between 5 and 7 `State` instances are added to the model, we may write: | ||
249 | |||
250 | ```refinery | ||
251 | State(s1). | ||
252 | State(s2). | ||
253 | scope State += 5..7. | ||
254 | ``` | ||
255 | |||
256 | Since `s1` and `s2` are also instances of the `State` class, the generated concrete model will have between 7 and 9 `State` instances altogether. | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/DerivedFeature.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/DerivedFeature.svg new file mode 100644 index 00000000..be9465b8 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/DerivedFeature.svg | |||
@@ -0,0 +1,76 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="216pt" height="226pt" viewBox="-6 -6 227.8699951171875 238.39999389648438" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-9k5t5y1ScYnYvNXEZbWT4"><style>.refinery-9k5t5y1ScYnYvNXEZbWT4 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-outline{stroke:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-header{fill:rgb(53, 161, 173);}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-bg{fill:#fff;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-i .node-header{fill:#98c379;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-line{stroke:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-arrow{fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN text{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR text{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-TRUE{fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-UNKNOWN{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-ERROR{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-UNKNOWN{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-9k5t5y1ScYnYvNXEZbWT4-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-9k5t5y1ScYnYvNXEZbWT4-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 222.39999389648438)"> | ||
4 | <!-- n2 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
6 | |||
7 | <rect stroke="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="60" y="-52" width="92" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="76.52" y="-33" font-size="12.00">transition1</text> | ||
10 | <use x="70.7485" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="86.75" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
12 | </g> | ||
13 | <polyline points="64.75,-25.4 148.97,-25.4" class="node-outline"/> | ||
14 | <rect fill="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-0"><rect stroke="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n3 --> | ||
17 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
18 | |||
19 | <rect stroke="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="70" y="-222" width="72" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="85.98" y="-202.6" font-size="12.00">vertex1</text> | ||
22 | <use x="80.8442" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="96.84" y="-179.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
24 | </g> | ||
25 | <polyline points="74.84,-195 138.87,-195" class="node-outline"/> | ||
26 | <rect fill="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
27 | <clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-1"><rect stroke="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
28 | <!-- n2->n3 --> | ||
29 | <g class="edge edge-TRUE"> | ||
30 | |||
31 | <path fill="none" d="M111.71,-48.78C113.97,-77.25 114.21,-125.59 112.45,-158.38" class="edge-line"/> | ||
32 | <polygon points="108.96,-157.98 111.8,-168.19 115.95,-158.44 108.96,-157.98" class="edge-line edge-arrow"/> | ||
33 | <text text-anchor="middle" x="97.12" y="-99.47" font-size="10.50">source</text> | ||
34 | </g> | ||
35 | <!-- n4 --> | ||
36 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
37 | |||
38 | <rect stroke="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
39 | <rect stroke="none" x="139" y="-137" width="72" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-2)" class="node-header"/> | ||
40 | <text text-anchor="start" x="154.98" y="-117.8" font-size="12.00">vertex2</text> | ||
41 | <use x="149.844" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/> | ||
42 | <g><text text-anchor="start" x="165.84" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
43 | </g> | ||
44 | <polyline points="143.84,-110.2 207.87,-110.2" class="node-outline"/> | ||
45 | <rect fill="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
46 | <clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-2"><rect stroke="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
47 | <!-- n2->n4 --> | ||
48 | <g class="edge edge-TRUE"> | ||
49 | |||
50 | <path fill="none" d="M132.35,-48.63C140.1,-57.19 148.46,-67.04 155.81,-76.23" class="edge-line"/> | ||
51 | <polygon points="152.92,-78.21 161.83,-83.94 158.44,-73.91 152.92,-78.21" class="edge-line edge-arrow"/> | ||
52 | <text text-anchor="middle" x="133.35" y="-57.15" font-size="10.50">target</text> | ||
53 | </g> | ||
54 | <!-- n3->n2 --> | ||
55 | <g class="edge edge-TRUE"> | ||
56 | |||
57 | <path fill="none" stroke-width="2" d="M102.01,-169.7C99.75,-141.26 99.5,-92.92 101.27,-60.1" class="edge-line"/> | ||
58 | <polygon stroke-width="2" points="104.3,-60.73 101.81,-51.8 98.18,-60.33 104.3,-60.73" class="edge-line edge-arrow"/> | ||
59 | <text text-anchor="start" x="0" y="-113.73" font-weight="bold" font-size="10.50">outgoingTransition</text> | ||
60 | </g> | ||
61 | <!-- n4->n2 --> | ||
62 | <!-- n4->n2 --> | ||
63 | <g class="edge edge-TRUE"> | ||
64 | |||
65 | <path fill="none" d="M150.52,-85.14C142.77,-76.59 134.41,-66.75 127.05,-57.54" class="edge-line"/> | ||
66 | <polygon points="129.93,-55.54 121.01,-49.82 124.41,-59.86 129.93,-55.54" class="edge-line edge-arrow"/> | ||
67 | <text text-anchor="middle" x="87.8" y="-70.33" font-size="10.50">incomingTransition</text> | ||
68 | </g><g class="edge edge-TRUE"> | ||
69 | |||
70 | <path fill="none" d="M126.08,-169.94C133.27,-161.31 141.57,-151.34 149.31,-142.06" class="edge-line"/> | ||
71 | <polygon points="151.82,-144.51 155.53,-134.59 146.44,-140.03 151.82,-144.51" class="edge-line edge-arrow"/> | ||
72 | <text text-anchor="middle" x="116.57" y="-154.94" font-size="10.50">neighbors</text> | ||
73 | </g> | ||
74 | |||
75 | </g> | ||
76 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/DerivedFeature.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/DerivedFeature.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/DerivedFeature.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/index.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/index.md new file mode 100644 index 00000000..261054c1 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/language/predicates/index.md | |||
@@ -0,0 +1,284 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: Model queries and model validation | ||
5 | sidebar_position: 2 | ||
6 | --- | ||
7 | |||
8 | # Graph predicates | ||
9 | |||
10 | Graph predicates are logic expressions that can be used to query for interesting model fragments, as well as for validating the consistency of models. They are evaluated on partial models according to [four-valued logic](../logic) semantics. | ||
11 | |||
12 | Predicates in Refinery are written in [Disjunctive Normal Form](https://en.wikipedia.org/wiki/Disjunctive_normal_form) (DNF) as an _OR_ of _ANDs_, i.e., a _disjunction_ of _clauses_ formed as a _conjunction_ of positive or negated logic _literals._ | ||
13 | This matches the syntax and semantics of logical query languages, such as [Datalog](https://en.wikipedia.org/wiki/Datalog), and logical programming languages, such as [Prolog](https://en.wikipedia.org/wiki/Prolog). | ||
14 | |||
15 | import Link from '@docusaurus/Link'; | ||
16 | |||
17 | <details> | ||
18 | <summary>Example metamodel</summary> | ||
19 | |||
20 | In the examples on this page, we will use the following metamodel as illustration: | ||
21 | |||
22 | ```refinery | ||
23 | abstract class CompositeElement { | ||
24 | contains Region[] regions | ||
25 | } | ||
26 | |||
27 | class Region { | ||
28 | contains Vertex[] vertices opposite region | ||
29 | } | ||
30 | |||
31 | abstract class Vertex { | ||
32 | container Region region opposite vertices | ||
33 | contains Transition[] outgoingTransition opposite source | ||
34 | Transition[] incomingTransition opposite target | ||
35 | } | ||
36 | |||
37 | class Transition { | ||
38 | container Vertex source opposite outgoingTransition | ||
39 | Vertex[1] target opposite incomingTransition | ||
40 | } | ||
41 | |||
42 | abstract class Pseudostate extends Vertex. | ||
43 | |||
44 | abstract class RegularState extends Vertex. | ||
45 | |||
46 | class Entry extends Pseudostate. | ||
47 | |||
48 | class Exit extends Pseudostate. | ||
49 | |||
50 | class Choice extends Pseudostate. | ||
51 | |||
52 | class FinalState extends RegularState. | ||
53 | |||
54 | class State extends RegularState, CompositeElement. | ||
55 | |||
56 | class Statechart extends CompositeElement. | ||
57 | ``` | ||
58 | |||
59 | <p> | ||
60 | <Link | ||
61 | href="https://refinery.services/#/1/KLUv_WAEAiUIAOIKIR5gadMGg1ajk9jLoipJ58vc0vAE5opt1YaDpyOCAAdaCjMohSdgl4rj1yTo8UCgpTDHCIAE-o3Jr28mGO9AEoDcR-tLGh4liE2Z3IOX50z-FksLaNWLpLXd1QiUII2vNjCMBWOVEgTzjhG0eHVMIyIyFOjoxcrBv83FkgftlmJ0K_0eVDQgEBSCrXYvD1Q2wlwGXecz2HjRADQOLMh6iIYIWBPuFBBCI2igVgiHAFH4uclAydd4TFayN-oOpjzxgd0FlTzkN6QZ8CQDXBN4EPjB5VJZCANQlJA3wDd_PVyUA5eA0gaeAcgENsm4YnCogWihMAMkA8-CoB-gm9HJC0AB" | ||
62 | className="button button--lg button--primary button--play" | ||
63 | >Try in Refinery</Link> | ||
64 | </p> | ||
65 | |||
66 | </details> | ||
67 | |||
68 | [Assertions](../logic/#assertions) about graph predicates can prescribe where the predicate should (for positive assertions) or should not (for negative assertions) hold. | ||
69 | When generating consistent models | ||
70 | |||
71 | ## Atoms | ||
72 | |||
73 | An _atom_ is formed by a _symbol_ and _argument list_ of variables. | ||
74 | Possible symbols include [classes](../classes/#classes), [references](../classes/#references), and [predicates](../predicates). | ||
75 | We may write a basic graph query as a conjunction (AND) of atoms. | ||
76 | |||
77 | The `pred` keyword defines a graph predicate. After the _predicate name_, a _parameter list_ of variables is provided. The atoms of the graph predicate are written after the `<->` operator, and a full stop `.` terminates the predicate definition. | ||
78 | |||
79 | The following predicate `entryInRegion` will match pairs of `Region` instances `r` and `Entry` instances `e` such that `e` is a vertex in `r`. | ||
80 | |||
81 | ```refinery | ||
82 | pred entryInRegion(r, e) <-> | ||
83 | Region(r), | ||
84 | vertices(r, e), | ||
85 | Entry(e). | ||
86 | ``` | ||
87 | |||
88 | We may write unary symbols that act as _parameter types_ directly in the parameter list. The following definition is equivalent to the previous one: | ||
89 | |||
90 | ```refinery | ||
91 | pred entryInRegion(Region r, Entry e) <-> | ||
92 | vertices(r, e). | ||
93 | ``` | ||
94 | |||
95 | import TableIcon from '@material-icons/svg/svg/table_chart/baseline.svg'; | ||
96 | |||
97 | :::info | ||
98 | |||
99 | You may display the result of graph predicate matching in the <TableIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Table view icon" /> _table view_ of the Refinery web UI. | ||
100 | |||
101 | ::: | ||
102 | |||
103 | ## Quantification | ||
104 | |||
105 | Variables not appearing in the parameter list are _existentially quantified._ | ||
106 | |||
107 | The following predicate matches `Region` instances with two entries: | ||
108 | |||
109 | ```refinery | ||
110 | pred multipleEntriesInRegion(Region r) <-> | ||
111 | entryInRegion(r, e1), | ||
112 | entryInRegion(r, e2), | ||
113 | e1 != e2. | ||
114 | ``` | ||
115 | |||
116 | Existentially quantified variables that appear only once in the predicate should be prefixed with `_`. This shows that the variable is intentionally used only once (as opposite to the second reference to the variable being omitted by mistake). | ||
117 | |||
118 | ```refinery | ||
119 | pred regionWithEntry(Region r) <-> | ||
120 | entryInRegion(r, _e). | ||
121 | ``` | ||
122 | |||
123 | Alternatively, you may use a single `_` whenever a variable occurring only once is desired. Different occurrences of `_` are considered distinct variables. | ||
124 | |||
125 | ```refinery | ||
126 | pred regionWithEntry(Region r) <-> | ||
127 | entryInRegion(r, _). | ||
128 | ``` | ||
129 | |||
130 | ## Negation | ||
131 | |||
132 | Negative literals are written by prefixing the corresponding atom with `!`. | ||
133 | |||
134 | Inside negative literals, quantification is _universal:_ the literal matches if there is no assignment of the variables solely appearing in it that satisfies the corresponding atom. | ||
135 | |||
136 | The following predicate matches `Region` instances that have no `Entry`: | ||
137 | |||
138 | ```refinery | ||
139 | pred regionWithoutEntry(Region r) <-> | ||
140 | !entryInRegion(r, _). | ||
141 | ``` | ||
142 | |||
143 | In a graph predicate, all parameter variables must be _positively bound,_ i.e., appear in at least one positive literal (atom). | ||
144 | Negative literals may further constrain the predicate match one it has been established by the positive literals. | ||
145 | |||
146 | ## Object equality | ||
147 | |||
148 | The operators `a == b` and `a != b` correspond to the literals `equals(a, b)` and `!equals(a, b)`, respectively. | ||
149 | See the section about [multi-objects](../logic/#multi-objects) for more information about the `equals` symbol. | ||
150 | |||
151 | ## Transitive closure | ||
152 | |||
153 | The `+` operator forms the [transitive closure](https://en.wikipedia.org/wiki/Transitive_closure) of symbols with exactly 2 parameters. | ||
154 | The transitive closure `r+(a, b)` holds if either `r(a, b)` is `true`, or there is a sequence of objects `c1`, `c2`, …, `cn` such that `r(a, c1)`, `r(c1, c2)`, `r(c2, c3)`, …, `r(cn, b)`. | ||
155 | In other words, there is a path labelled with `r` in the graph from `a` to `b`. | ||
156 | |||
157 | Transitive closure may express queries about graph reachability: | ||
158 | |||
159 | ```refinery | ||
160 | pred neighbors(Vertex v1, Vertex v2) <-> | ||
161 | Transition(t), | ||
162 | source(t, v1), | ||
163 | target(t, v2). | ||
164 | |||
165 | pred cycle(Vertex v) <-> | ||
166 | neighbors+(v, v). | ||
167 | ``` | ||
168 | |||
169 | ## Disjunction | ||
170 | |||
171 | Disjunction (OR) of _clauses_ formed by a conjunction (AND) of literals is denoted by `;`. | ||
172 | |||
173 | ```refinery | ||
174 | pred regionWithInvalidNumberOfEntries(Region r) <-> | ||
175 | !entryInRegion(r, _) | ||
176 | ; | ||
177 | entryInRegion(r, e1), | ||
178 | entryInRegion(r, e2), | ||
179 | e1 != e2. | ||
180 | ``` | ||
181 | |||
182 | Every clause of a disjunction must bind every parameter variable of the graph predicate _positively._ | ||
183 | _Type annotations_ on parameter are applied in all clauses. | ||
184 | Therefore, the previous graph pattern is equivalent to the following: | ||
185 | |||
186 | ```refinery | ||
187 | pred regionWithInvalidNumberOfEntries(r) <-> | ||
188 | Region(r), | ||
189 | !entryInRegion(r, _) | ||
190 | ; | ||
191 | Region(r), | ||
192 | entryInRegion(r, e1), | ||
193 | entryInRegion(r, e2), | ||
194 | e1 != e2. | ||
195 | ``` | ||
196 | |||
197 | ## Derived features | ||
198 | |||
199 | Graph predicates may act as _derived types_ and _references_ in metamodel. | ||
200 | |||
201 | A graph predicate with exactly 1 parameters can be use as if it was a class: you may use it as a [_parameter type_](#atoms) in other graph patterns, as a _target type_ of a (non-containment) [reference](../classes/#references), or in a [_scope constraint_](../logic#type-scopes). | ||
202 | |||
203 | _Derived references_ are graph predicates with exactly 2 parameters, which correspond the source and target node of the reference. | ||
204 | |||
205 | import TuneIcon from '@material-icons/svg/svg/tune/baseline.svg'; | ||
206 | import LabelIcon from '@material-icons/svg/svg/label/baseline.svg'; | ||
207 | import LabelOutlineIcon from '@material-icons/svg/svg/label/outline.svg'; | ||
208 | |||
209 | :::info | ||
210 | |||
211 | You may use the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" /> _filter panel_ icon in Refinery to toggle the visibility of graph predicates with 1 or 2 parameters. | ||
212 | You may either show <LabelOutlineIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Unknown value icon" /> _both true and unknown_ values or <LabelIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="True value icon" /> _just true_ values. | ||
213 | |||
214 | ::: | ||
215 | |||
216 | --- | ||
217 | |||
218 | For example, we may replace the reference `neighbors` in the class `Vertex`: | ||
219 | |||
220 | ```refinery | ||
221 | class Vertex { | ||
222 | Vertex[] neighbors | ||
223 | } | ||
224 | ``` | ||
225 | |||
226 | with the graph predicate `neighbors` as follows: | ||
227 | |||
228 | |||
229 | ```refinery | ||
230 | class Vertex { | ||
231 | contains Transition[] outgoingTransition opposite source | ||
232 | Transition[] incomingTransition opposite target | ||
233 | } | ||
234 | |||
235 | class Transition { | ||
236 | container Vertex source opposite outgoingTransition | ||
237 | Vertex[1] target opposite incomingTransition | ||
238 | } | ||
239 | |||
240 | pred neighbors(Vertex v1, Vertex v2) <-> | ||
241 | Transition(t), | ||
242 | source(t, v1), | ||
243 | target(t, v2). | ||
244 | ``` | ||
245 | |||
246 | Since `neighbors` is now computed based on the `Transition` instances and their `source` and `target` references present in the model, the assertion | ||
247 | |||
248 | ```refinery | ||
249 | neighbors(vertex1, vertex2). | ||
250 | ``` | ||
251 | |||
252 | will only be satisfied if a corresponding node `transition1` is present in the generated model that also satisfies | ||
253 | |||
254 | ```refinery | ||
255 | Transition(transition1). | ||
256 | source(transition1, vertex1). | ||
257 | target(transition1, vertex2). | ||
258 | ``` | ||
259 | |||
260 | import DerivedFeature from './DerivedFeature.svg'; | ||
261 | |||
262 | <DerivedFeature /> | ||
263 | |||
264 | ## Error predicates | ||
265 | |||
266 | A common use-case for graph predicates is _model validation_, where a predicate highlights _errors_ in the model. | ||
267 | Such predicates are called _error predicates._ | ||
268 | In a consistent generated model, an error predicates should have no matches. | ||
269 | |||
270 | You can declare error predicates with the `error` keyword: | ||
271 | |||
272 | ```refinery | ||
273 | error regionWithoutEntry(Region r) <-> | ||
274 | !entryInRegion(r, _). | ||
275 | ``` | ||
276 | |||
277 | This is equivalent to asserting that the error predicate is `false` everywhere: | ||
278 | |||
279 | ```refinery | ||
280 | pred regionWithoutEntry(Region r) <-> | ||
281 | !entryInRegion(r, _). | ||
282 | |||
283 | !regionWithoutEntry(*). | ||
284 | ``` | ||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/_category_.yml b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/_category_.yml new file mode 100644 index 00000000..fd563704 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/_category_.yml | |||
@@ -0,0 +1,11 @@ | |||
1 | # SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
2 | # | ||
3 | # SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | position: 1 | ||
6 | label: Tutorials | ||
7 | link: | ||
8 | type: generated-index | ||
9 | slug: /learn/tutorials | ||
10 | title: Tutorial overview | ||
11 | description: Try Refinery in practical partial modeling challenges! | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig1.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig1.svg index 1e20393a..1e20393a 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig1.svg +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig1.svg | |||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig1.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig1.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig1.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig2.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig2.svg index 6375bfd6..6375bfd6 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig2.svg +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig2.svg | |||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig2.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig2.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig2.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig3.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig3.svg index 0d020a71..0d020a71 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig3.svg +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig3.svg | |||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig3.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig3.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig3.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/fig4.svg b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig4.svg index d6701bdd..d6701bdd 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/fig4.svg +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig4.svg | |||
diff --git a/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig4.svg.license b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig4.svg.license new file mode 100644 index 00000000..b80566a0 --- /dev/null +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/fig4.svg.license | |||
@@ -0,0 +1,3 @@ | |||
1 | SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/src/learn/tutorials/file-system/index.md b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/index.md index 365d0fba..365d0fba 100644 --- a/subprojects/docs/src/learn/tutorials/file-system/index.md +++ b/subprojects/docs/versioned_docs/version-0.1.0/learn/tutorials/file-system/index.md | |||
diff --git a/subprojects/docs/versioned_sidebars/version-0.1.0-sidebars.json b/subprojects/docs/versioned_sidebars/version-0.1.0-sidebars.json new file mode 100644 index 00000000..2f70ab5e --- /dev/null +++ b/subprojects/docs/versioned_sidebars/version-0.1.0-sidebars.json | |||
@@ -0,0 +1,14 @@ | |||
1 | { | ||
2 | "learnSidebar": [ | ||
3 | { | ||
4 | "type": "autogenerated", | ||
5 | "dirName": "learn" | ||
6 | } | ||
7 | ], | ||
8 | "developSidebar": [ | ||
9 | { | ||
10 | "type": "autogenerated", | ||
11 | "dirName": "develop" | ||
12 | } | ||
13 | ] | ||
14 | } | ||
diff --git a/subprojects/docs/versioned_sidebars/version-0.1.0-sidebars.json.license b/subprojects/docs/versioned_sidebars/version-0.1.0-sidebars.json.license new file mode 100644 index 00000000..cfe95706 --- /dev/null +++ b/subprojects/docs/versioned_sidebars/version-0.1.0-sidebars.json.license | |||
@@ -0,0 +1,3 @@ | |||
1 | Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: EPL-2.0 | ||
diff --git a/subprojects/docs/versions.json b/subprojects/docs/versions.json new file mode 100644 index 00000000..7b999c75 --- /dev/null +++ b/subprojects/docs/versions.json | |||
@@ -0,0 +1,3 @@ | |||
1 | [ | ||
2 | "0.1.0" | ||
3 | ] | ||
diff --git a/subprojects/docs/versions.json.license b/subprojects/docs/versions.json.license new file mode 100644 index 00000000..2891afa3 --- /dev/null +++ b/subprojects/docs/versions.json.license | |||
@@ -0,0 +1,3 @@ | |||
1 | Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | |||
3 | SPDX-License-Identifier: CC0-1.0 | ||
diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json index acff57db..ca7bff56 100644 --- a/subprojects/frontend/package.json +++ b/subprojects/frontend/package.json | |||
@@ -28,35 +28,36 @@ | |||
28 | }, | 28 | }, |
29 | "homepage": "https://refinery.tools", | 29 | "homepage": "https://refinery.tools", |
30 | "dependencies": { | 30 | "dependencies": { |
31 | "@codemirror/autocomplete": "^6.17.0", | 31 | "@codemirror/autocomplete": "^6.18.0", |
32 | "@codemirror/commands": "^6.6.0", | 32 | "@codemirror/commands": "^6.6.0", |
33 | "@codemirror/language": "^6.10.2", | 33 | "@codemirror/language": "^6.10.2", |
34 | "@codemirror/lint": "^6.8.1", | 34 | "@codemirror/lint": "^6.8.1", |
35 | "@codemirror/search": "^6.5.6", | 35 | "@codemirror/search": "^6.5.6", |
36 | "@codemirror/state": "^6.4.1", | 36 | "@codemirror/state": "^6.4.1", |
37 | "@codemirror/view": "^6.28.4", | 37 | "@codemirror/view": "^6.33.0", |
38 | "@emotion/cache": "^11.11.0", | 38 | "@emotion/cache": "^11.13.1", |
39 | "@emotion/react": "^11.11.4", | 39 | "@emotion/react": "^11.13.3", |
40 | "@emotion/serialize": "^1.1.4", | 40 | "@emotion/serialize": "^1.3.1", |
41 | "@emotion/styled": "^11.11.5", | 41 | "@emotion/sheet": "^1.4.0", |
42 | "@emotion/utils": "^1.2.1", | 42 | "@emotion/styled": "^11.13.0", |
43 | "@fontsource-variable/jetbrains-mono": "^5.0.21", | 43 | "@fontsource-variable/jetbrains-mono": "^5.0.22", |
44 | "@fontsource-variable/open-sans": "^5.0.29", | 44 | "@fontsource-variable/open-sans": "^5.0.30", |
45 | "@fontsource/open-sans": "^5.0.28", | 45 | "@fontsource/open-sans": "^5.0.29", |
46 | "@hpcc-js/wasm": "^2.18.0", | 46 | "@hpcc-js/wasm": "^2.21.0", |
47 | "@hpcc-js/wasm-zstd": "^1.2.0", | ||
47 | "@lezer/common": "^1.2.1", | 48 | "@lezer/common": "^1.2.1", |
48 | "@lezer/highlight": "^1.2.0", | 49 | "@lezer/highlight": "^1.2.1", |
49 | "@lezer/lr": "^1.4.1", | 50 | "@lezer/lr": "^1.4.2", |
50 | "@material-icons/svg": "^1.0.33", | 51 | "@material-icons/svg": "^1.0.33", |
51 | "@mui/icons-material": "^5.16.0", | 52 | "@mui/icons-material": "^6.0.1", |
52 | "@mui/material": "^5.16.0", | 53 | "@mui/material": "^6.0.1", |
53 | "@mui/system": "^5.16.0", | 54 | "@mui/system": "^6.0.1", |
54 | "@mui/x-data-grid": "^7.9.0", | 55 | "@mui/x-data-grid": "^7.15.0", |
55 | "ansi-styles": "^6.2.1", | 56 | "ansi-styles": "^6.2.1", |
56 | "csstype": "^3.1.3", | 57 | "csstype": "^3.1.3", |
57 | "d3": "^7.9.0", | 58 | "d3": "^7.9.0", |
58 | "d3-color": "^3.1.0", | 59 | "d3-color": "^3.1.0", |
59 | "d3-graphviz": "patch:d3-graphviz@npm%3A5.4.0#~/.yarn/patches/d3-graphviz-npm-5.4.0-4298b33e9f.patch", | 60 | "d3-graphviz": "patch:d3-graphviz@npm%3A5.6.0#~/.yarn/patches/d3-graphviz-npm-5.6.0-129e64ec05.patch", |
60 | "d3-selection": "^3.0.0", | 61 | "d3-selection": "^3.0.0", |
61 | "d3-zoom": "patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch", | 62 | "d3-zoom": "patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch", |
62 | "escape-string-regexp": "^5.0.0", | 63 | "escape-string-regexp": "^5.0.0", |
@@ -64,7 +65,7 @@ | |||
64 | "lodash-es": "^4.17.21", | 65 | "lodash-es": "^4.17.21", |
65 | "loglevel": "^1.9.1", | 66 | "loglevel": "^1.9.1", |
66 | "loglevel-plugin-prefix": "^0.8.4", | 67 | "loglevel-plugin-prefix": "^0.8.4", |
67 | "mobx": "^6.13.0", | 68 | "mobx": "^6.13.1", |
68 | "mobx-react-lite": "^4.0.7", | 69 | "mobx-react-lite": "^4.0.7", |
69 | "ms": "^2.1.3", | 70 | "ms": "^2.1.3", |
70 | "nanoid": "^5.0.7", | 71 | "nanoid": "^5.0.7", |
@@ -88,18 +89,18 @@ | |||
88 | "@types/lodash-es": "^4.17.12", | 89 | "@types/lodash-es": "^4.17.12", |
89 | "@types/micromatch": "^4.0.9", | 90 | "@types/micromatch": "^4.0.9", |
90 | "@types/ms": "^0.7.34", | 91 | "@types/ms": "^0.7.34", |
91 | "@types/node": "^20.14.10", | 92 | "@types/node": "^20.16.2", |
92 | "@types/pnpapi": "^0.0.5", | 93 | "@types/pnpapi": "^0.0.5", |
93 | "@types/react": "^18.3.3", | 94 | "@types/react": "^18.3.5", |
94 | "@types/react-dom": "^18.3.0", | 95 | "@types/react-dom": "^18.3.0", |
95 | "@vitejs/plugin-react-swc": "^3.7.0", | 96 | "@vitejs/plugin-react-swc": "^3.7.0", |
96 | "@xstate/cli": "^0.5.17", | 97 | "@xstate/cli": "^0.5.17", |
97 | "html-minifier-terser": "^7.2.0", | 98 | "html-minifier-terser": "^7.2.0", |
98 | "micromatch": "^4.0.7", | 99 | "micromatch": "^4.0.8", |
99 | "pnpapi": "^0.0.0", | 100 | "pnpapi": "^0.0.0", |
100 | "typescript": "5.5.3", | 101 | "typescript": "5.5.4", |
101 | "vite": "^5.3.3", | 102 | "vite": "^5.4.2", |
102 | "vite-plugin-pwa": "^0.20.0", | 103 | "vite-plugin-pwa": "^0.20.2", |
103 | "workbox-window": "^7.1.0" | 104 | "workbox-window": "^7.1.0" |
104 | } | 105 | } |
105 | } | 106 | } |
diff --git a/subprojects/frontend/src/graph/GraphStore.ts b/subprojects/frontend/src/graph/GraphStore.ts index 86ffd802..a133d636 100644 --- a/subprojects/frontend/src/graph/GraphStore.ts +++ b/subprojects/frontend/src/graph/GraphStore.ts | |||
@@ -25,6 +25,7 @@ export function getDefaultVisibility( | |||
25 | case 'class': | 25 | case 'class': |
26 | case 'reference': | 26 | case 'reference': |
27 | case 'opposite': | 27 | case 'opposite': |
28 | case 'base': | ||
28 | return 'all'; | 29 | return 'all'; |
29 | case 'predicate': | 30 | case 'predicate': |
30 | return detail.error ? 'must' : 'none'; | 31 | return detail.error ? 'must' : 'none'; |
@@ -233,4 +234,9 @@ export default class GraphStore { | |||
233 | get name(): string { | 234 | get name(): string { |
234 | return this.nameOverride ?? this.editorStore.simpleNameOrFallback; | 235 | return this.nameOverride ?? this.editorStore.simpleNameOrFallback; |
235 | } | 236 | } |
237 | |||
238 | get showNonExistent(): boolean { | ||
239 | const existsVisibility = this.visibility.get('builtin::exists') ?? 'none'; | ||
240 | return existsVisibility !== 'none' || this.scopes; | ||
241 | } | ||
236 | } | 242 | } |
diff --git a/subprojects/frontend/src/graph/GraphTheme.tsx b/subprojects/frontend/src/graph/GraphTheme.tsx index bdc01b78..1127a46d 100644 --- a/subprojects/frontend/src/graph/GraphTheme.tsx +++ b/subprojects/frontend/src/graph/GraphTheme.tsx | |||
@@ -143,6 +143,21 @@ export function createGraphTheme({ | |||
143 | 'text.label-ERROR': { | 143 | 'text.label-ERROR': { |
144 | fill: theme.palette.error.main, | 144 | fill: theme.palette.error.main, |
145 | }, | 145 | }, |
146 | '.node-exists-FALSE': { | ||
147 | 'text:not(.label-ERROR)': { | ||
148 | fill: theme.palette.text.secondary, | ||
149 | }, | ||
150 | '.node-outline': { | ||
151 | stroke: theme.palette.text.secondary, | ||
152 | strokeDasharray: '2 4', | ||
153 | }, | ||
154 | '.node-header': { | ||
155 | fill: theme.palette.background.default, | ||
156 | }, | ||
157 | '.icon-TRUE': { | ||
158 | fill: theme.palette.text.secondary, | ||
159 | }, | ||
160 | }, | ||
146 | }; | 161 | }; |
147 | } | 162 | } |
148 | 163 | ||
diff --git a/subprojects/frontend/src/graph/dotSource.ts b/subprojects/frontend/src/graph/dotSource.ts index ce504c37..9099cd09 100644 --- a/subprojects/frontend/src/graph/dotSource.ts +++ b/subprojects/frontend/src/graph/dotSource.ts | |||
@@ -128,11 +128,16 @@ function createNodes( | |||
128 | const { | 128 | const { |
129 | semantics: { nodes }, | 129 | semantics: { nodes }, |
130 | scopes, | 130 | scopes, |
131 | showNonExistent, | ||
131 | } = graph; | 132 | } = graph; |
132 | 133 | ||
133 | nodes.forEach((node, i) => { | 134 | nodes.forEach((node, i) => { |
134 | const data = nodeData[i]; | 135 | const data = nodeData[i]; |
135 | if (data === undefined || data.isolated || data.exists === 'FALSE') { | 136 | if ( |
137 | data === undefined || | ||
138 | data.isolated || | ||
139 | (!showNonExistent && data.exists === 'FALSE') | ||
140 | ) { | ||
136 | return; | 141 | return; |
137 | } | 142 | } |
138 | const classList = [ | 143 | const classList = [ |
@@ -255,6 +260,7 @@ function createRelationEdges( | |||
255 | ): void { | 260 | ): void { |
256 | const { | 261 | const { |
257 | semantics: { nodes, partialInterpretation }, | 262 | semantics: { nodes, partialInterpretation }, |
263 | showNonExistent, | ||
258 | } = graph; | 264 | } = graph; |
259 | const { detail } = relation; | 265 | const { detail } = relation; |
260 | 266 | ||
@@ -297,9 +303,9 @@ function createRelationEdges( | |||
297 | const toData = nodeData[to]; | 303 | const toData = nodeData[to]; |
298 | if ( | 304 | if ( |
299 | fromData === undefined || | 305 | fromData === undefined || |
300 | fromData.exists === 'FALSE' || | ||
301 | toData === undefined || | 306 | toData === undefined || |
302 | toData.exists === 'FALSE' | 307 | (!showNonExistent && |
308 | (fromData.exists === 'FALSE' || toData.exists === 'FALSE')) | ||
303 | ) { | 309 | ) { |
304 | return; | 310 | return; |
305 | } | 311 | } |
diff --git a/subprojects/frontend/src/graph/export/exportDiagram.tsx b/subprojects/frontend/src/graph/export/exportDiagram.tsx index 73b40fea..663cafe1 100644 --- a/subprojects/frontend/src/graph/export/exportDiagram.tsx +++ b/subprojects/frontend/src/graph/export/exportDiagram.tsx | |||
@@ -6,7 +6,7 @@ | |||
6 | 6 | ||
7 | import createCache from '@emotion/cache'; | 7 | import createCache from '@emotion/cache'; |
8 | import { serializeStyles } from '@emotion/serialize'; | 8 | import { serializeStyles } from '@emotion/serialize'; |
9 | import type { StyleSheet } from '@emotion/utils'; | 9 | import type { StyleSheet } from '@emotion/sheet'; |
10 | import italicFontURL from '@fontsource/open-sans/files/open-sans-latin-400-italic.woff2?url'; | 10 | import italicFontURL from '@fontsource/open-sans/files/open-sans-latin-400-italic.woff2?url'; |
11 | import normalFontURL from '@fontsource/open-sans/files/open-sans-latin-400-normal.woff2?url'; | 11 | import normalFontURL from '@fontsource/open-sans/files/open-sans-latin-400-normal.woff2?url'; |
12 | import boldFontURL from '@fontsource/open-sans/files/open-sans-latin-700-normal.woff2?url'; | 12 | import boldFontURL from '@fontsource/open-sans/files/open-sans-latin-700-normal.woff2?url'; |
diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar index b8038b70..56867964 100644 --- a/subprojects/frontend/src/language/problem.grammar +++ b/subprojects/frontend/src/language/problem.grammar | |||
@@ -52,8 +52,7 @@ statement { | |||
52 | kw<"extern"> ckw<"aggregator"> AggregatorName "." | 52 | kw<"extern"> ckw<"aggregator"> AggregatorName "." |
53 | } | | 53 | } | |
54 | PredicateDefinition { | 54 | PredicateDefinition { |
55 | ckw<"shadow">? | 55 | ((kw<"error"> | kw<"partial"> | ckw<"shadow">)* kw<"pred"> | kw<"error">) |
56 | (kw<"error">? kw<"pred"> | kw<"error">) | ||
57 | RelationName ParameterList<Parameter>? | 56 | RelationName ParameterList<Parameter>? |
58 | PredicateBody { ("<->" sep<OrOp, Conjunction>)? "." } | 57 | PredicateBody { ("<->" sep<OrOp, Conjunction>)? "." } |
59 | } | | 58 | } | |
diff --git a/subprojects/frontend/src/persistence/compressionWorker.ts b/subprojects/frontend/src/persistence/compressionWorker.ts index 7b93b20b..df476535 100644 --- a/subprojects/frontend/src/persistence/compressionWorker.ts +++ b/subprojects/frontend/src/persistence/compressionWorker.ts | |||
@@ -1,13 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | 6 | ||
7 | import type { Zstd } from '@hpcc-js/wasm'; | 7 | import { Zstd } from '@hpcc-js/wasm-zstd'; |
8 | // We need to use a deep import for proper code splitting with `vite-plugin-pwa`. | ||
9 | // @ts-expect-error Typescript doesn't find the declarations for the deep import. | ||
10 | import { Zstd as zstdLoader } from '@hpcc-js/wasm/zstd'; | ||
11 | 8 | ||
12 | import type { | 9 | import type { |
13 | CompressResponse, | 10 | CompressResponse, |
@@ -56,13 +53,13 @@ async function base64Decode(compressedText: string): Promise<Uint8Array> { | |||
56 | return new Uint8Array(await result.arrayBuffer()); | 53 | return new Uint8Array(await result.arrayBuffer()); |
57 | } | 54 | } |
58 | 55 | ||
59 | let zstd: Awaited<ReturnType<typeof Zstd.load>> | undefined; | 56 | let zstd: Zstd | undefined; |
60 | 57 | ||
61 | globalThis.onmessage = (event) => { | 58 | globalThis.onmessage = (event) => { |
62 | (async () => { | 59 | (async () => { |
63 | if (zstd === undefined) { | 60 | if (zstd === undefined) { |
64 | // Since we don't have types for the deep import, we have to cast here. | 61 | // Since we don't have types for the deep import, we have to cast here. |
65 | zstd = await (zstdLoader as { load: typeof Zstd.load }).load(); | 62 | zstd = await Zstd.load(); |
66 | } | 63 | } |
67 | // Since the render thread will only send us valid messages, | 64 | // Since the render thread will only send us valid messages, |
68 | // we can save a bit of bundle size by using a cast instead of `parse` | 65 | // we can save a bit of bundle size by using a cast instead of `parse` |
diff --git a/subprojects/frontend/src/xtext/xtextServiceResults.ts b/subprojects/frontend/src/xtext/xtextServiceResults.ts index c5bc1320..7c2fb8ec 100644 --- a/subprojects/frontend/src/xtext/xtextServiceResults.ts +++ b/subprojects/frontend/src/xtext/xtextServiceResults.ts | |||
@@ -156,6 +156,7 @@ export const RelationMetadata = z.object({ | |||
156 | opposite: z.string(), | 156 | opposite: z.string(), |
157 | }), | 157 | }), |
158 | z.object({ type: z.literal('predicate'), error: z.boolean() }), | 158 | z.object({ type: z.literal('predicate'), error: z.boolean() }), |
159 | z.object({ type: z.literal('base') }), | ||
159 | z.object({ type: z.literal('builtin') }), | 160 | z.object({ type: z.literal('builtin') }), |
160 | ]), | 161 | ]), |
161 | }); | 162 | }); |
diff --git a/subprojects/generator-cli/build.gradle.kts b/subprojects/generator-cli/build.gradle.kts index 6c681222..edb13b65 100644 --- a/subprojects/generator-cli/build.gradle.kts +++ b/subprojects/generator-cli/build.gradle.kts | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -11,14 +11,9 @@ plugins { | |||
11 | dependencies { | 11 | dependencies { |
12 | implementation(project(":refinery-generator")) | 12 | implementation(project(":refinery-generator")) |
13 | implementation(libs.jcommander) | 13 | implementation(libs.jcommander) |
14 | implementation(libs.slf4j.api) | 14 | implementation(libs.slf4j) |
15 | } | 15 | } |
16 | 16 | ||
17 | application { | 17 | application { |
18 | mainClass.set("tools.refinery.generator.cli.RefineryCli") | 18 | mainClass.set("tools.refinery.generator.cli.RefineryCli") |
19 | } | 19 | } |
20 | |||
21 | tasks.shadowJar { | ||
22 | // Silence Xtext warning. | ||
23 | append("plugin.properties") | ||
24 | } | ||
diff --git a/subprojects/generator/build.gradle.kts b/subprojects/generator/build.gradle.kts index 36e3537a..14eebf7d 100644 --- a/subprojects/generator/build.gradle.kts +++ b/subprojects/generator/build.gradle.kts | |||
@@ -6,6 +6,7 @@ | |||
6 | 6 | ||
7 | plugins { | 7 | plugins { |
8 | id("tools.refinery.gradle.java-library") | 8 | id("tools.refinery.gradle.java-library") |
9 | id("tools.refinery.gradle.java-test-fixtures") | ||
9 | } | 10 | } |
10 | 11 | ||
11 | mavenArtifact { | 12 | mavenArtifact { |
@@ -14,7 +15,7 @@ mavenArtifact { | |||
14 | 15 | ||
15 | dependencies { | 16 | dependencies { |
16 | api(project(":refinery-language-semantics")) | 17 | api(project(":refinery-language-semantics")) |
17 | api(libs.eclipseCollections.api) | ||
18 | implementation(project(":refinery-store-query-interpreter")) | 18 | implementation(project(":refinery-store-query-interpreter")) |
19 | testImplementation(testFixtures(project(":refinery-language"))) | 19 | testFixturesApi(testFixtures(project(":refinery-language"))) |
20 | testFixturesImplementation(libs.junit.api) | ||
20 | } | 21 | } |
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/CancellableCancellationToken.java b/subprojects/generator/src/main/java/tools/refinery/generator/CancellableCancellationToken.java new file mode 100644 index 00000000..53bac196 --- /dev/null +++ b/subprojects/generator/src/main/java/tools/refinery/generator/CancellableCancellationToken.java | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | import tools.refinery.store.util.CancellationToken; | ||
9 | |||
10 | class CancellableCancellationToken implements CancellationToken { | ||
11 | private volatile boolean cancelled; | ||
12 | |||
13 | private final CancellationToken wrappedToken; | ||
14 | |||
15 | public CancellableCancellationToken(CancellationToken wrappedToken) { | ||
16 | this.wrappedToken = wrappedToken; | ||
17 | } | ||
18 | |||
19 | public boolean isCancelled() { | ||
20 | return cancelled; | ||
21 | } | ||
22 | |||
23 | public void cancel() { | ||
24 | cancelled = true; | ||
25 | } | ||
26 | |||
27 | public void reset() { | ||
28 | cancelled = false; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public void checkCancelled() { | ||
33 | wrappedToken.checkCancelled(); | ||
34 | if (cancelled) { | ||
35 | throw new GeneratorTimeoutException(); | ||
36 | } | ||
37 | } | ||
38 | } | ||
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/FilteredInterpretation.java b/subprojects/generator/src/main/java/tools/refinery/generator/FilteredInterpretation.java new file mode 100644 index 00000000..c068615f --- /dev/null +++ b/subprojects/generator/src/main/java/tools/refinery/generator/FilteredInterpretation.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | import tools.refinery.logic.AbstractValue; | ||
9 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
10 | import tools.refinery.store.map.AnyVersionedMap; | ||
11 | import tools.refinery.store.map.Cursor; | ||
12 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
13 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | ||
14 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
15 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
16 | import tools.refinery.store.tuple.Tuple; | ||
17 | |||
18 | import java.util.Set; | ||
19 | |||
20 | public class FilteredInterpretation<A extends AbstractValue<A, C>, C> implements PartialInterpretation<A, C> { | ||
21 | private final PartialInterpretation<A, C> wrappedInterpretation; | ||
22 | private final PartialInterpretation<TruthValue, Boolean> existsInterpretation; | ||
23 | |||
24 | public FilteredInterpretation(PartialInterpretation<A, C> wrappedInterpretation, PartialInterpretation<TruthValue, | ||
25 | Boolean> existsInterpretation) { | ||
26 | this.wrappedInterpretation = wrappedInterpretation; | ||
27 | this.existsInterpretation = existsInterpretation; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public ReasoningAdapter getAdapter() { | ||
32 | return wrappedInterpretation.getAdapter(); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public PartialSymbol<A, C> getPartialSymbol() { | ||
37 | return wrappedInterpretation.getPartialSymbol(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Concreteness getConcreteness() { | ||
42 | return wrappedInterpretation.getConcreteness(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public A get(Tuple key) { | ||
47 | return tupleExists(key) ? wrappedInterpretation.get(key) : | ||
48 | wrappedInterpretation.getPartialSymbol().defaultValue(); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public Cursor<Tuple, A> getAll() { | ||
53 | return new FilteredCursor(wrappedInterpretation.getAll()); | ||
54 | } | ||
55 | |||
56 | private boolean tupleExists(Tuple key) { | ||
57 | int arity = key.getSize(); | ||
58 | for (int i = 0; i < arity; i++) { | ||
59 | if (!existsInterpretation.get(Tuple.of(key.get(i))).may()) { | ||
60 | return false; | ||
61 | } | ||
62 | } | ||
63 | return true; | ||
64 | } | ||
65 | |||
66 | private class FilteredCursor implements Cursor<Tuple, A> { | ||
67 | private final Cursor<Tuple, A> wrappedCursor; | ||
68 | |||
69 | private FilteredCursor(Cursor<Tuple, A> wrappedCursor) { | ||
70 | this.wrappedCursor = wrappedCursor; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public Tuple getKey() { | ||
75 | return wrappedCursor.getKey(); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public A getValue() { | ||
80 | return wrappedCursor.getValue(); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public boolean isTerminated() { | ||
85 | return wrappedCursor.isTerminated(); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public boolean move() { | ||
90 | while (wrappedCursor.move()) { | ||
91 | if (tupleExists(getKey())) { | ||
92 | return true; | ||
93 | } | ||
94 | } | ||
95 | return false; | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public boolean isDirty() { | ||
100 | return wrappedCursor.isDirty(); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Set<AnyVersionedMap> getDependingMaps() { | ||
105 | return wrappedCursor.getDependingMaps(); | ||
106 | } | ||
107 | } | ||
108 | } | ||
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/GeneratorResult.java b/subprojects/generator/src/main/java/tools/refinery/generator/GeneratorResult.java new file mode 100644 index 00000000..3ce2631a --- /dev/null +++ b/subprojects/generator/src/main/java/tools/refinery/generator/GeneratorResult.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | public enum GeneratorResult { | ||
9 | SUCCESS { | ||
10 | @Override | ||
11 | public void orThrow() { | ||
12 | // No need to throw on error. | ||
13 | } | ||
14 | }, | ||
15 | UNSATISFIABLE { | ||
16 | @Override | ||
17 | public void orThrow() { | ||
18 | throw new UnsatisfiableProblemException(); | ||
19 | } | ||
20 | }, | ||
21 | TIMEOUT { | ||
22 | @Override | ||
23 | public void orThrow() { | ||
24 | throw new GeneratorTimeoutException(); | ||
25 | } | ||
26 | }; | ||
27 | |||
28 | public abstract void orThrow(); | ||
29 | } | ||
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/GeneratorTimeoutException.java b/subprojects/generator/src/main/java/tools/refinery/generator/GeneratorTimeoutException.java new file mode 100644 index 00000000..4312a324 --- /dev/null +++ b/subprojects/generator/src/main/java/tools/refinery/generator/GeneratorTimeoutException.java | |||
@@ -0,0 +1,12 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | public class GeneratorTimeoutException extends RuntimeException { | ||
9 | public GeneratorTimeoutException() { | ||
10 | super("Model generation timed out"); | ||
11 | } | ||
12 | } | ||
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java new file mode 100644 index 00000000..173be38e --- /dev/null +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import tools.refinery.language.semantics.ModelInitializer; | ||
11 | import tools.refinery.store.util.CancellationToken; | ||
12 | |||
13 | // This class is used as a fluent builder, so it's not necessary to use the return value of all of its methods. | ||
14 | @SuppressWarnings("UnusedReturnValue") | ||
15 | public abstract sealed class ModelFacadeFactory<T extends ModelFacadeFactory<T>> permits ModelSemanticsFactory, | ||
16 | ModelGeneratorFactory { | ||
17 | @Inject | ||
18 | private Provider<ModelInitializer> initializerProvider; | ||
19 | |||
20 | private CancellationToken cancellationToken = CancellationToken.NONE; | ||
21 | |||
22 | private boolean keepNonExistingObjects; | ||
23 | |||
24 | private boolean keepShadowPredicates = true; | ||
25 | |||
26 | protected abstract T getSelf(); | ||
27 | |||
28 | public T cancellationToken(CancellationToken cancellationToken) { | ||
29 | this.cancellationToken = cancellationToken; | ||
30 | return getSelf(); | ||
31 | } | ||
32 | |||
33 | public T keepNonExistingObjects(boolean keepNonExistentObjects) { | ||
34 | this.keepNonExistingObjects = keepNonExistentObjects; | ||
35 | return getSelf(); | ||
36 | } | ||
37 | |||
38 | public T keepShadowPredicates(boolean keepShadowPredicates) { | ||
39 | this.keepShadowPredicates = keepShadowPredicates; | ||
40 | return getSelf(); | ||
41 | } | ||
42 | |||
43 | protected ModelInitializer createModelInitializer() { | ||
44 | var initializer = initializerProvider.get(); | ||
45 | initializer.setKeepNonExistingObjects(keepNonExistingObjects); | ||
46 | initializer.setKeepShadowPredicates(keepShadowPredicates); | ||
47 | return initializer; | ||
48 | } | ||
49 | |||
50 | protected CancellationToken getCancellationToken() { | ||
51 | return cancellationToken; | ||
52 | } | ||
53 | |||
54 | protected boolean isKeepNonExistingObjects() { | ||
55 | return keepNonExistingObjects; | ||
56 | } | ||
57 | |||
58 | protected void checkCancelled() { | ||
59 | cancellationToken.checkCancelled(); | ||
60 | } | ||
61 | } | ||
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelGenerator.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelGenerator.java index 8dff5622..2f459eb9 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelGenerator.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelGenerator.java | |||
@@ -10,26 +10,38 @@ import tools.refinery.language.model.problem.Problem; | |||
10 | import tools.refinery.language.semantics.ProblemTrace; | 10 | import tools.refinery.language.semantics.ProblemTrace; |
11 | import tools.refinery.language.semantics.SolutionSerializer; | 11 | import tools.refinery.language.semantics.SolutionSerializer; |
12 | import tools.refinery.logic.AbstractValue; | 12 | import tools.refinery.logic.AbstractValue; |
13 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
13 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; | 14 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; |
14 | import tools.refinery.store.dse.transition.statespace.SolutionStore; | 15 | import tools.refinery.store.dse.transition.statespace.SolutionStore; |
15 | import tools.refinery.store.map.Version; | 16 | import tools.refinery.store.map.Version; |
16 | import tools.refinery.store.model.ModelStore; | 17 | import tools.refinery.store.model.ModelStore; |
18 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
17 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | 19 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; |
18 | import tools.refinery.store.reasoning.literal.Concreteness; | 20 | import tools.refinery.store.reasoning.literal.Concreteness; |
19 | import tools.refinery.store.reasoning.representation.PartialSymbol; | 21 | import tools.refinery.store.reasoning.representation.PartialSymbol; |
20 | import tools.refinery.store.reasoning.seed.ModelSeed; | 22 | import tools.refinery.store.reasoning.seed.ModelSeed; |
21 | 23 | ||
24 | import java.util.concurrent.Executors; | ||
25 | import java.util.concurrent.TimeUnit; | ||
26 | |||
22 | public class ModelGenerator extends ModelFacade { | 27 | public class ModelGenerator extends ModelFacade { |
23 | private final Version initialVersion; | 28 | private final Version initialVersion; |
24 | private final Provider<SolutionSerializer> solutionSerializerProvider; | 29 | private final Provider<SolutionSerializer> solutionSerializerProvider; |
30 | private final CancellableCancellationToken cancellationToken; | ||
31 | private final boolean keepNonExistingObjects; | ||
32 | private final PartialInterpretation<TruthValue, Boolean> existsInterpretation; | ||
25 | private long randomSeed = 1; | 33 | private long randomSeed = 1; |
26 | private int maxNumberOfSolutions = 1; | 34 | private int maxNumberOfSolutions = 1; |
27 | private SolutionStore solutionStore; | 35 | private SolutionStore solutionStore; |
28 | 36 | ||
29 | ModelGenerator(ProblemTrace problemTrace, ModelStore store, ModelSeed modelSeed, | 37 | ModelGenerator(ProblemTrace problemTrace, ModelStore store, ModelSeed modelSeed, |
30 | Provider<SolutionSerializer> solutionSerializerProvider) { | 38 | Provider<SolutionSerializer> solutionSerializerProvider, |
39 | CancellableCancellationToken cancellationToken, boolean keepNonExistingObjects) { | ||
31 | super(problemTrace, store, modelSeed, Concreteness.CANDIDATE); | 40 | super(problemTrace, store, modelSeed, Concreteness.CANDIDATE); |
32 | this.solutionSerializerProvider = solutionSerializerProvider; | 41 | this.solutionSerializerProvider = solutionSerializerProvider; |
42 | this.cancellationToken = cancellationToken; | ||
43 | this.keepNonExistingObjects = keepNonExistingObjects; | ||
44 | existsInterpretation = super.getPartialInterpretation(ReasoningAdapter.EXISTS_SYMBOL); | ||
33 | initialVersion = getModel().commit(); | 45 | initialVersion = getModel().commit(); |
34 | } | 46 | } |
35 | 47 | ||
@@ -71,33 +83,52 @@ public class ModelGenerator extends ModelFacade { | |||
71 | return solutionStore != null; | 83 | return solutionStore != null; |
72 | } | 84 | } |
73 | 85 | ||
74 | // This method only makes sense if it returns {@code true} on success. | 86 | public GeneratorResult tryGenerate() { |
75 | @SuppressWarnings("BooleanMethodIsAlwaysInverted") | 87 | if (cancellationToken.isCancelled()) { |
76 | public boolean tryGenerate() { | 88 | throw new IllegalStateException("Model generation was previously cancelled"); |
89 | } | ||
77 | solutionStore = null; | 90 | solutionStore = null; |
78 | randomSeed++; | 91 | randomSeed++; |
79 | var bestFirst = new BestFirstStoreManager(getModelStore(), maxNumberOfSolutions); | 92 | var bestFirst = new BestFirstStoreManager(getModelStore(), maxNumberOfSolutions); |
80 | bestFirst.startExploration(initialVersion, randomSeed); | 93 | bestFirst.startExploration(initialVersion, randomSeed); |
81 | var solutions = bestFirst.getSolutionStore().getSolutions(); | 94 | var solutions = bestFirst.getSolutionStore().getSolutions(); |
82 | if (solutions.isEmpty()) { | 95 | if (solutions.isEmpty()) { |
83 | return false; | 96 | return GeneratorResult.UNSATISFIABLE; |
84 | } | 97 | } |
85 | getModel().restore(solutions.getFirst().version()); | 98 | getModel().restore(solutions.getFirst().version()); |
86 | solutionStore = bestFirst.getSolutionStore(); | 99 | solutionStore = bestFirst.getSolutionStore(); |
87 | return true; | 100 | return GeneratorResult.SUCCESS; |
88 | } | 101 | } |
89 | 102 | ||
90 | public void generate() { | 103 | public void generate() { |
91 | if (!tryGenerate()) { | 104 | tryGenerate().orThrow(); |
92 | throw new UnsatisfiableProblemException(); | 105 | } |
106 | |||
107 | public GeneratorResult tryGenerateWithTimeout(long l, TimeUnit timeUnit) { | ||
108 | try (var executorService = Executors.newSingleThreadScheduledExecutor()) { | ||
109 | var timeoutFuture = executorService.schedule(cancellationToken::cancel, l, timeUnit); | ||
110 | try { | ||
111 | return tryGenerate(); | ||
112 | } catch (GeneratorTimeoutException e) { | ||
113 | return GeneratorResult.TIMEOUT; | ||
114 | } finally { | ||
115 | timeoutFuture.cancel(true); | ||
116 | cancellationToken.reset(); | ||
117 | } | ||
93 | } | 118 | } |
94 | } | 119 | } |
95 | 120 | ||
121 | public void generateWithTimeout(long l, TimeUnit timeUnit) { | ||
122 | tryGenerateWithTimeout(l, timeUnit).orThrow(); | ||
123 | } | ||
124 | |||
96 | @Override | 125 | @Override |
97 | public <A extends AbstractValue<A, C>, C> PartialInterpretation<A, C> getPartialInterpretation( | 126 | public <A extends AbstractValue<A, C>, C> PartialInterpretation<A, C> getPartialInterpretation( |
98 | PartialSymbol<A, C> partialSymbol) { | 127 | PartialSymbol<A, C> partialSymbol) { |
99 | checkSuccessfulGeneration(); | 128 | checkSuccessfulGeneration(); |
100 | return super.getPartialInterpretation(partialSymbol); | 129 | var partialInterpretation = super.getPartialInterpretation(partialSymbol); |
130 | return keepNonExistingObjects ? partialInterpretation : | ||
131 | new FilteredInterpretation<>(partialInterpretation, existsInterpretation); | ||
101 | } | 132 | } |
102 | 133 | ||
103 | public Problem serializeSolution() { | 134 | public Problem serializeSolution() { |
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java index 0a1b4396..3205eca2 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java | |||
@@ -8,7 +8,6 @@ package tools.refinery.generator; | |||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import com.google.inject.Provider; | 9 | import com.google.inject.Provider; |
10 | import tools.refinery.language.model.problem.Problem; | 10 | import tools.refinery.language.model.problem.Problem; |
11 | import tools.refinery.language.semantics.ModelInitializer; | ||
12 | import tools.refinery.language.semantics.SolutionSerializer; | 11 | import tools.refinery.language.semantics.SolutionSerializer; |
13 | import tools.refinery.store.dse.propagation.PropagationAdapter; | 12 | import tools.refinery.store.dse.propagation.PropagationAdapter; |
14 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; | 13 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; |
@@ -20,30 +19,28 @@ import tools.refinery.store.reasoning.literal.Concreteness; | |||
20 | import tools.refinery.store.statecoding.StateCodeCalculatorFactory; | 19 | import tools.refinery.store.statecoding.StateCodeCalculatorFactory; |
21 | import tools.refinery.store.statecoding.StateCoderAdapter; | 20 | import tools.refinery.store.statecoding.StateCoderAdapter; |
22 | import tools.refinery.store.statecoding.neighborhood.NeighborhoodCalculator; | 21 | import tools.refinery.store.statecoding.neighborhood.NeighborhoodCalculator; |
23 | import tools.refinery.store.util.CancellationToken; | ||
24 | 22 | ||
25 | import java.util.Collection; | 23 | import java.util.Collection; |
26 | import java.util.Set; | 24 | import java.util.Set; |
27 | 25 | ||
28 | // This class is used as a fluent builder, so it's not necessary to use the return value of all of its methods. | 26 | // This class is used as a fluent builder, so it's not necessary to use the return value of all of its methods. |
29 | @SuppressWarnings("UnusedReturnValue") | 27 | @SuppressWarnings("UnusedReturnValue") |
30 | public final class ModelGeneratorFactory { | 28 | public final class ModelGeneratorFactory extends ModelFacadeFactory<ModelGeneratorFactory> { |
31 | @Inject | ||
32 | private Provider<ModelInitializer> initializerProvider; | ||
33 | |||
34 | @Inject | 29 | @Inject |
35 | private Provider<SolutionSerializer> solutionSerializerProvider; | 30 | private Provider<SolutionSerializer> solutionSerializerProvider; |
36 | 31 | ||
37 | private CancellationToken cancellationToken = CancellationToken.NONE; | ||
38 | |||
39 | private boolean debugPartialInterpretations; | 32 | private boolean debugPartialInterpretations; |
40 | 33 | ||
41 | private boolean partialInterpretationBasedNeighborhoods; | 34 | private boolean partialInterpretationBasedNeighborhoods; |
42 | 35 | ||
43 | private int stateCoderDepth = NeighborhoodCalculator.DEFAULT_DEPTH; | 36 | private int stateCoderDepth = NeighborhoodCalculator.DEFAULT_DEPTH; |
44 | 37 | ||
45 | public ModelGeneratorFactory cancellationToken(CancellationToken cancellationToken) { | 38 | public ModelGeneratorFactory() { |
46 | this.cancellationToken = cancellationToken; | 39 | keepShadowPredicates(false); |
40 | } | ||
41 | |||
42 | @Override | ||
43 | protected ModelGeneratorFactory getSelf() { | ||
47 | return this; | 44 | return this; |
48 | } | 45 | } |
49 | 46 | ||
@@ -64,22 +61,23 @@ public final class ModelGeneratorFactory { | |||
64 | } | 61 | } |
65 | 62 | ||
66 | public ModelGenerator createGenerator(Problem problem) { | 63 | public ModelGenerator createGenerator(Problem problem) { |
67 | var initializer = initializerProvider.get(); | 64 | var initializer = createModelInitializer(); |
68 | initializer.readProblem(problem); | 65 | initializer.readProblem(problem); |
69 | cancellationToken.checkCancelled(); | 66 | checkCancelled(); |
67 | var cancellationToken = new CancellableCancellationToken(getCancellationToken()); | ||
70 | var storeBuilder = ModelStore.builder() | 68 | var storeBuilder = ModelStore.builder() |
71 | .cancellationToken(cancellationToken) | 69 | .cancellationToken(cancellationToken) |
72 | .with(QueryInterpreterAdapter.builder()) | 70 | .with(QueryInterpreterAdapter.builder()) |
73 | .with(PropagationAdapter.builder()) | 71 | .with(PropagationAdapter.builder()) |
74 | .with(StateCoderAdapter.builder() | 72 | .with(StateCoderAdapter.builder() |
75 | .stateCodeCalculatorFactory(getStateCoderCalculatorFactory())) | 73 | .stateCodeCalculatorFactory(getStateCodeCalculatorFactory())) |
76 | .with(DesignSpaceExplorationAdapter.builder()) | 74 | .with(DesignSpaceExplorationAdapter.builder()) |
77 | .with(ReasoningAdapter.builder() | 75 | .with(ReasoningAdapter.builder() |
78 | .requiredInterpretations(getRequiredInterpretations())); | 76 | .requiredInterpretations(getRequiredInterpretations())); |
79 | initializer.configureStoreBuilder(storeBuilder); | 77 | initializer.configureStoreBuilder(storeBuilder); |
80 | var store = storeBuilder.build(); | 78 | var store = storeBuilder.build(); |
81 | var generator = new ModelGenerator(initializer.getProblemTrace(), store, initializer.getModelSeed(), | 79 | var generator = new ModelGenerator(initializer.getProblemTrace(), store, initializer.getModelSeed(), |
82 | solutionSerializerProvider); | 80 | solutionSerializerProvider, cancellationToken, isKeepNonExistingObjects()); |
83 | generator.getPropagationResult().throwIfRejected(); | 81 | generator.getPropagationResult().throwIfRejected(); |
84 | return generator; | 82 | return generator; |
85 | } | 83 | } |
@@ -90,7 +88,7 @@ public final class ModelGeneratorFactory { | |||
90 | Set.of(Concreteness.CANDIDATE); | 88 | Set.of(Concreteness.CANDIDATE); |
91 | } | 89 | } |
92 | 90 | ||
93 | private StateCodeCalculatorFactory getStateCoderCalculatorFactory() { | 91 | private StateCodeCalculatorFactory getStateCodeCalculatorFactory() { |
94 | return partialInterpretationBasedNeighborhoods ? | 92 | return partialInterpretationBasedNeighborhoods ? |
95 | PartialNeighborhoodCalculator.factory(Concreteness.PARTIAL, stateCoderDepth) : | 93 | PartialNeighborhoodCalculator.factory(Concreteness.PARTIAL, stateCoderDepth) : |
96 | NeighborhoodCalculator.factory(stateCoderDepth); | 94 | NeighborhoodCalculator.factory(stateCoderDepth); |
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java index 3c3488df..37a478eb 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java | |||
@@ -5,27 +5,28 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.generator; | 6 | package tools.refinery.generator; |
7 | 7 | ||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import tools.refinery.language.model.problem.Problem; | 8 | import tools.refinery.language.model.problem.Problem; |
11 | import tools.refinery.language.semantics.ModelInitializer; | ||
12 | import tools.refinery.store.dse.propagation.PropagationAdapter; | 9 | import tools.refinery.store.dse.propagation.PropagationAdapter; |
13 | import tools.refinery.store.model.ModelStore; | 10 | import tools.refinery.store.model.ModelStore; |
14 | import tools.refinery.store.query.interpreter.QueryInterpreterAdapter; | 11 | import tools.refinery.store.query.interpreter.QueryInterpreterAdapter; |
15 | import tools.refinery.store.reasoning.ReasoningAdapter; | 12 | import tools.refinery.store.reasoning.ReasoningAdapter; |
16 | import tools.refinery.store.reasoning.literal.Concreteness; | 13 | import tools.refinery.store.reasoning.literal.Concreteness; |
17 | import tools.refinery.store.util.CancellationToken; | ||
18 | 14 | ||
15 | import java.util.Collection; | ||
19 | import java.util.Set; | 16 | import java.util.Set; |
20 | 17 | ||
21 | public final class ModelSemanticsFactory { | 18 | // This class is used as a fluent builder, so it's not necessary to use the return value of all of its methods. |
22 | @Inject | 19 | @SuppressWarnings("UnusedReturnValue") |
23 | private Provider<ModelInitializer> initializerProvider; | 20 | public final class ModelSemanticsFactory extends ModelFacadeFactory<ModelSemanticsFactory> { |
21 | private boolean withCandidateInterpretations; | ||
24 | 22 | ||
25 | private CancellationToken cancellationToken = CancellationToken.NONE; | 23 | @Override |
24 | protected ModelSemanticsFactory getSelf() { | ||
25 | return this; | ||
26 | } | ||
26 | 27 | ||
27 | public ModelSemanticsFactory cancellationToken(CancellationToken cancellationToken) { | 28 | public ModelSemanticsFactory withCandidateInterpretations(boolean withCandidateInterpretations) { |
28 | this.cancellationToken = cancellationToken; | 29 | this.withCandidateInterpretations = withCandidateInterpretations; |
29 | return this; | 30 | return this; |
30 | } | 31 | } |
31 | 32 | ||
@@ -36,17 +37,23 @@ public final class ModelSemanticsFactory { | |||
36 | } | 37 | } |
37 | 38 | ||
38 | public ModelSemantics tryCreateSemantics(Problem problem) { | 39 | public ModelSemantics tryCreateSemantics(Problem problem) { |
39 | var initializer = initializerProvider.get(); | 40 | var initializer = createModelInitializer(); |
40 | initializer.readProblem(problem); | 41 | initializer.readProblem(problem); |
42 | checkCancelled(); | ||
41 | var storeBuilder = ModelStore.builder() | 43 | var storeBuilder = ModelStore.builder() |
42 | .cancellationToken(cancellationToken) | 44 | .cancellationToken(getCancellationToken()) |
43 | .with(QueryInterpreterAdapter.builder()) | 45 | .with(QueryInterpreterAdapter.builder()) |
44 | .with(PropagationAdapter.builder() | 46 | .with(PropagationAdapter.builder() |
45 | .throwOnFatalRejection(false)) | 47 | .throwOnFatalRejection(false)) |
46 | .with(ReasoningAdapter.builder() | 48 | .with(ReasoningAdapter.builder() |
47 | .requiredInterpretations(Set.of(Concreteness.PARTIAL))); | 49 | .requiredInterpretations(getRequiredInterpretations())); |
48 | initializer.configureStoreBuilder(storeBuilder); | 50 | initializer.configureStoreBuilder(storeBuilder); |
49 | var store = storeBuilder.build(); | 51 | var store = storeBuilder.build(); |
50 | return new ModelSemantics(initializer.getProblemTrace(), store, initializer.getModelSeed()); | 52 | return new ModelSemantics(initializer.getProblemTrace(), store, initializer.getModelSeed()); |
51 | } | 53 | } |
54 | |||
55 | private Collection<Concreteness> getRequiredInterpretations() { | ||
56 | return withCandidateInterpretations ? Set.of(Concreteness.PARTIAL, Concreteness.CANDIDATE) : | ||
57 | Set.of(Concreteness.PARTIAL); | ||
58 | } | ||
52 | } | 59 | } |
diff --git a/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java b/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java new file mode 100644 index 00000000..42ae9466 --- /dev/null +++ b/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import org.junit.jupiter.api.DynamicNode; | ||
11 | import org.junit.jupiter.api.TestFactory; | ||
12 | import tools.refinery.generator.tests.DynamicTestLoader; | ||
13 | import tools.refinery.language.tests.InjectWithRefinery; | ||
14 | |||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | @InjectWithRefinery | ||
18 | class FileBasedSemanticsTest { | ||
19 | @Inject | ||
20 | private DynamicTestLoader loader; | ||
21 | |||
22 | @Inject | ||
23 | private Provider<ModelSemanticsFactory> semanticsFactoryProvider; | ||
24 | |||
25 | @TestFactory | ||
26 | Stream<DynamicNode> testWithNonExistingObjects() { | ||
27 | return getFileBasedTests(true, true); | ||
28 | } | ||
29 | |||
30 | @TestFactory | ||
31 | Stream<DynamicNode> testWithoutNonExistingObjects() { | ||
32 | return getFileBasedTests(false, false); | ||
33 | } | ||
34 | |||
35 | private Stream<DynamicNode> getFileBasedTests(boolean keepNonExistingObjects, boolean keepShadowPredicates) { | ||
36 | loader.setSemanticsFactoryProvider(() -> semanticsFactoryProvider.get() | ||
37 | .keepNonExistingObjects(keepNonExistingObjects) | ||
38 | .keepShadowPredicates(keepShadowPredicates)); | ||
39 | return loader.allFromClasspath(getClass()); | ||
40 | } | ||
41 | } | ||
diff --git a/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java b/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java index f0b546d7..14ae1493 100644 --- a/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java +++ b/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java | |||
@@ -5,15 +5,11 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.generator; | 6 | package tools.refinery.generator; |
7 | 7 | ||
8 | |||
9 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | 9 | import org.junit.jupiter.params.ParameterizedTest; |
14 | import org.junit.jupiter.params.provider.Arguments; | 10 | import org.junit.jupiter.params.provider.Arguments; |
15 | import org.junit.jupiter.params.provider.MethodSource; | 11 | import org.junit.jupiter.params.provider.MethodSource; |
16 | import tools.refinery.language.tests.ProblemInjectorProvider; | 12 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | 13 | ||
18 | import java.io.ByteArrayOutputStream; | 14 | import java.io.ByteArrayOutputStream; |
19 | import java.io.IOException; | 15 | import java.io.IOException; |
@@ -25,8 +21,7 @@ import java.util.stream.Stream; | |||
25 | import static org.hamcrest.MatcherAssert.assertThat; | 21 | import static org.hamcrest.MatcherAssert.assertThat; |
26 | import static org.hamcrest.Matchers.is; | 22 | import static org.hamcrest.Matchers.is; |
27 | 23 | ||
28 | @ExtendWith(InjectionExtension.class) | 24 | @InjectWithRefinery |
29 | @InjectWith(ProblemInjectorProvider.class) | ||
30 | class ProblemLoaderTest { | 25 | class ProblemLoaderTest { |
31 | private static final String PREFIX = """ | 26 | private static final String PREFIX = """ |
32 | class Foo. | 27 | class Foo. |
diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem new file mode 100644 index 00000000..4c24fa5a --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem | |||
@@ -0,0 +1,85 @@ | |||
1 | % Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | % | ||
3 | % SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | % TEST WITH ERRORS: directed cross reference type constraint | ||
6 | |||
7 | class A { | ||
8 | B[] foo | ||
9 | } | ||
10 | |||
11 | class B. | ||
12 | |||
13 | foo(a1, b1). | ||
14 | !foo(a2, b2). | ||
15 | ?foo(a3, b3). | ||
16 | foo(a4, b4): error. | ||
17 | |||
18 | % EXPECT EXACTLY: | ||
19 | foo(a1, b1). | ||
20 | !foo(a2, b2). | ||
21 | ?foo(a3, b3). | ||
22 | foo(a4, b4): error. | ||
23 | A(a1). | ||
24 | B(b1). | ||
25 | ?A(a2). | ||
26 | ?B(b2). | ||
27 | ?A(a3). | ||
28 | ?B(b3). | ||
29 | A(a4). | ||
30 | B(b4). | ||
31 | |||
32 | % TEST: directed cross reference with predicate type | ||
33 | |||
34 | class A { | ||
35 | bar[] foo | ||
36 | } | ||
37 | |||
38 | pred bar(A a) <-> !foo(a, _). | ||
39 | |||
40 | foo(a1, b1). | ||
41 | |||
42 | % EXPECT: | ||
43 | A(a1). | ||
44 | bar(b1). | ||
45 | |||
46 | % TEST WITH ERRORS: directed cross reference with predicate type and assertion | ||
47 | |||
48 | class A { | ||
49 | bar[] foo | ||
50 | } | ||
51 | |||
52 | pred bar(A a) <-> !foo(a, _). | ||
53 | |||
54 | !bar(b1). | ||
55 | foo(a1, b1). | ||
56 | |||
57 | % EXPECT EXACTLY: | ||
58 | foo(a1, b1): error. | ||
59 | A(a1). | ||
60 | bar(b1): error. | ||
61 | |||
62 | % TEST WITH ERRORS: undirected cross reference type constraint | ||
63 | |||
64 | class A { | ||
65 | A[] bar opposite bar | ||
66 | } | ||
67 | |||
68 | bar(a1, b1). | ||
69 | !bar(a2, b2). | ||
70 | ?bar(a3, b3). | ||
71 | bar(a4, b4): error. | ||
72 | |||
73 | % EXPECT EXACTLY: | ||
74 | bar(a1, b1). | ||
75 | !bar(a2, b2). | ||
76 | ?bar(a3, b3). | ||
77 | bar(a4, b4): error. | ||
78 | A(a1). | ||
79 | A(b1). | ||
80 | ?A(a2). | ||
81 | ?A(b2). | ||
82 | ?A(a3). | ||
83 | ?A(b3). | ||
84 | A(a4). | ||
85 | A(b4). | ||
diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem new file mode 100644 index 00000000..bd9fd147 --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem | |||
@@ -0,0 +1,57 @@ | |||
1 | % Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | % | ||
3 | % SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | % TEST: upper bound propagation | ||
6 | |||
7 | class Person { | ||
8 | Person[0..2] friend opposite friend | ||
9 | } | ||
10 | |||
11 | friend(a, b). | ||
12 | friend(a, c). | ||
13 | friend(b, c). | ||
14 | !exists(Person::new). | ||
15 | |||
16 | % EXPECT: | ||
17 | friend(b, a). | ||
18 | friend(c, a). | ||
19 | friend(c, b). | ||
20 | !friend(a, a). | ||
21 | !friend(b, b). | ||
22 | !friend(c, c). | ||
23 | |||
24 | % TEST: lower bound propagation | ||
25 | |||
26 | class Person { | ||
27 | Person[2..*] friend opposite friend | ||
28 | } | ||
29 | |||
30 | Person(a). | ||
31 | !friend(a, a). | ||
32 | !friend(b, b). | ||
33 | !friend(c, c). | ||
34 | !exists(Person::new). | ||
35 | |||
36 | % EXPECT: | ||
37 | friend(a, b). | ||
38 | friend(a, c). | ||
39 | friend(b, c). | ||
40 | |||
41 | % TEST: upper and lower bound propagation | ||
42 | |||
43 | class Person { | ||
44 | Person[2] friend opposite friend | ||
45 | } | ||
46 | |||
47 | friend(a, b). | ||
48 | friend(a, c). | ||
49 | !friend(b, b). | ||
50 | !friend(c, c). | ||
51 | !exists(Person::new). | ||
52 | |||
53 | % EXPECT: | ||
54 | friend(b, c). | ||
55 | !friend(a, a). | ||
56 | !friend(b, b). | ||
57 | !friend(c, c). | ||
diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/sudoku.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/sudoku.problem new file mode 100644 index 00000000..fa5b7159 --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/sudoku.problem | |||
@@ -0,0 +1,384 @@ | |||
1 | % Copyright (c) 2020 Frank McSherry | ||
2 | % Copyright (c) 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | % | ||
4 | % SPDX-License-Identifier: MIT AND EPL-2.0 | ||
5 | % | ||
6 | % The sudoku example in this file was adapted from Frank McSherry's | ||
7 | % differential-dataflow tutorial, which is available at | ||
8 | % https://www.youtube.com/watch?v=DR5V5bNpclg and | ||
9 | % https://github.com/frankmcsherry/WIP/tree/master/sudoku | ||
10 | |||
11 | class Row { | ||
12 | Field[9] field opposite row | ||
13 | } | ||
14 | |||
15 | class Column { | ||
16 | Field[9] field opposite column | ||
17 | } | ||
18 | |||
19 | class Block { | ||
20 | Field[9] field opposite block | ||
21 | } | ||
22 | |||
23 | class Field { | ||
24 | Row[1] row opposite field | ||
25 | Column[1] column opposite field | ||
26 | Block[1] block opposite field | ||
27 | Number[1] number | ||
28 | } | ||
29 | |||
30 | class Number. | ||
31 | |||
32 | error multipleInRow(r, n) <-> | ||
33 | row(f1, r), | ||
34 | row(f2, r), | ||
35 | f1 != f2, | ||
36 | number(f1, n), | ||
37 | number(f2, n). | ||
38 | |||
39 | propagation rule singleInRow(f, n) <-> | ||
40 | may number(f, n), | ||
41 | row(f, r), | ||
42 | !multipleInRow::computed(r, n) | ||
43 | ==> | ||
44 | number(f, n). | ||
45 | |||
46 | propagation rule noMoreInRow(f, n) <-> | ||
47 | row(f, r), | ||
48 | number(f2, n), | ||
49 | row(f2, r), | ||
50 | f != f2 | ||
51 | ==> | ||
52 | !number(f, n). | ||
53 | |||
54 | error multipleInColumn(c, n) <-> | ||
55 | column(f1, c), | ||
56 | column(f2, c), | ||
57 | f1 != f2, | ||
58 | number(f1, n), | ||
59 | number(f2, n). | ||
60 | |||
61 | propagation rule singleInColumn(f, n) <-> | ||
62 | may number(f, n), | ||
63 | column(n, c), | ||
64 | !multipleInColumn::computed(c, n) | ||
65 | ==> | ||
66 | number(f, n). | ||
67 | |||
68 | propagation rule noMoreInColumn(f, n) <-> | ||
69 | column(f, c), | ||
70 | number(f2, n), | ||
71 | column(f2, c), | ||
72 | f != f2 | ||
73 | ==> | ||
74 | !number(f, n). | ||
75 | |||
76 | error multipleInBlock(b, n) <-> | ||
77 | block(f1, b), | ||
78 | block(f2, b), | ||
79 | f1 != f2, | ||
80 | number(f1, n), | ||
81 | number(f2, n). | ||
82 | |||
83 | propagation rule singleInBlock(f, n) <-> | ||
84 | may number(f, n), | ||
85 | block(f, b), | ||
86 | !multipleInBlock::computed(b, n) | ||
87 | ==> | ||
88 | number(f, n). | ||
89 | |||
90 | propagation rule noMoreInBlock(f, n) <-> | ||
91 | block(f, b), | ||
92 | number(f2, n), | ||
93 | block(f2, b), | ||
94 | f != f2 | ||
95 | ==> | ||
96 | !number(f, n). | ||
97 | |||
98 | Number(n1). | ||
99 | Number(n2). | ||
100 | Number(n3). | ||
101 | Number(n4). | ||
102 | Number(n5). | ||
103 | Number(n6). | ||
104 | Number(n7). | ||
105 | Number(n8). | ||
106 | Number(n9). | ||
107 | |||
108 | row(f1_1, r1). column(f1_1, c1). block(f1_1, b1_1). | ||
109 | row(f1_2, r1). column(f1_2, c2). block(f1_2, b1_1). | ||
110 | row(f1_3, r1). column(f1_3, c3). block(f1_3, b1_1). | ||
111 | row(f1_4, r1). column(f1_4, c4). block(f1_4, b1_2). | ||
112 | row(f1_5, r1). column(f1_5, c5). block(f1_5, b1_2). | ||
113 | row(f1_6, r1). column(f1_6, c6). block(f1_6, b1_2). | ||
114 | row(f1_7, r1). column(f1_7, c7). block(f1_7, b1_3). | ||
115 | row(f1_8, r1). column(f1_8, c8). block(f1_8, b1_3). | ||
116 | row(f1_9, r1). column(f1_9, c9). block(f1_9, b1_3). | ||
117 | row(f2_1, r2). column(f2_1, c1). block(f2_1, b1_1). | ||
118 | row(f2_2, r2). column(f2_2, c2). block(f2_2, b1_1). | ||
119 | row(f2_3, r2). column(f2_3, c3). block(f2_3, b1_1). | ||
120 | row(f2_4, r2). column(f2_4, c4). block(f2_4, b1_2). | ||
121 | row(f2_5, r2). column(f2_5, c5). block(f2_5, b1_2). | ||
122 | row(f2_6, r2). column(f2_6, c6). block(f2_6, b1_2). | ||
123 | row(f2_7, r2). column(f2_7, c7). block(f2_7, b1_3). | ||
124 | row(f2_8, r2). column(f2_8, c8). block(f2_8, b1_3). | ||
125 | row(f2_9, r2). column(f2_9, c9). block(f2_9, b1_3). | ||
126 | row(f3_1, r3). column(f3_1, c1). block(f3_1, b1_1). | ||
127 | row(f3_2, r3). column(f3_2, c2). block(f3_2, b1_1). | ||
128 | row(f3_3, r3). column(f3_3, c3). block(f3_3, b1_1). | ||
129 | row(f3_4, r3). column(f3_4, c4). block(f3_4, b1_2). | ||
130 | row(f3_5, r3). column(f3_5, c5). block(f3_5, b1_2). | ||
131 | row(f3_6, r3). column(f3_6, c6). block(f3_6, b1_2). | ||
132 | row(f3_7, r3). column(f3_7, c7). block(f3_7, b1_3). | ||
133 | row(f3_8, r3). column(f3_8, c8). block(f3_8, b1_3). | ||
134 | row(f3_9, r3). column(f3_9, c9). block(f3_9, b1_3). | ||
135 | row(f4_1, r4). column(f4_1, c1). block(f4_1, b2_1). | ||
136 | row(f4_2, r4). column(f4_2, c2). block(f4_2, b2_1). | ||
137 | row(f4_3, r4). column(f4_3, c3). block(f4_3, b2_1). | ||
138 | row(f4_4, r4). column(f4_4, c4). block(f4_4, b2_2). | ||
139 | row(f4_5, r4). column(f4_5, c5). block(f4_5, b2_2). | ||
140 | row(f4_6, r4). column(f4_6, c6). block(f4_6, b2_2). | ||
141 | row(f4_7, r4). column(f4_7, c7). block(f4_7, b2_3). | ||
142 | row(f4_8, r4). column(f4_8, c8). block(f4_8, b2_3). | ||
143 | row(f4_9, r4). column(f4_9, c9). block(f4_9, b2_3). | ||
144 | row(f5_1, r5). column(f5_1, c1). block(f5_1, b2_1). | ||
145 | row(f5_2, r5). column(f5_2, c2). block(f5_2, b2_1). | ||
146 | row(f5_3, r5). column(f5_3, c3). block(f5_3, b2_1). | ||
147 | row(f5_4, r5). column(f5_4, c4). block(f5_4, b2_2). | ||
148 | row(f5_5, r5). column(f5_5, c5). block(f5_5, b2_2). | ||
149 | row(f5_6, r5). column(f5_6, c6). block(f5_6, b2_2). | ||
150 | row(f5_7, r5). column(f5_7, c7). block(f5_7, b2_3). | ||
151 | row(f5_8, r5). column(f5_8, c8). block(f5_8, b2_3). | ||
152 | row(f5_9, r5). column(f5_9, c9). block(f5_9, b2_3). | ||
153 | row(f6_1, r6). column(f6_1, c1). block(f6_1, b2_1). | ||
154 | row(f6_2, r6). column(f6_2, c2). block(f6_2, b2_1). | ||
155 | row(f6_3, r6). column(f6_3, c3). block(f6_3, b2_1). | ||
156 | row(f6_4, r6). column(f6_4, c4). block(f6_4, b2_2). | ||
157 | row(f6_5, r6). column(f6_5, c5). block(f6_5, b2_2). | ||
158 | row(f6_6, r6). column(f6_6, c6). block(f6_6, b2_2). | ||
159 | row(f6_7, r6). column(f6_7, c7). block(f6_7, b2_3). | ||
160 | row(f6_8, r6). column(f6_8, c8). block(f6_8, b2_3). | ||
161 | row(f6_9, r6). column(f6_9, c9). block(f6_9, b2_3). | ||
162 | row(f7_1, r7). column(f7_1, c1). block(f7_1, b3_1). | ||
163 | row(f7_2, r7). column(f7_2, c2). block(f7_2, b3_1). | ||
164 | row(f7_3, r7). column(f7_3, c3). block(f7_3, b3_1). | ||
165 | row(f7_4, r7). column(f7_4, c4). block(f7_4, b3_2). | ||
166 | row(f7_5, r7). column(f7_5, c5). block(f7_5, b3_2). | ||
167 | row(f7_6, r7). column(f7_6, c6). block(f7_6, b3_2). | ||
168 | row(f7_7, r7). column(f7_7, c7). block(f7_7, b3_3). | ||
169 | row(f7_8, r7). column(f7_8, c8). block(f7_8, b3_3). | ||
170 | row(f7_9, r7). column(f7_9, c9). block(f7_9, b3_3). | ||
171 | row(f8_1, r8). column(f8_1, c1). block(f8_1, b3_1). | ||
172 | row(f8_2, r8). column(f8_2, c2). block(f8_2, b3_1). | ||
173 | row(f8_3, r8). column(f8_3, c3). block(f8_3, b3_1). | ||
174 | row(f8_4, r8). column(f8_4, c4). block(f8_4, b3_2). | ||
175 | row(f8_5, r8). column(f8_5, c5). block(f8_5, b3_2). | ||
176 | row(f8_6, r8). column(f8_6, c6). block(f8_6, b3_2). | ||
177 | row(f8_7, r8). column(f8_7, c7). block(f8_7, b3_3). | ||
178 | row(f8_8, r8). column(f8_8, c8). block(f8_8, b3_3). | ||
179 | row(f8_9, r8). column(f8_9, c9). block(f8_9, b3_3). | ||
180 | row(f9_1, r9). column(f9_1, c1). block(f9_1, b3_1). | ||
181 | row(f9_2, r9). column(f9_2, c2). block(f9_2, b3_1). | ||
182 | row(f9_3, r9). column(f9_3, c3). block(f9_3, b3_1). | ||
183 | row(f9_4, r9). column(f9_4, c4). block(f9_4, b3_2). | ||
184 | row(f9_5, r9). column(f9_5, c5). block(f9_5, b3_2). | ||
185 | row(f9_6, r9). column(f9_6, c6). block(f9_6, b3_2). | ||
186 | row(f9_7, r9). column(f9_7, c7). block(f9_7, b3_3). | ||
187 | row(f9_8, r9). column(f9_8, c8). block(f9_8, b3_3). | ||
188 | row(f9_9, r9). column(f9_9, c9). block(f9_9, b3_3). | ||
189 | |||
190 | scope Field += 0, Number += 0. | ||
191 | |||
192 | % TEST: initial grid is not filled | ||
193 | |||
194 | pred filled() <-> number(_, _). | ||
195 | |||
196 | % EXPECT EXACTLY: | ||
197 | |||
198 | ?filled(). | ||
199 | |||
200 | % TEST: sudoku with single solution | ||
201 | |||
202 | % Adapted from Frank McSherry's differential-dataflow tutorial, | ||
203 | % which is available at https://www.youtube.com/watch?v=DR5V5bNpclg and | ||
204 | % https://github.com/frankmcsherry/WIP/tree/master/sudoku | ||
205 | % | ||
206 | % 53.|.7.|... | ||
207 | % 6..|195|... | ||
208 | % .98|...|.6. | ||
209 | % ---+---+--- | ||
210 | % 8..|.6.|..3 | ||
211 | % 4..|8.3|..1 | ||
212 | % 7..|.2.|..6 | ||
213 | % ---+---+--- | ||
214 | % .6.|...|28. | ||
215 | % ...|419|..5 | ||
216 | % ...|.8.|.79 | ||
217 | |||
218 | number(f1_1, n5). | ||
219 | number(f1_2, n3). | ||
220 | number(f1_5, n7). | ||
221 | number(f2_1, n6). | ||
222 | number(f2_4, n1). | ||
223 | number(f2_5, n9). | ||
224 | number(f2_6, n5). | ||
225 | number(f3_2, n9). | ||
226 | number(f3_3, n8). | ||
227 | number(f3_8, n6). | ||
228 | number(f4_1, n8). | ||
229 | number(f4_5, n6). | ||
230 | number(f4_9, n3). | ||
231 | number(f5_1, n4). | ||
232 | number(f5_4, n8). | ||
233 | number(f5_6, n3). | ||
234 | number(f5_9, n1). | ||
235 | number(f6_1, n7). | ||
236 | number(f6_5, n2). | ||
237 | number(f6_9, n6). | ||
238 | number(f7_2, n6). | ||
239 | number(f7_7, n2). | ||
240 | number(f7_8, n8). | ||
241 | number(f8_4, n4). | ||
242 | number(f8_5, n1). | ||
243 | number(f8_6, n9). | ||
244 | number(f8_9, n5). | ||
245 | number(f9_5, n8). | ||
246 | number(f9_8, n7). | ||
247 | number(f9_9, n9). | ||
248 | |||
249 | % EXPECT: | ||
250 | |||
251 | % 534|678|912 | ||
252 | % 672|195|348 | ||
253 | % 198|342|567 | ||
254 | % ---+---+--- | ||
255 | % 859|761|423 | ||
256 | % 426|853|791 | ||
257 | % 713|924|856 | ||
258 | % ---+---+--- | ||
259 | % 961|537|284 | ||
260 | % 287|419|635 | ||
261 | % 345|286|179 | ||
262 | |||
263 | number(f1_1, n5). | ||
264 | number(f1_2, n3). | ||
265 | number(f1_3, n4). | ||
266 | number(f1_4, n6). | ||
267 | number(f1_5, n7). | ||
268 | number(f1_6, n8). | ||
269 | number(f1_7, n9). | ||
270 | number(f1_8, n1). | ||
271 | number(f1_9, n2). | ||
272 | number(f2_1, n6). | ||
273 | number(f2_2, n7). | ||
274 | number(f2_3, n2). | ||
275 | number(f2_4, n1). | ||
276 | number(f2_5, n9). | ||
277 | number(f2_6, n5). | ||
278 | number(f2_7, n3). | ||
279 | number(f2_8, n4). | ||
280 | number(f2_9, n8). | ||
281 | number(f3_1, n1). | ||
282 | number(f3_2, n9). | ||
283 | number(f3_3, n8). | ||
284 | number(f3_4, n3). | ||
285 | number(f3_5, n4). | ||
286 | number(f3_6, n2). | ||
287 | number(f3_7, n5). | ||
288 | number(f3_8, n6). | ||
289 | number(f3_9, n7). | ||
290 | number(f4_1, n8). | ||
291 | number(f4_2, n5). | ||
292 | number(f4_3, n9). | ||
293 | number(f4_4, n7). | ||
294 | number(f4_5, n6). | ||
295 | number(f4_6, n1). | ||
296 | number(f4_7, n4). | ||
297 | number(f4_8, n2). | ||
298 | number(f4_9, n3). | ||
299 | number(f5_1, n4). | ||
300 | number(f5_2, n2). | ||
301 | number(f5_3, n6). | ||
302 | number(f5_4, n8). | ||
303 | number(f5_5, n5). | ||
304 | number(f5_6, n3). | ||
305 | number(f5_7, n7). | ||
306 | number(f5_8, n9). | ||
307 | number(f5_9, n1). | ||
308 | number(f6_1, n7). | ||
309 | number(f6_2, n1). | ||
310 | number(f6_3, n3). | ||
311 | number(f6_4, n9). | ||
312 | number(f6_5, n2). | ||
313 | number(f6_6, n4). | ||
314 | number(f6_7, n8). | ||
315 | number(f6_8, n5). | ||
316 | number(f6_9, n6). | ||
317 | number(f7_1, n9). | ||
318 | number(f7_2, n6). | ||
319 | number(f7_3, n1). | ||
320 | number(f7_4, n5). | ||
321 | number(f7_5, n3). | ||
322 | number(f7_6, n7). | ||
323 | number(f7_7, n2). | ||
324 | number(f7_8, n8). | ||
325 | number(f7_9, n4). | ||
326 | number(f8_1, n2). | ||
327 | number(f8_2, n8). | ||
328 | number(f8_3, n7). | ||
329 | number(f8_4, n4). | ||
330 | number(f8_5, n1). | ||
331 | number(f8_6, n9). | ||
332 | number(f8_7, n6). | ||
333 | number(f8_8, n3). | ||
334 | number(f8_9, n5). | ||
335 | number(f9_1, n3). | ||
336 | number(f9_2, n4). | ||
337 | number(f9_3, n5). | ||
338 | number(f9_4, n2). | ||
339 | number(f9_5, n8). | ||
340 | number(f9_6, n6). | ||
341 | number(f9_7, n1). | ||
342 | number(f9_8, n7). | ||
343 | number(f9_9, n9). | ||
344 | |||
345 | % TEST: sudoku with multiple solutions | ||
346 | |||
347 | pred notFilled(Field f) <-> !number(f, _). | ||
348 | |||
349 | pred allFilled() <-> !notFilled(_). | ||
350 | |||
351 | number(f1_1, n5). | ||
352 | number(f1_2, n3). | ||
353 | number(f1_5, n7). | ||
354 | number(f2_1, n6). | ||
355 | number(f2_4, n1). | ||
356 | number(f2_5, n9). | ||
357 | number(f2_6, n5). | ||
358 | number(f3_2, n9). | ||
359 | number(f3_3, n8). | ||
360 | number(f3_8, n6). | ||
361 | number(f4_1, n8). | ||
362 | number(f4_5, n6). | ||
363 | number(f4_9, n3). | ||
364 | number(f5_1, n4). | ||
365 | number(f5_4, n8). | ||
366 | number(f5_6, n3). | ||
367 | number(f5_9, n1). | ||
368 | number(f6_1, n7). | ||
369 | number(f6_5, n2). | ||
370 | number(f6_9, n6). | ||
371 | number(f7_2, n6). | ||
372 | % number(f7_7, n2). | ||
373 | number(f7_8, n8). | ||
374 | number(f8_4, n4). | ||
375 | number(f8_5, n1). | ||
376 | number(f8_6, n9). | ||
377 | number(f8_9, n5). | ||
378 | number(f9_5, n8). | ||
379 | number(f9_8, n7). | ||
380 | number(f9_9, n9). | ||
381 | |||
382 | % EXPECT EXACTLY: | ||
383 | |||
384 | ?allFilled(). | ||
diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/typehierarchy/abstract.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/typehierarchy/abstract.problem new file mode 100644 index 00000000..559c077d --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/typehierarchy/abstract.problem | |||
@@ -0,0 +1,47 @@ | |||
1 | % SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | % | ||
3 | % SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | abstract class A. | ||
6 | |||
7 | class B extends A. | ||
8 | |||
9 | class C extends A. | ||
10 | |||
11 | class D extends A. | ||
12 | |||
13 | % TEST: negative subclasses | ||
14 | !B(a). | ||
15 | !C(a). | ||
16 | !D(a). | ||
17 | % EXPECT: | ||
18 | !A(a). | ||
19 | |||
20 | % TEST: infer subclass by exclusion | ||
21 | A(a). | ||
22 | !B(a). | ||
23 | !C(a). | ||
24 | % EXPECT: | ||
25 | D(a). | ||
26 | |||
27 | % TEST: candidate rounding | ||
28 | A(a). | ||
29 | % EXPECT EXACTLY: | ||
30 | ?B(a). | ||
31 | ?C(a). | ||
32 | ?D(a). | ||
33 | % EXPECT CANDIDATE: | ||
34 | B(a). | ||
35 | !C(a). | ||
36 | !D(a). | ||
37 | |||
38 | % TEST: candidate rounding with exclusion | ||
39 | A(a). | ||
40 | !B(a). | ||
41 | % EXPECT EXACTLY: | ||
42 | ?C(a). | ||
43 | ?D(a). | ||
44 | % EXPECT CANDIDATE: | ||
45 | !B(a). | ||
46 | C(a). | ||
47 | !D(a). | ||
diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/typehierarchy/basic.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/typehierarchy/basic.problem new file mode 100644 index 00000000..2d297067 --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/typehierarchy/basic.problem | |||
@@ -0,0 +1,24 @@ | |||
1 | % SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | % | ||
3 | % SPDX-License-Identifier: EPL-2.0 | ||
4 | |||
5 | class A. | ||
6 | |||
7 | class B extends A. | ||
8 | |||
9 | % TEST: positive subclass | ||
10 | B(b). | ||
11 | % EXPECT: | ||
12 | A(b). | ||
13 | |||
14 | % TEST: negative superclass | ||
15 | !A(b). | ||
16 | % EXPECT: | ||
17 | !B(b). | ||
18 | |||
19 | % TEST WITH ERRORS: inconsistent type | ||
20 | !A(b). | ||
21 | B(b). | ||
22 | % EXPECT: | ||
23 | A(b): error. | ||
24 | B(b): error. | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java new file mode 100644 index 00000000..001ee3a4 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import org.junit.jupiter.api.DynamicNode; | ||
11 | import org.junit.jupiter.api.function.Executable; | ||
12 | import org.opentest4j.TestAbortedException; | ||
13 | import tools.refinery.generator.ModelSemanticsFactory; | ||
14 | |||
15 | import java.io.IOException; | ||
16 | import java.net.URI; | ||
17 | import java.net.URISyntaxException; | ||
18 | import java.net.URL; | ||
19 | import java.nio.file.FileSystemNotFoundException; | ||
20 | import java.nio.file.FileSystems; | ||
21 | import java.nio.file.Files; | ||
22 | import java.nio.file.Path; | ||
23 | import java.util.ArrayList; | ||
24 | import java.util.Enumeration; | ||
25 | import java.util.List; | ||
26 | import java.util.stream.Stream; | ||
27 | |||
28 | import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; | ||
29 | import static org.junit.jupiter.api.DynamicTest.dynamicTest; | ||
30 | |||
31 | public class DynamicTestLoader { | ||
32 | private static final String TEST_SUFFIX = ".problem"; | ||
33 | |||
34 | @Inject | ||
35 | private Provider<SemanticsTestLoader> testLoaderProvider; | ||
36 | |||
37 | private Provider<ModelSemanticsFactory> semanticsFactoryProvider; | ||
38 | |||
39 | @Inject | ||
40 | public void setSemanticsFactoryProvider(Provider<ModelSemanticsFactory> semanticsFactoryProvider) { | ||
41 | this.semanticsFactoryProvider = semanticsFactoryProvider; | ||
42 | } | ||
43 | |||
44 | public Stream<DynamicNode> allFromClasspath(Class<?> contextClass) { | ||
45 | var paths = getExtraPaths(contextClass); | ||
46 | if (paths.isEmpty()) { | ||
47 | throw new IllegalArgumentException("No file system paths found for class: " + contextClass); | ||
48 | } | ||
49 | var loader = getTestLoader(paths); | ||
50 | var nodes = new ArrayList<DynamicNode>(); | ||
51 | for (var path : paths) { | ||
52 | List<DynamicNode> childNodes; | ||
53 | try { | ||
54 | childNodes = nodesFromPath(loader, path); | ||
55 | } catch (IOException e) { | ||
56 | throw new IllegalArgumentException("Failed to enumerate path: " + path, e); | ||
57 | } | ||
58 | nodes.addAll(childNodes); | ||
59 | } | ||
60 | if (nodes.isEmpty()) { | ||
61 | throw new TestAbortedException("No tests found for class: " + contextClass); | ||
62 | } | ||
63 | return nodes.stream(); | ||
64 | } | ||
65 | |||
66 | private List<DynamicNode> nodesFromPath(SemanticsTestLoader loader, Path path) throws IOException { | ||
67 | var nodes = new ArrayList<DynamicNode>(); | ||
68 | try (var childList = Files.list(path)) { | ||
69 | var iterator = childList.iterator(); | ||
70 | while (iterator.hasNext()) { | ||
71 | var childPath = iterator.next(); | ||
72 | var fileName = childPath.getFileName().toString(); | ||
73 | var uri = childPath.toUri(); | ||
74 | try { | ||
75 | if (Files.isDirectory(childPath)) { | ||
76 | var childNodes = nodesFromPath(loader, childPath); | ||
77 | if (!childNodes.isEmpty()) { | ||
78 | nodes.add(dynamicContainer(fileName, uri, childNodes.stream())); | ||
79 | } | ||
80 | } else if (fileName.endsWith(TEST_SUFFIX)) { | ||
81 | SemanticsTest test = loader.loadFile(childPath.toFile()); | ||
82 | if (test != null) { | ||
83 | nodes.add(createDynamicNode(fileName, uri, test)); | ||
84 | } | ||
85 | } | ||
86 | } catch (IOException | RuntimeException e) { | ||
87 | nodes.add(createErrorNode(fileName, uri, e)); | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | return nodes; | ||
92 | } | ||
93 | |||
94 | public Stream<DynamicNode> fromClasspath(Class<?> contextClass, String name) { | ||
95 | var loader = getTestLoader(contextClass); | ||
96 | URL url; | ||
97 | URI uri; | ||
98 | try { | ||
99 | url = safeGetResource(contextClass, name); | ||
100 | uri = url.toURI(); | ||
101 | } catch (RuntimeException | URISyntaxException e) { | ||
102 | return Stream.of(createErrorNode(name, null, e)); | ||
103 | } | ||
104 | SemanticsTest test; | ||
105 | try { | ||
106 | test = loadFromUrl(loader, uri, url); | ||
107 | } catch (IOException | RuntimeException e) { | ||
108 | return Stream.of(createErrorNode(name, uri, e)); | ||
109 | } | ||
110 | return createDynamicNodes(uri, test); | ||
111 | } | ||
112 | |||
113 | public Stream<DynamicNode> fromClasspath(Class<?> contextClass, String... names) { | ||
114 | return fromClasspath(contextClass, Stream.of(names)); | ||
115 | } | ||
116 | |||
117 | public Stream<DynamicNode> fromClasspath(Class<?> contextClass, Stream<String> names) { | ||
118 | var loader = getTestLoader(contextClass); | ||
119 | return names.map(name -> nodeFromClasspath(loader, contextClass, name)); | ||
120 | } | ||
121 | |||
122 | private DynamicNode nodeFromClasspath(SemanticsTestLoader loader, Class<?> contextClass, String name) { | ||
123 | URL url; | ||
124 | URI uri; | ||
125 | try { | ||
126 | url = safeGetResource(contextClass, name); | ||
127 | uri = url.toURI(); | ||
128 | } catch (RuntimeException | URISyntaxException e) { | ||
129 | return createErrorNode(name, null, e); | ||
130 | } | ||
131 | SemanticsTest test; | ||
132 | try { | ||
133 | test = loadFromUrl(loader, uri, url); | ||
134 | } catch (IOException | RuntimeException e) { | ||
135 | return createErrorNode(name, uri, e); | ||
136 | } | ||
137 | return createDynamicNode(name, uri, test); | ||
138 | } | ||
139 | |||
140 | private static URL safeGetResource(Class<?> contextClass, String name) { | ||
141 | var url = contextClass.getResource(name); | ||
142 | if (url == null) { | ||
143 | throw new IllegalStateException("Test resource %s was not found.".formatted(name)); | ||
144 | } | ||
145 | return url; | ||
146 | } | ||
147 | |||
148 | private SemanticsTest loadFromUrl(SemanticsTestLoader testLoader, URI uri, URL url) throws IOException { | ||
149 | var eclipseUri = org.eclipse.emf.common.util.URI.createURI(uri.toString()); | ||
150 | try (var inputStream = url.openStream()) { | ||
151 | return testLoader.loadStream(inputStream, eclipseUri); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | public Stream<DynamicNode> fromString(Class<?> contextClass, String problem) { | ||
156 | var testLoader = getTestLoader(contextClass); | ||
157 | SemanticsTest test; | ||
158 | try { | ||
159 | test = testLoader.loadString(problem); | ||
160 | } catch (RuntimeException e) { | ||
161 | return Stream.of(createErrorNode("<string>", null, e)); | ||
162 | } | ||
163 | return createDynamicNodes(null, test); | ||
164 | } | ||
165 | |||
166 | public Stream<DynamicNode> fromString(String problem) { | ||
167 | return fromString(null, problem); | ||
168 | } | ||
169 | |||
170 | private DynamicNode createDynamicNode(String name, URI uri, SemanticsTest test) { | ||
171 | var testCases = test.testCases(); | ||
172 | if (testCases.size() == 1 && testCases.getFirst().name() == null) { | ||
173 | var testCase = testCases.getFirst(); | ||
174 | return dynamicTest(name, uri, asExecutable(testCase)); | ||
175 | } | ||
176 | var children = createDynamicNodes(uri, test); | ||
177 | return dynamicContainer(name, uri, children); | ||
178 | } | ||
179 | |||
180 | private Stream<DynamicNode> createDynamicNodes(URI uri, SemanticsTest test) { | ||
181 | var testCases = test.testCases(); | ||
182 | var children = new ArrayList<DynamicNode>(); | ||
183 | int testCaseCount = testCases.size(); | ||
184 | for (int i = 0; i < testCaseCount; i++) { | ||
185 | var testCase = testCases.get(i); | ||
186 | var testCaseName = testCase.name(); | ||
187 | if (testCaseName == null) { | ||
188 | testCaseName = "[%d]".formatted(i + 1); | ||
189 | } | ||
190 | children.add(dynamicTest(testCaseName, uri, asExecutable(testCase))); | ||
191 | } | ||
192 | return children.stream(); | ||
193 | } | ||
194 | |||
195 | private Executable asExecutable(SemanticsTestCase testCase) { | ||
196 | return () -> testCase.execute(semanticsFactoryProvider.get()); | ||
197 | } | ||
198 | |||
199 | private DynamicNode createErrorNode(String name, URI uri, Throwable cause) { | ||
200 | var messageBuilder = new StringBuilder(); | ||
201 | messageBuilder.append("Error while reading test '").append(name).append("'"); | ||
202 | if (uri != null) { | ||
203 | messageBuilder.append(" from ").append(uri); | ||
204 | } | ||
205 | if (cause != null) { | ||
206 | messageBuilder.append(": ").append(cause.getMessage()); | ||
207 | } | ||
208 | var message = messageBuilder.toString(); | ||
209 | return dynamicTest(name, uri, () -> { | ||
210 | throw new RuntimeException(message, cause); | ||
211 | }); | ||
212 | } | ||
213 | |||
214 | private SemanticsTestLoader getTestLoader(Class<?> contextClass) { | ||
215 | var extraPaths = getExtraPaths(contextClass); | ||
216 | return getTestLoader(extraPaths); | ||
217 | } | ||
218 | |||
219 | private SemanticsTestLoader getTestLoader(List<Path> extraPaths) { | ||
220 | var loader = testLoaderProvider.get(); | ||
221 | extraPaths.forEach(loader::extraPath); | ||
222 | return loader; | ||
223 | } | ||
224 | |||
225 | private List<Path> getExtraPaths(Class<?> contextClass) { | ||
226 | if (contextClass == null) { | ||
227 | return List.of(); | ||
228 | } | ||
229 | var resourceName = contextClass.getPackageName().replace('.', '/'); | ||
230 | Enumeration<URL> resources; | ||
231 | try { | ||
232 | resources = contextClass.getClassLoader().getResources(resourceName); | ||
233 | } catch (IOException e) { | ||
234 | // Failed to find any resources. | ||
235 | return List.of(); | ||
236 | } | ||
237 | var directories = new ArrayList<Path>(); | ||
238 | while (resources.hasMoreElements()) { | ||
239 | var url = resources.nextElement(); | ||
240 | var path = getPath(url); | ||
241 | if (path != null && path.getFileSystem() == FileSystems.getDefault()) { | ||
242 | directories.add(path); | ||
243 | } | ||
244 | } | ||
245 | return directories; | ||
246 | } | ||
247 | |||
248 | private static Path getPath(URL url) { | ||
249 | URI uri; | ||
250 | try { | ||
251 | uri = url.toURI(); | ||
252 | } catch (URISyntaxException e) { | ||
253 | return null; | ||
254 | } | ||
255 | Path path; | ||
256 | try { | ||
257 | path = Path.of(uri); | ||
258 | } catch (FileSystemNotFoundException e) { | ||
259 | return null; | ||
260 | } | ||
261 | return path.toAbsolutePath(); | ||
262 | } | ||
263 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsExpectation.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsExpectation.java new file mode 100644 index 00000000..5cc28cd9 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsExpectation.java | |||
@@ -0,0 +1,117 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import org.eclipse.core.runtime.AssertionFailedException; | ||
9 | import tools.refinery.generator.FilteredInterpretation; | ||
10 | import tools.refinery.generator.ModelSemantics; | ||
11 | import tools.refinery.language.model.problem.Assertion; | ||
12 | import tools.refinery.language.model.problem.Node; | ||
13 | import tools.refinery.language.model.problem.NodeAssertionArgument; | ||
14 | import tools.refinery.language.model.problem.WildcardAssertionArgument; | ||
15 | import tools.refinery.language.semantics.SemanticsUtils; | ||
16 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
17 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
18 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | ||
19 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
20 | import tools.refinery.store.tuple.Tuple; | ||
21 | |||
22 | public record SemanticsExpectation(Assertion assertion, Concreteness concreteness, boolean exact, | ||
23 | int lineNumber, String description, String source) { | ||
24 | public void execute(ModelSemantics semantics) { | ||
25 | var trace = semantics.getProblemTrace(); | ||
26 | var symbol = trace.getPartialRelation(assertion.getRelation()); | ||
27 | var reasoningAdapter = semantics.getModel().getAdapter(ReasoningAdapter.class); | ||
28 | var interpretation = reasoningAdapter.getPartialInterpretation(concreteness, symbol); | ||
29 | var existsInterpretation = reasoningAdapter.getPartialInterpretation(concreteness, | ||
30 | ReasoningAdapter.EXISTS_SYMBOL); | ||
31 | var filteredInterpretation = new FilteredInterpretation<>(interpretation, existsInterpretation); | ||
32 | |||
33 | var arguments = assertion.getArguments(); | ||
34 | int arity = arguments.size(); | ||
35 | var nodeIds = new int[arity]; | ||
36 | boolean wildcard = false; | ||
37 | for (int i = 0; i < arity; i++) { | ||
38 | var argument = arguments.get(i); | ||
39 | switch (argument) { | ||
40 | case NodeAssertionArgument nodeAssertionArgument -> { | ||
41 | var nodeOrVariable = nodeAssertionArgument.getNode(); | ||
42 | if (!(nodeOrVariable instanceof Node node)) { | ||
43 | throw new IllegalArgumentException("Invalid Node: " + nodeOrVariable); | ||
44 | } | ||
45 | nodeIds[i] = trace.getNodeId(node); | ||
46 | } | ||
47 | case WildcardAssertionArgument ignored -> { | ||
48 | nodeIds[i] = 1; | ||
49 | wildcard = true; | ||
50 | } | ||
51 | default -> throw new IllegalArgumentException("Invalid AssertionArgument: " + argument); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | var expectedValue = SemanticsUtils.getTruthValue(assertion.getValue()); | ||
56 | if (wildcard) { | ||
57 | checkWildcard(filteredInterpretation, nodeIds, expectedValue); | ||
58 | } else { | ||
59 | checkSingle(filteredInterpretation, nodeIds, expectedValue); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | |||
64 | private void checkSingle(PartialInterpretation<TruthValue, Boolean> interpretation, int[] nodeIds, | ||
65 | TruthValue expectedValue) { | ||
66 | var tuple = Tuple.of(nodeIds); | ||
67 | var actual = interpretation.get(tuple); | ||
68 | if (assertionFailed(expectedValue, actual)) { | ||
69 | throw new AssertionFailedException(getMessage(actual)); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | private void checkWildcard(PartialInterpretation<TruthValue, Boolean> interpretation, int[] nodeIds, | ||
74 | TruthValue expectedValue) { | ||
75 | int arity = nodeIds.length; | ||
76 | var cursor = interpretation.getAll(); | ||
77 | while (cursor.move()) { | ||
78 | var key = cursor.getKey(); | ||
79 | boolean matches = true; | ||
80 | for (int i = 0; matches && i < arity; i++) { | ||
81 | int nodeId = nodeIds[i]; | ||
82 | if (nodeId >= 0 && key.get(i) != nodeId) { | ||
83 | matches = false; | ||
84 | } | ||
85 | } | ||
86 | if (matches && assertionFailed(expectedValue, cursor.getValue())) { | ||
87 | throw new AssertionFailedException(getMessage(null)); | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | private boolean assertionFailed(TruthValue expectedValue, TruthValue actual) { | ||
93 | return !(exact ? actual.equals(expectedValue) : actual.isRefinementOf(expectedValue)); | ||
94 | } | ||
95 | |||
96 | private String getMessage(TruthValue actual) { | ||
97 | var messageBuilder = new StringBuilder(); | ||
98 | messageBuilder.append("EXPECT"); | ||
99 | if (concreteness == Concreteness.CANDIDATE) { | ||
100 | messageBuilder.append(" CANDIDATE"); | ||
101 | } | ||
102 | if (exact) { | ||
103 | messageBuilder.append(" EXACTLY"); | ||
104 | } | ||
105 | messageBuilder.append(" ").append(source); | ||
106 | if (description != null) { | ||
107 | messageBuilder.append(" (").append(description).append(")"); | ||
108 | } | ||
109 | if (actual == null) { | ||
110 | messageBuilder.append(" failed"); | ||
111 | } else { | ||
112 | messageBuilder.append(" was ").append(actual).append(" instead"); | ||
113 | } | ||
114 | messageBuilder.append(" in line ").append(lineNumber); | ||
115 | return messageBuilder.toString(); | ||
116 | } | ||
117 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTest.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTest.java new file mode 100644 index 00000000..6a34f6ef --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTest.java | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import tools.refinery.generator.ModelSemanticsFactory; | ||
9 | |||
10 | import java.util.List; | ||
11 | |||
12 | public record SemanticsTest(List<SemanticsTestCase> testCases) { | ||
13 | public void execute(ModelSemanticsFactory semanticsFactory) { | ||
14 | for (var testCase : testCases) { | ||
15 | testCase.execute(semanticsFactory); | ||
16 | } | ||
17 | } | ||
18 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestBuilder.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestBuilder.java new file mode 100644 index 00000000..2d40af5f --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestBuilder.java | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import org.eclipse.emf.common.util.URI; | ||
9 | import tools.refinery.generator.ProblemLoader; | ||
10 | import tools.refinery.generator.tests.internal.*; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | import java.util.regex.Pattern; | ||
16 | |||
17 | class SemanticsTestBuilder implements ChunkAcceptor { | ||
18 | private final Pattern LINE_PATTERN = Pattern.compile("^.+$", Pattern.MULTILINE); | ||
19 | |||
20 | private final ProblemLoader problemLoader; | ||
21 | private final URI uri; | ||
22 | private final StringBuilder commonBuilder = new StringBuilder(); | ||
23 | private final List<SemanticsTestCase> testCases = new ArrayList<>(); | ||
24 | private SemanticsTestCaseBuilder testCaseBuilder; | ||
25 | private boolean singleTestMode; | ||
26 | |||
27 | public SemanticsTestBuilder(ProblemLoader problemLoader, URI uri) { | ||
28 | this.problemLoader = problemLoader; | ||
29 | this.uri = uri; | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void acceptChunk(ChunkHeader header, String body) { | ||
34 | switch (header) { | ||
35 | case CommonHeader ignored -> { | ||
36 | if (testCaseBuilder != null) { | ||
37 | throw new IllegalStateException("Can't accept common test code after the first test case."); | ||
38 | } | ||
39 | commonBuilder.append(body); | ||
40 | } | ||
41 | case TestCaseHeader testCaseHeader -> { | ||
42 | if (singleTestMode) { | ||
43 | throw new IllegalStateException("Can't accept TEST chunk after an EXPECT chunk."); | ||
44 | } | ||
45 | acceptTestCase(testCaseHeader, body); | ||
46 | appendEmptyLines(body); | ||
47 | } | ||
48 | case ExpectationHeader expectationHeader -> { | ||
49 | if (testCaseBuilder == null) { | ||
50 | acceptTestCase(new TestCaseHeader(false, null), null); | ||
51 | singleTestMode = true; | ||
52 | } | ||
53 | testCaseBuilder.acceptExpectation(expectationHeader, body); | ||
54 | appendEmptyLines(body); | ||
55 | } | ||
56 | default -> throw new IllegalArgumentException("Unknown ChunkHeader: " + header); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | private void appendEmptyLines(String body) { | ||
61 | if (singleTestMode) { | ||
62 | return; | ||
63 | } | ||
64 | var placeholder = LINE_PATTERN.matcher(body).replaceAll(""); | ||
65 | commonBuilder.append(placeholder); | ||
66 | } | ||
67 | |||
68 | private void acceptTestCase(TestCaseHeader header, String body) { | ||
69 | if (testCaseBuilder != null) { | ||
70 | testCases.add(testCaseBuilder.build()); | ||
71 | } | ||
72 | var problemStringBuilder = new StringBuilder(commonBuilder); | ||
73 | if (body != null) { | ||
74 | problemStringBuilder.append(body); | ||
75 | } | ||
76 | testCaseBuilder = new SemanticsTestCaseBuilder(header, problemStringBuilder, problemLoader, uri); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public void acceptEnd() { | ||
81 | if (testCaseBuilder == null) { | ||
82 | throw new IllegalStateException("Test file contained no TEST or EXPECT chunks."); | ||
83 | } | ||
84 | testCases.add(testCaseBuilder.build()); | ||
85 | } | ||
86 | |||
87 | public SemanticsTest build() { | ||
88 | if (testCases.isEmpty()) { | ||
89 | throw new IllegalStateException("No test cases."); | ||
90 | } | ||
91 | return new SemanticsTest(Collections.unmodifiableList(testCases)); | ||
92 | } | ||
93 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java new file mode 100644 index 00000000..f75cac8e --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import org.eclipse.collections.api.factory.primitive.IntObjectMaps; | ||
9 | import org.eclipse.collections.api.map.primitive.IntObjectMap; | ||
10 | import org.eclipse.core.runtime.AssertionFailedException; | ||
11 | import tools.refinery.generator.FilteredInterpretation; | ||
12 | import tools.refinery.generator.ModelSemantics; | ||
13 | import tools.refinery.generator.ModelSemanticsFactory; | ||
14 | import tools.refinery.language.model.problem.Problem; | ||
15 | import tools.refinery.language.semantics.ProblemTrace; | ||
16 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
17 | import tools.refinery.store.map.Cursor; | ||
18 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
19 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
20 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
21 | import tools.refinery.store.tuple.Tuple; | ||
22 | |||
23 | import java.util.List; | ||
24 | |||
25 | public record SemanticsTestCase(String name, boolean allowErrors, Problem problem, | ||
26 | List<SemanticsExpectation> expectations) { | ||
27 | public void execute(ModelSemanticsFactory semanticsFactory) { | ||
28 | semanticsFactory.withCandidateInterpretations(needsCandidateInterpretations()); | ||
29 | var semantics = semanticsFactory.createSemantics(problem); | ||
30 | if (!allowErrors) { | ||
31 | checkNoErrors(semantics); | ||
32 | } | ||
33 | for (var expectation : expectations) { | ||
34 | expectation.execute(semantics); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | public boolean needsCandidateInterpretations() { | ||
39 | for (var expectation : expectations) { | ||
40 | if (expectation.concreteness() == Concreteness.CANDIDATE) { | ||
41 | return true; | ||
42 | } | ||
43 | } | ||
44 | return false; | ||
45 | } | ||
46 | |||
47 | private void checkNoErrors(ModelSemantics semantics) { | ||
48 | boolean hasError = false; | ||
49 | var errorsBuilder = new StringBuilder("Errors found in partial model:\n\n"); | ||
50 | var trace = semantics.getProblemTrace(); | ||
51 | IntObjectMap<String> nodeNames = null; | ||
52 | var existsInterpretation = semantics.getPartialInterpretation(ReasoningAdapter.EXISTS_SYMBOL); | ||
53 | for (var symbol : trace.getRelationTrace().values()) { | ||
54 | var interpretation = new FilteredInterpretation<>(semantics.getPartialInterpretation(symbol), | ||
55 | existsInterpretation); | ||
56 | var cursor = interpretation.getAll(); | ||
57 | while (cursor.move()) { | ||
58 | if (!cursor.getValue().isError()) { | ||
59 | continue; | ||
60 | } | ||
61 | hasError = true; | ||
62 | if (nodeNames == null) { | ||
63 | nodeNames = getNodeNames(trace); | ||
64 | } | ||
65 | appendError(symbol, errorsBuilder, cursor, nodeNames); | ||
66 | } | ||
67 | } | ||
68 | if (hasError) { | ||
69 | throw new AssertionFailedException(errorsBuilder.toString()); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | private IntObjectMap<String> getNodeNames(ProblemTrace trace) { | ||
74 | var nodeNames = IntObjectMaps.mutable.<String>empty(); | ||
75 | trace.getNodeTrace().forEachKeyValue((node, i) -> { | ||
76 | var name = node.getName(); | ||
77 | if (name != null) { | ||
78 | nodeNames.put(i, name); | ||
79 | } | ||
80 | }); | ||
81 | return nodeNames; | ||
82 | } | ||
83 | |||
84 | private static void appendError(PartialRelation symbol, StringBuilder errorsBuilder, | ||
85 | Cursor<Tuple, TruthValue> cursor, IntObjectMap<String> nodeNames) { | ||
86 | errorsBuilder.append('\t').append(symbol.name()).append("("); | ||
87 | var key = cursor.getKey(); | ||
88 | int arity = key.getSize(); | ||
89 | for (int i = 0; i < arity; i++) { | ||
90 | if (i > 0) { | ||
91 | errorsBuilder.append(", "); | ||
92 | } | ||
93 | int nodeId = key.get(i); | ||
94 | var name = nodeNames.get(nodeId); | ||
95 | if (name == null) { | ||
96 | errorsBuilder.append("::").append(i); | ||
97 | } else { | ||
98 | errorsBuilder.append(name); | ||
99 | } | ||
100 | } | ||
101 | errorsBuilder.append("): error.\n"); | ||
102 | } | ||
103 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCaseBuilder.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCaseBuilder.java new file mode 100644 index 00000000..cd163cb8 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCaseBuilder.java | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import org.eclipse.emf.common.util.URI; | ||
9 | import org.eclipse.xtext.nodemodel.INode; | ||
10 | import org.eclipse.xtext.nodemodel.util.NodeModelUtils; | ||
11 | import tools.refinery.generator.ProblemLoader; | ||
12 | import tools.refinery.generator.tests.internal.ExpectationHeader; | ||
13 | import tools.refinery.generator.tests.internal.TestCaseHeader; | ||
14 | import tools.refinery.language.model.problem.Assertion; | ||
15 | import tools.refinery.language.model.problem.Problem; | ||
16 | import tools.refinery.language.model.problem.Statement; | ||
17 | |||
18 | import java.io.IOException; | ||
19 | import java.util.ArrayDeque; | ||
20 | import java.util.ArrayList; | ||
21 | import java.util.Collections; | ||
22 | import java.util.Deque; | ||
23 | |||
24 | class SemanticsTestCaseBuilder { | ||
25 | private final TestCaseHeader testCaseHeader; | ||
26 | private final StringBuilder stringBuilder; | ||
27 | private final ProblemLoader problemLoader; | ||
28 | private final URI uri; | ||
29 | private final Deque<ExpectationHeader> expectationsDeque = new ArrayDeque<>(); | ||
30 | |||
31 | public SemanticsTestCaseBuilder(TestCaseHeader testCaseHeader, StringBuilder stringBuilder, | ||
32 | ProblemLoader problemLoader, URI uri) { | ||
33 | this.testCaseHeader = testCaseHeader; | ||
34 | this.stringBuilder = stringBuilder; | ||
35 | this.problemLoader = problemLoader; | ||
36 | this.uri = uri; | ||
37 | } | ||
38 | |||
39 | public void acceptExpectation(ExpectationHeader header, String body) { | ||
40 | stringBuilder.append(body); | ||
41 | expectationsDeque.addLast(header); | ||
42 | } | ||
43 | |||
44 | public SemanticsTestCase build() { | ||
45 | Problem problem; | ||
46 | try { | ||
47 | problem = problemLoader.loadString(stringBuilder.toString(), uri); | ||
48 | } catch (IOException e) { | ||
49 | throw new IllegalStateException("Failed to parse problem: " + uri, e); | ||
50 | } | ||
51 | if (expectationsDeque.isEmpty() && testCaseHeader.allowErrors()) { | ||
52 | throw new IllegalStateException("Test has no EXPECT chunks."); | ||
53 | } | ||
54 | var statements = problem.getStatements(); | ||
55 | int initialStatementCount = 0; | ||
56 | ExpectationHeader currentHeader = null; | ||
57 | var expectations = new ArrayList<SemanticsExpectation>(); | ||
58 | for (var statement : statements) { | ||
59 | var node = NodeModelUtils.findActualNodeFor(statement); | ||
60 | if (node == null) { | ||
61 | throw new IllegalStateException("No node for statement: " + statement); | ||
62 | } | ||
63 | var nextHeader = expectationsDeque.peekFirst(); | ||
64 | if (nextHeader != null && node.getStartLine() >= nextHeader.startLine()) { | ||
65 | currentHeader = nextHeader; | ||
66 | expectationsDeque.removeFirst(); | ||
67 | } | ||
68 | if (currentHeader == null) { | ||
69 | initialStatementCount++; | ||
70 | } else { | ||
71 | var expectation = createExpectation(currentHeader, statement, node); | ||
72 | expectations.add(expectation); | ||
73 | } | ||
74 | } | ||
75 | int statementCount = statements.size(); | ||
76 | if (statementCount > initialStatementCount) { | ||
77 | statements.subList(initialStatementCount, statementCount).clear(); | ||
78 | } | ||
79 | return new SemanticsTestCase(testCaseHeader.name(), testCaseHeader.allowErrors(), problem, | ||
80 | Collections.unmodifiableList(expectations)); | ||
81 | } | ||
82 | |||
83 | private static SemanticsExpectation createExpectation(ExpectationHeader header, Statement statement, | ||
84 | INode node) { | ||
85 | if (!(statement instanceof Assertion assertion)) { | ||
86 | throw new IllegalArgumentException("Only assertions are supported in EXPECT chunks, got %s instead." | ||
87 | .formatted(statement.eClass().getName())); | ||
88 | } | ||
89 | if (assertion.isDefault()) { | ||
90 | throw new IllegalArgumentException("Default assertions are not supported in EXPECT chunks."); | ||
91 | } | ||
92 | return new SemanticsExpectation(assertion, header.concreteness(), header.exact(), | ||
93 | node.getStartLine(), header.description(), NodeModelUtils.getTokenText(node).strip()); | ||
94 | } | ||
95 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestLoader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestLoader.java new file mode 100644 index 00000000..82c8deb1 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestLoader.java | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import org.eclipse.emf.common.util.URI; | ||
10 | import tools.refinery.generator.ProblemLoader; | ||
11 | import tools.refinery.generator.tests.internal.ProblemSplitter; | ||
12 | |||
13 | import java.io.*; | ||
14 | import java.nio.charset.StandardCharsets; | ||
15 | import java.nio.file.Path; | ||
16 | |||
17 | public class SemanticsTestLoader { | ||
18 | @Inject | ||
19 | private ProblemSplitter problemSplitter; | ||
20 | |||
21 | @Inject | ||
22 | private ProblemLoader problemLoader; | ||
23 | |||
24 | public SemanticsTestLoader extraPath(String path) { | ||
25 | problemLoader.extraPath(Path.of(path)); | ||
26 | return this; | ||
27 | } | ||
28 | |||
29 | public SemanticsTestLoader extraPath(Path path) { | ||
30 | problemLoader.extraPath(path); | ||
31 | return this; | ||
32 | } | ||
33 | |||
34 | public SemanticsTest loadString(String problemString, URI uri) { | ||
35 | var builder = new SemanticsTestBuilder(problemLoader, uri); | ||
36 | problemSplitter.transformProblem(problemString, builder); | ||
37 | return builder.build(); | ||
38 | } | ||
39 | |||
40 | public SemanticsTest loadString(String problemString) { | ||
41 | return loadString(problemString, null); | ||
42 | } | ||
43 | |||
44 | public SemanticsTest loadStream(InputStream inputStream, URI uri) throws IOException { | ||
45 | byte[] bytes; | ||
46 | try (var outputStream = new ByteArrayOutputStream()) { | ||
47 | inputStream.transferTo(outputStream); | ||
48 | bytes = outputStream.toByteArray(); | ||
49 | } | ||
50 | var problemString = new String(bytes, StandardCharsets.UTF_8); | ||
51 | return loadString(problemString, uri); | ||
52 | } | ||
53 | |||
54 | public SemanticsTest loadStream(InputStream inputStream) throws IOException { | ||
55 | return loadStream(inputStream, null); | ||
56 | } | ||
57 | |||
58 | public SemanticsTest loadFile(File file) throws IOException { | ||
59 | var uri = URI.createFileURI(file.getAbsolutePath()); | ||
60 | try (var inputStream = new FileInputStream(file)) { | ||
61 | return loadStream(inputStream, uri); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | public SemanticsTest loadFile(String filePath) throws IOException { | ||
66 | var uri = URI.createFileURI(filePath); | ||
67 | try (var inputStream = new FileInputStream(filePath)) { | ||
68 | return loadStream(inputStream, uri); | ||
69 | } | ||
70 | } | ||
71 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ChunkAcceptor.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ChunkAcceptor.java new file mode 100644 index 00000000..cf867b19 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ChunkAcceptor.java | |||
@@ -0,0 +1,12 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests.internal; | ||
7 | |||
8 | public interface ChunkAcceptor { | ||
9 | void acceptChunk(ChunkHeader header, String body); | ||
10 | |||
11 | void acceptEnd(); | ||
12 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ChunkHeader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ChunkHeader.java new file mode 100644 index 00000000..a69668ec --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ChunkHeader.java | |||
@@ -0,0 +1,9 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests.internal; | ||
7 | |||
8 | public sealed interface ChunkHeader permits CommonHeader, TestCaseHeader, ExpectationHeader { | ||
9 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/CommonHeader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/CommonHeader.java new file mode 100644 index 00000000..bec9e748 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/CommonHeader.java | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests.internal; | ||
7 | |||
8 | public final class CommonHeader implements ChunkHeader { | ||
9 | public static final CommonHeader INSTANCE = new CommonHeader(); | ||
10 | |||
11 | private CommonHeader() { | ||
12 | } | ||
13 | |||
14 | @Override | ||
15 | public String toString() { | ||
16 | return getClass().getSimpleName() + "[]"; | ||
17 | } | ||
18 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ExpectationHeader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ExpectationHeader.java new file mode 100644 index 00000000..00608739 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ExpectationHeader.java | |||
@@ -0,0 +1,12 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests.internal; | ||
7 | |||
8 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
9 | |||
10 | public record ExpectationHeader(Concreteness concreteness, boolean exact, String description, | ||
11 | int startLine) implements ChunkHeader { | ||
12 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ProblemSplitter.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ProblemSplitter.java new file mode 100644 index 00000000..33a0ca6e --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/ProblemSplitter.java | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests.internal; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Injector; | ||
10 | import com.google.inject.Provider; | ||
11 | import com.google.inject.Singleton; | ||
12 | import com.google.inject.name.Named; | ||
13 | import org.antlr.runtime.ANTLRStringStream; | ||
14 | import org.antlr.runtime.CommonToken; | ||
15 | import org.antlr.runtime.Token; | ||
16 | import org.antlr.runtime.TokenSource; | ||
17 | import org.eclipse.xtext.parser.antlr.Lexer; | ||
18 | import tools.refinery.language.parser.antlr.ProblemTokenSource; | ||
19 | import tools.refinery.language.parser.antlr.lexer.InternalProblemLexer; | ||
20 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
21 | |||
22 | import java.util.regex.Pattern; | ||
23 | |||
24 | @Singleton | ||
25 | public class ProblemSplitter { | ||
26 | private static final String COMMENT_PREFIX = "(//|%)\\s*"; | ||
27 | |||
28 | private static final Pattern TEST_CASE_PATTERN = Pattern.compile(COMMENT_PREFIX + | ||
29 | "TEST(?<allowErrors>\\s+WITH\\s+ERRORS)?(\\s*:\\s*(?<name>\\S.*)?)?"); | ||
30 | |||
31 | private static final Pattern EXPECTATION_PATTERN = Pattern.compile(COMMENT_PREFIX + | ||
32 | "EXPECT(?<candidate>\\s+CANDIDATE)?(?<exact>\\s+EXACTLY)?(\\s*:\\s*(?<description>\\S.*)?)?"); | ||
33 | |||
34 | @Inject | ||
35 | @Named("org.eclipse.xtext.parser.antlr.Lexer.RUNTIME") | ||
36 | private Provider<Lexer> lexerProvider; | ||
37 | |||
38 | @Inject | ||
39 | private Injector injector; | ||
40 | |||
41 | public void transformProblem(String problemString, ChunkAcceptor acceptor) { | ||
42 | var tokenSource = getTokenSource(problemString); | ||
43 | Token token = tokenSource.nextToken(); | ||
44 | ChunkHeader lastHeader = CommonHeader.INSTANCE; | ||
45 | int lastStartIndex = 0; | ||
46 | do { | ||
47 | if (token.getType() == InternalProblemLexer.RULE_SL_COMMENT) { | ||
48 | if (!(token instanceof CommonToken commonToken)) { | ||
49 | throw new IllegalStateException("Unexpected token: " + token); | ||
50 | } | ||
51 | var header = parseHeader(token); | ||
52 | if (header != null) { | ||
53 | int startIndex = commonToken.getStartIndex(); | ||
54 | var body = problemString.substring(lastStartIndex, startIndex); | ||
55 | acceptor.acceptChunk(lastHeader, body); | ||
56 | lastHeader = header; | ||
57 | lastStartIndex = startIndex; | ||
58 | } | ||
59 | } | ||
60 | token = tokenSource.nextToken(); | ||
61 | } while (token != null && token.getType() != Token.EOF); | ||
62 | acceptor.acceptChunk(lastHeader, problemString.substring(lastStartIndex)); | ||
63 | acceptor.acceptEnd(); | ||
64 | } | ||
65 | |||
66 | private TokenSource getTokenSource(String problemString) { | ||
67 | var charStream = new ANTLRStringStream(problemString); | ||
68 | var lexer = lexerProvider.get(); | ||
69 | lexer.setCharStream(charStream); | ||
70 | var tokenSource = new ProblemTokenSource(lexer); | ||
71 | injector.injectMembers(tokenSource); | ||
72 | return tokenSource; | ||
73 | } | ||
74 | |||
75 | private ChunkHeader parseHeader(Token token) { | ||
76 | var headerText = token.getText().strip(); | ||
77 | var testCaseMatcher = TEST_CASE_PATTERN.matcher(headerText); | ||
78 | if (testCaseMatcher.matches()) { | ||
79 | boolean allowErrors = testCaseMatcher.group("allowErrors") != null; | ||
80 | return new TestCaseHeader(allowErrors, testCaseMatcher.group("name")); | ||
81 | } | ||
82 | var expectationMatcher = EXPECTATION_PATTERN.matcher(headerText); | ||
83 | if (expectationMatcher.matches()) { | ||
84 | var concreteness = expectationMatcher.group("candidate") == null ? Concreteness.PARTIAL : | ||
85 | Concreteness.CANDIDATE; | ||
86 | var exact = expectationMatcher.group("exact") != null; | ||
87 | return new ExpectationHeader(concreteness, exact, expectationMatcher.group("description"), | ||
88 | token.getLine()); | ||
89 | } | ||
90 | return null; | ||
91 | } | ||
92 | } | ||
diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/TestCaseHeader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/TestCaseHeader.java new file mode 100644 index 00000000..505b4769 --- /dev/null +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/internal/TestCaseHeader.java | |||
@@ -0,0 +1,9 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator.tests.internal; | ||
7 | |||
8 | public record TestCaseHeader(boolean allowErrors, String name) implements ChunkHeader { | ||
9 | } | ||
diff --git a/subprojects/gradle-plugins/build.gradle.kts b/subprojects/gradle-plugins/build.gradle.kts new file mode 100644 index 00000000..0c87b67e --- /dev/null +++ b/subprojects/gradle-plugins/build.gradle.kts | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | plugins { | ||
8 | `java-gradle-plugin` | ||
9 | `maven-publish` | ||
10 | id("com.gradle.plugin-publish") | ||
11 | id("tools.refinery.gradle.java-conventions") | ||
12 | id("tools.refinery.gradle.signing") | ||
13 | } | ||
14 | |||
15 | val mavenRepositoryDir = rootProject.layout.buildDirectory.map { it.dir("repo") } | ||
16 | val generatedSourcesDir = project.layout.buildDirectory.dir("generated/sources/refineryVersion") | ||
17 | val generatedSourceFile = generatedSourcesDir.map { | ||
18 | it.file("tools/refinery/gradle/plugins/internal/Versions.java") | ||
19 | } | ||
20 | |||
21 | java { | ||
22 | setSourceCompatibility(11) | ||
23 | setTargetCompatibility(11) | ||
24 | } | ||
25 | |||
26 | sourceSets.main { | ||
27 | java.srcDir(generatedSourcesDir) | ||
28 | } | ||
29 | |||
30 | gradlePlugin { | ||
31 | website = "https://refinery.tools/" | ||
32 | vcsUrl = "https://github.com/graphs4value/refinery" | ||
33 | |||
34 | plugins { | ||
35 | create("settings") { | ||
36 | id = "tools.refinery.settings" | ||
37 | displayName = "Refinery settings plugin" | ||
38 | description = "Configures common build settings for projects using Refinery" | ||
39 | @Suppress("UnstableApiUsage") | ||
40 | tags = listOf("refinery", "settings", "conventions") | ||
41 | implementationClass = "tools.refinery.gradle.plugins.RefinerySettingsPlugin" | ||
42 | } | ||
43 | |||
44 | create("javaConventions") { | ||
45 | id = "tools.refinery.java" | ||
46 | displayName = "Refinery Java conventions plugin" | ||
47 | description = "Configures common Java settings for projects using Refinery" | ||
48 | @Suppress("UnstableApiUsage") | ||
49 | tags = listOf("refinery", "java", "conventions") | ||
50 | implementationClass = "tools.refinery.gradle.plugins.RefineryJavaPlugin" | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | abstract class GenerateVersionsFileTask : DefaultTask() { | ||
56 | @get:OutputFile | ||
57 | abstract val outputFile: RegularFileProperty | ||
58 | |||
59 | @get:Input | ||
60 | abstract val refineryVersion: Property<String> | ||
61 | |||
62 | @get:Input | ||
63 | abstract val useMavenLocal: Property<Boolean> | ||
64 | |||
65 | @get:Input | ||
66 | abstract val javaLanguageVersion: Property<Int> | ||
67 | |||
68 | @TaskAction | ||
69 | fun execute() { | ||
70 | val file = outputFile.asFile.get() | ||
71 | file.parentFile.mkdirs() | ||
72 | file.writeText( | ||
73 | """ | ||
74 | package tools.refinery.gradle.plugins.internal; | ||
75 | |||
76 | public final class Versions { | ||
77 | public static final String REFINERY_VERSION = "${refineryVersion.get()}"; | ||
78 | |||
79 | public static final boolean USE_MAVEN_LOCAL = ${useMavenLocal.get()}; | ||
80 | |||
81 | public static final int JAVA_LANGUAGE_VERSION = ${javaLanguageVersion.get()}; | ||
82 | |||
83 | private Versions() { | ||
84 | throw new IllegalStateException("This is a static utility class and should not be " + | ||
85 | "instantiated directly."); | ||
86 | } | ||
87 | } | ||
88 | """.trimIndent() | ||
89 | ) | ||
90 | } | ||
91 | } | ||
92 | |||
93 | val generateVersionsFile by tasks.registering(GenerateVersionsFileTask::class) { | ||
94 | outputs.dir(generatedSourcesDir) | ||
95 | outputFile = generatedSourceFile | ||
96 | refineryVersion = version.toString() | ||
97 | useMavenLocal = false | ||
98 | javaLanguageVersion = java.toolchain.languageVersion.map { it.asInt() } | ||
99 | } | ||
100 | |||
101 | gradle.taskGraph.whenReady { | ||
102 | generateVersionsFile.configure { | ||
103 | useMavenLocal = gradle.taskGraph.hasTask(":${project.name}:publishToMavenLocal") | ||
104 | } | ||
105 | } | ||
106 | |||
107 | tasks.compileJava { | ||
108 | dependsOn(generateVersionsFile) | ||
109 | } | ||
110 | |||
111 | tasks.sourcesJar { | ||
112 | dependsOn(generateVersionsFile) | ||
113 | } | ||
114 | |||
115 | publishing { | ||
116 | publications { | ||
117 | create<MavenPublication>("pluginMaven") { | ||
118 | pom { | ||
119 | name = "Refinery Gradle Plugins" | ||
120 | description = "Gradle convention plugins in Refinery, an efficient graph solver for generating " + | ||
121 | "well-formed models" | ||
122 | url = "https://refinery.tools/" | ||
123 | licenses { | ||
124 | license { | ||
125 | name = "Eclipse Public License - v 2.0" | ||
126 | url = "https://www.eclipse.org/legal/epl-2.0/" | ||
127 | } | ||
128 | } | ||
129 | developers { | ||
130 | developer { | ||
131 | name = "The Refinery Authors" | ||
132 | url = "https://refinery.tools/" | ||
133 | } | ||
134 | } | ||
135 | scm { | ||
136 | connection = "scm:git:https://github.com/graphs4value/refinery.git" | ||
137 | developerConnection = "scm:git:ssh://github.com:graphs4value/refinery.git" | ||
138 | url = "https://github.com/graphs4value/refinery" | ||
139 | } | ||
140 | issueManagement { | ||
141 | url = "https://github.com/graphs4value/refinery/issues" | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | repositories { | ||
148 | maven { | ||
149 | name = "file" | ||
150 | setUrl(mavenRepositoryDir.map { uri(it) }) | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | afterEvaluate { | ||
156 | for (publication in listOf("pluginMaven", "settingsPluginMarkerMaven", "javaConventionsPluginMarkerMaven")) { | ||
157 | val capitalizedName = publication.replaceFirstChar { it.uppercase() } | ||
158 | tasks.named("publish${capitalizedName}PublicationToFileRepository") { | ||
159 | mustRunAfter(rootProject.tasks.named("cleanMavenRepository")) | ||
160 | outputs.dir(mavenRepositoryDir) | ||
161 | } | ||
162 | } | ||
163 | } | ||
diff --git a/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefineryJavaExtension.java b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefineryJavaExtension.java new file mode 100644 index 00000000..79f37fbd --- /dev/null +++ b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefineryJavaExtension.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.gradle.plugins; | ||
7 | |||
8 | import org.gradle.api.provider.Property; | ||
9 | |||
10 | public abstract class RefineryJavaExtension { | ||
11 | public abstract Property<Boolean> getAddBundleSymbolicName(); | ||
12 | |||
13 | public abstract Property<Boolean> getDistTar(); | ||
14 | |||
15 | public abstract Property<Boolean> getDistZip(); | ||
16 | |||
17 | public abstract Property<Boolean> getEnforcePlatform(); | ||
18 | |||
19 | public abstract Property<String> getRefineryVersion(); | ||
20 | |||
21 | public abstract Property<TestDependencies> getTestDependencies(); | ||
22 | |||
23 | public abstract Property<Boolean> getUseSlf4JLog4J(); | ||
24 | |||
25 | public abstract Property<Boolean> getUseSlf4JSimple(); | ||
26 | } | ||
diff --git a/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefineryJavaPlugin.java b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefineryJavaPlugin.java new file mode 100644 index 00000000..39176707 --- /dev/null +++ b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefineryJavaPlugin.java | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.gradle.plugins; | ||
7 | |||
8 | import org.gradle.api.Plugin; | ||
9 | import org.gradle.api.Project; | ||
10 | import org.gradle.api.artifacts.Configuration; | ||
11 | import org.gradle.api.component.AdhocComponentWithVariants; | ||
12 | import org.gradle.api.component.ConfigurationVariantDetails; | ||
13 | import org.gradle.api.internal.tasks.JvmConstants; | ||
14 | import org.gradle.api.plugins.ApplicationPlugin; | ||
15 | import org.gradle.api.plugins.JavaPlugin; | ||
16 | import org.gradle.api.plugins.JavaPluginExtension; | ||
17 | import org.gradle.api.tasks.bundling.Tar; | ||
18 | import org.gradle.api.tasks.bundling.Zip; | ||
19 | import org.gradle.api.tasks.testing.Test; | ||
20 | import org.gradle.jvm.tasks.Jar; | ||
21 | import org.gradle.jvm.toolchain.JavaLanguageVersion; | ||
22 | import org.jetbrains.annotations.NotNull; | ||
23 | import tools.refinery.gradle.plugins.internal.RefineryPluginUtils; | ||
24 | import tools.refinery.gradle.plugins.internal.Versions; | ||
25 | |||
26 | import java.lang.reflect.InvocationTargetException; | ||
27 | import java.lang.reflect.Method; | ||
28 | import java.util.Map; | ||
29 | |||
30 | public class RefineryJavaPlugin implements Plugin<Project> { | ||
31 | private static final String JUNIT_API = "org.junit.jupiter:junit-jupiter-api"; | ||
32 | private static final String JUNIT_BOM = "org.junit:junit-bom"; | ||
33 | private static final String JUNIT_ENGINE = "org.junit.jupiter:junit-jupiter-engine"; | ||
34 | private static final String JUNIT_LAUNCHER = "org.junit.platform:junit-platform-launcher"; | ||
35 | private static final String JUNIT_PARAMS = "org.junit.jupiter:junit-jupiter-params"; | ||
36 | private static final String HAMCREST = "org.hamcrest:hamcrest"; | ||
37 | private static final String REFINERY_BOM = "tools.refinery:refinery-bom"; | ||
38 | private static final String SLF4J_LOG4J = "org.slf4j:log4j-over-slf4j"; | ||
39 | private static final String SLF4J_SIMPLE = "org.slf4j:slf4j-simple"; | ||
40 | |||
41 | @Override | ||
42 | public void apply(@NotNull Project target) { | ||
43 | target.getPluginManager().apply(JavaPlugin.class); | ||
44 | configureJavaLanguageVersion(target); | ||
45 | var extension = configureExtension(target); | ||
46 | configureDependencies(target, extension); | ||
47 | configureTasks(target, extension); | ||
48 | RefineryPluginUtils.withShadowPlugin(target, RefineryJavaPlugin::configureShadowPlugin); | ||
49 | target.afterEvaluate(RefineryJavaPlugin::configureAfterEvaluate); | ||
50 | } | ||
51 | |||
52 | private static void configureJavaLanguageVersion(Project target) { | ||
53 | var javaExtension = target.getExtensions().getByType(JavaPluginExtension.class); | ||
54 | javaExtension.getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(Versions.JAVA_LANGUAGE_VERSION)); | ||
55 | } | ||
56 | |||
57 | private static RefineryJavaExtension configureExtension(Project target) { | ||
58 | var extension = target.getExtensions().create("refineryJava", RefineryJavaExtension.class); | ||
59 | extension.getAddBundleSymbolicName().convention(target.getProviders() | ||
60 | .gradleProperty("tools.refinery.java.add-bundle-symbolic-name") | ||
61 | .map(Boolean::valueOf) | ||
62 | .orElse(true)); | ||
63 | extension.getDistTar().convention(target.getProviders() | ||
64 | .gradleProperty("tools.refinery.java.dist-tar") | ||
65 | .map(Boolean::valueOf) | ||
66 | .orElse(target.provider(() -> target.getPlugins().hasPlugin(ApplicationPlugin.class) && | ||
67 | !RefineryPluginUtils.hasShadowPlugin(target)))); | ||
68 | extension.getDistZip().convention(target.getProviders() | ||
69 | .gradleProperty("tools.refinery.java.dist-zip") | ||
70 | .map(Boolean::valueOf) | ||
71 | .orElse(false)); | ||
72 | extension.getEnforcePlatform().convention(target.getProviders() | ||
73 | .gradleProperty("tools.refinery.java.enforce-platform") | ||
74 | .map(Boolean::valueOf) | ||
75 | .orElse(target.provider(() -> target.getPlugins().hasPlugin(ApplicationPlugin.class)))); | ||
76 | extension.getRefineryVersion().convention(target.getProviders() | ||
77 | .gradleProperty(RefineryPluginUtils.VERSION_PROPERTY) | ||
78 | .orElse(Versions.REFINERY_VERSION)); | ||
79 | extension.getTestDependencies().convention(target.getProviders() | ||
80 | .gradleProperty("tools.refinery.java.test-dependencies") | ||
81 | .map(TestDependencies::valueOfIgnoreCase) | ||
82 | .orElse(TestDependencies.FULL)); | ||
83 | extension.getUseSlf4JLog4J().convention(target.getProviders() | ||
84 | .gradleProperty("tools.refinery.java.use-slf4j-logj4") | ||
85 | .map(Boolean::valueOf) | ||
86 | .orElse(true)); | ||
87 | extension.getUseSlf4JSimple().convention(target.getProviders() | ||
88 | .gradleProperty("tools.refinery.java.use-slf4j-simple") | ||
89 | .map(Boolean::valueOf) | ||
90 | .orElse(extension.getUseSlf4JLog4J())); | ||
91 | return extension; | ||
92 | } | ||
93 | |||
94 | private static void configureDependencies(Project target, RefineryJavaExtension extension) { | ||
95 | var dependencies = target.getDependencies(); | ||
96 | dependencies.add(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, target.provider(() -> { | ||
97 | var artifact = REFINERY_BOM + ":" + extension.getRefineryVersion().get(); | ||
98 | return Boolean.TRUE.equals(extension.getEnforcePlatform().get()) ? | ||
99 | dependencies.enforcedPlatform(artifact) : dependencies.platform(artifact); | ||
100 | })); | ||
101 | |||
102 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, | ||
103 | SLF4J_LOG4J, extension.getUseSlf4JLog4J()); | ||
104 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, | ||
105 | SLF4J_LOG4J, extension.getUseSlf4JLog4J()); | ||
106 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, | ||
107 | SLF4J_LOG4J, extension.getUseSlf4JLog4J().map(value -> | ||
108 | Boolean.TRUE.equals(value) && target.getPlugins().hasPlugin(ApplicationPlugin.class))); | ||
109 | |||
110 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME, | ||
111 | SLF4J_SIMPLE, extension.getUseSlf4JSimple()); | ||
112 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME, | ||
113 | SLF4J_SIMPLE, extension.getUseSlf4JSimple().map(value -> | ||
114 | Boolean.TRUE.equals(value) && target.getPlugins().hasPlugin(ApplicationPlugin.class))); | ||
115 | |||
116 | var addJUnit5 = extension.getTestDependencies().map(TestDependencies::isAddJUnit5); | ||
117 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, | ||
118 | JUNIT_API, addJUnit5); | ||
119 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, | ||
120 | dependencies.enforcedPlatform(JUNIT_BOM), addJUnit5); | ||
121 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME, | ||
122 | JUNIT_ENGINE, addJUnit5); | ||
123 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME, | ||
124 | JUNIT_LAUNCHER, addJUnit5); | ||
125 | |||
126 | var addOtherTestDependencies = extension.getTestDependencies().map(TestDependencies::isAddOtherDependencies); | ||
127 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, | ||
128 | HAMCREST, addOtherTestDependencies); | ||
129 | RefineryPluginUtils.addConditionalDependency(dependencies, JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, | ||
130 | JUNIT_PARAMS, addOtherTestDependencies); | ||
131 | } | ||
132 | |||
133 | private static void configureTasks(Project target, RefineryJavaExtension extension) { | ||
134 | var tasks = target.getTasks(); | ||
135 | tasks.withType(Tar.class, task -> { | ||
136 | var name = task.getName(); | ||
137 | if (ApplicationPlugin.TASK_DIST_TAR_NAME.equals(name)) { | ||
138 | task.setOnlyIf("Configured by refineryJava.distTar", ignored -> extension.getDistTar().get()); | ||
139 | } else if ("shadowDistTar".equals(name)) { | ||
140 | task.setEnabled(false); | ||
141 | } | ||
142 | }); | ||
143 | tasks.withType(Zip.class, task -> { | ||
144 | var name = task.getName(); | ||
145 | if (ApplicationPlugin.TASK_DIST_ZIP_NAME.equals(name)) { | ||
146 | task.setOnlyIf("Configured by refineryJava.distZip", ignored -> extension.getDistZip().get()); | ||
147 | } else if ("shadowDistZip".equals(name)) { | ||
148 | task.setEnabled(false); | ||
149 | } | ||
150 | }); | ||
151 | } | ||
152 | |||
153 | private static void configureShadowPlugin(Project target) { | ||
154 | target.getTasks().named("shadowJar", Jar.class, task -> { | ||
155 | var shadowJarClass = task.getClass(); | ||
156 | Method appendMethod; | ||
157 | try { | ||
158 | appendMethod = shadowJarClass.getMethod("append", String.class); | ||
159 | } catch (NoSuchMethodException e) { | ||
160 | throw new IllegalStateException("Failed to access ShadowJar task method", e); | ||
161 | } | ||
162 | try { | ||
163 | // Silence Xtext warning. | ||
164 | appendMethod.invoke(task, "plugin.properties"); | ||
165 | } catch (IllegalAccessException | InvocationTargetException e) { | ||
166 | throw new IllegalStateException("Failed to add plugin.properties to the ShadowJar task", e); | ||
167 | } | ||
168 | }); | ||
169 | |||
170 | // See https://github.com/johnrengelman/shadow/issues/586 | ||
171 | // https://github.com/johnrengelman/shadow/issues/651 | ||
172 | target.getComponents() | ||
173 | .named(JvmConstants.JAVA_MAIN_COMPONENT_NAME, AdhocComponentWithVariants.class, component -> { | ||
174 | var configuration = target.getConfigurations().getByName("shadowRuntimeElements"); | ||
175 | component.withVariantsFromConfiguration(configuration, ConfigurationVariantDetails::skip); | ||
176 | }); | ||
177 | } | ||
178 | |||
179 | private static void configureAfterEvaluate(Project project) { | ||
180 | var extension = project.getExtensions().getByType(RefineryJavaExtension.class); | ||
181 | if (Boolean.TRUE.equals(extension.getAddBundleSymbolicName().get())) { | ||
182 | addBundleSymbolicName(project); | ||
183 | } | ||
184 | if (Boolean.TRUE.equals(extension.getUseSlf4JLog4J().get())) { | ||
185 | excludeLog4J(project); | ||
186 | } | ||
187 | if (extension.getTestDependencies().get().isAddJUnit5()) { | ||
188 | configureJunitPlatform(project); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | private static void addBundleSymbolicName(Project project) { | ||
193 | var group = project.getGroup().toString(); | ||
194 | var name = project.getName(); | ||
195 | var symbolicName = "".equals(group) ? name : group + "." + name; | ||
196 | project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class, task -> { | ||
197 | var attributes = task.getManifest().getAttributes(); | ||
198 | attributes.put("Bundle-SymbolicName", symbolicName); | ||
199 | attributes.put("Bundle-Version", project.getVersion()); | ||
200 | }); | ||
201 | } | ||
202 | |||
203 | private static void excludeLog4J(Project project) { | ||
204 | project.getConfigurations().withType(Configuration.class, configuration -> { | ||
205 | if (configuration.getName().endsWith("Classpath")) { | ||
206 | configuration.exclude(Map.of("group", "log4j", "module", "log4j")); | ||
207 | configuration.exclude(Map.of("group", "ch.qos.reload4j", "module", "reload4j")); | ||
208 | } | ||
209 | }); | ||
210 | } | ||
211 | |||
212 | private static void configureJunitPlatform(Project project) { | ||
213 | project.getTasks().named(JavaPlugin.TEST_TASK_NAME, Test.class, Test::useJUnitPlatform); | ||
214 | } | ||
215 | } | ||
diff --git a/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefinerySettingsPlugin.java b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefinerySettingsPlugin.java new file mode 100644 index 00000000..54db10e5 --- /dev/null +++ b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/RefinerySettingsPlugin.java | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.gradle.plugins; | ||
7 | |||
8 | import org.gradle.api.Plugin; | ||
9 | import org.gradle.api.initialization.Settings; | ||
10 | import org.gradle.api.plugins.JavaPlugin; | ||
11 | import org.jetbrains.annotations.NotNull; | ||
12 | import tools.refinery.gradle.plugins.internal.RefineryPluginUtils; | ||
13 | import tools.refinery.gradle.plugins.internal.Versions; | ||
14 | |||
15 | import java.net.URI; | ||
16 | import java.net.URISyntaxException; | ||
17 | |||
18 | public class RefinerySettingsPlugin implements Plugin<Settings> { | ||
19 | public static final String MAVEN_SNAPSHOTS = "https://refinery.tools/maven/snapshots/"; | ||
20 | |||
21 | @Override | ||
22 | public void apply(@NotNull Settings target) { | ||
23 | configureRepositories(target); | ||
24 | createVersionCatalog(target); | ||
25 | configureAllProjects(target); | ||
26 | } | ||
27 | |||
28 | private static void configureRepositories(Settings target) { | ||
29 | var dependencyResolutionManagement = target.getDependencyResolutionManagement(); | ||
30 | // We depend on unstable API to configure dependency management for the version catalog. | ||
31 | @SuppressWarnings("UnstableApiUsage") | ||
32 | var repositories = dependencyResolutionManagement.getRepositories(); | ||
33 | var repository = target.getProviders() | ||
34 | .gradleProperty("tools.refinery.repository") | ||
35 | .map(Repository::valueOfIgnoreCase) | ||
36 | .getOrElse(Repository.getDefault()); | ||
37 | switch (repository) { | ||
38 | case LOCAL: | ||
39 | repositories.mavenLocal(); | ||
40 | break; | ||
41 | case SNAPSHOT: | ||
42 | repositories.maven(mavenArtifactRepository -> { | ||
43 | try { | ||
44 | mavenArtifactRepository.setUrl(new URI(MAVEN_SNAPSHOTS)); | ||
45 | } catch (URISyntaxException e) { | ||
46 | throw new IllegalStateException(e); | ||
47 | } | ||
48 | mavenArtifactRepository.setName("refinery-snapshots"); | ||
49 | }); | ||
50 | break; | ||
51 | case CENTRAL: { | ||
52 | // Since the central repository is common for all configurations, we handle it below. | ||
53 | } | ||
54 | break; | ||
55 | default: | ||
56 | throw new IllegalArgumentException("Unknown repository: " + repository); | ||
57 | } | ||
58 | repositories.mavenCentral(); | ||
59 | } | ||
60 | |||
61 | private static void createVersionCatalog(Settings target) { | ||
62 | var version = target.getProviders() | ||
63 | .gradleProperty(RefineryPluginUtils.VERSION_PROPERTY) | ||
64 | .getOrElse(Versions.REFINERY_VERSION); | ||
65 | var dependencyResolutionManagement = target.getDependencyResolutionManagement(); | ||
66 | var versionCatalogs = dependencyResolutionManagement.getVersionCatalogs(); | ||
67 | var versionCatalog = versionCatalogs.create("refinery"); | ||
68 | versionCatalog.from("tools.refinery:refinery-versions:" + version); | ||
69 | } | ||
70 | |||
71 | private static void configureAllProjects(Settings target) { | ||
72 | target.getGradle().allprojects(project -> project.getPlugins().withType(JavaPlugin.class, ignored -> { | ||
73 | var autoApply = project.findProperty("tools.refinery.auto-apply"); | ||
74 | if (autoApply == null || !Boolean.FALSE.equals(Boolean.valueOf(autoApply.toString()))) { | ||
75 | project.getPlugins().apply("tools.refinery.java"); | ||
76 | } | ||
77 | })); | ||
78 | } | ||
79 | } | ||
diff --git a/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/Repository.java b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/Repository.java new file mode 100644 index 00000000..4d934487 --- /dev/null +++ b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/Repository.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.gradle.plugins; | ||
7 | |||
8 | import tools.refinery.gradle.plugins.internal.Versions; | ||
9 | |||
10 | import java.util.Locale; | ||
11 | |||
12 | public enum Repository { | ||
13 | LOCAL, | ||
14 | SNAPSHOT, | ||
15 | CENTRAL; | ||
16 | |||
17 | public static Repository valueOfIgnoreCase(String value) { | ||
18 | return valueOf(value.toUpperCase(Locale.ROOT)); | ||
19 | } | ||
20 | |||
21 | // The default value depends on source files generated at build time. | ||
22 | @SuppressWarnings("ConstantValue") | ||
23 | public static Repository getDefault() { | ||
24 | if (Versions.USE_MAVEN_LOCAL) { | ||
25 | return LOCAL; | ||
26 | } | ||
27 | if (Versions.REFINERY_VERSION.endsWith("-SNAPSHOT")) { | ||
28 | return SNAPSHOT; | ||
29 | } | ||
30 | return CENTRAL; | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/TestDependencies.java b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/TestDependencies.java new file mode 100644 index 00000000..aa8afccc --- /dev/null +++ b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/TestDependencies.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.gradle.plugins; | ||
7 | |||
8 | import java.util.Locale; | ||
9 | |||
10 | public enum TestDependencies { | ||
11 | NONE(false, false), | ||
12 | MINIMAL(true, false), | ||
13 | FULL(true, true); | ||
14 | |||
15 | private final boolean addJUnit5; | ||
16 | |||
17 | private final boolean addOtherDependencies; | ||
18 | |||
19 | TestDependencies(boolean addJUnit5, boolean addOtherDependencies) { | ||
20 | this.addJUnit5 = addJUnit5; | ||
21 | this.addOtherDependencies = addOtherDependencies; | ||
22 | } | ||
23 | |||
24 | public boolean isAddJUnit5() { | ||
25 | return addJUnit5; | ||
26 | } | ||
27 | |||
28 | public boolean isAddOtherDependencies() { | ||
29 | return addOtherDependencies; | ||
30 | } | ||
31 | |||
32 | public static TestDependencies valueOfIgnoreCase(String value) { | ||
33 | return valueOf(value.toUpperCase(Locale.ROOT)); | ||
34 | } | ||
35 | } | ||
diff --git a/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/internal/RefineryPluginUtils.java b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/internal/RefineryPluginUtils.java new file mode 100644 index 00000000..0004f249 --- /dev/null +++ b/subprojects/gradle-plugins/src/main/java/tools/refinery/gradle/plugins/internal/RefineryPluginUtils.java | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.gradle.plugins.internal; | ||
7 | |||
8 | import org.gradle.api.Action; | ||
9 | import org.gradle.api.Plugin; | ||
10 | import org.gradle.api.Project; | ||
11 | import org.gradle.api.artifacts.dsl.DependencyHandler; | ||
12 | import org.gradle.api.provider.Provider; | ||
13 | |||
14 | import java.util.List; | ||
15 | |||
16 | public final class RefineryPluginUtils { | ||
17 | public static final String VERSION_PROPERTY = "tools.refinery.version"; | ||
18 | |||
19 | private static final List<String> SHADOW_PLUGIN_IDS = List.of( | ||
20 | "com.github.johnrengelman.shadow", | ||
21 | "io.github.goooler.shadow" | ||
22 | ); | ||
23 | |||
24 | private RefineryPluginUtils() { | ||
25 | throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly."); | ||
26 | } | ||
27 | |||
28 | public static void withShadowPlugin(Project project, Action<? super Project> action) { | ||
29 | // Method parameter in Gradle API uses raw type. | ||
30 | @SuppressWarnings("rawtypes") | ||
31 | Action<? super Plugin> pluginAction = ignored -> action.execute(project); | ||
32 | var plugins = project.getPlugins(); | ||
33 | for (var pluginId : SHADOW_PLUGIN_IDS) { | ||
34 | plugins.withId(pluginId, pluginAction); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | public static boolean hasShadowPlugin(Project project) { | ||
39 | var plugins = project.getPlugins(); | ||
40 | for (var pluginId : SHADOW_PLUGIN_IDS) { | ||
41 | if (plugins.hasPlugin(pluginId)) { | ||
42 | return true; | ||
43 | } | ||
44 | } | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | public static void addConditionalDependency(DependencyHandler dependencies, String configuration, | ||
49 | Object dependency, Provider<Boolean> condition) { | ||
50 | var provider = condition.map(value -> Boolean.TRUE.equals(value) ? dependency : null); | ||
51 | dependencies.add(configuration, provider); | ||
52 | } | ||
53 | } | ||
diff --git a/subprojects/interpreter/build.gradle.kts b/subprojects/interpreter/build.gradle.kts index 70faa812..f48c48f9 100644 --- a/subprojects/interpreter/build.gradle.kts +++ b/subprojects/interpreter/build.gradle.kts | |||
@@ -16,5 +16,5 @@ dependencies { | |||
16 | implementation(libs.slf4j.log4j) | 16 | implementation(libs.slf4j.log4j) |
17 | // Code in this subproject inherits from Eclipse Collection implementation classes, so this can't be `runtimeOnly`. | 17 | // Code in this subproject inherits from Eclipse Collection implementation classes, so this can't be `runtimeOnly`. |
18 | implementation(libs.eclipseCollections) | 18 | implementation(libs.eclipseCollections) |
19 | implementation(libs.eclipseCollections.api) | 19 | implementation(libs.eclipseCollections.impl) |
20 | } | 20 | } |
diff --git a/subprojects/language-model/problem.aird b/subprojects/language-model/problem.aird index 7c1f7368..f2495bcf 100644 --- a/subprojects/language-model/problem.aird +++ b/subprojects/language-model/problem.aird | |||
@@ -7,7 +7,7 @@ | |||
7 | <semanticResources>build/resources/main/model/problem.genmodel</semanticResources> | 7 | <semanticResources>build/resources/main/model/problem.genmodel</semanticResources> |
8 | <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg"> | 8 | <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg"> |
9 | <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/> | 9 | <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/> |
10 | <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="declarations" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="1719861573100"> | 10 | <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="declarations" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="1722526715953"> |
11 | <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> | 11 | <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> |
12 | <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/> | 12 | <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/> |
13 | </ownedRepresentationDescriptors> | 13 | </ownedRepresentationDescriptors> |
@@ -91,10 +91,6 @@ | |||
91 | <styles xmi:type="notation:FontStyle" xmi:id="_bmoagTrQEe62Q_vL_UTCsA" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/> | 91 | <styles xmi:type="notation:FontStyle" xmi:id="_bmoagTrQEe62Q_vL_UTCsA" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/> |
92 | <layoutConstraint xmi:type="notation:Location" xmi:id="_bmoagjrQEe62Q_vL_UTCsA"/> | 92 | <layoutConstraint xmi:type="notation:Location" xmi:id="_bmoagjrQEe62Q_vL_UTCsA"/> |
93 | </children> | 93 | </children> |
94 | <children xmi:type="notation:Node" xmi:id="_7PR4kDfOEe-Iy-tQWPZJFQ" type="3010" element="_7N28QDfOEe-Iy-tQWPZJFQ"> | ||
95 | <styles xmi:type="notation:FontStyle" xmi:id="_7PR4kTfOEe-Iy-tQWPZJFQ" fontColor="2697711" fontName="Noto Sans" fontHeight="8"/> | ||
96 | <layoutConstraint xmi:type="notation:Location" xmi:id="_7PR4kjfOEe-Iy-tQWPZJFQ"/> | ||
97 | </children> | ||
98 | <styles xmi:type="notation:SortingStyle" xmi:id="_fit3laA5EeuqkpDnuik1sg"/> | 94 | <styles xmi:type="notation:SortingStyle" xmi:id="_fit3laA5EeuqkpDnuik1sg"/> |
99 | <styles xmi:type="notation:FilteringStyle" xmi:id="_fit3lqA5EeuqkpDnuik1sg"/> | 95 | <styles xmi:type="notation:FilteringStyle" xmi:id="_fit3lqA5EeuqkpDnuik1sg"/> |
100 | </children> | 96 | </children> |
@@ -494,6 +490,31 @@ | |||
494 | <styles xmi:type="notation:ShapeStyle" xmi:id="_Z2K9bRO0Ee-4k7CzzL6IsA" fontName="Noto Sans" fontHeight="8"/> | 490 | <styles xmi:type="notation:ShapeStyle" xmi:id="_Z2K9bRO0Ee-4k7CzzL6IsA" fontName="Noto Sans" fontHeight="8"/> |
495 | <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z2K9bhO0Ee-4k7CzzL6IsA" x="2039" y="332" width="120" height="100"/> | 491 | <layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z2K9bhO0Ee-4k7CzzL6IsA" x="2039" y="332" width="120" height="100"/> |
496 | </children> | 492 | </children> |
493 | <children xmi:type="notation:Node" xmi:id="_94vUUFAbEe-B_MJ4aHA_-Q" type="2003" element="_93bswFAbEe-B_MJ4aHA_-Q"> | ||
494 | <children xmi:type="notation:Node" xmi:id="_94y-sFAbEe-B_MJ4aHA_-Q" type="5007"/> | ||
495 | <children xmi:type="notation:Node" xmi:id="_94zlwFAbEe-B_MJ4aHA_-Q" type="7004"> | ||
496 | <children xmi:type="notation:Node" xmi:id="_ANvc0FAcEe-B_MJ4aHA_-Q" type="3010" element="_AM69cFAcEe-B_MJ4aHA_-Q"> | ||
497 | <styles xmi:type="notation:FontStyle" xmi:id="_ANvc0VAcEe-B_MJ4aHA_-Q" fontName="Noto Sans" fontHeight="8"/> | ||
498 | <layoutConstraint xmi:type="notation:Location" xmi:id="_ANvc0lAcEe-B_MJ4aHA_-Q"/> | ||
499 | </children> | ||
500 | <children xmi:type="notation:Node" xmi:id="_BJNmkFAcEe-B_MJ4aHA_-Q" type="3010" element="_BIQkUFAcEe-B_MJ4aHA_-Q"> | ||
501 | <styles xmi:type="notation:FontStyle" xmi:id="_BJNmkVAcEe-B_MJ4aHA_-Q" fontName="Noto Sans" fontHeight="8"/> | ||
502 | <layoutConstraint xmi:type="notation:Location" xmi:id="_BJNmklAcEe-B_MJ4aHA_-Q"/> | ||
503 | </children> | ||
504 | <children xmi:type="notation:Node" xmi:id="_D2-NUFAcEe-B_MJ4aHA_-Q" type="3010" element="_D2cB0FAcEe-B_MJ4aHA_-Q"> | ||
505 | <styles xmi:type="notation:FontStyle" xmi:id="_D2-NUVAcEe-B_MJ4aHA_-Q" fontName="Noto Sans" fontHeight="8"/> | ||
506 | <layoutConstraint xmi:type="notation:Location" xmi:id="_D2-NUlAcEe-B_MJ4aHA_-Q"/> | ||
507 | </children> | ||
508 | <children xmi:type="notation:Node" xmi:id="_Eoo60FAcEe-B_MJ4aHA_-Q" type="3010" element="_EoDsAFAcEe-B_MJ4aHA_-Q"> | ||
509 | <styles xmi:type="notation:FontStyle" xmi:id="_Eoo60VAcEe-B_MJ4aHA_-Q" fontName="Noto Sans" fontHeight="8"/> | ||
510 | <layoutConstraint xmi:type="notation:Location" xmi:id="_Eoo60lAcEe-B_MJ4aHA_-Q"/> | ||
511 | </children> | ||
512 | <styles xmi:type="notation:SortingStyle" xmi:id="_94zlwVAbEe-B_MJ4aHA_-Q"/> | ||
513 | <styles xmi:type="notation:FilteringStyle" xmi:id="_94zlwlAbEe-B_MJ4aHA_-Q"/> | ||
514 | </children> | ||
515 | <styles xmi:type="notation:ShapeStyle" xmi:id="_94vUUVAbEe-B_MJ4aHA_-Q" fontName="Noto Sans" fontHeight="8"/> | ||
516 | <layoutConstraint xmi:type="notation:Bounds" xmi:id="_94vUUlAbEe-B_MJ4aHA_-Q" x="1695" y="1288" width="120" height="100"/> | ||
517 | </children> | ||
497 | <styles xmi:type="notation:DiagramStyle" xmi:id="_CsZB8qA4EeuqkpDnuik1sg"/> | 518 | <styles xmi:type="notation:DiagramStyle" xmi:id="_CsZB8qA4EeuqkpDnuik1sg"/> |
498 | <edges xmi:type="notation:Edge" xmi:id="_4eaYwKA8EeuqkpDnuik1sg" type="4001" element="_4eU5TqA8EeuqkpDnuik1sg" source="_D1D6MKA4EeuqkpDnuik1sg" target="_xsq_MKA8EeuqkpDnuik1sg"> | 519 | <edges xmi:type="notation:Edge" xmi:id="_4eaYwKA8EeuqkpDnuik1sg" type="4001" element="_4eU5TqA8EeuqkpDnuik1sg" source="_D1D6MKA4EeuqkpDnuik1sg" target="_xsq_MKA8EeuqkpDnuik1sg"> |
499 | <children xmi:type="notation:Node" xmi:id="_4ea_06A8EeuqkpDnuik1sg" type="6001"> | 520 | <children xmi:type="notation:Node" xmi:id="_4ea_06A8EeuqkpDnuik1sg" type="6001"> |
@@ -1591,22 +1612,14 @@ | |||
1591 | <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/> | 1612 | <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@style"/> |
1592 | </ownedStyle> | 1613 | </ownedStyle> |
1593 | <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/> | 1614 | <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']"/> |
1594 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_blvCoDrQEe62Q_vL_UTCsA" name="error : EBoolean = false" tooltipText=""> | 1615 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_blvCoDrQEe62Q_vL_UTCsA" name="kind : PredicateKind = DEFAULT" tooltipText=""> |
1595 | <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/error"/> | 1616 | <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/kind"/> |
1596 | <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/error"/> | 1617 | <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/kind"/> |
1597 | <ownedStyle xmi:type="diagram:BundledImage" uid="_dFx5EzrQEe62Q_vL_UTCsA" labelAlignment="LEFT"> | 1618 | <ownedStyle xmi:type="diagram:BundledImage" uid="_dFx5EzrQEe62Q_vL_UTCsA" labelAlignment="LEFT"> |
1598 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/> | 1619 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/> |
1599 | </ownedStyle> | 1620 | </ownedStyle> |
1600 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/> | 1621 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/> |
1601 | </ownedElements> | 1622 | </ownedElements> |
1602 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_7N28QDfOEe-Iy-tQWPZJFQ" name="shadow : EBoolean = false" tooltipText=""> | ||
1603 | <target xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/shadow"/> | ||
1604 | <semanticElements xmi:type="ecore:EAttribute" href="src/main/resources/model/problem.ecore#//PredicateDefinition/shadow"/> | ||
1605 | <ownedStyle xmi:type="diagram:BundledImage" uid="_t8lBgTfQEe-Iy-tQWPZJFQ" labelAlignment="LEFT"> | ||
1606 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']/@style"/> | ||
1607 | </ownedStyle> | ||
1608 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EClass']/@subNodeMappings[name='EC%20EAttribute']"/> | ||
1609 | </ownedElements> | ||
1610 | </ownedDiagramElements> | 1623 | </ownedDiagramElements> |
1611 | <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_QKD2EKA6EeuqkpDnuik1sg" name="Parameter" tooltipText="" outgoingEdges="_oni4rKA6EeuqkpDnuik1sg _iWzpAmTzEe2qdtyPWAtoxA" incomingEdges="_Uy4bWaA6EeuqkpDnuik1sg" width="12" height="10"> | 1624 | <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_QKD2EKA6EeuqkpDnuik1sg" name="Parameter" tooltipText="" outgoingEdges="_oni4rKA6EeuqkpDnuik1sg _iWzpAmTzEe2qdtyPWAtoxA" incomingEdges="_Uy4bWaA6EeuqkpDnuik1sg" width="12" height="10"> |
1612 | <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Parameter"/> | 1625 | <target xmi:type="ecore:EClass" href="src/main/resources/model/problem.ecore#//Parameter"/> |
@@ -2865,6 +2878,49 @@ | |||
2865 | </ownedStyle> | 2878 | </ownedStyle> |
2866 | <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/> | 2879 | <actualMapping xmi:type="description_1:EdgeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@edgeMappings[name='EC_EReference']"/> |
2867 | </ownedDiagramElements> | 2880 | </ownedDiagramElements> |
2881 | <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_93bswFAbEe-B_MJ4aHA_-Q" name="PredicateKind" tooltipText="" width="12" height="10"> | ||
2882 | <target xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//PredicateKind"/> | ||
2883 | <semanticElements xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//PredicateKind"/> | ||
2884 | <arrangeConstraints>KEEP_LOCATION</arrangeConstraints> | ||
2885 | <arrangeConstraints>KEEP_SIZE</arrangeConstraints> | ||
2886 | <arrangeConstraints>KEEP_RATIO</arrangeConstraints> | ||
2887 | <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_93cT0FAbEe-B_MJ4aHA_-Q" borderSize="1" borderSizeComputationExpression="1" borderColor="125,125,125" backgroundStyle="Liquid" foregroundColor="221,236,202"> | ||
2888 | <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@style"/> | ||
2889 | </ownedStyle> | ||
2890 | <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']"/> | ||
2891 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_AM69cFAcEe-B_MJ4aHA_-Q" name="DEFAULT" tooltipText=""> | ||
2892 | <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/DEFAULT"/> | ||
2893 | <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/DEFAULT"/> | ||
2894 | <ownedStyle xmi:type="diagram:BundledImage" uid="_AM69cVAcEe-B_MJ4aHA_-Q" labelAlignment="LEFT"> | ||
2895 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/> | ||
2896 | </ownedStyle> | ||
2897 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/> | ||
2898 | </ownedElements> | ||
2899 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_BIQkUFAcEe-B_MJ4aHA_-Q" name="ERROR" tooltipText=""> | ||
2900 | <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/ERROR"/> | ||
2901 | <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/ERROR"/> | ||
2902 | <ownedStyle xmi:type="diagram:BundledImage" uid="_BIRLYFAcEe-B_MJ4aHA_-Q" labelAlignment="LEFT"> | ||
2903 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/> | ||
2904 | </ownedStyle> | ||
2905 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/> | ||
2906 | </ownedElements> | ||
2907 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_D2cB0FAcEe-B_MJ4aHA_-Q" name="SHADOW" tooltipText=""> | ||
2908 | <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/SHADOW"/> | ||
2909 | <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/SHADOW"/> | ||
2910 | <ownedStyle xmi:type="diagram:BundledImage" uid="_D2co4FAcEe-B_MJ4aHA_-Q" labelAlignment="LEFT"> | ||
2911 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/> | ||
2912 | </ownedStyle> | ||
2913 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/> | ||
2914 | </ownedElements> | ||
2915 | <ownedElements xmi:type="diagram:DNodeListElement" uid="_EoDsAFAcEe-B_MJ4aHA_-Q" name="PARTIAL" tooltipText=""> | ||
2916 | <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/PARTIAL"/> | ||
2917 | <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//PredicateKind/PARTIAL"/> | ||
2918 | <ownedStyle xmi:type="diagram:BundledImage" uid="_EoDsAVAcEe-B_MJ4aHA_-Q" labelAlignment="LEFT"> | ||
2919 | <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/> | ||
2920 | </ownedStyle> | ||
2921 | <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/> | ||
2922 | </ownedElements> | ||
2923 | </ownedDiagramElements> | ||
2868 | <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> | 2924 | <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> |
2869 | <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_CsWlsKA4EeuqkpDnuik1sg"/> | 2925 | <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_CsWlsKA4EeuqkpDnuik1sg"/> |
2870 | <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer"/> | 2926 | <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer"/> |
diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore b/subprojects/language-model/src/main/resources/model/problem.ecore index a9b6f660..0bc8caf9 100644 --- a/subprojects/language-model/src/main/resources/model/problem.ecore +++ b/subprojects/language-model/src/main/resources/model/problem.ecore | |||
@@ -34,8 +34,7 @@ | |||
34 | <eClassifiers xsi:type="ecore:EClass" name="PredicateDefinition" eSuperTypes="#//ParametricDefinition #//Relation"> | 34 | <eClassifiers xsi:type="ecore:EClass" name="PredicateDefinition" eSuperTypes="#//ParametricDefinition #//Relation"> |
35 | <eStructuralFeatures xsi:type="ecore:EReference" name="bodies" upperBound="-1" | 35 | <eStructuralFeatures xsi:type="ecore:EReference" name="bodies" upperBound="-1" |
36 | eType="#//Conjunction" containment="true"/> | 36 | eType="#//Conjunction" containment="true"/> |
37 | <eStructuralFeatures xsi:type="ecore:EAttribute" name="error" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/> | 37 | <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//PredicateKind"/> |
38 | <eStructuralFeatures xsi:type="ecore:EAttribute" name="shadow" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/> | ||
39 | <eStructuralFeatures xsi:type="ecore:EReference" name="computedValue" eType="#//PredicateDefinition" | 38 | <eStructuralFeatures xsi:type="ecore:EReference" name="computedValue" eType="#//PredicateDefinition" |
40 | transient="true" containment="true"/> | 39 | transient="true" containment="true"/> |
41 | </eClassifiers> | 40 | </eClassifiers> |
@@ -281,4 +280,10 @@ | |||
281 | <eStructuralFeatures xsi:type="ecore:EAttribute" name="default" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" | 280 | <eStructuralFeatures xsi:type="ecore:EAttribute" name="default" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" |
282 | defaultValueLiteral="false"/> | 281 | defaultValueLiteral="false"/> |
283 | </eClassifiers> | 282 | </eClassifiers> |
283 | <eClassifiers xsi:type="ecore:EEnum" name="PredicateKind"> | ||
284 | <eLiterals name="DEFAULT"/> | ||
285 | <eLiterals name="ERROR" value="1"/> | ||
286 | <eLiterals name="SHADOW" value="2"/> | ||
287 | <eLiterals name="PARTIAL" value="3"/> | ||
288 | </eClassifiers> | ||
284 | </ecore:EPackage> | 289 | </ecore:EPackage> |
diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel b/subprojects/language-model/src/main/resources/model/problem.genmodel index f3c367e1..e7cf6843 100644 --- a/subprojects/language-model/src/main/resources/model/problem.genmodel +++ b/subprojects/language-model/src/main/resources/model/problem.genmodel | |||
@@ -81,6 +81,12 @@ | |||
81 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ParameterBinding/FOCUS"/> | 81 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ParameterBinding/FOCUS"/> |
82 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ParameterBinding/MULTI"/> | 82 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ParameterBinding/MULTI"/> |
83 | </genEnums> | 83 | </genEnums> |
84 | <genEnums typeSafeEnumCompatible="false" ecoreEnum="problem.ecore#//PredicateKind"> | ||
85 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//PredicateKind/DEFAULT"/> | ||
86 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//PredicateKind/ERROR"/> | ||
87 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//PredicateKind/SHADOW"/> | ||
88 | <genEnumLiterals ecoreEnumLiteral="problem.ecore#//PredicateKind/PARTIAL"/> | ||
89 | </genEnums> | ||
84 | <genClasses ecoreClass="problem.ecore#//Problem"> | 90 | <genClasses ecoreClass="problem.ecore#//Problem"> |
85 | <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Problem/nodes"/> | 91 | <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Problem/nodes"/> |
86 | <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Problem/statements"/> | 92 | <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//Problem/statements"/> |
@@ -105,8 +111,7 @@ | |||
105 | </genClasses> | 111 | </genClasses> |
106 | <genClasses ecoreClass="problem.ecore#//PredicateDefinition"> | 112 | <genClasses ecoreClass="problem.ecore#//PredicateDefinition"> |
107 | <genFeatures children="true" createChild="true" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//PredicateDefinition/bodies"/> | 113 | <genFeatures children="true" createChild="true" propertySortChoices="true" ecoreFeature="ecore:EReference problem.ecore#//PredicateDefinition/bodies"/> |
108 | <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//PredicateDefinition/error"/> | 114 | <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//PredicateDefinition/kind"/> |
109 | <genFeatures createChild="false" ecoreFeature="ecore:EAttribute problem.ecore#//PredicateDefinition/shadow"/> | ||
110 | <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//PredicateDefinition/computedValue"/> | 115 | <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference problem.ecore#//PredicateDefinition/computedValue"/> |
111 | </genClasses> | 116 | </genClasses> |
112 | <genClasses ecoreClass="problem.ecore#//Parameter"> | 117 | <genClasses ecoreClass="problem.ecore#//Parameter"> |
diff --git a/subprojects/language-semantics/build.gradle.kts b/subprojects/language-semantics/build.gradle.kts index fe223c6d..98e5b366 100644 --- a/subprojects/language-semantics/build.gradle.kts +++ b/subprojects/language-semantics/build.gradle.kts | |||
@@ -13,13 +13,13 @@ mavenArtifact { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | dependencies { | 15 | dependencies { |
16 | api(libs.eclipseCollections.api) | ||
17 | api(project(":refinery-language")) | 16 | api(project(":refinery-language")) |
18 | api(project(":refinery-store")) | 17 | api(project(":refinery-store")) |
19 | api(project(":refinery-store-query")) | 18 | api(project(":refinery-store-query")) |
20 | api(project(":refinery-store-reasoning")) | 19 | api(project(":refinery-store-reasoning")) |
20 | api(libs.eclipseCollections) | ||
21 | implementation(project(":refinery-store-reasoning-scope")) | 21 | implementation(project(":refinery-store-reasoning-scope")) |
22 | runtimeOnly(libs.eclipseCollections) | 22 | runtimeOnly(libs.eclipseCollections.impl) |
23 | testImplementation(project(":refinery-store-dse-visualization")) | 23 | testImplementation(project(":refinery-store-dse-visualization")) |
24 | testImplementation(project(":refinery-store-query-interpreter")) | 24 | testImplementation(project(":refinery-store-query-interpreter")) |
25 | testImplementation(testFixtures(project(":refinery-language"))) | 25 | testImplementation(testFixtures(project(":refinery-language"))) |
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java index d71cf35f..f45acc96 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java | |||
@@ -39,7 +39,9 @@ import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslat | |||
39 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; | 39 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; |
40 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; | 40 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; |
41 | import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; | 41 | import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; |
42 | import tools.refinery.store.reasoning.translator.predicate.BasePredicateTranslator; | ||
42 | import tools.refinery.store.reasoning.translator.predicate.PredicateTranslator; | 43 | import tools.refinery.store.reasoning.translator.predicate.PredicateTranslator; |
44 | import tools.refinery.store.reasoning.translator.predicate.ShadowPredicateTranslator; | ||
43 | import tools.refinery.store.statecoding.StateCoderBuilder; | 45 | import tools.refinery.store.statecoding.StateCoderBuilder; |
44 | import tools.refinery.store.tuple.Tuple; | 46 | import tools.refinery.store.tuple.Tuple; |
45 | import tools.refinery.store.tuple.Tuple1; | 47 | import tools.refinery.store.tuple.Tuple1; |
@@ -68,6 +70,10 @@ public class ModelInitializer { | |||
68 | @Inject | 70 | @Inject |
69 | private RuleCompiler ruleCompiler; | 71 | private RuleCompiler ruleCompiler; |
70 | 72 | ||
73 | private boolean keepNonExistingObjects; | ||
74 | |||
75 | private boolean keepShadowPredicates = true; | ||
76 | |||
71 | private Problem problem; | 77 | private Problem problem; |
72 | 78 | ||
73 | private final Set<Problem> importedProblems = new HashSet<>(); | 79 | private final Set<Problem> importedProblems = new HashSet<>(); |
@@ -182,7 +188,7 @@ public class ModelInitializer { | |||
182 | public void configureStoreBuilder(ModelStoreBuilder storeBuilder) { | 188 | public void configureStoreBuilder(ModelStoreBuilder storeBuilder) { |
183 | checkProblem(); | 189 | checkProblem(); |
184 | try { | 190 | try { |
185 | storeBuilder.with(new MultiObjectTranslator()); | 191 | storeBuilder.with(new MultiObjectTranslator(keepNonExistingObjects)); |
186 | storeBuilder.with(new MetamodelTranslator(metamodel)); | 192 | storeBuilder.with(new MetamodelTranslator(metamodel)); |
187 | if (scopePropagator != null) { | 193 | if (scopePropagator != null) { |
188 | if (storeBuilder.tryGetAdapter(PropagationBuilder.class).isEmpty()) { | 194 | if (storeBuilder.tryGetAdapter(PropagationBuilder.class).isEmpty()) { |
@@ -194,6 +200,9 @@ public class ModelInitializer { | |||
194 | collectRules(storeBuilder); | 200 | collectRules(storeBuilder); |
195 | storeBuilder.tryGetAdapter(StateCoderBuilder.class) | 201 | storeBuilder.tryGetAdapter(StateCoderBuilder.class) |
196 | .ifPresent(stateCoderBuilder -> stateCoderBuilder.individuals(individuals)); | 202 | .ifPresent(stateCoderBuilder -> stateCoderBuilder.individuals(individuals)); |
203 | if (!keepShadowPredicates) { | ||
204 | problemTrace.removeShadowRelations(); | ||
205 | } | ||
197 | } catch (TranslationException e) { | 206 | } catch (TranslationException e) { |
198 | throw problemTrace.wrapException(e); | 207 | throw problemTrace.wrapException(e); |
199 | } | 208 | } |
@@ -287,7 +296,7 @@ public class ModelInitializer { | |||
287 | 296 | ||
288 | private void collectPredicateDefinitionSymbol(PredicateDefinition predicateDefinition) { | 297 | private void collectPredicateDefinitionSymbol(PredicateDefinition predicateDefinition) { |
289 | int arity = predicateDefinition.getParameters().size(); | 298 | int arity = predicateDefinition.getParameters().size(); |
290 | if (predicateDefinition.isError()) { | 299 | if (predicateDefinition.getKind() == PredicateKind.ERROR) { |
291 | collectPartialRelation(predicateDefinition, arity, TruthValue.FALSE, TruthValue.FALSE); | 300 | collectPartialRelation(predicateDefinition, arity, TruthValue.FALSE, TruthValue.FALSE); |
292 | } else { | 301 | } else { |
293 | collectPartialRelation(predicateDefinition, arity, null, TruthValue.UNKNOWN); | 302 | collectPartialRelation(predicateDefinition, arity, null, TruthValue.UNKNOWN); |
@@ -592,26 +601,32 @@ public class ModelInitializer { | |||
592 | } | 601 | } |
593 | 602 | ||
594 | private void collectPredicateDefinition(PredicateDefinition predicateDefinition, ModelStoreBuilder storeBuilder) { | 603 | private void collectPredicateDefinition(PredicateDefinition predicateDefinition, ModelStoreBuilder storeBuilder) { |
604 | if (ProblemUtil.isBasePredicate(predicateDefinition)) { | ||
605 | collectBasePredicateDefinition(predicateDefinition, storeBuilder); | ||
606 | } else if (predicateDefinition.getKind() == PredicateKind.SHADOW) { | ||
607 | collectShadowPredicateDefinition(predicateDefinition, storeBuilder); | ||
608 | } else { | ||
609 | collectComputedPredicateDefinition(predicateDefinition, storeBuilder); | ||
610 | } | ||
611 | } | ||
612 | |||
613 | private void collectComputedPredicateDefinition(PredicateDefinition predicateDefinition, | ||
614 | ModelStoreBuilder storeBuilder) { | ||
595 | var partialRelation = getPartialRelation(predicateDefinition); | 615 | var partialRelation = getPartialRelation(predicateDefinition); |
596 | var query = queryCompiler.toQuery(partialRelation.name(), predicateDefinition); | 616 | var query = queryCompiler.toQuery(partialRelation.name(), predicateDefinition); |
597 | boolean mutable; | 617 | boolean mutable = targetTypes.contains(partialRelation) || isActionTarget(predicateDefinition); |
598 | TruthValue defaultValue; | 618 | TruthValue defaultValue; |
599 | if (predicateDefinition.isShadow()) { | 619 | if (predicateDefinition.getKind() == PredicateKind.ERROR) { |
600 | mutable = false; | 620 | defaultValue = TruthValue.FALSE; |
601 | defaultValue = TruthValue.UNKNOWN; | ||
602 | } else { | 621 | } else { |
603 | mutable = targetTypes.contains(partialRelation) || isActionTarget(predicateDefinition); | 622 | var seed = modelSeed.getSeed(partialRelation); |
604 | if (predicateDefinition.isError()) { | 623 | defaultValue = seed.majorityValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; |
605 | defaultValue = TruthValue.FALSE; | 624 | var cursor = seed.getCursor(defaultValue, problemTrace.getNodeTrace().size()); |
606 | } else { | 625 | // The symbol should be mutable if there is at least one non-default entry in the seed. |
607 | var seed = modelSeed.getSeed(partialRelation); | 626 | mutable = mutable || cursor.move(); |
608 | defaultValue = seed.majorityValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; | 627 | } |
609 | var cursor = seed.getCursor(defaultValue, problemTrace.getNodeTrace().size()); | 628 | var parameterTypes = getParameterTypes(predicateDefinition, null); |
610 | // The symbol should be mutable if there is at least one non-default entry in the seed. | 629 | var translator = new PredicateTranslator(partialRelation, query, parameterTypes, mutable, defaultValue); |
611 | mutable = mutable || cursor.move(); | ||
612 | } | ||
613 | } | ||
614 | var translator = new PredicateTranslator(partialRelation, query, mutable, defaultValue); | ||
615 | storeBuilder.with(translator); | 630 | storeBuilder.with(translator); |
616 | } | 631 | } |
617 | 632 | ||
@@ -624,6 +639,36 @@ public class ModelInitializer { | |||
624 | return false; | 639 | return false; |
625 | } | 640 | } |
626 | 641 | ||
642 | private List<PartialRelation> getParameterTypes(ParametricDefinition parametricDefinition, | ||
643 | PartialRelation defaultType) { | ||
644 | var parameters = parametricDefinition.getParameters(); | ||
645 | var parameterTypes = new ArrayList<PartialRelation>(parameters.size()); | ||
646 | for (var parameter : parameters) { | ||
647 | var relation = parameter.getParameterType(); | ||
648 | parameterTypes.add(relation == null ? defaultType : getPartialRelation(relation)); | ||
649 | } | ||
650 | return Collections.unmodifiableList(parameterTypes); | ||
651 | } | ||
652 | |||
653 | private void collectBasePredicateDefinition(PredicateDefinition predicateDefinition, | ||
654 | ModelStoreBuilder storeBuilder) { | ||
655 | var partialRelation = getPartialRelation(predicateDefinition); | ||
656 | var parameterTypes = getParameterTypes(predicateDefinition, nodeRelation); | ||
657 | var seed = modelSeed.getSeed(partialRelation); | ||
658 | var defaultValue = seed.majorityValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; | ||
659 | boolean partial = predicateDefinition.getKind() == PredicateKind.PARTIAL; | ||
660 | var translator = new BasePredicateTranslator(partialRelation, parameterTypes, defaultValue, partial); | ||
661 | storeBuilder.with(translator); | ||
662 | } | ||
663 | |||
664 | private void collectShadowPredicateDefinition(PredicateDefinition predicateDefinition, | ||
665 | ModelStoreBuilder storeBuilder) { | ||
666 | var partialRelation = getPartialRelation(predicateDefinition); | ||
667 | var query = queryCompiler.toQuery(partialRelation.name(), predicateDefinition); | ||
668 | var translator = new ShadowPredicateTranslator(partialRelation, query, keepShadowPredicates); | ||
669 | storeBuilder.with(translator); | ||
670 | } | ||
671 | |||
627 | private void collectScopes() { | 672 | private void collectScopes() { |
628 | for (var importedProblem : importedProblems) { | 673 | for (var importedProblem : importedProblems) { |
629 | for (var statement : importedProblem.getStatements()) { | 674 | for (var statement : importedProblem.getStatements()) { |
@@ -672,6 +717,18 @@ public class ModelInitializer { | |||
672 | scopePropagator.scope(type, interval); | 717 | scopePropagator.scope(type, interval); |
673 | } | 718 | } |
674 | 719 | ||
720 | public void setKeepNonExistingObjects(boolean keepNonExistingObjects) { | ||
721 | this.keepNonExistingObjects = keepNonExistingObjects; | ||
722 | } | ||
723 | |||
724 | public boolean isKeepShadowPredicates() { | ||
725 | return keepShadowPredicates; | ||
726 | } | ||
727 | |||
728 | public void setKeepShadowPredicates(boolean keepShadowPredicates) { | ||
729 | this.keepShadowPredicates = keepShadowPredicates; | ||
730 | } | ||
731 | |||
675 | private record RelationInfo(PartialRelation partialRelation, MutableSeed<TruthValue> assertions, | 732 | private record RelationInfo(PartialRelation partialRelation, MutableSeed<TruthValue> assertions, |
676 | MutableSeed<TruthValue> defaultAssertions) { | 733 | MutableSeed<TruthValue> defaultAssertions) { |
677 | public RelationInfo(String name, int arity, TruthValue value, TruthValue defaultValue) { | 734 | public RelationInfo(String name, int arity, TruthValue value, TruthValue defaultValue) { |
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java index 457f2362..487ac9b9 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java | |||
@@ -113,6 +113,19 @@ class ProblemTraceImpl implements ProblemTrace { | |||
113 | } | 113 | } |
114 | } | 114 | } |
115 | 115 | ||
116 | void removeShadowRelations() { | ||
117 | var iterator = mutableRelationTrace.entrySet().iterator(); | ||
118 | while (iterator.hasNext()) { | ||
119 | var entry = iterator.next(); | ||
120 | var relation = entry.getKey(); | ||
121 | if (relation instanceof PredicateDefinition predicateDefinition && | ||
122 | predicateDefinition.getKind() == PredicateKind.SHADOW) { | ||
123 | iterator.remove(); | ||
124 | mutableInverseTrace.remove(entry.getValue()); | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | |||
116 | @Override | 129 | @Override |
117 | public Map<AnyPartialSymbol, Relation> getInverseRelationTrace() { | 130 | public Map<AnyPartialSymbol, Relation> getInverseRelationTrace() { |
118 | return inverseTrace; | 131 | return inverseTrace; |
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java index ed4841c4..16a41824 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java | |||
@@ -24,6 +24,7 @@ import tools.refinery.language.naming.NamingUtil; | |||
24 | import tools.refinery.language.scoping.imports.ImportAdapterProvider; | 24 | import tools.refinery.language.scoping.imports.ImportAdapterProvider; |
25 | import tools.refinery.language.typesystem.SignatureProvider; | 25 | import tools.refinery.language.typesystem.SignatureProvider; |
26 | import tools.refinery.language.utils.ProblemUtil; | 26 | import tools.refinery.language.utils.ProblemUtil; |
27 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
27 | import tools.refinery.store.model.Model; | 28 | import tools.refinery.store.model.Model; |
28 | import tools.refinery.store.reasoning.ReasoningAdapter; | 29 | import tools.refinery.store.reasoning.ReasoningAdapter; |
29 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | 30 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; |
@@ -31,13 +32,15 @@ import tools.refinery.store.reasoning.literal.Concreteness; | |||
31 | import tools.refinery.store.reasoning.representation.PartialRelation; | 32 | import tools.refinery.store.reasoning.representation.PartialRelation; |
32 | import tools.refinery.store.reasoning.translator.typehierarchy.InferredType; | 33 | import tools.refinery.store.reasoning.translator.typehierarchy.InferredType; |
33 | import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; | 34 | import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; |
34 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
35 | import tools.refinery.store.tuple.Tuple; | 35 | import tools.refinery.store.tuple.Tuple; |
36 | 36 | ||
37 | import java.io.ByteArrayInputStream; | 37 | import java.io.ByteArrayInputStream; |
38 | import java.io.ByteArrayOutputStream; | 38 | import java.io.ByteArrayOutputStream; |
39 | import java.io.IOException; | 39 | import java.io.IOException; |
40 | import java.util.*; | 40 | import java.util.Map; |
41 | import java.util.Objects; | ||
42 | import java.util.TreeMap; | ||
43 | import java.util.UUID; | ||
41 | import java.util.function.Function; | 44 | import java.util.function.Function; |
42 | import java.util.stream.Collectors; | 45 | import java.util.stream.Collectors; |
43 | 46 | ||
@@ -107,6 +110,7 @@ public class SolutionSerializer { | |||
107 | addExistsAssertions(); | 110 | addExistsAssertions(); |
108 | addClassAssertions(); | 111 | addClassAssertions(); |
109 | addReferenceAssertions(); | 112 | addReferenceAssertions(); |
113 | addBasePredicateAssertions(); | ||
110 | if (nodeDeclaration.getNodes().isEmpty()) { | 114 | if (nodeDeclaration.getNodes().isEmpty()) { |
111 | problem.getStatements().remove(nodeDeclaration); | 115 | problem.getStatements().remove(nodeDeclaration); |
112 | } | 116 | } |
@@ -249,8 +253,8 @@ public class SolutionSerializer { | |||
249 | } | 253 | } |
250 | 254 | ||
251 | private void addClassAssertions() { | 255 | private void addClassAssertions() { |
252 | var types = | 256 | var types = trace.getMetamodel().typeHierarchy().getPreservedTypes().keySet().stream() |
253 | trace.getMetamodel().typeHierarchy().getPreservedTypes().keySet().stream().collect(Collectors.toMap(Function.identity(), this::findPartialRelation)); | 257 | .collect(Collectors.toMap(Function.identity(), this::findPartialRelation)); |
254 | var cursor = model.getInterpretation(TypeHierarchyTranslator.TYPE_SYMBOL).getAll(); | 258 | var cursor = model.getInterpretation(TypeHierarchyTranslator.TYPE_SYMBOL).getAll(); |
255 | while (cursor.move()) { | 259 | while (cursor.move()) { |
256 | var key = cursor.getKey(); | 260 | var key = cursor.getKey(); |
@@ -306,6 +310,17 @@ public class SolutionSerializer { | |||
306 | } | 310 | } |
307 | } | 311 | } |
308 | 312 | ||
313 | private void addBasePredicateAssertions() { | ||
314 | for (var entry : trace.getRelationTrace().entrySet()) { | ||
315 | if (entry.getKey() instanceof PredicateDefinition predicateDefinition && | ||
316 | ProblemUtil.isBasePredicate(predicateDefinition)) { | ||
317 | var partialRelation = entry.getValue(); | ||
318 | addDefaultAssertion(partialRelation); | ||
319 | addAssertions(partialRelation); | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | |||
309 | private void addAssertions(PartialRelation partialRelation) { | 324 | private void addAssertions(PartialRelation partialRelation) { |
310 | var relation = findPartialRelation(partialRelation); | 325 | var relation = findPartialRelation(partialRelation); |
311 | var cursor = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, partialRelation).getAll(); | 326 | var cursor = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, partialRelation).getAll(); |
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/ModelGenerationTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/ModelGenerationTest.java index b4abce81..44d83cdc 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/ModelGenerationTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/ModelGenerationTest.java | |||
@@ -6,14 +6,11 @@ | |||
6 | package tools.refinery.language.semantics; | 6 | package tools.refinery.language.semantics; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Disabled; | 9 | import org.junit.jupiter.api.Disabled; |
12 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import tools.refinery.language.ProblemStandaloneSetup; | 11 | import tools.refinery.language.ProblemStandaloneSetup; |
15 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 12 | import tools.refinery.language.tests.InjectWithRefinery; |
16 | import tools.refinery.language.tests.ProblemInjectorProvider; | 13 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
17 | import tools.refinery.store.dse.propagation.PropagationAdapter; | 14 | import tools.refinery.store.dse.propagation.PropagationAdapter; |
18 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; | 15 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; |
19 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; | 16 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; |
@@ -33,8 +30,7 @@ import java.util.LinkedHashMap; | |||
33 | import static org.hamcrest.MatcherAssert.assertThat; | 30 | import static org.hamcrest.MatcherAssert.assertThat; |
34 | import static org.hamcrest.Matchers.empty; | 31 | import static org.hamcrest.Matchers.empty; |
35 | 32 | ||
36 | @ExtendWith(InjectionExtension.class) | 33 | @InjectWithRefinery |
37 | @InjectWith(ProblemInjectorProvider.class) | ||
38 | @Disabled("For debugging purposes only") | 34 | @Disabled("For debugging purposes only") |
39 | class ModelGenerationTest { | 35 | class ModelGenerationTest { |
40 | @Inject | 36 | @Inject |
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java index 9f36445a..fded04bf 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java | |||
@@ -7,14 +7,11 @@ package tools.refinery.language.semantics; | |||
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import com.google.inject.Provider; | 9 | import com.google.inject.Provider; |
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | 10 | import org.junit.jupiter.params.ParameterizedTest; |
14 | import org.junit.jupiter.params.provider.Arguments; | 11 | import org.junit.jupiter.params.provider.Arguments; |
15 | import org.junit.jupiter.params.provider.MethodSource; | 12 | import org.junit.jupiter.params.provider.MethodSource; |
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
18 | import tools.refinery.store.dse.propagation.PropagationAdapter; | 15 | import tools.refinery.store.dse.propagation.PropagationAdapter; |
19 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; | 16 | import tools.refinery.store.dse.strategy.BestFirstStoreManager; |
20 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; | 17 | import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; |
@@ -34,8 +31,7 @@ import java.util.stream.Stream; | |||
34 | import static org.hamcrest.MatcherAssert.assertThat; | 31 | import static org.hamcrest.MatcherAssert.assertThat; |
35 | import static org.hamcrest.Matchers.is; | 32 | import static org.hamcrest.Matchers.is; |
36 | 33 | ||
37 | @ExtendWith(InjectionExtension.class) | 34 | @InjectWithRefinery |
38 | @InjectWith(ProblemInjectorProvider.class) | ||
39 | class SolutionSerializerTest { | 35 | class SolutionSerializerTest { |
40 | @Inject | 36 | @Inject |
41 | private ProblemParseHelper parseHelper; | 37 | private ProblemParseHelper parseHelper; |
@@ -238,6 +234,65 @@ class SolutionSerializerTest { | |||
238 | !exists(a). | 234 | !exists(a). |
239 | !exists(Foo::new). | 235 | !exists(Foo::new). |
240 | Foo(foo1). | 236 | Foo(foo1). |
237 | """), Arguments.of(""" | ||
238 | class Foo { | ||
239 | partial Bar[] bar | ||
240 | } | ||
241 | |||
242 | class Bar. | ||
243 | """, """ | ||
244 | bar(a, b). | ||
245 | scope Foo = 2, Bar = 2. | ||
246 | """, """ | ||
247 | declare a, b, foo1, bar1. | ||
248 | !exists(Foo::new). | ||
249 | !exists(Bar::new). | ||
250 | Foo(foo1). | ||
251 | Bar(bar1). | ||
252 | Foo(a). | ||
253 | Bar(b). | ||
254 | default !bar(*, *). | ||
255 | ?bar(foo1, bar1). | ||
256 | ?bar(foo1, b). | ||
257 | ?bar(a, bar1). | ||
258 | bar(a, b). | ||
259 | """), Arguments.of(""" | ||
260 | class Foo. | ||
261 | class Bar. | ||
262 | pred bar(Foo x, Bar y). | ||
263 | """, """ | ||
264 | bar(a, b). | ||
265 | scope Foo = 2, Bar = 2. | ||
266 | """, """ | ||
267 | declare a, b, foo1, bar1. | ||
268 | !exists(Foo::new). | ||
269 | !exists(Bar::new). | ||
270 | Foo(foo1). | ||
271 | Bar(bar1). | ||
272 | Foo(a). | ||
273 | Bar(b). | ||
274 | default !bar(*, *). | ||
275 | bar(a, b). | ||
276 | """), Arguments.of(""" | ||
277 | class Foo. | ||
278 | class Bar. | ||
279 | partial pred bar(Foo x, Bar y). | ||
280 | """, """ | ||
281 | bar(a, b). | ||
282 | scope Foo = 2, Bar = 2. | ||
283 | """, """ | ||
284 | declare a, b, foo1, bar1. | ||
285 | !exists(Foo::new). | ||
286 | !exists(Bar::new). | ||
287 | Foo(foo1). | ||
288 | Bar(bar1). | ||
289 | Foo(a). | ||
290 | Bar(b). | ||
291 | default !bar(*, *). | ||
292 | ?bar(foo1, bar1). | ||
293 | ?bar(foo1, b). | ||
294 | ?bar(a, bar1). | ||
295 | bar(a, b). | ||
241 | """)); | 296 | """)); |
242 | } | 297 | } |
243 | } | 298 | } |
diff --git a/subprojects/language-web/build.gradle.kts b/subprojects/language-web/build.gradle.kts index 00570e48..bcc10030 100644 --- a/subprojects/language-web/build.gradle.kts +++ b/subprojects/language-web/build.gradle.kts | |||
@@ -24,7 +24,7 @@ dependencies { | |||
24 | implementation(libs.jetty.servlet) | 24 | implementation(libs.jetty.servlet) |
25 | implementation(libs.jetty.websocket.api) | 25 | implementation(libs.jetty.websocket.api) |
26 | implementation(libs.jetty.websocket.server) | 26 | implementation(libs.jetty.websocket.server) |
27 | implementation(libs.slf4j.api) | 27 | implementation(libs.slf4j) |
28 | implementation(libs.xtext.web) | 28 | implementation(libs.xtext.web) |
29 | xtextGenerated(project(":refinery-language", "generatedWebSources")) | 29 | xtextGenerated(project(":refinery-language", "generatedWebSources")) |
30 | webapp(project(":refinery-frontend", "productionAssets")) | 30 | webapp(project(":refinery-frontend", "productionAssets")) |
@@ -44,18 +44,6 @@ tasks { | |||
44 | } | 44 | } |
45 | } | 45 | } |
46 | 46 | ||
47 | shadowJar { | ||
48 | dependsOn(webapp) | ||
49 | from(project.sourceSets.main.map { it.output }) | ||
50 | exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "schema/*", | ||
51 | ".options", ".api_description", "*.profile", "about.*", "about_*.html", "about_files/*", | ||
52 | "plugin.xml", "systembundle.properties", "profile.list", "META-INF/resources/xtext/**") | ||
53 | append("plugin.properties") | ||
54 | from(webapp) { | ||
55 | into("webapp") | ||
56 | } | ||
57 | } | ||
58 | |||
59 | register<JavaExec>("serve") { | 47 | register<JavaExec>("serve") { |
60 | dependsOn(webapp) | 48 | dependsOn(webapp) |
61 | val mainRuntimeClasspath = sourceSets.main.map { it.runtimeClasspath } | 49 | val mainRuntimeClasspath = sourceSets.main.map { it.runtimeClasspath } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java index 7febce7d..e7facbf7 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/generator/ModelGenerationWorker.java | |||
@@ -9,11 +9,8 @@ import com.google.inject.Inject; | |||
9 | import org.eclipse.xtext.service.OperationCanceledManager; | 9 | import org.eclipse.xtext.service.OperationCanceledManager; |
10 | import org.slf4j.Logger; | 10 | import org.slf4j.Logger; |
11 | import org.slf4j.LoggerFactory; | 11 | import org.slf4j.LoggerFactory; |
12 | import tools.refinery.generator.ModelGenerator; | 12 | import tools.refinery.generator.*; |
13 | import tools.refinery.generator.ModelGeneratorFactory; | ||
14 | import tools.refinery.language.web.semantics.metadata.MetadataCreator; | 13 | import tools.refinery.language.web.semantics.metadata.MetadataCreator; |
15 | import tools.refinery.generator.ProblemLoader; | ||
16 | import tools.refinery.generator.ValidationErrorsException; | ||
17 | import tools.refinery.language.web.semantics.PartialInterpretation2Json; | 14 | import tools.refinery.language.web.semantics.PartialInterpretation2Json; |
18 | import tools.refinery.language.web.xtext.server.ThreadPoolExecutorServiceProvider; | 15 | import tools.refinery.language.web.xtext.server.ThreadPoolExecutorServiceProvider; |
19 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; | 16 | import tools.refinery.language.web.xtext.server.push.PushWebDocument; |
@@ -149,7 +146,7 @@ public class ModelGenerationWorker implements Runnable { | |||
149 | } | 146 | } |
150 | notifyResult(new ModelGenerationStatusResult(uuid, "Generating model")); | 147 | notifyResult(new ModelGenerationStatusResult(uuid, "Generating model")); |
151 | generator.setRandomSeed(randomSeed); | 148 | generator.setRandomSeed(randomSeed); |
152 | if (!generator.tryGenerate()) { | 149 | if (generator.tryGenerate() != GeneratorResult.SUCCESS) { |
153 | return new ModelGenerationErrorResult(uuid, "Problem is unsatisfiable"); | 150 | return new ModelGenerationErrorResult(uuid, "Problem is unsatisfiable"); |
154 | } | 151 | } |
155 | notifyResult(new ModelGenerationStatusResult(uuid, "Saving generated model")); | 152 | notifyResult(new ModelGenerationStatusResult(uuid, "Saving generated model")); |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java index a96b68f9..508e716e 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java | |||
@@ -72,7 +72,10 @@ class SemanticsWorker implements Callable<SemanticsResult> { | |||
72 | cancellationToken.checkCancelled(); | 72 | cancellationToken.checkCancelled(); |
73 | ModelSemantics semantics; | 73 | ModelSemantics semantics; |
74 | try { | 74 | try { |
75 | semantics = semanticsFactory.cancellationToken(cancellationToken).tryCreateSemantics(problem); | 75 | semantics = semanticsFactory |
76 | .cancellationToken(cancellationToken) | ||
77 | .keepNonExistingObjects(true) | ||
78 | .tryCreateSemantics(problem); | ||
76 | } catch (TranslationException e) { | 79 | } catch (TranslationException e) { |
77 | return new SemanticsResult(e.getMessage()); | 80 | return new SemanticsResult(e.getMessage()); |
78 | } catch (TracedException e) { | 81 | } catch (TracedException e) { |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java new file mode 100644 index 00000000..6ffaf267 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java | |||
@@ -0,0 +1,10 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.web.semantics.metadata; | ||
7 | |||
8 | public record BasePredicateDetail() implements RelationDetail { | ||
9 | public static final BasePredicateDetail INSTANCE = new BasePredicateDetail(); | ||
10 | } | ||
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java index 29f8ab8b..11f0a228 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java | |||
@@ -156,7 +156,10 @@ public class MetadataCreator { | |||
156 | } | 156 | } |
157 | 157 | ||
158 | private RelationDetail getPredicateDetail(PredicateDefinition predicate) { | 158 | private RelationDetail getPredicateDetail(PredicateDefinition predicate) { |
159 | return PredicateDetail.ofError(predicate.isError()); | 159 | if (ProblemUtil.isBasePredicate(predicate)) { |
160 | return BasePredicateDetail.INSTANCE; | ||
161 | } | ||
162 | return PredicateDetail.ofError(predicate.getKind() == PredicateKind.ERROR); | ||
160 | } | 163 | } |
161 | 164 | ||
162 | private QualifiedName getQualifiedName(EObject eObject) { | 165 | private QualifiedName getQualifiedName(EObject eObject) { |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java index bbe563cd..00177858 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java | |||
@@ -6,5 +6,5 @@ | |||
6 | package tools.refinery.language.web.semantics.metadata; | 6 | package tools.refinery.language.web.semantics.metadata; |
7 | 7 | ||
8 | public sealed interface RelationDetail permits ClassDetail, ReferenceDetail, PredicateDetail, OppositeReferenceDetail, | 8 | public sealed interface RelationDetail permits ClassDetail, ReferenceDetail, PredicateDetail, OppositeReferenceDetail, |
9 | BuiltInDetail { | 9 | BuiltInDetail, BasePredicateDetail { |
10 | } | 10 | } |
diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java index 818bd80e..13ae1221 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java | |||
@@ -37,7 +37,8 @@ public class XtextWebSocket implements ResponseHandler { | |||
37 | .registerSubtype(ReferenceDetail.class, "reference") | 37 | .registerSubtype(ReferenceDetail.class, "reference") |
38 | .registerSubtype(OppositeReferenceDetail.class, "opposite") | 38 | .registerSubtype(OppositeReferenceDetail.class, "opposite") |
39 | .registerSubtype(PredicateDetail.class, "predicate") | 39 | .registerSubtype(PredicateDetail.class, "predicate") |
40 | .registerSubtype(BuiltInDetail.class, "builtin")) | 40 | .registerSubtype(BuiltInDetail.class, "builtin") |
41 | .registerSubtype(BasePredicateDetail.class, "base")) | ||
41 | .create(); | 42 | .create(); |
42 | 43 | ||
43 | private final TransactionExecutor executor; | 44 | private final TransactionExecutor executor; |
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java index 52acee6d..fe4952a9 100644 --- a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java +++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/AwaitTerminationExecutorServiceProvider.java | |||
@@ -17,6 +17,13 @@ public class AwaitTerminationExecutorServiceProvider extends ExecutorServiceProv | |||
17 | private final List<RestartableCachedThreadPool> servicesToShutDown = new ArrayList<>(); | 17 | private final List<RestartableCachedThreadPool> servicesToShutDown = new ArrayList<>(); |
18 | 18 | ||
19 | @Override | 19 | @Override |
20 | public ExecutorService get(String key) { | ||
21 | synchronized (servicesToShutDown) { | ||
22 | return super.get(key); | ||
23 | } | ||
24 | } | ||
25 | |||
26 | @Override | ||
20 | protected ExecutorService createInstance(String key) { | 27 | protected ExecutorService createInstance(String key) { |
21 | var instance = new RestartableCachedThreadPool(() -> super.createInstance(key)); | 28 | var instance = new RestartableCachedThreadPool(() -> super.createInstance(key)); |
22 | synchronized (servicesToShutDown) { | 29 | synchronized (servicesToShutDown) { |
@@ -35,8 +42,8 @@ public class AwaitTerminationExecutorServiceProvider extends ExecutorServiceProv | |||
35 | 42 | ||
36 | @Override | 43 | @Override |
37 | public void dispose() { | 44 | public void dispose() { |
38 | super.dispose(); | ||
39 | synchronized (servicesToShutDown) { | 45 | synchronized (servicesToShutDown) { |
46 | super.dispose(); | ||
40 | for (var executorService : servicesToShutDown) { | 47 | for (var executorService : servicesToShutDown) { |
41 | executorService.waitForTermination(); | 48 | executorService.waitForTermination(); |
42 | } | 49 | } |
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java index 991ff114..4d55e548 100644 --- a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java +++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/RestartableCachedThreadPool.java | |||
@@ -13,29 +13,34 @@ import org.slf4j.LoggerFactory; | |||
13 | import java.util.Collection; | 13 | import java.util.Collection; |
14 | import java.util.List; | 14 | import java.util.List; |
15 | import java.util.concurrent.*; | 15 | import java.util.concurrent.*; |
16 | import java.util.concurrent.atomic.AtomicReference; | ||
16 | 17 | ||
17 | public class RestartableCachedThreadPool implements ExecutorService { | 18 | public class RestartableCachedThreadPool implements ExecutorService { |
18 | private static final Logger LOG = LoggerFactory.getLogger(RestartableCachedThreadPool.class); | 19 | private static final Logger LOG = LoggerFactory.getLogger(RestartableCachedThreadPool.class); |
19 | 20 | ||
20 | private ExecutorService delegate; | 21 | private final AtomicReference<ExecutorService> delegate = new AtomicReference<>(); |
21 | 22 | ||
22 | private final Provider<ExecutorService> executorServiceProvider; | 23 | private final Provider<ExecutorService> executorServiceProvider; |
23 | 24 | ||
24 | public RestartableCachedThreadPool(Provider<ExecutorService> executorServiceProvider) { | 25 | public RestartableCachedThreadPool(Provider<ExecutorService> executorServiceProvider) { |
25 | this.executorServiceProvider = executorServiceProvider; | 26 | this.executorServiceProvider = executorServiceProvider; |
26 | delegate = executorServiceProvider.get(); | 27 | delegate.set(executorServiceProvider.get()); |
27 | } | 28 | } |
28 | 29 | ||
29 | public void waitForAllTasksToFinish() { | 30 | public void waitForAllTasksToFinish() { |
30 | delegate.shutdown(); | 31 | var oldDelegate = delegate.getAndSet(executorServiceProvider.get()); |
31 | waitForTermination(); | 32 | oldDelegate.shutdown(); |
32 | delegate = executorServiceProvider.get(); | 33 | waitForTermination(oldDelegate); |
33 | } | 34 | } |
34 | 35 | ||
35 | public void waitForTermination() { | 36 | public void waitForTermination() { |
37 | waitForTermination(delegate.get()); | ||
38 | } | ||
39 | |||
40 | private static void waitForTermination(ExecutorService executorService) { | ||
36 | boolean result = false; | 41 | boolean result = false; |
37 | try { | 42 | try { |
38 | result = delegate.awaitTermination(10, TimeUnit.SECONDS); | 43 | result = executorService.awaitTermination(10, TimeUnit.SECONDS); |
39 | } catch (InterruptedException e) { | 44 | } catch (InterruptedException e) { |
40 | LOG.warn("Interrupted while waiting for delegate executor to stop", e); | 45 | LOG.warn("Interrupted while waiting for delegate executor to stop", e); |
41 | } | 46 | } |
@@ -46,70 +51,71 @@ public class RestartableCachedThreadPool implements ExecutorService { | |||
46 | 51 | ||
47 | @Override | 52 | @Override |
48 | public boolean awaitTermination(long arg0, @NotNull TimeUnit arg1) throws InterruptedException { | 53 | public boolean awaitTermination(long arg0, @NotNull TimeUnit arg1) throws InterruptedException { |
49 | return delegate.awaitTermination(arg0, arg1); | 54 | return delegate.get().awaitTermination(arg0, arg1); |
50 | } | 55 | } |
51 | 56 | ||
52 | @Override | 57 | @Override |
53 | public void execute(@NotNull Runnable arg0) { | 58 | public void execute(@NotNull Runnable arg0) { |
54 | delegate.execute(arg0); | 59 | delegate.get().execute(arg0); |
55 | } | 60 | } |
56 | 61 | ||
57 | @Override | 62 | @Override |
58 | public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> arg0, long arg1, | 63 | public <T> @NotNull List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> arg0, long arg1, |
59 | @NotNull TimeUnit arg2) | 64 | @NotNull TimeUnit arg2) |
60 | throws InterruptedException { | 65 | throws InterruptedException { |
61 | return delegate.invokeAll(arg0, arg1, arg2); | 66 | return delegate.get().invokeAll(arg0, arg1, arg2); |
62 | } | 67 | } |
63 | 68 | ||
64 | @Override | 69 | @Override |
65 | public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> arg0) throws InterruptedException { | 70 | public <T> @NotNull List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> arg0) |
66 | return delegate.invokeAll(arg0); | 71 | throws InterruptedException { |
72 | return delegate.get().invokeAll(arg0); | ||
67 | } | 73 | } |
68 | 74 | ||
69 | @Override | 75 | @Override |
70 | public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> arg0, long arg1, @NotNull TimeUnit arg2) | 76 | public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> arg0, long arg1, @NotNull TimeUnit arg2) |
71 | throws InterruptedException, ExecutionException, TimeoutException { | 77 | throws InterruptedException, ExecutionException, TimeoutException { |
72 | return delegate.invokeAny(arg0, arg1, arg2); | 78 | return delegate.get().invokeAny(arg0, arg1, arg2); |
73 | } | 79 | } |
74 | 80 | ||
75 | @Override | 81 | @Override |
76 | public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> arg0) throws InterruptedException, | 82 | public <T> @NotNull T invokeAny(@NotNull Collection<? extends Callable<T>> arg0) throws InterruptedException, |
77 | ExecutionException { | 83 | ExecutionException { |
78 | return delegate.invokeAny(arg0); | 84 | return delegate.get().invokeAny(arg0); |
79 | } | 85 | } |
80 | 86 | ||
81 | @Override | 87 | @Override |
82 | public boolean isShutdown() { | 88 | public boolean isShutdown() { |
83 | return delegate.isShutdown(); | 89 | return delegate.get().isShutdown(); |
84 | } | 90 | } |
85 | 91 | ||
86 | @Override | 92 | @Override |
87 | public boolean isTerminated() { | 93 | public boolean isTerminated() { |
88 | return delegate.isTerminated(); | 94 | return delegate.get().isTerminated(); |
89 | } | 95 | } |
90 | 96 | ||
91 | @Override | 97 | @Override |
92 | public void shutdown() { | 98 | public void shutdown() { |
93 | delegate.shutdown(); | 99 | delegate.get().shutdown(); |
94 | } | 100 | } |
95 | 101 | ||
96 | @Override | 102 | @Override |
97 | public List<Runnable> shutdownNow() { | 103 | public @NotNull List<Runnable> shutdownNow() { |
98 | return delegate.shutdownNow(); | 104 | return delegate.get().shutdownNow(); |
99 | } | 105 | } |
100 | 106 | ||
101 | @Override | 107 | @Override |
102 | public <T> Future<T> submit(@NotNull Callable<T> arg0) { | 108 | public <T> @NotNull Future<T> submit(@NotNull Callable<T> arg0) { |
103 | return delegate.submit(arg0); | 109 | return delegate.get().submit(arg0); |
104 | } | 110 | } |
105 | 111 | ||
106 | @Override | 112 | @Override |
107 | public <T> Future<T> submit(@NotNull Runnable arg0, T arg1) { | 113 | public <T> @NotNull Future<T> submit(@NotNull Runnable arg0, T arg1) { |
108 | return delegate.submit(arg0, arg1); | 114 | return delegate.get().submit(arg0, arg1); |
109 | } | 115 | } |
110 | 116 | ||
111 | @Override | 117 | @Override |
112 | public Future<?> submit(@NotNull Runnable arg0) { | 118 | public @NotNull Future<?> submit(@NotNull Runnable arg0) { |
113 | return delegate.submit(arg0); | 119 | return delegate.get().submit(arg0); |
114 | } | 120 | } |
115 | } | 121 | } |
diff --git a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java index d45a9d6b..6ccf1760 100644 --- a/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java +++ b/subprojects/language-web/src/test/java/tools/refinery/language/web/tests/WebSocketIntegrationTestClient.java | |||
@@ -18,7 +18,7 @@ import java.util.List; | |||
18 | import static org.junit.jupiter.api.Assertions.fail; | 18 | import static org.junit.jupiter.api.Assertions.fail; |
19 | 19 | ||
20 | public abstract class WebSocketIntegrationTestClient { | 20 | public abstract class WebSocketIntegrationTestClient { |
21 | private static final long TIMEOUT_MILLIS = Duration.ofSeconds(30).toMillis(); | 21 | private static final long TIMEOUT_MILLIS = Duration.ofSeconds(10).toMillis(); |
22 | 22 | ||
23 | private boolean finished = false; | 23 | private boolean finished = false; |
24 | 24 | ||
diff --git a/subprojects/language/build.gradle.kts b/subprojects/language/build.gradle.kts index dc338686..6cbecaa6 100644 --- a/subprojects/language/build.gradle.kts +++ b/subprojects/language/build.gradle.kts | |||
@@ -33,6 +33,7 @@ dependencies { | |||
33 | api(libs.xtext.core) | 33 | api(libs.xtext.core) |
34 | api(libs.xtext.xbase) | 34 | api(libs.xtext.xbase) |
35 | api(project(":refinery-language-model")) | 35 | api(project(":refinery-language-model")) |
36 | testFixturesApi(libs.junit.api) | ||
36 | testFixturesApi(libs.xtext.testing) | 37 | testFixturesApi(libs.xtext.testing) |
37 | mwe2(libs.xtext.generator) | 38 | mwe2(libs.xtext.generator) |
38 | mwe2(libs.xtext.generator.antlr) | 39 | mwe2(libs.xtext.generator.antlr) |
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 7e3b3c83..7cd62dcd 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext +++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext | |||
@@ -57,9 +57,14 @@ ReferenceDeclaration: | |||
57 | ReferenceMultiplicity returns Multiplicity: | 57 | ReferenceMultiplicity returns Multiplicity: |
58 | "[" Multiplicity "]"; | 58 | "[" Multiplicity "]"; |
59 | 59 | ||
60 | enum ErrorPredicateKind returns PredicateKind: | ||
61 | ERROR="error"; | ||
62 | |||
63 | enum PredicateKind: | ||
64 | ERROR="error" | PARTIAL="partial" | SHADOW="shadow"; | ||
65 | |||
60 | PredicateDefinition: | 66 | PredicateDefinition: |
61 | shadow?="shadow"? | 67 | (kind=ErrorPredicateKind | kind=PredicateKind? "pred") |
62 | ("pred" | error?="error" "pred"?) | ||
63 | name=Identifier | 68 | name=Identifier |
64 | "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" | 69 | "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" |
65 | ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? | 70 | ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? |
@@ -294,7 +299,7 @@ Identifier: | |||
294 | 299 | ||
295 | NonContainmentIdentifier: | 300 | NonContainmentIdentifier: |
296 | ID | "atom" | "multi" | "contained" | "problem" | "module" | | 301 | ID | "atom" | "multi" | "contained" | "problem" | "module" | |
297 | "datatype" | "aggregator" | "decision" | "propagation" | "computed"; | 302 | "datatype" | "aggregator" | "decision" | "propagation" | "shadow"; |
298 | 303 | ||
299 | Real returns ecore::EDouble: | 304 | Real returns ecore::EDouble: |
300 | EXPONENTIAL | INT "." (INT | EXPONENTIAL); | 305 | EXPONENTIAL | INT "." (INT | EXPONENTIAL); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java index 44f55563..97e93d46 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java | |||
@@ -102,7 +102,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { | |||
102 | if (declaration.getInvalidMultiplicity() == null) { | 102 | if (declaration.getInvalidMultiplicity() == null) { |
103 | var invalidMultiplicity = adapter.createInvalidMultiplicityPredicateIfAbsent(declaration, key -> { | 103 | var invalidMultiplicity = adapter.createInvalidMultiplicityPredicateIfAbsent(declaration, key -> { |
104 | var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); | 104 | var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); |
105 | predicate.setError(true); | 105 | predicate.setKind(PredicateKind.ERROR); |
106 | predicate.setName("invalidMultiplicity"); | 106 | predicate.setName("invalidMultiplicity"); |
107 | var parameter = ProblemFactory.eINSTANCE.createParameter(); | 107 | var parameter = ProblemFactory.eINSTANCE.createParameter(); |
108 | parameter.setParameterType(containingClassDeclaration); | 108 | parameter.setParameterType(containingClassDeclaration); |
@@ -125,7 +125,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { | |||
125 | if (ProblemUtil.hasComputedValue(predicateDefinition)) { | 125 | if (ProblemUtil.hasComputedValue(predicateDefinition)) { |
126 | var computedValue = adapter.createComputedValuePredicateIfAbsent(predicateDefinition, key -> { | 126 | var computedValue = adapter.createComputedValuePredicateIfAbsent(predicateDefinition, key -> { |
127 | var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); | 127 | var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); |
128 | predicate.setShadow(true); | 128 | predicate.setKind(PredicateKind.SHADOW); |
129 | predicate.setName("computed"); | 129 | predicate.setName("computed"); |
130 | return predicate; | 130 | return predicate; |
131 | }); | 131 | }); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java index f1e5a7ac..a8e3a783 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java | |||
@@ -75,7 +75,7 @@ public class TypedModule { | |||
75 | private void checkTypes(PredicateDefinition predicateDefinition) { | 75 | private void checkTypes(PredicateDefinition predicateDefinition) { |
76 | for (var conjunction : predicateDefinition.getBodies()) { | 76 | for (var conjunction : predicateDefinition.getBodies()) { |
77 | for (var literal : conjunction.getLiterals()) { | 77 | for (var literal : conjunction.getLiterals()) { |
78 | coerceIntoLiteral(literal, predicateDefinition.isShadow()); | 78 | coerceIntoLiteral(literal, predicateDefinition.getKind() == PredicateKind.SHADOW); |
79 | } | 79 | } |
80 | } | 80 | } |
81 | } | 81 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index 067e684a..75e2ded0 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java | |||
@@ -50,17 +50,19 @@ public final class ProblemUtil { | |||
50 | } | 50 | } |
51 | 51 | ||
52 | public static boolean isError(EObject eObject) { | 52 | public static boolean isError(EObject eObject) { |
53 | return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError(); | 53 | return eObject instanceof PredicateDefinition predicateDefinition && |
54 | predicateDefinition.getKind() == PredicateKind.ERROR; | ||
54 | } | 55 | } |
55 | 56 | ||
56 | public static boolean isShadow(EObject eObject) { | 57 | public static boolean isShadow(EObject eObject) { |
57 | return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isShadow(); | 58 | return eObject instanceof PredicateDefinition predicateDefinition && |
59 | predicateDefinition.getKind() == PredicateKind.SHADOW; | ||
58 | } | 60 | } |
59 | 61 | ||
60 | public static boolean mayReferToShadow(EObject context) { | 62 | public static boolean mayReferToShadow(EObject context) { |
61 | var definitionContext = EcoreUtil2.getContainerOfType(context, ParametricDefinition.class); | 63 | var definitionContext = EcoreUtil2.getContainerOfType(context, ParametricDefinition.class); |
62 | return switch (definitionContext) { | 64 | return switch (definitionContext) { |
63 | case PredicateDefinition predicateDefinition -> predicateDefinition.isShadow(); | 65 | case PredicateDefinition predicateDefinition -> predicateDefinition.getKind() == PredicateKind.SHADOW; |
64 | case RuleDefinition ignored -> true; | 66 | case RuleDefinition ignored -> true; |
65 | case null, default -> false; | 67 | case null, default -> false; |
66 | }; | 68 | }; |
@@ -112,8 +114,26 @@ public final class ProblemUtil { | |||
112 | return true; | 114 | return true; |
113 | } | 115 | } |
114 | 116 | ||
117 | public static boolean isDerivedStatePredicate(PredicateDefinition predicateDefinition) { | ||
118 | var containingFeature = predicateDefinition.eContainingFeature(); | ||
119 | return containingFeature == ProblemPackage.Literals.REFERENCE_DECLARATION__INVALID_MULTIPLICITY || | ||
120 | containingFeature == ProblemPackage.Literals.PREDICATE_DEFINITION__COMPUTED_VALUE; | ||
121 | } | ||
122 | |||
123 | public static boolean isBasePredicate(PredicateDefinition predicateDefinition) { | ||
124 | if (isBuiltIn(predicateDefinition) || predicateDefinition == null) { | ||
125 | // Built-in predicates have no clauses, but are not base. | ||
126 | return false; | ||
127 | } | ||
128 | return switch (predicateDefinition.getKind()) { | ||
129 | case DEFAULT -> predicateDefinition.getBodies().isEmpty(); | ||
130 | case PARTIAL -> true; | ||
131 | default -> false; | ||
132 | }; | ||
133 | } | ||
134 | |||
115 | public static boolean hasComputedValue(PredicateDefinition predicateDefinition) { | 135 | public static boolean hasComputedValue(PredicateDefinition predicateDefinition) { |
116 | return !predicateDefinition.isShadow() && !predicateDefinition.getBodies().isEmpty(); | 136 | return predicateDefinition.getKind() != PredicateKind.SHADOW && !isBasePredicate(predicateDefinition); |
117 | } | 137 | } |
118 | 138 | ||
119 | public static boolean isTypeLike(Relation relation) { | 139 | public static boolean isTypeLike(Relation relation) { |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index aef1e71d..74a6a0fc 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java | |||
@@ -47,6 +47,7 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
47 | public static final String INVALID_REFERENCE_TYPE_ISSUE = ISSUE_PREFIX + "INVALID_REFERENCE_TYPE"; | 47 | public static final String INVALID_REFERENCE_TYPE_ISSUE = ISSUE_PREFIX + "INVALID_REFERENCE_TYPE"; |
48 | public static final String INVALID_ARITY_ISSUE = ISSUE_PREFIX + "INVALID_ARITY"; | 48 | public static final String INVALID_ARITY_ISSUE = ISSUE_PREFIX + "INVALID_ARITY"; |
49 | public static final String INVALID_MODALITY_ISSUE = ISSUE_PREFIX + "INVALID_MODALITY"; | 49 | public static final String INVALID_MODALITY_ISSUE = ISSUE_PREFIX + "INVALID_MODALITY"; |
50 | public static final String INVALID_PREDICATE_ISSUE = ISSUE_PREFIX + "INVALID_PREDICATE"; | ||
50 | public static final String INVALID_RULE_ISSUE = ISSUE_PREFIX + "INVALID_RULE"; | 51 | public static final String INVALID_RULE_ISSUE = ISSUE_PREFIX + "INVALID_RULE"; |
51 | public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE"; | 52 | public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE"; |
52 | public static final String SHADOW_RELATION_ISSUE = ISSUE_PREFIX + "SHADOW_RELATION"; | 53 | public static final String SHADOW_RELATION_ISSUE = ISSUE_PREFIX + "SHADOW_RELATION"; |
@@ -381,10 +382,27 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
381 | 382 | ||
382 | @Check | 383 | @Check |
383 | public void checkPredicateDefinition(PredicateDefinition predicateDefinition) { | 384 | public void checkPredicateDefinition(PredicateDefinition predicateDefinition) { |
384 | if (predicateDefinition.isError() && predicateDefinition.isShadow()) { | 385 | if (ProblemUtil.isBuiltIn(predicateDefinition) || ProblemUtil.isDerivedStatePredicate(predicateDefinition)) { |
385 | var message = "Shadow predicates cannot be marked as error predicates."; | 386 | return; |
386 | acceptError(message, predicateDefinition, ProblemPackage.Literals.PREDICATE_DEFINITION__ERROR, 0, | 387 | } |
387 | SHADOW_RELATION_ISSUE); | 388 | String message = null; |
389 | if (ProblemUtil.isBasePredicate(predicateDefinition)) { | ||
390 | if (!predicateDefinition.getBodies().isEmpty()) { | ||
391 | var predicateType = predicateDefinition.getKind() == PredicateKind.PARTIAL ? "Partial base predicate" : | ||
392 | "Base predicate"; | ||
393 | message = "%s '%s' must not have any clauses.".formatted(predicateType, predicateDefinition.getName()); | ||
394 | } | ||
395 | } else if (predicateDefinition.getBodies().isEmpty()) { | ||
396 | var predicateType = switch (predicateDefinition.getKind()) { | ||
397 | case ERROR -> "Error predicate"; | ||
398 | case SHADOW -> "Shadow predicate"; | ||
399 | default -> "Predicate"; | ||
400 | }; | ||
401 | message = "%s '%s' must have at least one clause.".formatted(predicateType, predicateDefinition.getName()); | ||
402 | } | ||
403 | if (message != null) { | ||
404 | acceptError(message, predicateDefinition, ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0, | ||
405 | INVALID_PREDICATE_ISSUE); | ||
388 | } | 406 | } |
389 | } | 407 | } |
390 | 408 | ||
@@ -504,6 +522,11 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
504 | acceptError(message, assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, 0, | 522 | acceptError(message, assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, 0, |
505 | SHADOW_RELATION_ISSUE); | 523 | SHADOW_RELATION_ISSUE); |
506 | } | 524 | } |
525 | if (ProblemUtil.isError(relation)) { | ||
526 | var message = "Assertions for error predicates are not supported."; | ||
527 | acceptError(message, assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, 0, | ||
528 | UNSUPPORTED_ASSERTION_ISSUE); | ||
529 | } | ||
507 | int argumentCount = assertion.getArguments().size(); | 530 | int argumentCount = assertion.getArguments().size(); |
508 | checkArity(assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, argumentCount); | 531 | checkArity(assertion, ProblemPackage.Literals.ABSTRACT_ASSERTION__RELATION, argumentCount); |
509 | } | 532 | } |
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 17ae5fbb..68c5d31e 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 | |||
@@ -6,17 +6,13 @@ | |||
6 | package tools.refinery.language.tests; | 6 | package tools.refinery.language.tests; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Test; | 9 | import org.junit.jupiter.api.Test; |
12 | import org.junit.jupiter.api.extension.ExtendWith; | 10 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
13 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | ||
14 | 11 | ||
15 | import static org.hamcrest.MatcherAssert.assertThat; | 12 | import static org.hamcrest.MatcherAssert.assertThat; |
16 | import static org.hamcrest.Matchers.empty; | 13 | import static org.hamcrest.Matchers.empty; |
17 | 14 | ||
18 | @ExtendWith(InjectionExtension.class) | 15 | @InjectWithRefinery |
19 | @InjectWith(ProblemInjectorProvider.class) | ||
20 | class ProblemParsingTest { | 16 | class ProblemParsingTest { |
21 | @Inject | 17 | @Inject |
22 | private ProblemParseHelper parseHelper; | 18 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/documentation/DocumentationCommentParserTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/documentation/DocumentationCommentParserTest.java index 0566e7e2..5227a733 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/documentation/DocumentationCommentParserTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/documentation/DocumentationCommentParserTest.java | |||
@@ -6,23 +6,19 @@ | |||
6 | package tools.refinery.language.tests.documentation; | 6 | package tools.refinery.language.tests.documentation; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.extension.ExtendWith; | ||
12 | import org.junit.jupiter.params.ParameterizedTest; | 9 | import org.junit.jupiter.params.ParameterizedTest; |
13 | import org.junit.jupiter.params.provider.Arguments; | 10 | import org.junit.jupiter.params.provider.Arguments; |
14 | import org.junit.jupiter.params.provider.MethodSource; | 11 | import org.junit.jupiter.params.provider.MethodSource; |
15 | import tools.refinery.language.documentation.DocumentationCommentParser; | 12 | import tools.refinery.language.documentation.DocumentationCommentParser; |
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
18 | 15 | ||
19 | import java.util.stream.Stream; | 16 | import java.util.stream.Stream; |
20 | 17 | ||
21 | import static org.hamcrest.MatcherAssert.assertThat; | 18 | import static org.hamcrest.MatcherAssert.assertThat; |
22 | import static org.hamcrest.Matchers.is; | 19 | import static org.hamcrest.Matchers.is; |
23 | 20 | ||
24 | @ExtendWith(InjectionExtension.class) | 21 | @InjectWithRefinery |
25 | @InjectWith(ProblemInjectorProvider.class) | ||
26 | class DocumentationCommentParserTest { | 22 | class DocumentationCommentParserTest { |
27 | @Inject | 23 | @Inject |
28 | private ProblemParseHelper parseHelper; | 24 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java index 4a15f9de..52806be1 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java | |||
@@ -13,21 +13,17 @@ import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess; | |||
13 | import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement; | 13 | import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement; |
14 | import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder; | 14 | import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder; |
15 | import org.eclipse.xtext.resource.XtextResource; | 15 | import org.eclipse.xtext.resource.XtextResource; |
16 | import org.eclipse.xtext.testing.InjectWith; | ||
17 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
18 | import org.eclipse.xtext.testing.util.ParseHelper; | 16 | import org.eclipse.xtext.testing.util.ParseHelper; |
19 | import org.junit.jupiter.api.Test; | 17 | import org.junit.jupiter.api.Test; |
20 | import org.junit.jupiter.api.extension.ExtendWith; | ||
21 | import tools.refinery.language.model.problem.Problem; | 18 | import tools.refinery.language.model.problem.Problem; |
22 | import tools.refinery.language.tests.ProblemInjectorProvider; | 19 | import tools.refinery.language.tests.InjectWithRefinery; |
23 | 20 | ||
24 | import java.util.List; | 21 | import java.util.List; |
25 | 22 | ||
26 | import static org.hamcrest.MatcherAssert.assertThat; | 23 | import static org.hamcrest.MatcherAssert.assertThat; |
27 | import static org.hamcrest.Matchers.equalTo; | 24 | import static org.hamcrest.Matchers.equalTo; |
28 | 25 | ||
29 | @ExtendWith(InjectionExtension.class) | 26 | @InjectWithRefinery |
30 | @InjectWith(ProblemInjectorProvider.class) | ||
31 | class ProblemFormatterTest { | 27 | class ProblemFormatterTest { |
32 | @Inject | 28 | @Inject |
33 | private ParseHelper<Problem> parseHelper; | 29 | private ParseHelper<Problem> parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/linking/AmbiguousReferenceTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/linking/AmbiguousReferenceTest.java index 464c207c..ffeaace2 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/linking/AmbiguousReferenceTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/linking/AmbiguousReferenceTest.java | |||
@@ -8,19 +8,15 @@ package tools.refinery.language.tests.linking; | |||
8 | 8 | ||
9 | import com.google.inject.Inject; | 9 | import com.google.inject.Inject; |
10 | import org.eclipse.xtext.diagnostics.Diagnostic; | 10 | import org.eclipse.xtext.diagnostics.Diagnostic; |
11 | import org.eclipse.xtext.testing.InjectWith; | ||
12 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | 11 | import org.junit.jupiter.params.ParameterizedTest; |
15 | import org.junit.jupiter.params.provider.ValueSource; | 12 | import org.junit.jupiter.params.provider.ValueSource; |
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
18 | 15 | ||
19 | import static org.hamcrest.MatcherAssert.assertThat; | 16 | import static org.hamcrest.MatcherAssert.assertThat; |
20 | import static org.hamcrest.Matchers.*; | 17 | import static org.hamcrest.Matchers.*; |
21 | 18 | ||
22 | @ExtendWith(InjectionExtension.class) | 19 | @InjectWithRefinery |
23 | @InjectWith(ProblemInjectorProvider.class) | ||
24 | class AmbiguousReferenceTest { | 20 | class AmbiguousReferenceTest { |
25 | @Inject | 21 | @Inject |
26 | private ProblemParseHelper parseHelper; | 22 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/IdentifierTokenProviderTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/IdentifierTokenProviderTest.java index 37d38dd9..e798cc95 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/IdentifierTokenProviderTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/IdentifierTokenProviderTest.java | |||
@@ -6,23 +6,19 @@ | |||
6 | package tools.refinery.language.tests.parser.antlr; | 6 | package tools.refinery.language.tests.parser.antlr; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.extension.ExtendWith; | ||
12 | import org.junit.jupiter.params.ParameterizedTest; | 9 | import org.junit.jupiter.params.ParameterizedTest; |
13 | import org.junit.jupiter.params.provider.Arguments; | 10 | import org.junit.jupiter.params.provider.Arguments; |
14 | import org.junit.jupiter.params.provider.MethodSource; | 11 | import org.junit.jupiter.params.provider.MethodSource; |
12 | import tools.refinery.language.tests.InjectWithRefinery; | ||
15 | import tools.refinery.language.parser.antlr.IdentifierTokenProvider; | 13 | import tools.refinery.language.parser.antlr.IdentifierTokenProvider; |
16 | import tools.refinery.language.parser.antlr.internal.InternalProblemParser; | 14 | import tools.refinery.language.parser.antlr.internal.InternalProblemParser; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
18 | 15 | ||
19 | import java.util.stream.Stream; | 16 | import java.util.stream.Stream; |
20 | 17 | ||
21 | import static org.hamcrest.MatcherAssert.assertThat; | 18 | import static org.hamcrest.MatcherAssert.assertThat; |
22 | import static org.hamcrest.Matchers.equalTo; | 19 | import static org.hamcrest.Matchers.equalTo; |
23 | 20 | ||
24 | @ExtendWith(InjectionExtension.class) | 21 | @InjectWithRefinery |
25 | @InjectWith(ProblemInjectorProvider.class) | ||
26 | class IdentifierTokenProviderTest { | 22 | class IdentifierTokenProviderTest { |
27 | @Inject | 23 | @Inject |
28 | private IdentifierTokenProvider identifierTokenProvider; | 24 | private IdentifierTokenProvider identifierTokenProvider; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/ProblemTokenSourceTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/ProblemTokenSourceTest.java index 644744a0..d42a5728 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/ProblemTokenSourceTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/parser/antlr/ProblemTokenSourceTest.java | |||
@@ -12,18 +12,15 @@ import org.antlr.runtime.ANTLRStringStream; | |||
12 | import org.antlr.runtime.Token; | 12 | import org.antlr.runtime.Token; |
13 | import org.eclipse.xtext.parser.antlr.Lexer; | 13 | import org.eclipse.xtext.parser.antlr.Lexer; |
14 | import org.eclipse.xtext.parser.antlr.LexerBindings; | 14 | import org.eclipse.xtext.parser.antlr.LexerBindings; |
15 | import org.eclipse.xtext.testing.InjectWith; | ||
16 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
17 | import org.hamcrest.Matcher; | 15 | import org.hamcrest.Matcher; |
18 | import org.junit.jupiter.api.extension.ExtendWith; | ||
19 | import org.junit.jupiter.params.ParameterizedTest; | 16 | import org.junit.jupiter.params.ParameterizedTest; |
20 | import org.junit.jupiter.params.provider.Arguments; | 17 | import org.junit.jupiter.params.provider.Arguments; |
21 | import org.junit.jupiter.params.provider.MethodSource; | 18 | import org.junit.jupiter.params.provider.MethodSource; |
22 | import org.junit.jupiter.params.provider.ValueSource; | 19 | import org.junit.jupiter.params.provider.ValueSource; |
20 | import tools.refinery.language.tests.InjectWithRefinery; | ||
23 | import tools.refinery.language.parser.antlr.IdentifierTokenProvider; | 21 | import tools.refinery.language.parser.antlr.IdentifierTokenProvider; |
24 | import tools.refinery.language.parser.antlr.ProblemTokenSource; | 22 | import tools.refinery.language.parser.antlr.ProblemTokenSource; |
25 | import tools.refinery.language.parser.antlr.internal.InternalProblemParser; | 23 | import tools.refinery.language.parser.antlr.internal.InternalProblemParser; |
26 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
27 | 24 | ||
28 | import java.util.ArrayList; | 25 | import java.util.ArrayList; |
29 | import java.util.List; | 26 | import java.util.List; |
@@ -32,8 +29,7 @@ import java.util.stream.Stream; | |||
32 | import static org.hamcrest.MatcherAssert.assertThat; | 29 | import static org.hamcrest.MatcherAssert.assertThat; |
33 | import static org.hamcrest.Matchers.*; | 30 | import static org.hamcrest.Matchers.*; |
34 | 31 | ||
35 | @ExtendWith(InjectionExtension.class) | 32 | @InjectWithRefinery |
36 | @InjectWith(ProblemInjectorProvider.class) | ||
37 | class ProblemTokenSourceTest { | 33 | class ProblemTokenSourceTest { |
38 | @Inject | 34 | @Inject |
39 | @Named(LexerBindings.RUNTIME) | 35 | @Named(LexerBindings.RUNTIME) |
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 a9c5f62a..bfac1c0c 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 | |||
@@ -6,22 +6,18 @@ | |||
6 | package tools.refinery.language.tests.parser.antlr; | 6 | package tools.refinery.language.tests.parser.antlr; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Test; | 9 | import org.junit.jupiter.api.Test; |
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import tools.refinery.language.model.problem.ArithmeticBinaryExpr; | 10 | import tools.refinery.language.model.problem.ArithmeticBinaryExpr; |
14 | import tools.refinery.language.model.problem.Atom; | 11 | import tools.refinery.language.model.problem.Atom; |
15 | import tools.refinery.language.model.problem.BinaryOp; | 12 | import tools.refinery.language.model.problem.BinaryOp; |
16 | import tools.refinery.language.model.problem.ComparisonExpr; | 13 | import tools.refinery.language.model.problem.ComparisonExpr; |
17 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 14 | import tools.refinery.language.tests.InjectWithRefinery; |
18 | import tools.refinery.language.tests.ProblemInjectorProvider; | 15 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
19 | 16 | ||
20 | import static org.hamcrest.MatcherAssert.assertThat; | 17 | import static org.hamcrest.MatcherAssert.assertThat; |
21 | import static org.hamcrest.Matchers.*; | 18 | import static org.hamcrest.Matchers.*; |
22 | 19 | ||
23 | @ExtendWith(InjectionExtension.class) | 20 | @InjectWithRefinery |
24 | @InjectWith(ProblemInjectorProvider.class) | ||
25 | class TransitiveClosureParserTest { | 21 | class TransitiveClosureParserTest { |
26 | @Inject | 22 | @Inject |
27 | private ProblemParseHelper parseHelper; | 23 | private ProblemParseHelper parseHelper; |
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 deleted file mode 100644 index 15636483..00000000 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/rules/RuleParsingTest.java +++ /dev/null | |||
@@ -1,40 +0,0 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.tests.rules; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Disabled; | ||
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | ||
14 | import org.junit.jupiter.params.provider.ValueSource; | ||
15 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | ||
16 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
17 | |||
18 | import static org.hamcrest.MatcherAssert.assertThat; | ||
19 | import static org.hamcrest.Matchers.empty; | ||
20 | |||
21 | @Disabled("TODO: Rework transformation rules") | ||
22 | @ExtendWith(InjectionExtension.class) | ||
23 | @InjectWith(ProblemInjectorProvider.class) | ||
24 | class RuleParsingTest { | ||
25 | @Inject | ||
26 | private ProblemParseHelper parseHelper; | ||
27 | |||
28 | @ParameterizedTest | ||
29 | @ValueSource(strings = { """ | ||
30 | pred Person(p). | ||
31 | rule r(p1): must Person(p1) ==> Person(p1): false. | ||
32 | """, """ | ||
33 | pred Person(p). | ||
34 | rule r(p1): must Person(p1) ==> !Person(p1). | ||
35 | """ }) | ||
36 | void simpleTest(String text) { | ||
37 | var problem = parseHelper.parse(text); | ||
38 | assertThat(problem.getResourceErrors(), empty()); | ||
39 | } | ||
40 | } | ||
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 0704e026..ca843734 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 | |||
@@ -6,26 +6,22 @@ | |||
6 | package tools.refinery.language.tests.scoping; | 6 | package tools.refinery.language.tests.scoping; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Disabled; | 9 | import org.junit.jupiter.api.Disabled; |
12 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | 11 | import org.junit.jupiter.params.ParameterizedTest; |
15 | import org.junit.jupiter.params.provider.Arguments; | 12 | import org.junit.jupiter.params.provider.Arguments; |
16 | import org.junit.jupiter.params.provider.MethodSource; | 13 | import org.junit.jupiter.params.provider.MethodSource; |
17 | import org.junit.jupiter.params.provider.ValueSource; | 14 | import org.junit.jupiter.params.provider.ValueSource; |
18 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 15 | import tools.refinery.language.tests.InjectWithRefinery; |
19 | import tools.refinery.language.model.tests.utils.WrappedProblem; | 16 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
20 | import tools.refinery.language.tests.ProblemInjectorProvider; | 17 | import tools.refinery.language.tests.utils.WrappedProblem; |
21 | 18 | ||
22 | import java.util.stream.Stream; | 19 | import java.util.stream.Stream; |
23 | 20 | ||
24 | import static org.hamcrest.MatcherAssert.assertThat; | 21 | import static org.hamcrest.MatcherAssert.assertThat; |
25 | import static org.hamcrest.Matchers.*; | 22 | import static org.hamcrest.Matchers.*; |
26 | 23 | ||
27 | @ExtendWith(InjectionExtension.class) | 24 | @InjectWithRefinery |
28 | @InjectWith(ProblemInjectorProvider.class) | ||
29 | class NodeScopingTest { | 25 | class NodeScopingTest { |
30 | @Inject | 26 | @Inject |
31 | private ProblemParseHelper parseHelper; | 27 | private ProblemParseHelper parseHelper; |
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 ad583f8e..2757e08a 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 | |||
@@ -9,17 +9,14 @@ import com.google.inject.Inject; | |||
9 | import org.eclipse.emf.common.util.URI; | 9 | import org.eclipse.emf.common.util.URI; |
10 | import org.eclipse.emf.ecore.resource.Resource; | 10 | import org.eclipse.emf.ecore.resource.Resource; |
11 | import org.eclipse.emf.ecore.resource.ResourceSet; | 11 | import org.eclipse.emf.ecore.resource.ResourceSet; |
12 | import org.eclipse.xtext.testing.InjectWith; | ||
13 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
14 | import org.junit.jupiter.api.BeforeEach; | 12 | import org.junit.jupiter.api.BeforeEach; |
15 | import org.junit.jupiter.api.Test; | 13 | import org.junit.jupiter.api.Test; |
16 | import org.junit.jupiter.api.extension.ExtendWith; | ||
17 | import org.junit.jupiter.params.ParameterizedTest; | 14 | import org.junit.jupiter.params.ParameterizedTest; |
18 | import org.junit.jupiter.params.provider.Arguments; | 15 | import org.junit.jupiter.params.provider.Arguments; |
19 | import org.junit.jupiter.params.provider.MethodSource; | 16 | import org.junit.jupiter.params.provider.MethodSource; |
20 | import tools.refinery.language.model.problem.*; | 17 | import tools.refinery.language.model.problem.*; |
21 | import tools.refinery.language.model.tests.utils.WrappedProblem; | 18 | import tools.refinery.language.tests.InjectWithRefinery; |
22 | import tools.refinery.language.tests.ProblemInjectorProvider; | 19 | import tools.refinery.language.tests.utils.WrappedProblem; |
23 | 20 | ||
24 | import java.io.ByteArrayOutputStream; | 21 | import java.io.ByteArrayOutputStream; |
25 | import java.io.IOException; | 22 | import java.io.IOException; |
@@ -29,8 +26,7 @@ import java.util.stream.Stream; | |||
29 | import static org.hamcrest.MatcherAssert.assertThat; | 26 | import static org.hamcrest.MatcherAssert.assertThat; |
30 | import static org.hamcrest.Matchers.equalTo; | 27 | import static org.hamcrest.Matchers.equalTo; |
31 | 28 | ||
32 | @ExtendWith(InjectionExtension.class) | 29 | @InjectWithRefinery |
33 | @InjectWith(ProblemInjectorProvider.class) | ||
34 | class ProblemSerializerTest { | 30 | class ProblemSerializerTest { |
35 | @Inject | 31 | @Inject |
36 | private ResourceSet resourceSet; | 32 | private ResourceSet resourceSet; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java index 68e9fa8d..84ec19ab 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java | |||
@@ -7,16 +7,13 @@ package tools.refinery.language.tests.validation; | |||
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.emf.common.util.Diagnostic; | 9 | import org.eclipse.emf.common.util.Diagnostic; |
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | 11 | import org.junit.jupiter.params.ParameterizedTest; |
15 | import org.junit.jupiter.params.provider.Arguments; | 12 | import org.junit.jupiter.params.provider.Arguments; |
16 | import org.junit.jupiter.params.provider.MethodSource; | 13 | import org.junit.jupiter.params.provider.MethodSource; |
17 | import org.junit.jupiter.params.provider.ValueSource; | 14 | import org.junit.jupiter.params.provider.ValueSource; |
18 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 15 | import tools.refinery.language.tests.InjectWithRefinery; |
19 | import tools.refinery.language.tests.ProblemInjectorProvider; | 16 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
20 | import tools.refinery.language.validation.ProblemValidator; | 17 | import tools.refinery.language.validation.ProblemValidator; |
21 | 18 | ||
22 | import java.util.stream.Stream; | 19 | import java.util.stream.Stream; |
@@ -24,8 +21,7 @@ import java.util.stream.Stream; | |||
24 | import static org.hamcrest.MatcherAssert.assertThat; | 21 | import static org.hamcrest.MatcherAssert.assertThat; |
25 | import static org.hamcrest.Matchers.*; | 22 | import static org.hamcrest.Matchers.*; |
26 | 23 | ||
27 | @ExtendWith(InjectionExtension.class) | 24 | @InjectWithRefinery |
28 | @InjectWith(ProblemInjectorProvider.class) | ||
29 | class ArityValidationTest { | 25 | class ArityValidationTest { |
30 | @Inject | 26 | @Inject |
31 | private ProblemParseHelper parseHelper; | 27 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java index b995d0bb..67bea5d6 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java | |||
@@ -6,22 +6,17 @@ | |||
6 | package tools.refinery.language.tests.validation; | 6 | package tools.refinery.language.tests.validation; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Test; | 9 | import org.junit.jupiter.api.Test; |
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | 10 | import org.junit.jupiter.params.ParameterizedTest; |
14 | import org.junit.jupiter.params.provider.ValueSource; | 11 | import org.junit.jupiter.params.provider.ValueSource; |
15 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 12 | import tools.refinery.language.tests.InjectWithRefinery; |
16 | import tools.refinery.language.tests.ProblemInjectorProvider; | 13 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
17 | import tools.refinery.language.validation.ProblemValidator; | 14 | import tools.refinery.language.validation.ProblemValidator; |
18 | 15 | ||
19 | import static org.hamcrest.MatcherAssert.assertThat; | 16 | import static org.hamcrest.MatcherAssert.assertThat; |
20 | import static org.hamcrest.Matchers.*; | 17 | import static org.hamcrest.Matchers.*; |
21 | import static org.hamcrest.Matchers.is; | ||
22 | 18 | ||
23 | @ExtendWith(InjectionExtension.class) | 19 | @InjectWithRefinery |
24 | @InjectWith(ProblemInjectorProvider.class) | ||
25 | class AssertionValidationTest { | 20 | class AssertionValidationTest { |
26 | @Inject | 21 | @Inject |
27 | private ProblemParseHelper parseHelper; | 22 | private ProblemParseHelper parseHelper; |
@@ -107,4 +102,18 @@ class AssertionValidationTest { | |||
107 | assertThat(issues, not(hasItem(hasProperty("issueCode", | 102 | assertThat(issues, not(hasItem(hasProperty("issueCode", |
108 | is(ProblemValidator.UNSUPPORTED_ASSERTION_ISSUE))))); | 103 | is(ProblemValidator.UNSUPPORTED_ASSERTION_ISSUE))))); |
109 | } | 104 | } |
105 | |||
106 | @Test | ||
107 | void errorPredicateAssertionTest() { | ||
108 | var problem = parseHelper.parse(""" | ||
109 | class Foo. | ||
110 | |||
111 | error bar(x) <-> Foo(x). | ||
112 | |||
113 | bar(f1). | ||
114 | """); | ||
115 | var issues = problem.validate(); | ||
116 | assertThat(issues, hasItem(hasProperty("issueCode", | ||
117 | is(ProblemValidator.UNSUPPORTED_ASSERTION_ISSUE)))); | ||
118 | } | ||
110 | } | 119 | } |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java index a9e0e311..ef0c62ec 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java | |||
@@ -7,21 +7,17 @@ package tools.refinery.language.tests.validation; | |||
7 | 7 | ||
8 | 8 | ||
9 | import com.google.inject.Inject; | 9 | import com.google.inject.Inject; |
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | 11 | import org.junit.jupiter.params.ParameterizedTest; |
15 | import org.junit.jupiter.params.provider.ValueSource; | 12 | import org.junit.jupiter.params.provider.ValueSource; |
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
18 | import tools.refinery.language.validation.ProblemValidator; | 15 | import tools.refinery.language.validation.ProblemValidator; |
19 | 16 | ||
20 | import static org.hamcrest.MatcherAssert.assertThat; | 17 | import static org.hamcrest.MatcherAssert.assertThat; |
21 | import static org.hamcrest.Matchers.*; | 18 | import static org.hamcrest.Matchers.*; |
22 | 19 | ||
23 | @ExtendWith(InjectionExtension.class) | 20 | @InjectWithRefinery |
24 | @InjectWith(ProblemInjectorProvider.class) | ||
25 | class AssignmentValidationTest { | 21 | class AssignmentValidationTest { |
26 | @Inject | 22 | @Inject |
27 | private ProblemParseHelper parseHelper; | 23 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java index 00ad051b..6dc16029 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java | |||
@@ -6,15 +6,12 @@ | |||
6 | package tools.refinery.language.tests.validation; | 6 | package tools.refinery.language.tests.validation; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.xtext.testing.InjectWith; | ||
10 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
11 | import org.junit.jupiter.api.Test; | 9 | import org.junit.jupiter.api.Test; |
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | 10 | import org.junit.jupiter.params.ParameterizedTest; |
14 | import org.junit.jupiter.params.provider.Arguments; | 11 | import org.junit.jupiter.params.provider.Arguments; |
15 | import org.junit.jupiter.params.provider.MethodSource; | 12 | import org.junit.jupiter.params.provider.MethodSource; |
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
18 | import tools.refinery.language.validation.ProblemValidator; | 15 | import tools.refinery.language.validation.ProblemValidator; |
19 | 16 | ||
20 | import java.util.stream.Stream; | 17 | import java.util.stream.Stream; |
@@ -22,8 +19,7 @@ import java.util.stream.Stream; | |||
22 | import static org.hamcrest.MatcherAssert.assertThat; | 19 | import static org.hamcrest.MatcherAssert.assertThat; |
23 | import static org.hamcrest.Matchers.*; | 20 | import static org.hamcrest.Matchers.*; |
24 | 21 | ||
25 | @ExtendWith(InjectionExtension.class) | 22 | @InjectWithRefinery |
26 | @InjectWith(ProblemInjectorProvider.class) | ||
27 | class ModuleValidationTest { | 23 | class ModuleValidationTest { |
28 | @Inject | 24 | @Inject |
29 | private ProblemParseHelper parseHelper; | 25 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/MultiplicityValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/MultiplicityValidationTest.java index a8bcb1a6..81264d3a 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/MultiplicityValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/MultiplicityValidationTest.java | |||
@@ -7,20 +7,16 @@ package tools.refinery.language.tests.validation; | |||
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.emf.common.util.Diagnostic; | 9 | import org.eclipse.emf.common.util.Diagnostic; |
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | 10 | import org.junit.jupiter.params.ParameterizedTest; |
14 | import org.junit.jupiter.params.provider.ValueSource; | 11 | import org.junit.jupiter.params.provider.ValueSource; |
15 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 12 | import tools.refinery.language.tests.InjectWithRefinery; |
16 | import tools.refinery.language.tests.ProblemInjectorProvider; | 13 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
17 | import tools.refinery.language.validation.ProblemValidator; | 14 | import tools.refinery.language.validation.ProblemValidator; |
18 | 15 | ||
19 | import static org.hamcrest.MatcherAssert.assertThat; | 16 | import static org.hamcrest.MatcherAssert.assertThat; |
20 | import static org.hamcrest.Matchers.*; | 17 | import static org.hamcrest.Matchers.*; |
21 | 18 | ||
22 | @ExtendWith(InjectionExtension.class) | 19 | @InjectWithRefinery |
23 | @InjectWith(ProblemInjectorProvider.class) | ||
24 | class MultiplicityValidationTest { | 20 | class MultiplicityValidationTest { |
25 | @Inject | 21 | @Inject |
26 | private ProblemParseHelper parseHelper; | 22 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/OppositeValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/OppositeValidationTest.java index d73ef8e7..7844f49c 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/OppositeValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/OppositeValidationTest.java | |||
@@ -7,14 +7,11 @@ package tools.refinery.language.tests.validation; | |||
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.emf.common.util.Diagnostic; | 9 | import org.eclipse.emf.common.util.Diagnostic; |
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | 11 | import org.junit.jupiter.params.ParameterizedTest; |
15 | import org.junit.jupiter.params.provider.ValueSource; | 12 | import org.junit.jupiter.params.provider.ValueSource; |
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.tests.InjectWithRefinery; |
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.utils.ProblemParseHelper; |
18 | import tools.refinery.language.validation.ProblemValidator; | 15 | import tools.refinery.language.validation.ProblemValidator; |
19 | 16 | ||
20 | import java.util.Set; | 17 | import java.util.Set; |
@@ -22,8 +19,7 @@ import java.util.Set; | |||
22 | import static org.hamcrest.MatcherAssert.assertThat; | 19 | import static org.hamcrest.MatcherAssert.assertThat; |
23 | import static org.hamcrest.Matchers.*; | 20 | import static org.hamcrest.Matchers.*; |
24 | 21 | ||
25 | @ExtendWith(InjectionExtension.class) | 22 | @InjectWithRefinery |
26 | @InjectWith(ProblemInjectorProvider.class) | ||
27 | class OppositeValidationTest { | 23 | class OppositeValidationTest { |
28 | @Inject | 24 | @Inject |
29 | private ProblemParseHelper parseHelper; | 25 | private ProblemParseHelper parseHelper; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/InjectWithRefinery.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/InjectWithRefinery.java new file mode 100644 index 00000000..c9fb453e --- /dev/null +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/InjectWithRefinery.java | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.tests; | ||
7 | |||
8 | import org.eclipse.xtext.testing.InjectWith; | ||
9 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
10 | import org.junit.jupiter.api.extension.ExtendWith; | ||
11 | |||
12 | import java.lang.annotation.ElementType; | ||
13 | import java.lang.annotation.Retention; | ||
14 | import java.lang.annotation.RetentionPolicy; | ||
15 | import java.lang.annotation.Target; | ||
16 | |||
17 | @Target(ElementType.TYPE) | ||
18 | @Retention(RetentionPolicy.RUNTIME) | ||
19 | @ExtendWith(InjectionExtension.class) | ||
20 | @InjectWith(ProblemInjectorProvider.class) | ||
21 | public @interface InjectWithRefinery { | ||
22 | } | ||
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/ProblemNavigationUtil.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/ProblemNavigationUtil.java index d92011a9..7033dd45 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/ProblemNavigationUtil.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/ProblemNavigationUtil.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import java.util.List; | 8 | import java.util.List; |
9 | import java.util.stream.Stream; | 9 | import java.util.stream.Stream; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/ProblemParseHelper.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/ProblemParseHelper.java index f1535716..02fefa78 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/ProblemParseHelper.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/ProblemParseHelper.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import org.eclipse.emf.ecore.util.EcoreUtil; | 9 | import org.eclipse.emf.ecore.util.EcoreUtil; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAction.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAction.java index 4987cb89..1128b9ef 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAction.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAction.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Action; | 8 | import tools.refinery.language.model.problem.Action; |
9 | 9 | ||
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedArgument.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedArgument.java index ed749fed..c396cab3 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedArgument.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedArgument.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.*; | 8 | import tools.refinery.language.model.problem.*; |
9 | 9 | ||
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertion.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAssertion.java index b2ef6e48..b94e164d 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertion.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAssertion.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Assertion; | 8 | import tools.refinery.language.model.problem.Assertion; |
9 | 9 | ||
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/tests/utils/WrappedAssertionArgument.java index 50cb958d..bdc3dd49 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAssertionArgument.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAssertionArgument.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.AssertionArgument; | 8 | import tools.refinery.language.model.problem.AssertionArgument; |
9 | import tools.refinery.language.model.problem.Node; | 9 | import tools.refinery.language.model.problem.Node; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAtom.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAtom.java index c02f447b..c88b899e 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedAtom.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedAtom.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Atom; | 8 | import tools.refinery.language.model.problem.Atom; |
9 | 9 | ||
@@ -11,7 +11,7 @@ public record WrappedAtom(Atom atom) { | |||
11 | public Atom get() { | 11 | public Atom get() { |
12 | return atom; | 12 | return atom; |
13 | } | 13 | } |
14 | 14 | ||
15 | public WrappedArgument arg(int i) { | 15 | public WrappedArgument arg(int i) { |
16 | return new WrappedArgument(atom.getArguments().get(i)); | 16 | return new WrappedArgument(atom.getArguments().get(i)); |
17 | } | 17 | } |
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/tests/utils/WrappedClassDeclaration.java index 14ac7bfc..2f20b3bd 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedClassDeclaration.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.ClassDeclaration; | 8 | import tools.refinery.language.model.problem.ClassDeclaration; |
9 | import tools.refinery.language.model.problem.ReferenceDeclaration; | 9 | import tools.refinery.language.model.problem.ReferenceDeclaration; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedConjunction.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedConjunction.java index b126b1ce..a01594da 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedConjunction.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedConjunction.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Conjunction; | 8 | import tools.refinery.language.model.problem.Conjunction; |
9 | 9 | ||
@@ -11,7 +11,7 @@ public record WrappedConjunction(Conjunction conjunction) { | |||
11 | public Conjunction get() { | 11 | public Conjunction get() { |
12 | return conjunction; | 12 | return conjunction; |
13 | } | 13 | } |
14 | 14 | ||
15 | public WrappedLiteral lit(int i) { | 15 | public WrappedLiteral lit(int i) { |
16 | return new WrappedLiteral(conjunction.getLiterals().get(i)); | 16 | return new WrappedLiteral(conjunction.getLiterals().get(i)); |
17 | } | 17 | } |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedConsequent.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedConsequent.java index 8d6a92f8..5af3f352 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedConsequent.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedConsequent.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Consequent; | 8 | import tools.refinery.language.model.problem.Consequent; |
9 | 9 | ||
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedEnumDeclaration.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedEnumDeclaration.java index 229a8c0a..d20382d7 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedEnumDeclaration.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedEnumDeclaration.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.EnumDeclaration; | 8 | import tools.refinery.language.model.problem.EnumDeclaration; |
9 | import tools.refinery.language.model.problem.Node; | 9 | import tools.refinery.language.model.problem.Node; |
@@ -12,7 +12,7 @@ public record WrappedEnumDeclaration(EnumDeclaration enumDeclaration) { | |||
12 | public EnumDeclaration get() { | 12 | public EnumDeclaration get() { |
13 | return enumDeclaration; | 13 | return enumDeclaration; |
14 | } | 14 | } |
15 | 15 | ||
16 | public Node literal(String name) { | 16 | public Node literal(String name) { |
17 | return ProblemNavigationUtil.named(enumDeclaration.getLiterals(), name); | 17 | return ProblemNavigationUtil.named(enumDeclaration.getLiterals(), name); |
18 | } | 18 | } |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedLiteral.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedLiteral.java index 160e5dd8..f62fb630 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedLiteral.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedLiteral.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Atom; | 8 | import tools.refinery.language.model.problem.Atom; |
9 | import tools.refinery.language.model.problem.Expr; | 9 | import tools.refinery.language.model.problem.Expr; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedParametricDefinition.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedParametricDefinition.java index d44b79ed..598ff1f4 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedParametricDefinition.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedParametricDefinition.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.Parameter; | 8 | import tools.refinery.language.model.problem.Parameter; |
9 | import tools.refinery.language.model.problem.ParametricDefinition; | 9 | import tools.refinery.language.model.problem.ParametricDefinition; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedPredicateDefinition.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedPredicateDefinition.java index 2cf5fd89..15268d0d 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedPredicateDefinition.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedPredicateDefinition.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.PredicateDefinition; | 8 | import tools.refinery.language.model.problem.PredicateDefinition; |
9 | 9 | ||
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/tests/utils/WrappedProblem.java index b31eed6d..174f66e7 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedProblem.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; | 8 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; |
9 | import org.eclipse.emf.ecore.util.Diagnostician; | 9 | import org.eclipse.emf.ecore.util.Diagnostician; |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedRuleDefinition.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedRuleDefinition.java index 326d8ec3..e66b1030 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedRuleDefinition.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/tests/utils/WrappedRuleDefinition.java | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.RuleDefinition; | 8 | import tools.refinery.language.model.problem.RuleDefinition; |
9 | 9 | ||
diff --git a/subprojects/logic/src/main/java/tools/refinery/logic/term/truthvalue/TruthValue.java b/subprojects/logic/src/main/java/tools/refinery/logic/term/truthvalue/TruthValue.java index 59bdeab3..671e1a05 100644 --- a/subprojects/logic/src/main/java/tools/refinery/logic/term/truthvalue/TruthValue.java +++ b/subprojects/logic/src/main/java/tools/refinery/logic/term/truthvalue/TruthValue.java | |||
@@ -50,7 +50,6 @@ public enum TruthValue implements AbstractValue<TruthValue, Boolean> { | |||
50 | return !isError(); | 50 | return !isError(); |
51 | } | 51 | } |
52 | 52 | ||
53 | |||
54 | public boolean isComplete() { | 53 | public boolean isComplete() { |
55 | return this != UNKNOWN; | 54 | return this != UNKNOWN; |
56 | } | 55 | } |
diff --git a/subprojects/store-dse-visualization/build.gradle.kts b/subprojects/store-dse-visualization/build.gradle.kts index 98918863..099c9cce 100644 --- a/subprojects/store-dse-visualization/build.gradle.kts +++ b/subprojects/store-dse-visualization/build.gradle.kts | |||
@@ -15,5 +15,5 @@ mavenArtifact { | |||
15 | 15 | ||
16 | dependencies { | 16 | dependencies { |
17 | api(project(":refinery-store-query")) | 17 | api(project(":refinery-store-query")) |
18 | implementation(libs.slf4j.api) | 18 | implementation(libs.slf4j) |
19 | } | 19 | } |
diff --git a/subprojects/store-dse/build.gradle.kts b/subprojects/store-dse/build.gradle.kts index 3cc44fe6..f4cca199 100644 --- a/subprojects/store-dse/build.gradle.kts +++ b/subprojects/store-dse/build.gradle.kts | |||
@@ -16,7 +16,7 @@ mavenArtifact { | |||
16 | dependencies { | 16 | dependencies { |
17 | api(project(":refinery-store-query")) | 17 | api(project(":refinery-store-query")) |
18 | implementation(project(":refinery-store-dse-visualization")) | 18 | implementation(project(":refinery-store-dse-visualization")) |
19 | implementation(libs.eclipseCollections.api) | 19 | implementation(libs.eclipseCollections) |
20 | runtimeOnly(libs.eclipseCollections) | 20 | runtimeOnly(libs.eclipseCollections.impl) |
21 | testImplementation(project(":refinery-store-query-interpreter")) | 21 | testImplementation(project(":refinery-store-query-interpreter")) |
22 | } | 22 | } |
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java index c2e43e0d..b38774b5 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java | |||
@@ -65,6 +65,9 @@ public class RuleBuilder extends AbstractQueryBuilder<RuleBuilder> { | |||
65 | } | 65 | } |
66 | 66 | ||
67 | public Rule build() { | 67 | public Rule build() { |
68 | if (action == null) { | ||
69 | throw new IllegalStateException("Rule '%s' has no action".formatted(name)); | ||
70 | } | ||
68 | var precondition = dnfBuilder.build().asRelation(); | 71 | var precondition = dnfBuilder.build().asRelation(); |
69 | return new Rule(name, precondition, Action.ofPrecondition(precondition, action)); | 72 | return new Rule(name, precondition, Action.ofPrecondition(precondition, action)); |
70 | } | 73 | } |
diff --git a/subprojects/store-reasoning-scope/build.gradle.kts b/subprojects/store-reasoning-scope/build.gradle.kts index 8906c444..8c146cbf 100644 --- a/subprojects/store-reasoning-scope/build.gradle.kts +++ b/subprojects/store-reasoning-scope/build.gradle.kts | |||
@@ -14,8 +14,8 @@ mavenArtifact { | |||
14 | 14 | ||
15 | dependencies { | 15 | dependencies { |
16 | api(project(":refinery-store-reasoning")) | 16 | api(project(":refinery-store-reasoning")) |
17 | implementation(libs.eclipseCollections.api) | 17 | implementation(libs.eclipseCollections) |
18 | implementation(libs.ortools) | 18 | implementation(libs.ortools) |
19 | runtimeOnly(libs.eclipseCollections) | 19 | runtimeOnly(libs.eclipseCollections.impl) |
20 | testImplementation(project(":refinery-store-query-interpreter")) | 20 | testImplementation(project(":refinery-store-query-interpreter")) |
21 | } | 21 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java index 386ae1d8..8994ac6e 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java | |||
@@ -18,6 +18,7 @@ import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | |||
18 | import tools.refinery.store.reasoning.refinement.StorageRefiner; | 18 | import tools.refinery.store.reasoning.refinement.StorageRefiner; |
19 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | 19 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; |
20 | import tools.refinery.store.reasoning.representation.PartialSymbol; | 20 | import tools.refinery.store.reasoning.representation.PartialSymbol; |
21 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
21 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; | 22 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; |
22 | import tools.refinery.store.representation.Symbol; | 23 | import tools.refinery.store.representation.Symbol; |
23 | import tools.refinery.logic.term.cardinalityinterval.CardinalityInterval; | 24 | import tools.refinery.logic.term.cardinalityinterval.CardinalityInterval; |
@@ -106,6 +107,9 @@ class ReasoningAdapterImpl implements ReasoningAdapter { | |||
106 | var refiner = createRefiner(factory, partialSymbol); | 107 | var refiner = createRefiner(factory, partialSymbol); |
107 | refiners.put(partialSymbol, refiner); | 108 | refiners.put(partialSymbol, refiner); |
108 | } | 109 | } |
110 | for (var refiner : refiners.values()) { | ||
111 | refiner.afterCreate(); | ||
112 | } | ||
109 | } | 113 | } |
110 | 114 | ||
111 | private <A extends AbstractValue<A, C>, C> PartialInterpretationRefiner<A, C> createRefiner( | 115 | private <A extends AbstractValue<A, C>, C> PartialInterpretationRefiner<A, C> createRefiner( |
@@ -204,6 +208,8 @@ class ReasoningAdapterImpl implements ReasoningAdapter { | |||
204 | if (nodeToDelete == currentModelSize - 1) { | 208 | if (nodeToDelete == currentModelSize - 1) { |
205 | nodeCountInterpretation.put(Tuple.of(), nodeToDelete); | 209 | nodeCountInterpretation.put(Tuple.of(), nodeToDelete); |
206 | } | 210 | } |
211 | // We mustn't reuse the ID of {@code nodeToDelete} in any circumstance, because clients may depend on stable | ||
212 | // node IDs for nodes in the initial partial model. | ||
207 | return true; | 213 | return true; |
208 | } | 214 | } |
209 | 215 | ||
@@ -212,4 +218,10 @@ class ReasoningAdapterImpl implements ReasoningAdapter { | |||
212 | Integer nodeCount = nodeCountInterpretation.get(Tuple.of()); | 218 | Integer nodeCount = nodeCountInterpretation.get(Tuple.of()); |
213 | return nodeCount == null ? 0 : nodeCount; | 219 | return nodeCount == null ? 0 : nodeCount; |
214 | } | 220 | } |
221 | |||
222 | void afterInitialize(ModelSeed modelSeed) { | ||
223 | for (var refiner : refiners.values()) { | ||
224 | refiner.afterInitialize(modelSeed); | ||
225 | } | ||
226 | } | ||
215 | } | 227 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java index df4b6457..57cbb5d0 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java | |||
@@ -10,6 +10,7 @@ import tools.refinery.store.dse.propagation.PropagationResult; | |||
10 | import tools.refinery.store.model.Model; | 10 | import tools.refinery.store.model.Model; |
11 | import tools.refinery.store.model.ModelStore; | 11 | import tools.refinery.store.model.ModelStore; |
12 | import tools.refinery.store.query.ModelQueryAdapter; | 12 | import tools.refinery.store.query.ModelQueryAdapter; |
13 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
13 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; | 14 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; |
14 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | 15 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; |
15 | import tools.refinery.store.reasoning.literal.Concreteness; | 16 | import tools.refinery.store.reasoning.literal.Concreteness; |
@@ -107,6 +108,8 @@ class ReasoningStoreAdapterImpl implements ReasoningStoreAdapter { | |||
107 | for (var initializer : initializers) { | 108 | for (var initializer : initializers) { |
108 | initializer.initialize(model, modelSeed); | 109 | initializer.initialize(model, modelSeed); |
109 | } | 110 | } |
111 | var reasoningAdapter = ((ReasoningAdapterImpl) model.getAdapter(ReasoningAdapter.class)); | ||
112 | reasoningAdapter.afterInitialize(modelSeed); | ||
110 | var propagationResult = model.tryGetAdapter(PropagationAdapter.class) | 113 | var propagationResult = model.tryGetAdapter(PropagationAdapter.class) |
111 | .map(PropagationAdapter::propagate) | 114 | .map(PropagationAdapter::propagate) |
112 | .orElse(PropagationResult.UNCHANGED); | 115 | .orElse(PropagationResult.UNCHANGED); |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java index 6c381511..7fd17a3a 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java | |||
@@ -5,11 +5,44 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.reasoning.refinement; | 6 | package tools.refinery.store.reasoning.refinement; |
7 | 7 | ||
8 | import tools.refinery.logic.AbstractValue; | ||
9 | import tools.refinery.store.model.Model; | ||
8 | import tools.refinery.store.reasoning.ReasoningAdapter; | 10 | import tools.refinery.store.reasoning.ReasoningAdapter; |
9 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | 11 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; |
12 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
13 | import tools.refinery.store.tuple.Tuple; | ||
10 | 14 | ||
11 | public sealed interface AnyPartialInterpretationRefiner permits PartialInterpretationRefiner { | 15 | public sealed interface AnyPartialInterpretationRefiner permits PartialInterpretationRefiner { |
12 | ReasoningAdapter getAdapter(); | 16 | ReasoningAdapter getAdapter(); |
13 | 17 | ||
14 | AnyPartialSymbol getPartialSymbol(); | 18 | AnyPartialSymbol getPartialSymbol(); |
19 | |||
20 | /** | ||
21 | * Called after all {@link PartialInterpretationRefiner} instances have been created for symbols registered to | ||
22 | * the {@link tools.refinery.store.reasoning.ReasoningStoreAdapter}. | ||
23 | * <p> | ||
24 | * Override this method to access other {@link PartialInterpretationRefiner} instances associated to the | ||
25 | * {@link ReasoningAdapter} that are required for propagations executed by this | ||
26 | * {@link PartialInterpretationRefiner}. | ||
27 | */ | ||
28 | default void afterCreate() { | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Execute propagations based on the contents of the {@code modelSeed} that would by executed if the | ||
33 | * {@code modelSeed} were written to the model as a sequence of | ||
34 | * {@link PartialInterpretationRefiner#merge(Tuple, AbstractValue)} calls. | ||
35 | * <p> | ||
36 | * This method is called only after {@link PartialModelInitializer#initialize(Model, ModelSeed)} was called on all | ||
37 | * {@link PartialModelInitializer} instances registered to the | ||
38 | * {@link tools.refinery.store.reasoning.ReasoningStoreAdapter}. | ||
39 | * <p> | ||
40 | * The default implementation of this method performs no actions. Override it make the behavior consistent with | ||
41 | * your {@link PartialInterpretationRefiner#merge(Tuple, AbstractValue)} implementation. | ||
42 | * | ||
43 | * @param modelSeed The model seed which was written by a previous call of | ||
44 | * {@link PartialModelInitializer#initialize(Model, ModelSeed)}. | ||
45 | */ | ||
46 | default void afterInitialize(ModelSeed modelSeed) { | ||
47 | } | ||
15 | } | 48 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java index d6ac0e9d..5474a4ff 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java | |||
@@ -26,14 +26,22 @@ public class ConcreteSymbolRefiner<A extends AbstractValue<A, C>, C> | |||
26 | 26 | ||
27 | @Override | 27 | @Override |
28 | public boolean merge(Tuple key, A value) { | 28 | public boolean merge(Tuple key, A value) { |
29 | var currentValue = interpretation.get(key); | 29 | var currentValue = get(key); |
30 | var mergedValue = currentValue.meet(value); | 30 | var mergedValue = currentValue.meet(value); |
31 | if (!Objects.equals(currentValue, mergedValue)) { | 31 | if (!Objects.equals(currentValue, mergedValue)) { |
32 | interpretation.put(key, mergedValue); | 32 | put(key, mergedValue); |
33 | } | 33 | } |
34 | return true; | 34 | return true; |
35 | } | 35 | } |
36 | 36 | ||
37 | protected A get(Tuple key) { | ||
38 | return interpretation.get(key); | ||
39 | } | ||
40 | |||
41 | protected A put(Tuple key, A value) { | ||
42 | return interpretation.put(key, value); | ||
43 | } | ||
44 | |||
37 | public static <A1 extends AbstractValue<A1, C1>, C1> Factory<A1, C1> of(Symbol<A1> concreteSymbol) { | 45 | public static <A1 extends AbstractValue<A1, C1>, C1> Factory<A1, C1> of(Symbol<A1> concreteSymbol) { |
38 | return (adapter, partialSymbol) -> new ConcreteSymbolRefiner<>(adapter, partialSymbol, concreteSymbol); | 46 | return (adapter, partialSymbol) -> new ConcreteSymbolRefiner<>(adapter, partialSymbol, concreteSymbol); |
39 | } | 47 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java index 3b78db02..03111574 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java | |||
@@ -64,6 +64,10 @@ record MapBasedSeed<T>(int arity, Class<T> valueType, T majorityValue, Map<Tuple | |||
64 | public boolean move() { | 64 | public boolean move() { |
65 | return switch (state) { | 65 | return switch (state) { |
66 | case INITIAL -> { | 66 | case INITIAL -> { |
67 | if (nodeCount == 0) { | ||
68 | state = State.TERMINATED; | ||
69 | yield false; | ||
70 | } | ||
67 | state = State.STARTED; | 71 | state = State.STARTED; |
68 | yield checkValue() || moveToNext(); | 72 | yield checkValue() || moveToNext(); |
69 | } | 73 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java index 9cd4862b..31f6fa04 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java | |||
@@ -54,6 +54,10 @@ public class ModelSeed { | |||
54 | return getSeed(partialSymbol).getCursor(defaultValue, nodeCount); | 54 | return getSeed(partialSymbol).getCursor(defaultValue, nodeCount); |
55 | } | 55 | } |
56 | 56 | ||
57 | public <A extends AbstractValue<A, ?>> Cursor<Tuple, A> getCursor(PartialSymbol<A, ?> partialSymbol) { | ||
58 | return getCursor(partialSymbol, partialSymbol.defaultValue()); | ||
59 | } | ||
60 | |||
57 | public static Builder builder(int nodeCount) { | 61 | public static Builder builder(int nodeCount) { |
58 | return new Builder(nodeCount); | 62 | return new Builder(nodeCount); |
59 | } | 63 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java index 138e3a64..adc6421f 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java | |||
@@ -20,6 +20,14 @@ public class SeedInitializer<T extends AbstractValue<T, ?>> implements PartialMo | |||
20 | this.partialSymbol = partialSymbol; | 20 | this.partialSymbol = partialSymbol; |
21 | } | 21 | } |
22 | 22 | ||
23 | protected Symbol<T> getSymbol() { | ||
24 | return symbol; | ||
25 | } | ||
26 | |||
27 | protected PartialSymbol<T, ?> getPartialSymbol() { | ||
28 | return partialSymbol; | ||
29 | } | ||
30 | |||
23 | @Override | 31 | @Override |
24 | public void initialize(Model model, ModelSeed modelSeed) { | 32 | public void initialize(Model model, ModelSeed modelSeed) { |
25 | var interpretation = model.getInterpretation(symbol); | 33 | var interpretation = model.getInterpretation(symbol); |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java index adbea26b..14331dfc 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java | |||
@@ -21,14 +21,19 @@ import java.util.Set; | |||
21 | class ContainmentLinkRefiner extends AbstractPartialInterpretationRefiner<TruthValue, Boolean> { | 21 | class ContainmentLinkRefiner extends AbstractPartialInterpretationRefiner<TruthValue, Boolean> { |
22 | private final Factory factory; | 22 | private final Factory factory; |
23 | private final Interpretation<InferredContainment> interpretation; | 23 | private final Interpretation<InferredContainment> interpretation; |
24 | private final PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; | 24 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; |
25 | private final PartialInterpretationRefiner<TruthValue, Boolean> targetRefiner; | 25 | private PartialInterpretationRefiner<TruthValue, Boolean> targetRefiner; |
26 | 26 | ||
27 | private ContainmentLinkRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | 27 | private ContainmentLinkRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, |
28 | Factory factory) { | 28 | Factory factory) { |
29 | super(adapter, partialSymbol); | 29 | super(adapter, partialSymbol); |
30 | this.factory = factory; | 30 | this.factory = factory; |
31 | interpretation = adapter.getModel().getInterpretation(factory.symbol); | 31 | interpretation = adapter.getModel().getInterpretation(factory.symbol); |
32 | } | ||
33 | |||
34 | @Override | ||
35 | public void afterCreate() { | ||
36 | var adapter = getAdapter(); | ||
32 | sourceRefiner = adapter.getRefiner(factory.sourceType); | 37 | sourceRefiner = adapter.getRefiner(factory.sourceType); |
33 | targetRefiner = adapter.getRefiner(factory.targetType); | 38 | targetRefiner = adapter.getRefiner(factory.targetType); |
34 | } | 39 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java index 024774bc..4e5029bf 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java | |||
@@ -30,13 +30,18 @@ class ContainsRefiner extends AbstractPartialInterpretationRefiner<TruthValue, B | |||
30 | } | 30 | } |
31 | 31 | ||
32 | private final Interpretation<InferredContainment> interpretation; | 32 | private final Interpretation<InferredContainment> interpretation; |
33 | private final PartialInterpretationRefiner<TruthValue, Boolean> containerRefiner; | 33 | private PartialInterpretationRefiner<TruthValue, Boolean> containerRefiner; |
34 | private final PartialInterpretationRefiner<TruthValue, Boolean> containedRefiner; | 34 | private PartialInterpretationRefiner<TruthValue, Boolean> containedRefiner; |
35 | 35 | ||
36 | private ContainsRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | 36 | private ContainsRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, |
37 | Symbol<InferredContainment> containsStorage) { | 37 | Symbol<InferredContainment> containsStorage) { |
38 | super(adapter, partialSymbol); | 38 | super(adapter, partialSymbol); |
39 | interpretation = adapter.getModel().getInterpretation(containsStorage); | 39 | interpretation = adapter.getModel().getInterpretation(containsStorage); |
40 | } | ||
41 | |||
42 | @Override | ||
43 | public void afterCreate() { | ||
44 | var adapter = getAdapter(); | ||
40 | containerRefiner = adapter.getRefiner(ContainmentHierarchyTranslator.CONTAINER_SYMBOL); | 45 | containerRefiner = adapter.getRefiner(ContainmentHierarchyTranslator.CONTAINER_SYMBOL); |
41 | containedRefiner = adapter.getRefiner(ContainmentHierarchyTranslator.CONTAINED_SYMBOL); | 46 | containedRefiner = adapter.getRefiner(ContainmentHierarchyTranslator.CONTAINED_SYMBOL); |
42 | } | 47 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java deleted file mode 100644 index 7cb16a28..00000000 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.crossreference; | ||
7 | |||
8 | import tools.refinery.store.model.Model; | ||
9 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
10 | import tools.refinery.store.reasoning.refinement.PartialModelInitializer; | ||
11 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
12 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
13 | import tools.refinery.store.representation.Symbol; | ||
14 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
15 | import tools.refinery.store.tuple.Tuple; | ||
16 | |||
17 | class DirectedCrossReferenceInitializer implements PartialModelInitializer { | ||
18 | private final PartialRelation linkType; | ||
19 | private final PartialRelation sourceType; | ||
20 | private final PartialRelation targetType; | ||
21 | private final Symbol<TruthValue> symbol; | ||
22 | |||
23 | public DirectedCrossReferenceInitializer(PartialRelation linkType, PartialRelation sourceType, | ||
24 | PartialRelation targetType, Symbol<TruthValue> symbol) { | ||
25 | this.linkType = linkType; | ||
26 | this.sourceType = sourceType; | ||
27 | this.targetType = targetType; | ||
28 | this.symbol = symbol; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public void initialize(Model model, ModelSeed modelSeed) { | ||
33 | var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); | ||
34 | var sourceRefiner = reasoningAdapter.getRefiner(sourceType); | ||
35 | var targetRefiner = reasoningAdapter.getRefiner(targetType); | ||
36 | var interpretation = model.getInterpretation(symbol); | ||
37 | var cursor = modelSeed.getCursor(linkType, symbol.defaultValue()); | ||
38 | while (cursor.move()) { | ||
39 | var key = cursor.getKey(); | ||
40 | var value = cursor.getValue(); | ||
41 | interpretation.put(key, value); | ||
42 | if (value.must()) { | ||
43 | boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && | ||
44 | targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); | ||
45 | if (!merged) { | ||
46 | throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" | ||
47 | .formatted(linkType, key)); | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java index 75dd5dad..4471b193 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java | |||
@@ -5,27 +5,35 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.reasoning.translator.crossreference; | 6 | package tools.refinery.store.reasoning.translator.crossreference; |
7 | 7 | ||
8 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
8 | import tools.refinery.store.reasoning.ReasoningAdapter; | 9 | import tools.refinery.store.reasoning.ReasoningAdapter; |
9 | import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; | 10 | import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; |
10 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | 11 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; |
11 | import tools.refinery.store.reasoning.representation.PartialRelation; | 12 | import tools.refinery.store.reasoning.representation.PartialRelation; |
12 | import tools.refinery.store.reasoning.representation.PartialSymbol; | 13 | import tools.refinery.store.reasoning.representation.PartialSymbol; |
14 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
13 | import tools.refinery.store.representation.Symbol; | 15 | import tools.refinery.store.representation.Symbol; |
14 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
15 | import tools.refinery.store.tuple.Tuple; | 16 | import tools.refinery.store.tuple.Tuple; |
16 | 17 | ||
17 | class DirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, Boolean> { | 18 | class DirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, Boolean> { |
19 | private final PartialRelation sourceType; | ||
18 | private final PartialRelation targetType; | 20 | private final PartialRelation targetType; |
19 | private final PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; | 21 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; |
20 | private PartialInterpretationRefiner<TruthValue, Boolean> targetRefiner; | 22 | private PartialInterpretationRefiner<TruthValue, Boolean> targetRefiner; |
21 | 23 | ||
22 | public DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | 24 | protected DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, |
23 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType, | 25 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType, |
24 | PartialRelation targetType) { | 26 | PartialRelation targetType) { |
25 | super(adapter, partialSymbol, concreteSymbol); | 27 | super(adapter, partialSymbol, concreteSymbol); |
28 | this.sourceType = sourceType; | ||
26 | this.targetType = targetType; | 29 | this.targetType = targetType; |
27 | // Source is always a class, so we can rely on the fact that it is always constructed before this refiner. | 30 | } |
31 | |||
32 | @Override | ||
33 | public void afterCreate() { | ||
34 | var adapter = getAdapter(); | ||
28 | sourceRefiner = adapter.getRefiner(sourceType); | 35 | sourceRefiner = adapter.getRefiner(sourceType); |
36 | targetRefiner = adapter.getRefiner(targetType); | ||
29 | } | 37 | } |
30 | 38 | ||
31 | @Override | 39 | @Override |
@@ -34,16 +42,30 @@ class DirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, Bo | |||
34 | return false; | 42 | return false; |
35 | } | 43 | } |
36 | if (value.must()) { | 44 | if (value.must()) { |
37 | if (targetRefiner == null) { | ||
38 | // Access the target refinery lazily, since it may be constructed after this refiner. | ||
39 | targetRefiner = getAdapter().getRefiner(targetType); | ||
40 | } | ||
41 | return sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && | 45 | return sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && |
42 | targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); | 46 | targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); |
43 | } | 47 | } |
44 | return true; | 48 | return true; |
45 | } | 49 | } |
46 | 50 | ||
51 | @Override | ||
52 | public void afterInitialize(ModelSeed modelSeed) { | ||
53 | var linkType = getPartialSymbol(); | ||
54 | var cursor = modelSeed.getCursor(linkType); | ||
55 | while (cursor.move()) { | ||
56 | var value = cursor.getValue(); | ||
57 | if (value.must()) { | ||
58 | var key = cursor.getKey(); | ||
59 | boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && | ||
60 | targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); | ||
61 | if (!merged) { | ||
62 | throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" | ||
63 | .formatted(linkType, key)); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
47 | public static Factory<TruthValue, Boolean> of(Symbol<TruthValue> concreteSymbol, PartialRelation sourceType, | 69 | public static Factory<TruthValue, Boolean> of(Symbol<TruthValue> concreteSymbol, PartialRelation sourceType, |
48 | PartialRelation targetType) { | 70 | PartialRelation targetType) { |
49 | return (adapter, partialSymbol) -> new DirectedCrossReferenceRefiner(adapter, partialSymbol, concreteSymbol, | 71 | return (adapter, partialSymbol) -> new DirectedCrossReferenceRefiner(adapter, partialSymbol, concreteSymbol, |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java index d4c2afd2..c98971d9 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java | |||
@@ -47,7 +47,7 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration | |||
47 | var targetType = info.targetType(); | 47 | var targetType = info.targetType(); |
48 | var defaultValue = info.defaultValue(); | 48 | var defaultValue = info.defaultValue(); |
49 | if (defaultValue.must()) { | 49 | if (defaultValue.must()) { |
50 | throw new TranslationException(linkType, "Unsupported default value %s for directed cross references %s" | 50 | throw new TranslationException(linkType, "Unsupported default value %s for directed cross reference %s" |
51 | .formatted(defaultValue, linkType)); | 51 | .formatted(defaultValue, linkType)); |
52 | } | 52 | } |
53 | var translator = PartialRelationTranslator.of(linkType); | 53 | var translator = PartialRelationTranslator.of(linkType); |
@@ -58,7 +58,6 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration | |||
58 | configureWithDefaultFalse(storeBuilder); | 58 | configureWithDefaultFalse(storeBuilder); |
59 | } | 59 | } |
60 | translator.refiner(DirectedCrossReferenceRefiner.of(symbol, sourceType, targetType)); | 60 | translator.refiner(DirectedCrossReferenceRefiner.of(symbol, sourceType, targetType)); |
61 | translator.initializer(new DirectedCrossReferenceInitializer(linkType, sourceType, targetType, symbol)); | ||
62 | if (info.partial()) { | 61 | if (info.partial()) { |
63 | translator.roundingMode(RoundingMode.NONE); | 62 | translator.roundingMode(RoundingMode.NONE); |
64 | } else { | 63 | } else { |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java index 84dcfdc5..d0784c27 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java | |||
@@ -6,48 +6,34 @@ | |||
6 | package tools.refinery.store.reasoning.translator.crossreference; | 6 | package tools.refinery.store.reasoning.translator.crossreference; |
7 | 7 | ||
8 | import org.jetbrains.annotations.NotNull; | 8 | import org.jetbrains.annotations.NotNull; |
9 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
9 | import tools.refinery.store.model.Model; | 10 | import tools.refinery.store.model.Model; |
10 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
11 | import tools.refinery.store.reasoning.refinement.PartialModelInitializer; | 11 | import tools.refinery.store.reasoning.refinement.PartialModelInitializer; |
12 | import tools.refinery.store.reasoning.representation.PartialRelation; | 12 | import tools.refinery.store.reasoning.representation.PartialRelation; |
13 | import tools.refinery.store.reasoning.seed.ModelSeed; | 13 | import tools.refinery.store.reasoning.seed.ModelSeed; |
14 | import tools.refinery.store.reasoning.translator.TranslationException; | 14 | import tools.refinery.store.reasoning.translator.TranslationException; |
15 | import tools.refinery.store.representation.Symbol; | 15 | import tools.refinery.store.representation.Symbol; |
16 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
17 | import tools.refinery.store.tuple.Tuple; | 16 | import tools.refinery.store.tuple.Tuple; |
18 | 17 | ||
19 | import java.util.LinkedHashMap; | 18 | import java.util.LinkedHashMap; |
20 | 19 | ||
21 | class UndirectedCrossReferenceInitializer implements PartialModelInitializer { | 20 | class UndirectedCrossReferenceInitializer implements PartialModelInitializer { |
22 | private final PartialRelation linkType; | 21 | private final PartialRelation linkType; |
23 | private final PartialRelation sourceType; | ||
24 | private final Symbol<TruthValue> symbol; | 22 | private final Symbol<TruthValue> symbol; |
25 | 23 | ||
26 | UndirectedCrossReferenceInitializer(PartialRelation linkType, PartialRelation sourceType, | 24 | UndirectedCrossReferenceInitializer(PartialRelation linkType, Symbol<TruthValue> symbol) { |
27 | Symbol<TruthValue> symbol) { | ||
28 | this.linkType = linkType; | 25 | this.linkType = linkType; |
29 | this.sourceType = sourceType; | ||
30 | this.symbol = symbol; | 26 | this.symbol = symbol; |
31 | } | 27 | } |
32 | 28 | ||
33 | @Override | 29 | @Override |
34 | public void initialize(Model model, ModelSeed modelSeed) { | 30 | public void initialize(Model model, ModelSeed modelSeed) { |
35 | var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); | ||
36 | var mergedMap = getMergedMap(modelSeed); | 31 | var mergedMap = getMergedMap(modelSeed); |
37 | var sourceRefiner = reasoningAdapter.getRefiner(sourceType); | ||
38 | var interpretation = model.getInterpretation(symbol); | 32 | var interpretation = model.getInterpretation(symbol); |
39 | for (var entry : mergedMap.entrySet()) { | 33 | for (var entry : mergedMap.entrySet()) { |
40 | var key = entry.getKey(); | 34 | var key = entry.getKey(); |
41 | var value = entry.getValue(); | 35 | var value = entry.getValue(); |
42 | interpretation.put(key, value); | 36 | interpretation.put(key, value); |
43 | if (value.must()) { | ||
44 | boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && | ||
45 | sourceRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); | ||
46 | if (!merged) { | ||
47 | throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" | ||
48 | .formatted(linkType, key)); | ||
49 | } | ||
50 | } | ||
51 | } | 37 | } |
52 | } | 38 | } |
53 | 39 | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java index 54aca80f..116ac7ad 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java | |||
@@ -5,30 +5,48 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.reasoning.translator.crossreference; | 6 | package tools.refinery.store.reasoning.translator.crossreference; |
7 | 7 | ||
8 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
8 | import tools.refinery.store.reasoning.ReasoningAdapter; | 9 | import tools.refinery.store.reasoning.ReasoningAdapter; |
9 | import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; | 10 | import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; |
10 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | 11 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; |
11 | import tools.refinery.store.reasoning.representation.PartialRelation; | 12 | import tools.refinery.store.reasoning.representation.PartialRelation; |
12 | import tools.refinery.store.reasoning.representation.PartialSymbol; | 13 | import tools.refinery.store.reasoning.representation.PartialSymbol; |
14 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
13 | import tools.refinery.store.representation.Symbol; | 15 | import tools.refinery.store.representation.Symbol; |
14 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
15 | import tools.refinery.store.tuple.Tuple; | 16 | import tools.refinery.store.tuple.Tuple; |
16 | 17 | ||
18 | import java.util.Objects; | ||
19 | |||
17 | class UndirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, Boolean> { | 20 | class UndirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, Boolean> { |
18 | private final PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; | 21 | private final PartialRelation sourceType; |
22 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; | ||
19 | 23 | ||
20 | public UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | 24 | protected UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, |
21 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType) { | 25 | PartialSymbol<TruthValue, Boolean> partialSymbol, |
26 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType) { | ||
22 | super(adapter, partialSymbol, concreteSymbol); | 27 | super(adapter, partialSymbol, concreteSymbol); |
23 | sourceRefiner = adapter.getRefiner(sourceType); | 28 | this.sourceType = sourceType; |
29 | } | ||
30 | |||
31 | @Override | ||
32 | public void afterCreate() { | ||
33 | sourceRefiner = getAdapter().getRefiner(sourceType); | ||
24 | } | 34 | } |
25 | 35 | ||
26 | @Override | 36 | @Override |
27 | public boolean merge(Tuple key, TruthValue value) { | 37 | public boolean merge(Tuple key, TruthValue value) { |
28 | int source = key.get(0); | 38 | int source = key.get(0); |
29 | int target = key.get(1); | 39 | int target = key.get(1); |
30 | if (!super.merge(key, value) || !super.merge(Tuple.of(target, source), value)) { | 40 | var currentValue = get(key); |
31 | return false; | 41 | var mergedValue = currentValue.meet(value); |
42 | if (!Objects.equals(currentValue, mergedValue)) { | ||
43 | var oldValue = put(key, mergedValue); | ||
44 | if (source != target) { | ||
45 | var inverseOldValue = put(Tuple.of(target, source), mergedValue); | ||
46 | if (!Objects.equals(oldValue, inverseOldValue)) { | ||
47 | return false; | ||
48 | } | ||
49 | } | ||
32 | } | 50 | } |
33 | if (value.must()) { | 51 | if (value.must()) { |
34 | return sourceRefiner.merge(Tuple.of(source), TruthValue.TRUE) && | 52 | return sourceRefiner.merge(Tuple.of(source), TruthValue.TRUE) && |
@@ -37,6 +55,24 @@ class UndirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, | |||
37 | return true; | 55 | return true; |
38 | } | 56 | } |
39 | 57 | ||
58 | @Override | ||
59 | public void afterInitialize(ModelSeed modelSeed) { | ||
60 | var linkType = getPartialSymbol(); | ||
61 | var cursor = modelSeed.getCursor(linkType); | ||
62 | while (cursor.move()) { | ||
63 | var value = cursor.getValue(); | ||
64 | if (value.must()) { | ||
65 | var key = cursor.getKey(); | ||
66 | boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && | ||
67 | sourceRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); | ||
68 | if (!merged) { | ||
69 | throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" | ||
70 | .formatted(linkType, key)); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
40 | public static Factory<TruthValue, Boolean> of(Symbol<TruthValue> concreteSymbol, PartialRelation sourceType) { | 76 | public static Factory<TruthValue, Boolean> of(Symbol<TruthValue> concreteSymbol, PartialRelation sourceType) { |
41 | return (adapter, partialSymbol) -> new UndirectedCrossReferenceRefiner(adapter, partialSymbol, concreteSymbol, | 77 | return (adapter, partialSymbol) -> new UndirectedCrossReferenceRefiner(adapter, partialSymbol, concreteSymbol, |
42 | sourceType); | 78 | sourceType); |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java index 494211fe..0de1deac 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java | |||
@@ -44,7 +44,7 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati | |||
44 | var type = info.type(); | 44 | var type = info.type(); |
45 | var defaultValue = info.defaultValue(); | 45 | var defaultValue = info.defaultValue(); |
46 | if (defaultValue.must()) { | 46 | if (defaultValue.must()) { |
47 | throw new TranslationException(linkType, "Unsupported default value %s for directed cross references %s" | 47 | throw new TranslationException(linkType, "Unsupported default value %s for undirected cross reference %s" |
48 | .formatted(defaultValue, linkType)); | 48 | .formatted(defaultValue, linkType)); |
49 | } | 49 | } |
50 | var translator = PartialRelationTranslator.of(linkType); | 50 | var translator = PartialRelationTranslator.of(linkType); |
@@ -54,8 +54,8 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati | |||
54 | } else { | 54 | } else { |
55 | configureWithDefaultFalse(storeBuilder); | 55 | configureWithDefaultFalse(storeBuilder); |
56 | } | 56 | } |
57 | translator.initializer(new UndirectedCrossReferenceInitializer(linkType, symbol)); | ||
57 | translator.refiner(UndirectedCrossReferenceRefiner.of(symbol, type)); | 58 | translator.refiner(UndirectedCrossReferenceRefiner.of(symbol, type)); |
58 | translator.initializer(new UndirectedCrossReferenceInitializer(linkType, type, symbol)); | ||
59 | if (info.partial()) { | 59 | if (info.partial()) { |
60 | translator.roundingMode(RoundingMode.NONE); | 60 | translator.roundingMode(RoundingMode.NONE); |
61 | } else { | 61 | } else { |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/CleanupPropagator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/CleanupPropagator.java index f14b4783..6feccae3 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/CleanupPropagator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/CleanupPropagator.java | |||
@@ -11,6 +11,7 @@ import tools.refinery.logic.term.uppercardinality.UpperCardinalities; | |||
11 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; | 11 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; |
12 | import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; | 12 | import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; |
13 | import tools.refinery.store.dse.propagation.BoundPropagator; | 13 | import tools.refinery.store.dse.propagation.BoundPropagator; |
14 | import tools.refinery.store.dse.propagation.PropagationRejectedResult; | ||
14 | import tools.refinery.store.dse.propagation.PropagationResult; | 15 | import tools.refinery.store.dse.propagation.PropagationResult; |
15 | import tools.refinery.store.dse.propagation.Propagator; | 16 | import tools.refinery.store.dse.propagation.Propagator; |
16 | import tools.refinery.store.model.Model; | 17 | import tools.refinery.store.model.Model; |
@@ -48,7 +49,7 @@ public class CleanupPropagator implements Propagator { | |||
48 | return new BoundCleanupPropagator(model); | 49 | return new BoundCleanupPropagator(model); |
49 | } | 50 | } |
50 | 51 | ||
51 | private static class BoundCleanupPropagator implements BoundPropagator { | 52 | private class BoundCleanupPropagator implements BoundPropagator { |
52 | private final Model model; | 53 | private final Model model; |
53 | private final ModelQueryAdapter queryEngine; | 54 | private final ModelQueryAdapter queryEngine; |
54 | private final ResultSet<Boolean> resultSet; | 55 | private final ResultSet<Boolean> resultSet; |
@@ -70,7 +71,11 @@ public class CleanupPropagator implements Propagator { | |||
70 | var cursor = resultSet.getAll(); | 71 | var cursor = resultSet.getAll(); |
71 | while (cursor.move()) { | 72 | while (cursor.move()) { |
72 | propagated = true; | 73 | propagated = true; |
73 | reasoningAdapter.cleanup(cursor.getKey().get(0)); | 74 | var nodeToDelete = cursor.getKey().get(0); |
75 | if (!reasoningAdapter.cleanup(nodeToDelete)) { | ||
76 | return new PropagationRejectedResult(CleanupPropagator.this, | ||
77 | "Failed to remove node: " + nodeToDelete, true); | ||
78 | } | ||
74 | } | 79 | } |
75 | return propagated ? PropagationResult.PROPAGATED : PropagationResult.UNCHANGED; | 80 | return propagated ? PropagationResult.PROPAGATED : PropagationResult.UNCHANGED; |
76 | } | 81 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java index 05d96689..7b3be76d 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java | |||
@@ -41,6 +41,16 @@ public class MultiObjectTranslator implements ModelStoreConfiguration { | |||
41 | public static final PartialFunction<CardinalityInterval, Integer> COUNT_SYMBOL = new PartialFunction<>("COUNT", 1, | 41 | public static final PartialFunction<CardinalityInterval, Integer> COUNT_SYMBOL = new PartialFunction<>("COUNT", 1, |
42 | CardinalityDomain.INSTANCE); | 42 | CardinalityDomain.INSTANCE); |
43 | 43 | ||
44 | private final boolean keepNonExistingObjects; | ||
45 | |||
46 | public MultiObjectTranslator(boolean keepNonExistingObjects) { | ||
47 | this.keepNonExistingObjects = keepNonExistingObjects; | ||
48 | } | ||
49 | |||
50 | public MultiObjectTranslator() { | ||
51 | this(true); | ||
52 | } | ||
53 | |||
44 | @Override | 54 | @Override |
45 | public void apply(ModelStoreBuilder storeBuilder) { | 55 | public void apply(ModelStoreBuilder storeBuilder) { |
46 | storeBuilder.symbol(COUNT_STORAGE); | 56 | storeBuilder.symbol(COUNT_STORAGE); |
@@ -90,7 +100,9 @@ public class MultiObjectTranslator implements ModelStoreConfiguration { | |||
90 | reasoningBuilder.initializer(new MultiObjectInitializer(COUNT_STORAGE)); | 100 | reasoningBuilder.initializer(new MultiObjectInitializer(COUNT_STORAGE)); |
91 | reasoningBuilder.storageRefiner(COUNT_STORAGE, MultiObjectStorageRefiner::new); | 101 | reasoningBuilder.storageRefiner(COUNT_STORAGE, MultiObjectStorageRefiner::new); |
92 | 102 | ||
93 | storeBuilder.tryGetAdapter(PropagationBuilder.class) | 103 | if (!keepNonExistingObjects) { |
94 | .ifPresent(propagationBuilder -> propagationBuilder.propagator(new CleanupPropagator())); | 104 | storeBuilder.tryGetAdapter(PropagationBuilder.class) |
105 | .ifPresent(propagationBuilder -> propagationBuilder.propagator(new CleanupPropagator())); | ||
106 | } | ||
95 | } | 107 | } |
96 | } | 108 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java new file mode 100644 index 00000000..fb4521dd --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.predicate; | ||
7 | |||
8 | import tools.refinery.logic.dnf.AbstractQueryBuilder; | ||
9 | import tools.refinery.logic.dnf.Query; | ||
10 | import tools.refinery.logic.literal.Literal; | ||
11 | import tools.refinery.logic.term.NodeVariable; | ||
12 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
13 | import tools.refinery.store.dse.propagation.PropagationBuilder; | ||
14 | import tools.refinery.store.dse.transition.Rule; | ||
15 | import tools.refinery.store.model.ModelStoreBuilder; | ||
16 | import tools.refinery.store.model.ModelStoreConfiguration; | ||
17 | import tools.refinery.store.query.view.ForbiddenView; | ||
18 | import tools.refinery.store.reasoning.lifting.DnfLifter; | ||
19 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
20 | import tools.refinery.store.reasoning.literal.Modality; | ||
21 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
22 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | ||
23 | import tools.refinery.store.reasoning.translator.RoundingMode; | ||
24 | import tools.refinery.store.reasoning.translator.TranslationException; | ||
25 | import tools.refinery.store.representation.Symbol; | ||
26 | |||
27 | import java.util.ArrayList; | ||
28 | import java.util.List; | ||
29 | |||
30 | import static tools.refinery.logic.literal.Literals.not; | ||
31 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; | ||
32 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove; | ||
33 | import static tools.refinery.store.reasoning.literal.PartialLiterals.*; | ||
34 | import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; | ||
35 | |||
36 | public class BasePredicateTranslator implements ModelStoreConfiguration { | ||
37 | private final PartialRelation predicate; | ||
38 | private final List<PartialRelation> parameterTypes; | ||
39 | private final TruthValue defaultValue; | ||
40 | private final boolean partial; | ||
41 | private final Symbol<TruthValue> symbol; | ||
42 | |||
43 | public BasePredicateTranslator(PartialRelation predicate, List<PartialRelation> parameterTypes, | ||
44 | TruthValue defaultValue, boolean partial) { | ||
45 | this.predicate = predicate; | ||
46 | this.parameterTypes = parameterTypes; | ||
47 | this.defaultValue = defaultValue; | ||
48 | this.partial = partial; | ||
49 | symbol = Symbol.of(predicate.name(), predicate.arity(), TruthValue.class, defaultValue); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void apply(ModelStoreBuilder storeBuilder) { | ||
54 | int arity = predicate.arity(); | ||
55 | if (arity != parameterTypes.size()) { | ||
56 | throw new TranslationException(predicate, | ||
57 | "Expected %d parameter type for base predicate %s, got %d instead" | ||
58 | .formatted(arity, predicate, parameterTypes.size())); | ||
59 | } | ||
60 | if (defaultValue.must()) { | ||
61 | throw new TranslationException(predicate, "Unsupported default value %s for base predicate %s" | ||
62 | .formatted(defaultValue, predicate)); | ||
63 | } | ||
64 | var translator = PartialRelationTranslator.of(predicate); | ||
65 | translator.symbol(symbol); | ||
66 | if (defaultValue.may()) { | ||
67 | configureWithDefaultUnknown(translator); | ||
68 | } else { | ||
69 | configureWithDefaultFalse(storeBuilder); | ||
70 | } | ||
71 | translator.refiner(PredicateRefiner.of(symbol, parameterTypes)); | ||
72 | if (partial) { | ||
73 | translator.roundingMode(RoundingMode.NONE); | ||
74 | } else { | ||
75 | translator.decision(Rule.of(predicate.name(), builder -> { | ||
76 | var parameters = createParameters(builder); | ||
77 | var literals = new ArrayList<Literal>(arity + 2); | ||
78 | literals.add(may(predicate.call(parameters))); | ||
79 | literals.add(not(candidateMust(predicate.call(parameters)))); | ||
80 | for (int i = 0; i < arity; i++) { | ||
81 | literals.add(not(MULTI_VIEW.call(parameters[i]))); | ||
82 | } | ||
83 | builder.clause(literals); | ||
84 | builder.action(add(predicate, parameters)); | ||
85 | })); | ||
86 | } | ||
87 | storeBuilder.with(translator); | ||
88 | } | ||
89 | |||
90 | private NodeVariable[] createParameters(AbstractQueryBuilder<?> builder) { | ||
91 | int arity = predicate.arity(); | ||
92 | var parameters = new NodeVariable[arity]; | ||
93 | for (int i = 0; i < arity; i++) { | ||
94 | parameters[i] = builder.parameter("p" + (i + 1)); | ||
95 | } | ||
96 | return parameters; | ||
97 | } | ||
98 | |||
99 | private void configureWithDefaultUnknown(PartialRelationTranslator translator) { | ||
100 | var name = predicate.name(); | ||
101 | var mayName = DnfLifter.decorateName(name, Modality.MAY, Concreteness.PARTIAL); | ||
102 | int arity = predicate.arity(); | ||
103 | var forbiddenView = new ForbiddenView(symbol); | ||
104 | translator.may(Query.of(mayName, builder -> { | ||
105 | var parameters = createParameters(builder); | ||
106 | var literals = new ArrayList<Literal>(arity + 1); | ||
107 | for (int i = 0; i < arity; i++) { | ||
108 | literals.add(may(parameterTypes.get(i).call(parameters[i]))); | ||
109 | } | ||
110 | literals.add(not(forbiddenView.call(parameters))); | ||
111 | builder.clause(literals); | ||
112 | })); | ||
113 | if (partial) { | ||
114 | var candidateMayName = DnfLifter.decorateName(name, Modality.MAY, Concreteness.CANDIDATE); | ||
115 | translator.candidateMay(Query.of(candidateMayName, builder -> { | ||
116 | var parameters = createParameters(builder); | ||
117 | var literals = new ArrayList<Literal>(arity + 1); | ||
118 | for (int i = 0; i < arity; i++) { | ||
119 | literals.add(candidateMay(parameterTypes.get(i).call(parameters[i]))); | ||
120 | } | ||
121 | literals.add(not(forbiddenView.call(parameters))); | ||
122 | builder.clause(literals); | ||
123 | })); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | private void configureWithDefaultFalse(ModelStoreBuilder storeBuilder) { | ||
128 | var name = predicate.name(); | ||
129 | // Fail if there is no {@link PropagationBuilder}, since it is required for soundness. | ||
130 | var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class); | ||
131 | propagationBuilder.rule(Rule.of(name + "#invalid", builder -> { | ||
132 | var parameters = createParameters(builder); | ||
133 | int arity = parameters.length; | ||
134 | for (int i = 0; i < arity; i++) { | ||
135 | builder.clause( | ||
136 | may(predicate.call(parameters)), | ||
137 | not(may(parameterTypes.get(i).call(parameters[i]))) | ||
138 | ); | ||
139 | } | ||
140 | builder.action(remove(predicate, parameters)); | ||
141 | })); | ||
142 | } | ||
143 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java new file mode 100644 index 00000000..0fccced2 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.predicate; | ||
7 | |||
8 | import org.jetbrains.annotations.Nullable; | ||
9 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
10 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
11 | import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; | ||
12 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | ||
13 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
14 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
15 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
16 | import tools.refinery.store.representation.Symbol; | ||
17 | import tools.refinery.store.tuple.Tuple; | ||
18 | |||
19 | import java.util.List; | ||
20 | import java.util.Objects; | ||
21 | |||
22 | class PredicateRefiner extends ConcreteSymbolRefiner<TruthValue, Boolean> { | ||
23 | private final List<PartialRelation> parameterTypes; | ||
24 | private @Nullable PartialInterpretationRefiner<TruthValue, Boolean>[] parameterTypeRefiners; | ||
25 | |||
26 | protected PredicateRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | ||
27 | Symbol<TruthValue> concreteSymbol, List<PartialRelation> parameterTypes) { | ||
28 | super(adapter, partialSymbol, concreteSymbol); | ||
29 | this.parameterTypes = parameterTypes; | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void afterCreate() { | ||
34 | int arity = parameterTypes.size(); | ||
35 | // Generic array creation. | ||
36 | @SuppressWarnings("unchecked") | ||
37 | PartialInterpretationRefiner<TruthValue, Boolean>[] array = new PartialInterpretationRefiner[arity]; | ||
38 | parameterTypeRefiners = array; | ||
39 | var adapter = getAdapter(); | ||
40 | for (int i = 0; i < arity; i++) { | ||
41 | var parameterType = parameterTypes.get(i); | ||
42 | if (parameterType != null) { | ||
43 | array[i] = adapter.getRefiner(parameterType); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public boolean merge(Tuple key, TruthValue value) { | ||
50 | var currentValue = get(key); | ||
51 | var mergedValue = currentValue.meet(value); | ||
52 | if (!Objects.equals(currentValue, mergedValue)) { | ||
53 | put(key, mergedValue); | ||
54 | } | ||
55 | // Avoid cyclic propagation between parameter types by avoiding propagation after reaching a fixed point. | ||
56 | if (mergedValue.must() && !currentValue.must()) { | ||
57 | return refineParameters(key); | ||
58 | } | ||
59 | return true; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void afterInitialize(ModelSeed modelSeed) { | ||
64 | var predicate = getPartialSymbol(); | ||
65 | var cursor = modelSeed.getCursor(predicate); | ||
66 | while (cursor.move()) { | ||
67 | var value = cursor.getValue(); | ||
68 | if (value.must()) { | ||
69 | var key = cursor.getKey(); | ||
70 | if (!refineParameters(key)) { | ||
71 | throw new IllegalArgumentException("Failed to merge parameter types of predicate %s for key %s" | ||
72 | .formatted(predicate, key)); | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | private boolean refineParameters(Tuple key) { | ||
79 | int arity = parameterTypeRefiners.length; | ||
80 | for (int i = 0; i < arity; i++) { | ||
81 | var refiner = parameterTypeRefiners[i]; | ||
82 | if (refiner != null && !refiner.merge(Tuple.of(key.get(i)), TruthValue.TRUE)) { | ||
83 | return false; | ||
84 | } | ||
85 | } | ||
86 | return true; | ||
87 | } | ||
88 | |||
89 | public static Factory<TruthValue, Boolean> of(Symbol<TruthValue> concreteSymbol, | ||
90 | List<PartialRelation> parameterTypes) { | ||
91 | return (adapter, partialSymbol) -> new PredicateRefiner(adapter, partialSymbol, concreteSymbol, | ||
92 | parameterTypes); | ||
93 | } | ||
94 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java index 010ce977..8dd9cff6 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java | |||
@@ -21,6 +21,9 @@ import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | |||
21 | import tools.refinery.store.reasoning.translator.TranslationException; | 21 | import tools.refinery.store.reasoning.translator.TranslationException; |
22 | import tools.refinery.store.representation.Symbol; | 22 | import tools.refinery.store.representation.Symbol; |
23 | 23 | ||
24 | import java.util.List; | ||
25 | import java.util.Objects; | ||
26 | |||
24 | import static tools.refinery.logic.literal.Literals.not; | 27 | import static tools.refinery.logic.literal.Literals.not; |
25 | import static tools.refinery.store.reasoning.literal.PartialLiterals.may; | 28 | import static tools.refinery.store.reasoning.literal.PartialLiterals.may; |
26 | import static tools.refinery.store.reasoning.literal.PartialLiterals.must; | 29 | import static tools.refinery.store.reasoning.literal.PartialLiterals.must; |
@@ -30,9 +33,11 @@ public class PredicateTranslator implements ModelStoreConfiguration { | |||
30 | private final RelationalQuery query; | 33 | private final RelationalQuery query; |
31 | private final boolean mutable; | 34 | private final boolean mutable; |
32 | private final TruthValue defaultValue; | 35 | private final TruthValue defaultValue; |
36 | private final List<PartialRelation> parameterTypes; | ||
33 | 37 | ||
34 | public PredicateTranslator(PartialRelation relation, RelationalQuery query, boolean mutable, | 38 | public PredicateTranslator(PartialRelation relation, RelationalQuery query, List<PartialRelation> parameterTypes, |
35 | TruthValue defaultValue) { | 39 | boolean mutable, TruthValue defaultValue) { |
40 | this.parameterTypes = parameterTypes; | ||
36 | if (relation.arity() != query.arity()) { | 41 | if (relation.arity() != query.arity()) { |
37 | throw new TranslationException(relation, "Expected arity %d query for partial relation %s, got %d instead" | 42 | throw new TranslationException(relation, "Expected arity %d query for partial relation %s, got %d instead" |
38 | .formatted(relation.arity(), relation, query.arity())); | 43 | .formatted(relation.arity(), relation, query.arity())); |
@@ -78,6 +83,10 @@ public class PredicateTranslator implements ModelStoreConfiguration { | |||
78 | .clause(mayLiterals) | 83 | .clause(mayLiterals) |
79 | .build(); | 84 | .build(); |
80 | translator.may(may); | 85 | translator.may(may); |
86 | |||
87 | if (parameterTypes != null && parameterTypes.stream().anyMatch(Objects::nonNull)) { | ||
88 | translator.refiner(PredicateRefiner.of(symbol, parameterTypes)); | ||
89 | } | ||
81 | } else if (defaultValue.may()) { | 90 | } else if (defaultValue.may()) { |
82 | // If all values are permitted, we don't need to check for any forbidden values in the model. | 91 | // If all values are permitted, we don't need to check for any forbidden values in the model. |
83 | // If the result of this predicate of {@code ERROR}, some other partial relation (that we check for) | 92 | // If the result of this predicate of {@code ERROR}, some other partial relation (that we check for) |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java new file mode 100644 index 00000000..08cc9235 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.predicate; | ||
7 | |||
8 | import tools.refinery.logic.dnf.RelationalQuery; | ||
9 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
10 | import tools.refinery.store.map.Cursor; | ||
11 | import tools.refinery.store.model.ModelStoreBuilder; | ||
12 | import tools.refinery.store.model.ModelStoreConfiguration; | ||
13 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
14 | import tools.refinery.store.reasoning.ReasoningBuilder; | ||
15 | import tools.refinery.store.reasoning.interpretation.AbstractPartialInterpretation; | ||
16 | import tools.refinery.store.reasoning.interpretation.QueryBasedComputedRewriter; | ||
17 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
18 | import tools.refinery.store.reasoning.literal.Modality; | ||
19 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
20 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
21 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | ||
22 | import tools.refinery.store.tuple.Tuple; | ||
23 | |||
24 | public class ShadowPredicateTranslator implements ModelStoreConfiguration { | ||
25 | private final PartialRelation relation; | ||
26 | private final RelationalQuery query; | ||
27 | private final boolean hasInterpretation; | ||
28 | |||
29 | public ShadowPredicateTranslator(PartialRelation relation, RelationalQuery query, boolean hasInterpretation) { | ||
30 | this.relation = relation; | ||
31 | this.query = query; | ||
32 | this.hasInterpretation = hasInterpretation; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public void apply(ModelStoreBuilder storeBuilder) { | ||
37 | var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class); | ||
38 | var may = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); | ||
39 | var must = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); | ||
40 | // Do not let {@link PartialRelationTranslator} merge the partial queries into the candidate ones. | ||
41 | var candidateMay = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); | ||
42 | var candidateMust = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); | ||
43 | var translator = PartialRelationTranslator.of(relation) | ||
44 | .rewriter(new QueryBasedComputedRewriter(may, must, candidateMay, candidateMust, query)); | ||
45 | if (!hasInterpretation) { | ||
46 | translator.interpretation(MissingInterpretation::new); | ||
47 | } | ||
48 | storeBuilder.with(translator); | ||
49 | } | ||
50 | |||
51 | private static class MissingInterpretation extends AbstractPartialInterpretation<TruthValue, Boolean> { | ||
52 | public MissingInterpretation(ReasoningAdapter adapter, Concreteness concreteness, | ||
53 | PartialSymbol<TruthValue, Boolean> partialSymbol) { | ||
54 | super(adapter, concreteness, partialSymbol); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public TruthValue get(Tuple key) { | ||
59 | return fail(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Cursor<Tuple, TruthValue> getAll() { | ||
64 | return fail(); | ||
65 | } | ||
66 | |||
67 | private <T> T fail() { | ||
68 | throw new UnsupportedOperationException("No interpretation for shadow predicate: " + getPartialSymbol()); | ||
69 | } | ||
70 | } | ||
71 | } | ||
diff --git a/subprojects/store/build.gradle.kts b/subprojects/store/build.gradle.kts index ac4b92f6..e0742cbe 100644 --- a/subprojects/store/build.gradle.kts +++ b/subprojects/store/build.gradle.kts | |||
@@ -14,6 +14,6 @@ mavenArtifact { | |||
14 | } | 14 | } |
15 | 15 | ||
16 | dependencies { | 16 | dependencies { |
17 | implementation(libs.eclipseCollections.api) | 17 | implementation(libs.eclipseCollections) |
18 | runtimeOnly(libs.eclipseCollections) | 18 | runtimeOnly(libs.eclipseCollections.impl) |
19 | } | 19 | } |
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index 3b55434c..45922fa7 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java | |||
@@ -76,6 +76,7 @@ class MultiThreadFuzzTest { | |||
76 | @MethodSource | 76 | @MethodSource |
77 | @Timeout(value = 10) | 77 | @Timeout(value = 10) |
78 | @Tag("fuzz") | 78 | @Tag("fuzz") |
79 | @Tag("slow") | ||
79 | void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, | 80 | void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, |
80 | int commitFrequency, int seed, VersionedMapStoreFactoryBuilder<Integer, String> builder) { | 81 | int commitFrequency, int seed, VersionedMapStoreFactoryBuilder<Integer, String> builder) { |
81 | runFuzzTest("MultiThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + | 82 | runFuzzTest("MultiThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + |
diff --git a/subprojects/versions/build.gradle.kts b/subprojects/versions/build.gradle.kts index 0cc9e811..054158cb 100644 --- a/subprojects/versions/build.gradle.kts +++ b/subprojects/versions/build.gradle.kts | |||
@@ -17,8 +17,9 @@ mavenArtifact { | |||
17 | } | 17 | } |
18 | 18 | ||
19 | val refineryVersion = "refinery" | 19 | val refineryVersion = "refinery" |
20 | val interpreterVersion = "refineryInterpreter" | 20 | val interpreterVersion = "refinery-interpreter" |
21 | val interpreterGroup = property("tools.refinery.interpreter.group").toString() | 21 | val interpreterGroup = property("tools.refinery.interpreter.group").toString() |
22 | val shadowVersion = "shadow" | ||
22 | 23 | ||
23 | catalog.versionCatalog { | 24 | catalog.versionCatalog { |
24 | from(files("../../gradle/libs.versions.toml")) | 25 | from(files("../../gradle/libs.versions.toml")) |
@@ -26,6 +27,10 @@ catalog.versionCatalog { | |||
26 | version(interpreterVersion, property("tools.refinery.interpreter.version").toString()) | 27 | version(interpreterVersion, property("tools.refinery.interpreter.version").toString()) |
27 | library("bom", group.toString(), "refinery-bom").versionRef(refineryVersion) | 28 | library("bom", group.toString(), "refinery-bom").versionRef(refineryVersion) |
28 | library("bom-dependencies", group.toString(), "refinery-bom-dependencies").versionRef(refineryVersion) | 29 | library("bom-dependencies", group.toString(), "refinery-bom-dependencies").versionRef(refineryVersion) |
30 | |||
31 | // Let downstream users add the shadow plugin to bundle their dependencies. | ||
32 | version(shadowVersion, pluginLibs.versions.shadow.get()) | ||
33 | plugin("shadow", "io.github.goooler.shadow").versionRef(shadowVersion) | ||
29 | } | 34 | } |
30 | 35 | ||
31 | publishing.publications.named<MavenPublication>("mavenJava") { | 36 | publishing.publications.named<MavenPublication>("mavenJava") { |