blob: 14470f8fca06cf1dd7d04ffd4fac7447924db7eb [file] [edit]
# Copyright 2026 The Dawn & Tint Authors
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""AST nodes produced by the Tint intrinsic definition parser."""
from __future__ import annotations
from dataclasses import dataclass, field
import enum
from typing import Any, Optional
import tok
def _format_val(item: Any) -> str:
if isinstance(item, float) and item.is_integer():
return str(int(item))
return str(item)
def _format_list(items: list[Any]) -> str:
return ", ".join(_format_val(item) for item in items)
@dataclass
class Attribute:
"""Describes a single attribute (e.g. @a(x))."""
name: str = ""
values: list[Any] = field(default_factory=list)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
if self.values:
return f"{self.name}({_format_list(self.values)})"
return self.name
class Attributes(list[Attribute]):
"""A list of Attribute nodes with lookup/removal support."""
def take(self, name: str) -> Optional[Attribute]:
"""Removes and returns the first attribute matching `name`, or None."""
for i, attr in enumerate(self):
if attr.name == name:
return self.pop(i)
return None
def __str__(self) -> str:
return "".join(f"@{attr} " for attr in self)
@dataclass
class TemplatedName:
"""An identifier with optional template arguments (e.g. vec<N, T>)."""
name: str = ""
template_args: list[TemplatedName] = field(default_factory=list)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
if self.template_args:
return f"{self.name}<{_format_list(self.template_args)}>"
return self.name
@dataclass
class MemberName:
"""Two identifiers separated by a dot (Owner.Member)."""
owner: str = ""
member: str = ""
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
return f"{self.owner}.{self.member}"
@dataclass
class TemplateParam:
"""A template parameter with optional type (e.g. <Name> or <Name: Type>)."""
name: str = ""
type: TemplatedName = field(default_factory=TemplatedName)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
if self.type.name:
return f"{self.name} : {self.type}"
return self.name
@dataclass
class EnumEntry:
"""Describes an entry in an enumerator."""
name: str = ""
attributes: Attributes = field(default_factory=Attributes)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
if self.attributes:
return f"{self.attributes} {self.name}"
return self.name
@dataclass
class EnumDecl:
"""Describes an enumerator declaration."""
name: str = ""
attributes: Attributes = field(default_factory=Attributes)
entries: list[EnumEntry] = field(default_factory=list)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
lines = [f"enum {self.name} {{\n"]
for entry in self.entries:
lines.append(f" {entry}\n")
lines.append("}\n")
return "".join(lines)
@dataclass
class TypeDecl:
"""Describes a type declaration."""
name: str = ""
attributes: Attributes = field(default_factory=Attributes)
template_params: list[TemplateParam] = field(default_factory=list)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
out = ""
if self.attributes:
out += f"{self.attributes} type {self.name}"
out += f"type {self.name}"
if self.template_params:
out += f"<{_format_list(self.template_params)}>"
return out
@dataclass
class MatcherOptions:
"""A list of TemplatedNames or MemberNames in a matcher declaration."""
types: list[TemplatedName] = field(default_factory=list)
enumerants: list[MemberName] = field(default_factory=list)
def __str__(self) -> str:
parts: list[str] = []
if self.types:
parts.append(" | ".join(str(t) for t in self.types))
if self.enumerants:
parts.append(" | ".join(str(e) for e in self.enumerants))
return "".join(parts)
@dataclass
class MatcherDecl:
"""Describes a matcher declaration."""
name: str = ""
options: MatcherOptions = field(default_factory=MatcherOptions)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
return f"match {self.name}: {self.options}"
class IntrinsicKind(str, enum.Enum):
"""Kind of intrinsic declaration."""
Builtin = "builtin"
Operator = "operator"
Constructor = "constructor"
Converter = "converter"
def __str__(self) -> str:
return self.value
@dataclass
class Parameter:
"""Describes a single parameter of a function."""
name: str = ""
attributes: Attributes = field(default_factory=Attributes)
type: TemplatedName = field(default_factory=TemplatedName)
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
if self.name:
return f"{self.name}: {self.type}"
return str(self.type)
def format_parameters(params: list[Parameter]) -> str:
parts = [f"{p.attributes}{p}" for p in params]
return f"({', '.join(parts)})"
@dataclass
class IntrinsicDecl:
"""Describes a builtin, operator, constructor, or converter declaration."""
kind: IntrinsicKind = IntrinsicKind.Builtin
name: str = ""
attributes: Attributes = field(default_factory=Attributes)
implicit_template_params: list[TemplateParam] = field(default_factory=list)
explicit_template_params: list[TemplateParam] = field(default_factory=list)
parameters: list[Parameter] = field(default_factory=list)
return_type: Optional[TemplatedName] = None
source: tok.Source = field(default_factory=tok.Source, compare=False)
def __str__(self) -> str:
prefix = {
IntrinsicKind.Builtin: "fn ",
IntrinsicKind.Operator: "op ",
IntrinsicKind.Constructor: "ctor ",
IntrinsicKind.Converter: "conv ",
}[self.kind]
out = f"{prefix}{self.name}"
if self.explicit_template_params:
out += f"<{_format_list(self.explicit_template_params)}>"
if self.implicit_template_params:
out += f"[{_format_list(self.implicit_template_params)}]"
out += format_parameters(self.parameters)
if self.return_type is not None:
out += f" -> {self.return_type}"
return out
@dataclass
class AST:
"""The parsed syntax tree of an intrinsic definition file."""
enums: list[EnumDecl] = field(default_factory=list)
types: list[TypeDecl] = field(default_factory=list)
matchers: list[MatcherDecl] = field(default_factory=list)
builtins: list[IntrinsicDecl] = field(default_factory=list)
constructors: list[IntrinsicDecl] = field(default_factory=list)
converters: list[IntrinsicDecl] = field(default_factory=list)
operators: list[IntrinsicDecl] = field(default_factory=list)
def __str__(self) -> str:
lines: list[str] = []
for group in (
self.enums,
self.types,
self.matchers,
self.builtins,
self.constructors,
self.converters,
self.operators,
):
for item in group:
lines.append(f"{item}\n")
return "".join(lines)