Syntax Overview
Dolfin uses indentation-sensitive syntax, like Python. Blocks are opened with a colon (:) and delimited by consistent indentation, tabs or spaces, but never mixed. All lines inside a block must be at the same indentation level.
Comments begin with # and run to the end of the line.
# This is a comment
concept Foo:
has bar: string # inline comment
File types
A Dolfin project uses two kinds of files.
package.dlf: one per project, declares the package identity:
package <http://example.com/my-ontology>:
dolfin_version "1"
version "0.3.0"
author "Alice"
description "A short description"
| Field | Required | Description |
|---|---|---|
dolfin_version | yes | Language version to use (currently "1") |
version | yes | Ontology version (semver string) |
author | no | Author name |
description | no | Human-readable description of the ontology |
*.dlf ontology files: one or more files containing declarations (concepts, enums, properties, rules).
Prefixes
Prefixes bind short aliases to IRI namespaces, enabling interoperability with external vocabularies.
prefix schema as <http://schema.org/>
prefix dc as <http://purl.org/dc/elements/1.1/>
Once declared, a prefix can be used in qualified names:
has address: optional schema.PostalAddress
Prefixes can also be declared in a hierarchical block to share a common path:
prefix com.example:
Person
Organization as Org
This is equivalent to:
prefix com.example.Person
prefix com.example.Organization as Org
Concepts
A concept defines a category of things and the attributes they can carry.
concept Person:
has first_name: one string
has last_name: one string
has email: optional string
has age: optional int
An empty concept (no body) is also valid, useful as a flag or tag:
concept FlaggedForReview
Inheritance
A concept can inherit from one or more parents using sub:
concept Employee:
sub Person
has employee_id: one string
has department: one string
concept Manager:
sub Employee
has reports: Employee
Multiple parents are comma-separated:
concept PartTimeEmployee:
sub Employee, Contractor
Attributes
Each attribute is declared with has:
has <name>: [cardinality] <type>
The type can be a primitive or another concept name:
has count: one int
has owner: optional Person
has tags: string # zero or more (default cardinality)
Cardinality
Cardinality constrains how many values an attribute can hold.
| Keyword | Meaning |
|---|---|
| (none) | Any number (zero or more) |
any | Any number (zero or more, explicit) |
one | Exactly one (required) |
optional | Zero or one |
some | One or more |
N | Exactly N (integer literal) |
N..M | Between N and M (inclusive) |
N..* | At least N |
has name: one string # required, single value
has nickname: optional string # may be absent
has tags: some string # at least one
has aliases: string # any number (default)
has lucky_numbers: 3 int # exactly three
has scores: 1..5 float # between one and five
has comments: 0..* string # zero or more (explicit range)
Primitive types
| Type | Description | Examples |
|---|---|---|
string | Text | "hello", "" |
int | Integer | 0, 42, -7 |
float | Floating-point | 3.14, -0.5 |
boolean | Boolean | true, false |
Closed concepts
An closed concept defines a closed set of named values. Only the declared variants are valid:
concept Status:
one of:
Pending
Active
Archived
Enumurated values are referenced by name in attributes and rules:
has status: one Status
Properties
A property is a named relationship declared independently of any concept, rather than inside one. It connects a domain type to a range type:
property worksFor: Employee -> Organization
Cardinality can be specified on either side:
property manages: one Manager -> Employee
Properties are used in rule patterns and assertions like any attribute.
Rules
A rule defines an if-then inference. When all patterns in the match: block hold, the assertions in the then: block are applied.
rule classify_senior:
match:
?p a Person
?p age [ >= 65 ]
then:
?p a SeniorPerson
Variables
Variables begin with ?. They bind to values during matching and can be referenced in assertions:
rule link_preferred_contact:
match:
?org a Organization
?org primary_contact ?person
then:
?person worksFor ?org
Match patterns
Each line in match: is a pattern. All patterns are combined with implicit AND.
Type pattern: checks that a variable is an instance of a concept:
?x a SomeConcept
Triple pattern: checks that a subject has a property with a given value:
?x someProperty someValue
?x someProperty ?y
?x someProperty "literal"
?x someProperty 42
Constraint block: inline conditions on a value using [...]:
?x age [ > 18 ]
?x status [ = Active ]
?x address [ city "Paris" ]
?x manager [ a Director ]
Multiple constraints in a block are AND-combined:
?x score [ >= 50, <= 100 ]
Constraint blocks can be nested:
?x owner [ address [ country "France" ] ]
Comparison operators
Used inside constraint blocks:
| Operator | Meaning |
|---|---|
= | Equal |
!= | Not equal |
< | Less than |
<= | Less than or equal |
> | Greater than |
>= | Greater than or equal |
Quantifiers
Quantifiers express conditions over collections of matching bindings:
| Quantifier | Meaning |
|---|---|
all | Every binding must satisfy the sub-patterns |
none | No binding may satisfy the sub-patterns |
at_least N | At least N bindings must satisfy the sub-patterns |
at_most N | At most N bindings may satisfy the sub-patterns |
exactly N | Exactly N bindings must satisfy the sub-patterns |
between N, M | Between N and M bindings (inclusive) |
rule require_manager_approval:
match:
?req a Request
none ?approver:
?approver a Manager
?req approvedBy ?approver
then:
?req a UnapprovedRequest
An optional constraint block on the quantifier variable filters the set being quantified:
at_least 2 ?member [ a SeniorEmployee ]:
?member worksIn ?dept
Then assertions
Each line in then: asserts a new fact:
Type assertion: classifies a variable as an instance of a concept:
?x a SomeConcept
Triple assertion: asserts a property relationship:
?x someProperty ?y
?x someProperty "value"
Nested rules
A then: block can contain a nested match:/then: block for conditional sub-inferences:
rule complex_inference:
match:
?x a Foo
then:
match:
?x bar ?y
then:
?y a Baz
Names and identifiers
Simple names are alphanumeric identifiers (with underscores): Person, first_name, status.
Qualified names use dot-notation to reference names in a namespace: schema.Person, com.example.Thing.
IRIs are enclosed in angle brackets: <http://example.com/Thing>. They can appear as package names and prefix targets.
Variables begin with ?: ?person, ?count.
The @iri_name annotation
The @iri_name annotation overrides the IRI segment derived from a file’s name. It appears at the top of an ontology file, before any declarations:
@iri_name "custom-segment"
concept Foo:
has bar: string
This is useful when the file name doesn’t match the IRI fragment expected by external systems.