File system tutorial
The goal of this tutorial is to give a brief overview of the partial modeling and model generation features of the Refinery framework. The running example will be the modeling of files, directories, and repositories.
Partial models
Types and relations
- First, let us introduce some basic types:
Dir
,File
, andFileSystem
, along with the relations between them:element
androot
. There is ascope
expression at the end, which we will ignore for now.
class FileSystem {
contains File[1] root
}
class File.
class Dir extends File {
contains File[] element
}
scope node = 10.
- Notice that the syntax is essentially identical to Xcore.
- Review the partial model visualization. You should get something like this:
- Add some statements about a partial model:
class FileSystem {
contains File[1] root
}
class File.
class Dir extends File {
contains File[] element
}
Dir(resources).
element(resources, img).
File(img).
scope node = 10.
Partial models
-
Notice that the instance model elements are coexisting with
<type>::new
nodes representing the prototypes of newly created objects. -
Check the disabled
equals
andexist
predicates. check the visual annotation of those predicates in the visualization (dashed line, shadow).
Information merging
-
For the object
img
, we didn’t specify if it is a directory or not. Therefore, it will typically be a folder. -
If we want to state that img is not a directory, we need to a negative statement:
!Dir(img).
-
Statements are merged with respect to the refinement relation of 4-valued logic.
-
If we add, a statement both negatively and positively, it will create an inconsistency:
element(resources, img).
!element(resources, img).
- Inconsistent models parts in a partial model typically make the problem trivially unsatisfiable.
- However, the model can be saved if the inconsistent part may not exist…
!File(File::new).
Default values
- A large amount of statements can be expressed by using
*
. - The
default
keyword defines lower priority statements that need to be considered unless other statement specifies otherwise. No information merging is happening.
Constraints
Let’s extend the metamodel with a new class SymLink
:
class FileSystem {
contains File[1] root
}
class File.
class Dir extends File {
contains File[0..10] element
}
class SymLink extends File {
File[1] target
}
Dir(resources).
element(resources, img).
element(resources, link).
target(link, img).
scope node = 10.
- Add some simple constraints:
% Simple constraints:
pred hasReference(f) <-> target(_, f).
error pred selfLoop(s) <-> target(s, s).
target(x,x).
- There are no empty directories in a git repository, so let’s forbid them!
error pred emptyDir(d) <-> Dir(d), !element(d,_).
- End result:
class FileSystem {
contains File[1] root
}
class File.
class Dir extends File {
contains File[0..10] element
}
class SymLink extends File {
File[1] target
}
Dir(resources).
element(resources, img).
!Dir(img).
element(resources, link).
target(link,img).
% Simple constraints:
pred hasReference(f) <-> target(_, f).
error pred selfLoop(s) <-> target(s, s).
% Object equality with ==:
error pred emptyDir(d) <-> Dir(d), !element(d, _).
pred importantFile(f) <-> target(l1, f), target(l2, f), l1 != l2.
% Transitive closure, and
pred containsFile(fs, file) <->
FileSystem(fs),
root(fs, file)
;
FileSystem(fs),
root(fs, rootDir),
element+(rootDir, file).
% Predicate reuse
error conflictBetweenTwoFileSystem(fs1, fs2, l, t) <->
containsFile(fs1, l),
containsFile(fs2, t),
fs1 != fs2,
target(l, t).
scope node = 40..50, FileSystem = 2, importantFile = 1..*.