Experiments with the Semantic Spacetime knowledge representation by Mark Burgess, with live editing and preview in Visual Studio Code.
Example of the ipmt text representation of the Hokus1 diagram:
e1 ::e --> e2::a Spacetime Event 2. ::e
tA --> tAp1::a Thing A part 1. --> e1
tB --> e1
e2 <-- tC
tB --- tC
e1, tAp1 --> c-V ::c
tC --> c-X ::c --> c-Y ::c
c-Z ::c --- c-X
ipmt lives either in standalone .ipmt files or in ```ipmt fenced blocks
inside markdown. The fence info-string may carry processing-metadata tokens
(e.g. ```ipmt embed=false, ```ipmt unresolved), and the first
non-empty line of any ipmt source may be a # ipmt: pragma carrying the same
tokens — see ipmt-unresolved.md for the flag vocabulary
and md-embed.md for how the render tools consume it.
Deliberately broken examples are written as ```ipmt-invalid fences: a
separate fence language that every render tool skips and the test suite pins
as negative examples (this document's INVALID sections use it). The sections
below define the language itself.
Lines starting with # are comments and ignored.
# This is a comment
Agent A --> E1::a Event 1 ::e
# Another comment
Agent B --> E1
A comment can start with whitespaces before #:
# This is also a comment
Agent A
# And this is a comment in between
--> E1::a Event 1 ::e
A # glued to a non-space character is not a comment, so it stays part of
the name:
Agent #1 --> Event #1 ::e
is valid and means:
"Agent #1" --> "Event #1" ::e
A # that is surrounded by whitespace (preceded by a space or tab, and
followed by a space or end of line) starts a trailing comment — everything
from that # to the end of the line is ignored:
Agent A --> E1::a Event 1 ::e # leads-to inferred from event
means:
Agent A --> E1::a Event 1 ::e
To keep a whitespace-surrounded # literal, quote the name:
"Step # 3" ::t --> E1::a Event 1 ::e
An unquoted [...] at the end of a name is a bracket label: a note for the
reader that is stripped from the name.
open [the long label of the first event] ::e --> close [the second one] ::e
is valid and means:
open ::e --> close ::e
A name that must KEEP its brackets is quoted — inside double quotes nothing is stripped:
"not until [when used before a time expression]" ::c
To specify a node just write the whole text:
Thing A part 1.
or with the explicit ::t marker:
Thing A part 1. ::t
in case you need to mark where the long name ends (e.g. to be able to use ::tip).
Note that double quotes and escapes can be used to include special characters such as ::, , and --> in the node name. Using newlines in node name is not recommended.
Canonical form: place the identifier followed by the ::a marker before the node name:
tAp1::a Thing A part 1.
Current tools also accept a legacy trailing-alias form:
Thing A part 1. tAp1::a
Alias
- must be unique within the document (not currently checked — a redeclared alias silently merges into the first node and the second name is dropped)
- must start with a letter
[A-Za-z] - can contain letters, digits, hyphens, and underscores
[A-Za-z0-9_-] - should use the canonical
alias::a nameform in new documents
See Identifiers below for the full pattern.
To specify type, use ::e for event, ::c for concept, ::t for thing (default):
Event 1 ::e
Concept X ::c
Thing A ::t
Note: ::t is optional because thing is the default type. Uppercase markers
(::E, ::C, ::T) are rejected.
A fourth marker leaves the kind UNDECIDED: ::? followed by two or three
distinct letters from e/t/c lists the candidate kinds, first = primary
(::?et, ::?tc, ::?etc). Such a node is Unresolved, and its PRIMARY
candidate drives edge-type inference:
n1 ::?et --> e9 ::e
See ipmt-unresolved.md for how unresolved nodes render and how the node-kind solver resolves them.
To specify tooltip text, use the ::tip marker:
Thing A part 1. ::t "This is \"Thing A part 1.\" tooltip text." ::tip
Event 1 ::e "This is\nEvent 1\ntooltip text." ::tip
Tooltip text must be enclosed in double quotes ". A type marker (::t/::e/::c) may precede the tooltip but is not required — the tooltip is the quoted string immediately before ::tip. If more than one ::tip is written, only the first is used.
See Double quotes and escapes for details.
Canonical annotation order:
[alias::a] name [::type] ["tooltip" ::tip]
Current tools also accept a legacy variant with alias at the end:
name [::type] ["tooltip" ::tip] [alias::a]
Example:
ste2::a Spacetime Event 2 ::e
Means:
ste2is the alias (placed first, before the name)Spacetime Event 2is the long nameeventis the node type (placed after the name)
tA --> tAp1
Arrow semantics are inferred from the type of the source node (s-type) and the type of the target node (t-type).
Rule: One edge per pair
- For any unordered pair of nodes (A,B), the document may define at most one conceptual edge between them.
- Attempting to define another edge between the same pair elsewhere (e.g., mixing
A-->BwithA<--B) is invalid.
See invalid semantics: Duplicate pair relation.
-->directed arrow from source to target<--reverse arrow form (logical edge is reversed to-->, but original span records<--)---undirected proximity (near to)
Explicit SST link type arrows:
--::L-->explicit leads-to (directed)--::P-->explicit part-of (directed)--::X-->explicit expresses (directed)--::N--explicit near-to (undirected)
Explicit arrows with tooltip (combined form):
--::P "tooltip"-->explicit part-of with tooltip--::P ident-->explicit part-of with unquoted tooltip<--::P "tooltip"--reverse explicit with tooltip
Reverse explicit arrows:
<--::L--reverse leads-to<--::P--reverse part-of<--::X--reverse expresses
See also: Reverse arrow and Chaining arrows.
Whitespace around arrows is optional. A --> B and A-->B are equivalent. Undirected proximity must use three dashes (---). Two dashes (A--B or A -- B) are invalid.
Basic eleven Semantic Spacetime’s γ(3,4) representation link types:
| id | ipm syntax | explicit syntax | source arrow target | γ(3,4) | |
|---|---|---|---|---|---|
| 1 | e1 --> e2 |
e1 --::L--> e2 |
event --> event |
leads to | +L |
| 2 | e4s1 --::P--> e4 |
event --::P--> event |
part-of (contains) | -C | |
| 3 | e4 --::X--> e5 |
event --::X--> event |
expresses | +E | |
| 4 | e4 --- e5 |
e4 --::N-- e5 |
event --- event |
near to | N |
| 5 | tAp1 --> e1 |
tAp1 --::P--> e1 |
thing --> event |
part-of (contains) | -C |
| 6 | e1 --> cJ |
e1 --::X--> cJ |
event --> concept |
expresses | +E |
| 7 | tAp1 --> tA |
tAp1 --::P--> tA |
thing --> thing |
part-of (contains) | -C |
| 8 | tB --- tC |
tB --::N-- tC |
thing --- thing |
near to | N |
| 9 | tAp1 --> cJ |
tAp1 --::X--> cJ |
thing --> concept |
expresses | +E |
| 10 | cJ --> cK |
cJ --::X--> cK |
concept --> concept |
expresses | +E |
| 11 | c-Z --- cJ |
c-Z --::N-- cJ |
concept --- concept |
near to | N |
Visualization conventions for these link types:
| γ(3,4) edge | ipm visualization | |
|---|---|---|
| leads to | +L | orange solid, target arrow |
| part-of (contains) | -C | green solid, target arrow |
| expresses | +E | blue dashed, target arrow |
| near to | N | gray dotted, no arrows |
Mutual Exclusivity Rule: The 4 SST relations (LeadsTo, PartOf, Expresses, NearTo) are mutually exclusive semantic primitives. Between any two nodes, only ONE of these base relation types can exist. Mixing different relations between the same pair is invalid.
Notes:
- The ipm-tools renderers provide visualization as per the above table.
- See also Semantic Spacetime γ(3,4).
e1 <-- tB
Produces a single edge tB --> e1 (logical direction reversed). The arrow span still records <-- for reconstruction.
Reverse form also works with explicit SST link type arrows:
e1 ::e <--::P-- e1sub ::e
is equivalent to
e1sub ::e --::P--> e1 ::e
Another example with expresses:
cJ ::c <--::X-- e1 ::e
is equivalent to
e1 ::e --::X--> cJ ::c
e1 ::e --"e1 leads to e2"--> e2 ::e
Here "e1 leads to e2" is the tooltip text of the edge.
Other examples:
tAp1 --"tAp1 is part of tA"--> tA
tAp1 --"e1 updates existing tAp1"--> e1
tC --e-creates-t--> e2
Note that whitespace around the tooltip text is allowed:
e1 ::e -- "leads to" --> e2 ::e
There is no explicit ::tip marker for edge tooltip text. Any text between the arrow markers is treated as tooltip text.
Simple text is allowed without double quotes — letters, digits and hyphens only (no underscore); quote anything else:
e1 ::e --leadsto--> e2 ::e
or
artifact X --created-by--> eBuild ::e
If the tooltip text contains spaces or characters like ::, ,, -->, it must be enclosed in double quotes.
Explicit SST link type arrows can be combined with a tooltip:
e2a ::e --::P "e2a is part of e2"--> e2 ::e
Here ::P specifies the PartOf link type and "e2a is part of e2" is the tooltip.
Also works with unquoted identifiers:
e2a ::e --::P contains--> e2 ::e
Reverse explicit with tooltip:
e1 ::e <--::P "e2a is part of e1"-- e2a ::e
Undirected explicit with tooltip:
e1 ::e --::N "near"-- e2 ::e
Arrows can be chained to create multiple edges in one line:
tA --> tAp1 --> tAp1sX
is equivalent to:
tA --> tAp1
tAp1 --> tAp1sX
Also can be mixed with various arrow types:
tA <-- tB --> e1 ::e --- e2 ::e
is equivalent to:
tB --> tA
tB --> e1 ::e
e1 --- e2 ::e
With edge tooltips and near link:
tB --"tB is part of tA"--> tA
tB --e1-hosts-tB--> e1 ::e --"e1 near to e2"-- e2 ::e
Double quotes " are required around any text where e.g. double colons, commas, arrows, or newlines are used.
Double quotes can be used for
Inside double quotes, the following escape sequences are supported:
| Escape | Meaning |
|---|---|
\n |
Line break (newline) |
\t |
Tab |
\\ |
Literal backslash \\ |
\" |
Literal double quote " |
Other sequences beginning with \ are reported as errors.
Example with newlines escaped as \n and double quotes escaped as \":
Thing A part 1. ::t "First line with ::e ::tip <-->.\nSecond line after newline with \\ character.\nThird line with \"double quoted text\"." ::tip
Logical rendering for tooltip part:
First line with ::e ::tip <-->.
Second line after newline with \ character.
Third line with "double quoted text".
If no backslash escapes are used the tooltip is taken verbatim.
Backslash escapes are not processed outside double quotes — the backslash is taken literally (this is not a parse error):
Thing A part 1. ::t line 1\nline 2 ::tip
Here \n stays as the two characters \ and n (part of the name), and ::tip is not a tooltip because no quoted string precedes it.
Only the ASCII space (U+0020) and ASCII tab (U+0009) are treated as
whitespace INSIDE a logical line; no other Unicode whitespace is recognized or
normalized there (leading/trailing trimming does also strip other Unicode
whitespace). Leading and trailing whitespace on a logical line is trimmed, but
interior whitespace is not collapsed — runs of spaces (and any non-ASCII
whitespace such as an em space) inside a long name are preserved verbatim.
Newlines followed by indent (two spaces) are treated as whitespace. This applies everywhere, including inside double quotes. Use the \n escape sequence for actual newlines in tooltip values:
a::a Thing A.
::t
"line 1
line 2" ::tip
a
--> e1 ::e
--> e2 ::e
--> e3 ::e
is equivalent to
a::a Thing A. ::t "line 1 line 2" ::tip --> e1 ::e --> e2 ::e --> e3 ::e
To get a newline in the tooltip, use \n:
a::a Thing A. ::t "line 1\nline 2" ::tip --> e1 ::e --> e2 ::e --> e3 ::e
The same newline+indent rule also applies to long names (outside double quotes), so a line break can be used instead of an explicit space:
Thing A
part 1. ::t "line 1
line 2" ::tip
is equivalent to
Thing A part 1. ::t "line 1 line 2" ::tip
The tooltip value is line 1 line 2 (space, not newline). To get a newline, use \n:
Thing A part 1. ::t "line 1\nline 2" ::tip
A "statement" ends at newline. INDENTATION is what continues it: each continued line must be indented with at least two space characters (tabs are not allowed). A trailing comma or arrow does NOT continue a statement on its own — A --> B, followed by an unindented C leaves C as a separate, edgeless node.
Identifiers must match this ASCII pattern:
[A-Za-z][A-Za-z0-9_-]*
Hyphens and underscores are allowed.
No spaces. No leading digits. No Unicode letters. (Only the no-whitespace part is
currently enforced for aliases — 1ab::a X and a.b::a X are accepted today.)
Single-letter identifiers are allowed.
Use commas to create multiple target edges from one source within a single segment or vice versa:
c-X ::c, c-Y ::c, c-Z ::c
e1 ::e --> c-X, c-Y, c-Z
expands to:
c-X ::c
c-Y ::c
c-Z ::c
e1 ::e --> c-X
e1 ::e --> c-Y
e1 ::e --> c-Z
Or
tA, e1 ::e --> c-X ::c
expands to:
tA --> c-X ::c
e1 ::e --> c-X
The result must be the same as writing each line separately with one part of comma-separated list per line.
Having multiple sources and multiple targets in one segment is not allowed.
Comma separated targets can be used with chaining arrows:
c-V ::c, c-W ::c --> c-X ::c --> c-Y ::c --> c-Z ::c
expands to:
c-V ::c --> c-X ::c
c-W ::c --> c-X ::c
c-X ::c --> c-Y ::c --> c-Z ::c
Similar with opposite direction:
c-G ::c --> c-H ::c --> c-I ::c, c-J ::c
expands to:
c-G ::c --> c-H ::c
c-H ::c --> c-I ::c
c-H ::c --> c-J ::c
Tooltip text can be used with comma separated targets:
cA ::c --"cA described by cB and cC"--> cB ::c, cC ::c
expands to two expresses type edges, each with same tooltip:
cA ::c --"cA described by cB and cC"--> cB ::c
cA ::c --"cA described by cB and cC"--> cC ::c
An alias identifier cannot contain a space. This is not a parse error — the parser does not recognize ::a and keeps the whole run as the node name (the alias is silently dropped):
klm opq::a Text abc ::t
Use hyphens and the ::a alias annotation
klm-opq::a Text abc ::t
Only one ::e, ::c, or ::t type marker is allowed per node.
Invalid syntax:
nodeY ::e ::c
nodeY ::t ::t
Conflicting (
::e ::c) or duplicate (::t ::t) type markers on a single node are rejected as a syntax error. Write exactly one type marker per node.
Separate nodes with commas — a comma-less run is read as a single node spec.
Invalid syntax:
nodeA ::t nodeB ::t nodeC ::t
Without commas this is one node spec carrying multiple type markers, which is rejected. Use commas to separate nodes:
nodeA ::t, nodeB ::t, nodeC ::t
Invalid syntax:
A --> B, C --> D
Use either
A --> B
C --> D
or
A --> B
A --> C
B --> D
C --> D
t1 --"missing end--> t2
t1 --"bad "quote" usage"--> t2
Must escape inner quotes: \".
A, B --> C, D
I, J --- K, L
Not allowed: mix of multiple sources and multiple targets in one segment.
A node cannot link to itself. Invalid syntax:
tA --> tA
e1 ::e --> e1 ::e
Cx ::c --> Cx ::c
A thing can be used or modified any time. A thing cannot be created after it is used or modified.
Not yet enforced. This is a future/unimplemented validation: neither the parser nor
ipm-validateflags it today, so the example below parses cleanly and produces zero findings. It is documented as a modeling rule, not a currently-checked one.
Modeling violation (not currently rejected):
# semantically invalid (not enforced)
e1 ::e --> e2 ::e
e1 <-- tA
tA --> e2
Two arrows between the same source and target are not allowed.
Invalid syntax duplicate arrows from A to B:
A --> B
B --> C
A --> B
The same edge (same source, target, and type) cannot be defined more than once. Additionally, the 4 SST base relations (LeadsTo, PartOf, Expresses, NearTo) are mutually exclusive - only ONE base relation type can exist between any pair of nodes.
Invalid examples:
# multiple base relation types between same pair
e1 ::e --::L--> e2 ::e
e1 --::P--> e2
# mixing different base types
tA --> tB
tA --::N-- tB
Agent 1 --> EA::a Event A ::e
Agent 2 --> EA
Artifact X --> EA
Maghull --"Maghull is near UK"-- UK
Maghull --"Town where Mark was born"--> BR::a Born ::e
Mark --> BR
Mark --> Enjoy ::e
BR --> Enjoy
SB::a school bookshop, books --> Enjoy
SB --> Banbury --> UK
ipmt-spec-ex.md— every link type and arrow form as runnable examples.ipmt-parser.md— parser behaviour and output structure.ipmt-unresolved.md— the::?undecided marker, fence meta and the# ipmt:pragma.ipm-validator-rules.md— the semantic rules beyond syntax.sst-gamma34.md— the γ(3,4) theory behind the link types.
A separate ipm-collection tool (planned) will provide validation across multiple diagrams.