Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 103 additions & 11 deletions packages/morphir/src/morphir/ir/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,104 @@
"""Morphir Intermediate Representation (IR) models.
from . import module, package
from .access_controlled import AccessControlled
from .decorations import (
DecorationFormat,
DecorationValuesFile,
LayerManifest,
SchemaRef,
)
from .distribution import (
ApplicationDistribution,
Distribution,
EntryPoint,
EntryPointKind,
LibraryDistribution,
PackageInfo,
SpecsDistribution,
)
from .document import (
DocArray,
DocBool,
DocFloat,
DocInt,
DocNull,
DocObject,
DocString,
Document,
)
from .documented import Documented
from .fqname import FQName
from .literal import (
BoolLiteral,
CharLiteral,
DecimalLiteral,
FloatLiteral,
IntegerLiteral,
Literal,
StringLiteral,
)
from .meta import FileMeta, SourceRange
from .name import Name
from .path import Path
from .qname import QName
from .ref import DefRef, FileWithDefs, PointerRef, Ref
from .type import Field, Type, TypeAttributes
from .type_constraints import TypeConstraints
from .type_def import Definition as TypeDefinition
from .type_spec import Specification as TypeSpecification
from .value import Definition as ValueDefinition
from .value import Pattern, Value, ValueAttributes
from .value import Specification as ValueSpecification

This module contains the core IR types that represent Morphir's type-safe
domain modeling primitives. The IR serves as the canonical representation
of domain models that can be transformed into various target languages.

Note:
This module is currently a placeholder. The full IR implementation
will be added in subsequent development phases.
"""

__all__: list[str] = []
__all__ = [
"AccessControlled",
"ApplicationDistribution",
"BoolLiteral",
"CharLiteral",
"DecimalLiteral",
"DecorationFormat",
"DecorationValuesFile",
"DefRef",
"Distribution",
"DocArray",
"DocBool",
"DocFloat",
"DocInt",
"DocNull",
"DocObject",
"DocString",
"Document",
"Documented",
"EntryPoint",
"EntryPointKind",
"FQName",
"Field",
"FileMeta",
"FileWithDefs",
"FloatLiteral",
"IntegerLiteral",
"LayerManifest",
"LibraryDistribution",
"Literal",
"Name",
"PackageInfo",
"Path",
"Pattern",
"PointerRef",
"QName",
"Ref",
"SchemaRef",
"SourceRange",
"SpecsDistribution",
"StringLiteral",
"Type",
"TypeAttributes",
"TypeConstraints",
"TypeDefinition",
"TypeSpecification",
"Value",
"ValueAttributes",
"ValueDefinition",
"ValueSpecification",
"module",
"package",
]
17 changes: 17 additions & 0 deletions packages/morphir/src/morphir/ir/access_controlled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from dataclasses import dataclass
from typing import Generic, TypeVar, Union

T = TypeVar("T")


@dataclass(frozen=True)
class Public(Generic[T]):
value: T


@dataclass(frozen=True)
class Private(Generic[T]):
value: T


AccessControlled = Union[Public[T], Private[T]]
88 changes: 88 additions & 0 deletions packages/morphir/src/morphir/ir/decorations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from dataclasses import dataclass, field
from typing import Any


@dataclass(frozen=True)
class SchemaRef:
display_name: str
local_path: str
entry_point: str # FQName as string for simplicity in meta structures
description: str | None = None
remote_ref: str | None = None
cached_at: str | None = None


@dataclass(frozen=True)
class DecorationFormat:
format_version: str
schema_registry: dict[str, SchemaRef] = field(default_factory=dict)
layers: list[str] = field(default_factory=list)
layer_priority: dict[str, int] = field(default_factory=dict)


@dataclass(frozen=True)
class LayerManifest:
format_version: str
layer: str
display_name: str
priority: int
created_at: str
updated_at: str
description: str | None = None
decoration_types: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class DecorationValuesFile:
format_version: str
decoration_type: str
layer: str
values: dict[str, Any] = field(default_factory=dict) # Key is FQName string


