Blue Language Specification
This document provides the technical specification for Blue Language, defining core structures, behaviors, and rules that compliant processors must implement.
1. Document Structure
1.1 Base Format
Blue documents use YAML or JSON as their foundation. Any valid YAML or JSON document is a valid Blue document if it adheres to the conventions defined in this specification.
1.2 Node Structure
A node is the fundamental unit in Blue. Every node belongs to exactly one of these categories:
- Value Node: Contains a primitive value (Integer, Text, Double, Boolean)
- List Node: Contains an ordered collection of other nodes
- Structure Node: Contains named references to other nodes
2. Reserved Attributes
Blue reserves several attribute names with special meaning:
Attribute | Purpose |
---|---|
name | The name of the element |
description | A textual description of the element |
type | Reference to another document/type that this element inherits from |
value | The primitive value of the element |
blueId | The content-based identifier of the element |
contracts | Rules that define how the document responds to events |
blue | Transformations for Blue processors |
items | List items (for list nodes only) |
itemType | Type constraint for list items |
keyType | Type constraint for dictionary keys |
valueType | Type constraint for dictionary values |
These attributes must only be used according to their intended purpose to ensure consistent behavior across processors.
2.1 Special Text Attributes
The name
and description
attributes have special properties:
- Always treated as text values
- Are part of the node itself (not references to other nodes)
- Used directly in BlueId calculation
- Cannot be referenced as separate nodes
For example:
# Correct
name: Some Name
otherTextParam:
value: Some Value
type: Text
# Also correct - referencing a node with BlueId
name: Some Name
otherTextParam:
blueId: CLthNx8MWUfPWr1xXzSivsxjye9uQyi77Ep6LGPLvYpM
3. Node Categories and Constraints
3.1 Value Nodes
A value node must:
- Have a
value
property - Not have
items
orproperties
- Be of a basic type (Integer, Text, Double, Boolean) or a subtype
Example:
myInteger:
value: 42
type:
blueId: DHmxTkFbXePZHCHCYmQr2dSzcNLcryFVjXVHkdQrrZr8 # Integer
3.2 List Nodes
A list node must:
- Have an
items
property containing a list - Not have
value
orproperties
- Optionally have an
itemType
constraint
Example:
myList:
items:
- 1
- 2
- 3
itemType:
blueId: DHmxTkFbXePZHCHCYmQr2dSzcNLcryFVjXVHkdQrrZr8 # Integer
3.3 Structure Nodes
A structure node must:
- Have properties referencing other nodes
- Not have
value
oritems
Example:
person:
name: John Doe
age: 30
address:
street: 123 Main St
city: Anytown
4. Type System
4.1 Primitive Types
Blue defines four primitive types:
Type | BlueId | Description | Example Values |
---|---|---|---|
Text | F92yo19rCcbBoBSpUA5LRxpfDejJDAaP1PRxxbWAraVP | Text strings | "Hello" |
Integer | DHmxTkFbXePZHCHCYmQr2dSzcNLcryFVjXVHkdQrrZr8 | Arbitrary precision integer | 42 , 9007199254740991 |
Double | 68ryJtnmui4j5rCZWUnkZ3DChtmEb7Z9F8atn1mBSM3L | IEEE 754 double-precision | 3.14159 |
Boolean | EL6AjrbJsxTWRTPzY8WR8Y2zAMXRbydQj83PcZwuAHbo | Logical true/false | true , false |
4.2 Integer Representation
Integers in Blue have arbitrary precision:
- Values within JavaScript safe integer range (-9007199254740991 to 9007199254740991) are stored natively
- Values outside this range are stored as strings with the Integer type
Example:
x1:
value: 123
type:
blueId: DHmxTkFbXePZHCHCYmQr2dSzcNLcryFVjXVHkdQrrZr8 # Integer
x2:
value: "132452345234524739582739458723948572934875"
type:
blueId: DHmxTkFbXePZHCHCYmQr2dSzcNLcryFVjXVHkdQrrZr8 # Integer
4.3 Type Inheritance
When a node specifies a type
, it inherits all properties from the referenced type. If document B specifies type: A
, then B is considered a subtype of A, inheriting its properties.
4.4 Type Extension Rules
When extending a type:
- Fixed values in the parent type cannot be overridden
- New properties can be added
- Properties with no fixed values can be assigned values
- Type compatibility must be maintained (child properties must use the same or compatible subtypes)
5. BlueId Calculation
5.1 BlueId Definition
A BlueId is a content-based identifier calculated for every node in a Blue document. It uniquely identifies a node based on its semantic content.
5.2 BlueId Algorithm
The BlueId calculation algorithm works as follows:
-
Clean the structure: Remove null values
-
For primitive values (String, Number, Boolean):
- Hash the string representation directly
-
For maps (structure nodes):
- If the map contains a
blueId
key, use its value directly - Otherwise:
- Keep
name
,value
, anddescription
as direct values - Replace all other properties with their BlueId references
- Hash this transformed map
- Keep
- If the map contains a
-
For lists:
- If the list has only one item, return the BlueId of that item
- Otherwise:
- Calculate the BlueId of the sublist excluding the last element
- Calculate the BlueId of the last element
- Create a list with these two BlueIds and hash it
Here is the complete algorithm in Java:
public String calculate(Object object) {
Object cleanedObject = cleanStructure(object);
if (cleanedObject instanceof String || cleanedObject instanceof Number || cleanedObject instanceof Boolean) {
return hashProvider.apply(cleanedObject.toString());
} else if (cleanedObject instanceof Map) {
return calculateMap((Map<String, Object>) cleanedObject);
} else if (cleanedObject instanceof List) {
return calculateList((List<Object>) cleanedObject);
}
throw new IllegalArgumentException(
"Object must be a String, Number, Boolean, List or Map - found " + cleanedObject.getClass());
}
private String calculateMap(Map<String, Object> map) {
if (map.containsKey(OBJECT_BLUE_ID)) {
return (String) map.get(OBJECT_BLUE_ID);
}
Map<String, Object> hashes = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (OBJECT_NAME.equals(key) || OBJECT_VALUE.equals(key) || OBJECT_DESCRIPTION.equals(key)) {
hashes.put(key, entry.getValue());
} else {
String blueId = calculate(entry.getValue());
hashes.put(key, Collections.singletonMap("blueId", blueId));
}
}
return hashProvider.apply(hashes);
}
private String calculateList(List<Object> list) {
if (list.size() == 1) {
return calculate(list.get(0));
}
List<Object> subList = list.subList(0, list.size() - 1);
String hashOfSubList = calculateList(subList);
Object lastElement = list.get(list.size() - 1);
String hashOfLastElement = calculate(lastElement);
List<Map<String, String>> result = Arrays.asList(
Collections.singletonMap("blueId", hashOfSubList),
Collections.singletonMap("blueId", hashOfLastElement));
return hashProvider.apply(result);
}
5.3 Hash Function
The default hash function is Base58-encoded SHA-256, but the blue
directive can specify an alternative hash algorithm.
6. Circular References
Circular references occur when two or more nodes reference each other, creating a dependency cycle.
- name: A
x:
type:
blueId: this#1 # References B
aVal:
constraints:
maxLength: 4
- name: B
y:
type:
blueId: this#0 # References A
bVal:
constraints:
maxLength: 4
bConst: xyz
When calculating BlueIds with circular references:
- Temporary markers in the format
this#n
refer to nodes within the current calculation context - The final BlueIds are calculated simultaneously, each aware of the others
- During extension and resolution, cycles are detected and handled appropriately
Example usage with circular types:
name: Some
a:
type:
blueId: [A's BlueId]
aVal: abcd
x:
bVal: abcd
7. Document Operations
7.1 Resolution
Resolving a node involves expanding all fields from its subtypes and inherited documents:
# Define A
name: A
x: 10
y:
type: Integer
---
# Define B inheriting from A
name: B
type: A
y: 5
z: 5
# After resolving B, it will have:
# - x: 10 (inherited from A)
# - y: 5 (overridden)
# - z: 5 (additional property)
7.2 Extension
Extending a node populates it with additional information from its referenced types or blueIds without fully resolving all inherited properties:
Node oneLevelExtended = blue.extend(node, PathLimits.withSinglePath("/*"));
7.3 Simplification
Simplifying a node reduces it to its minimal form by removing inherited properties that match defaults from parent types:
# Resolved Node B
name: B
type: A
x: 10 # Inherited value
y: 5
z: 5
---
# After simplification, Node B becomes:
name: B
type: A
y: 5 # Only differs from A
z: 5 # Not in A
8. Blue Directive
The blue
directive specifies transformations to apply during preprocessing. For complete details, see The Blue Directive.
9. Java Implementation Model
The Java reference implementation models nodes as:
public class Node implements Cloneable {
private String name;
private String description;
private Node type;
private Node itemType;
private Node keyType;
private Node valueType;
private Object value;
private List<Node> items;
private Map<String, Node> properties;
private String blueId;
private Node blue;
// Methods omitted
}
This structure enforces that:
- A node is either a value node, a list node, or a structure node
- Special attributes like
name
anddescription
are treated as strings - BlueId can be cached with the node after calculation
10. Processing Pipeline
When a Blue processor handles a document, it follows this pipeline:
- Parsing: Convert from YAML/JSON to node structure
- Preprocessing: Apply transformations from
blue
directive - BlueId Calculation: Calculate BlueIds for all nodes
- Reference Resolution: Resolve BlueId references
- Type Resolution: Apply type inheritance
- Validation: Verify type constraints
- Contract Processing: Process contract definitions (if applicable)
11. Error Conditions
A compliant Blue processor must generate errors for:
- Type incompatibilities (attempting to override a parent type)
- Constraint violations (exceeding maxLength, etc.)
- Invalid attribute usage (using reserved attributes incorrectly)
- BlueId calculation failures
- Circular reference issues not properly resolved
- Ambiguous or conflicting type definitions