def deep_merge(base: Any, override: Any) -> Any:
"""Deep merge two values.

- If both are dicts, merge recursively.
- If both are lists, concatenate (override extends base).
- Otherwise, override wins.
"""
if isinstance(base, dict) and isinstance(override, dict):
merged = base.copy()
for k, v in override.items():
if k in merged:
merged[k] = deep_merge(merged[k], v)
else:
merged[k] = v
return merged
elif isinstance(base, list) and isinstance(override, list):
return base + override
else:
return override


def merge_decoration_values(layers: list[tuple[int, dict[str, Any]]]) -> dict[str, Any]:
"""Merge decoration values from multiple layers based on priority.

Args:
layers: List of (priority, values_dict) tuples.

Returns:
Merged dictionary of decoration values.
"""
# Sort by priority (ascending) -> processed in order, so higher priority overrides later
# Wait, simple override logic usually means last one wins.
# If priority 0 is base, and 100 is override, then 0 should be processed first, then 100 merged on top.
# So ascending sort is correct for standard "last write wins" merge logic.
sorted_layers = sorted(layers, key=lambda x: x[0])

merged: dict[str, Any] = {}

for _, values in sorted_layers:
for key, value in values.items():
if key in merged:
merged[key] = deep_merge(merged[key], value)
else:
merged[key] = value

return merged
56 changes: 56 additions & 0 deletions packages/morphir/src/morphir/ir/distribution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from dataclasses import dataclass, field
from enum import Enum
from typing import Union

from .documented import Documented
from .fqname import FQName
from .name import Name
from .package import Definition as PackageDefinition
from .package import Specification as PackageSpecification
from .path import Path


@dataclass(frozen=True)
class PackageInfo:
name: Path
version: str


@dataclass(frozen=True)
class LibraryDistribution:
package: PackageInfo
definition: PackageDefinition
dependencies: dict[Path, PackageSpecification] = field(default_factory=dict)


@dataclass(frozen=True)
class SpecsDistribution:
package: PackageInfo
specification: PackageSpecification
dependencies: dict[Path, PackageSpecification] = field(default_factory=dict)


class EntryPointKind(Enum):
Main = "Main"
Command = "Command"
Handler = "Handler"
Job = "Job"
Policy = "Policy"


@dataclass(frozen=True)
class EntryPoint:
target: FQName
kind: EntryPointKind
doc: Documented[str] | None = None


@dataclass(frozen=True)
class ApplicationDistribution:
package: PackageInfo
definition: PackageDefinition
dependencies: dict[Path, PackageDefinition] = field(default_factory=dict)
entry_points: dict[Name, EntryPoint] = field(default_factory=dict)


Distribution = Union[LibraryDistribution, SpecsDistribution, ApplicationDistribution]
69 changes: 69 additions & 0 deletions packages/morphir/src/morphir/ir/document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from dataclasses import dataclass
from typing import Union


@dataclass(frozen=True)
class DocNull:
pass


@dataclass(frozen=True)
class DocBool:
value: bool


@dataclass(frozen=True)
class DocInt:
value: int


@dataclass(frozen=True)
class DocFloat:
value: float


@dataclass(frozen=True)
class DocString:
value: str


# Forward references for recursive types
@dataclass(frozen=True)
class DocArray:
elements: list[Document]


@dataclass(frozen=True)
class DocObject:
fields: dict[str, Document]


Document = Union[DocNull, DocBool, DocInt, DocFloat, DocString, DocArray, DocObject]


def null() -> Document:
return DocNull()


def bool_(value: bool) -> Document:
return DocBool(value)


def int_(value: int) -> Document:
return DocInt(value)


def float_(value: float) -> Document:
return DocFloat(value)


def string(value: str) -> Document:
return DocString(value)


def array(elements: list[Document]) -> Document:
return DocArray(elements)


def object_(fields: dict[str, Document]) -> Document:
return DocObject(fields)
10 changes: 10 additions & 0 deletions packages/morphir/src/morphir/ir/documented.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from dataclasses import dataclass
from typing import Generic, TypeVar

T = TypeVar("T")


@dataclass(frozen=True)
class Documented(Generic[T]):
doc: str | None
value: T
Loading
Loading