use ordered_float::OrderedFloat;
use std::fmt::{Display, Formatter};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
pub const INTERNAL_ENUM_TYPE_NAME: &str = "__RelonEnum";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct NodeId(pub u32);
impl NodeId {
pub const SYNTHETIC: NodeId = NodeId(0);
pub fn alloc() -> NodeId {
static COUNTER: AtomicU32 = AtomicU32::new(1);
NodeId(COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Debug, PartialEq, Clone, Eq, Copy, Default, Hash)]
pub struct TokenPosition {
pub line: u32,
pub column: usize,
pub offset: usize,
}
#[derive(Debug, PartialEq, Clone, Eq, Copy, Default, Hash)]
pub struct TokenRange {
pub start: TokenPosition,
pub end: TokenPosition,
}
impl From<TokenRange> for miette::SourceSpan {
fn from(range: TokenRange) -> Self {
let len = range.end.offset.saturating_sub(range.start.offset);
(range.start.offset, len).into()
}
}
#[derive(Debug, PartialEq, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum TokenKey {
Dummy,
Index(usize, bool), String(String, TokenRange, bool), Dynamic(Node, bool), Spread(TokenRange),
}
impl TokenKey {
pub fn name(&self) -> String {
match self {
TokenKey::Dummy => "_".to_string(),
TokenKey::Index(i, _) => i.to_string(),
TokenKey::String(s, _, _) => s.clone(),
TokenKey::Dynamic(_, _) => "<dynamic>".to_string(),
TokenKey::Spread(_) => "...".to_string(),
}
}
pub fn to_string_key(&self) -> String {
self.name()
}
pub fn is_optional(&self) -> bool {
match self {
TokenKey::Index(_, opt) => *opt,
TokenKey::String(_, _, opt) => *opt,
TokenKey::Dynamic(_, opt) => *opt,
_ => false,
}
}
}
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub struct TokenId(pub String, pub TokenRange);
impl TokenId {
pub fn name(&self) -> &str {
&self.0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct CallArg {
pub name: Option<String>,
pub value: Node,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Decorator {
pub path: Vec<TokenKey>,
pub args: Vec<CallArg>,
pub range: TokenRange,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DirectiveShape {
Bare,
Value,
NameBody,
Enum,
Import,
Main,
}
#[derive(Debug, PartialEq, Clone)]
pub enum DirectiveBody {
Bare,
Value(Box<Node>),
NameBody {
name: String,
name_range: TokenRange,
generics: Vec<String>,
body: Box<Node>,
methods: Vec<SchemaMethod>,
schema_no_auto_derives: Vec<String>,
},
Import {
spec: DirectiveImportSpec,
path: String,
path_range: TokenRange,
integrity: Option<IntegrityHash>,
},
Main {
params: Vec<DirectiveMainParam>,
return_type: Option<TypeNode>,
},
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum HashAlgorithm {
Sha256,
}
impl HashAlgorithm {
pub fn as_str(&self) -> &'static str {
match self {
HashAlgorithm::Sha256 => "sha256",
}
}
pub fn from_ident(name: &str) -> Option<Self> {
match name {
"sha256" => Some(HashAlgorithm::Sha256),
_ => None,
}
}
pub fn hex_len(&self) -> usize {
match self {
HashAlgorithm::Sha256 => 64,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct IntegrityHash {
pub algorithm: Option<HashAlgorithm>,
pub algorithm_text: String,
pub hex: String,
pub range: TokenRange,
}
#[derive(Debug, PartialEq, Clone)]
pub enum DirectiveImportSpec {
Alias(String),
Spread,
Destructure(Vec<(String, Option<String>)>),
}
#[derive(Debug, PartialEq, Clone)]
pub struct DirectiveMainParam {
pub name: String,
pub name_range: TokenRange,
pub type_node: TypeNode,
}
#[derive(Debug, PartialEq, Clone)]
pub struct SchemaMethodParam {
pub name: String,
pub name_range: TokenRange,
pub type_node: TypeNode,
}
#[derive(Debug, PartialEq, Clone)]
pub struct SchemaMethod {
pub name: String,
pub name_range: TokenRange,
pub generics: Vec<String>,
pub params: Vec<SchemaMethodParam>,
pub return_type: TypeNode,
pub body: Option<Box<Node>>,
pub derives: Vec<String>,
pub is_native: bool,
pub is_private: bool,
pub range: TokenRange,
pub doc_comment: Option<String>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Directive {
pub name: String,
pub body: DirectiveBody,
pub range: TokenRange,
}
#[derive(Debug, PartialEq, Clone)]
pub struct TypeNode {
pub path: Vec<String>,
pub generics: Vec<TypeNode>,
pub is_optional: bool,
pub range: TokenRange,
pub variant_fields: Option<Vec<(String, TypeNode)>>,
pub doc_comment: Option<String>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct ClosureParam {
pub name: String,
pub type_hint: Option<TypeNode>,
pub range: TokenRange,
}
#[derive(Debug, PartialEq, Clone)]
pub struct PatternBinding {
pub field: Option<String>,
pub binding: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Node {
pub id: NodeId,
pub expr: Arc<Expr>,
pub decorators: Vec<Decorator>,
pub directives: Vec<Directive>,
pub type_hint: Option<TypeNode>,
pub range: TokenRange,
pub doc_comment: Option<String>,
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.expr == other.expr
&& self.decorators == other.decorators
&& self.directives == other.directives
&& self.type_hint == other.type_hint
&& self.range == other.range
&& self.doc_comment == other.doc_comment
}
}
impl Node {
pub fn new(expr: Expr, range: TokenRange) -> Self {
Self {
id: NodeId::alloc(),
expr: Arc::new(expr),
decorators: Vec::new(),
directives: Vec::new(),
type_hint: None,
range,
doc_comment: None,
}
}
pub fn with_id(id: NodeId, expr: Expr, range: TokenRange) -> Self {
Self {
id,
expr: Arc::new(expr),
decorators: Vec::new(),
directives: Vec::new(),
type_hint: None,
range,
doc_comment: None,
}
}
pub fn with_decorators(mut self, decorators: Vec<Decorator>) -> Self {
self.decorators = decorators;
self
}
pub fn with_directives(mut self, directives: Vec<Directive>) -> Self {
self.directives = directives;
self
}
pub fn with_type_hint(mut self, type_hint: Option<TypeNode>) -> Self {
self.type_hint = type_hint;
self
}
pub fn with_doc_comment(mut self, doc_comment: Option<String>) -> Self {
self.doc_comment = doc_comment;
self
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Expr {
Missing,
Bool(bool),
Int(i64),
Float(OrderedFloat<f64>),
String(String),
List(Vec<Node>),
Tuple(Vec<Node>),
Dict(Vec<(TokenKey, Node)>),
Spread(Node),
Comprehension {
element: Node,
id: String,
iterable: Node,
condition: Option<Node>,
},
Variable(Vec<TokenKey>),
Reference {
base: RefBase,
path: Vec<TokenKey>,
},
Binary(Operator, Node, Node),
Unary(Operator, Node),
Ternary {
cond: Node,
then: Node,
els: Node,
},
FnCall {
path: Vec<TokenKey>,
args: Vec<CallArg>,
},
FString(Vec<FStringPart>),
Type(TypeNode),
Wildcard,
Where {
expr: Node,
bindings: Node,
},
Match {
expr: Node,
arms: Vec<(Node, Node)>,
},
VariantPattern {
enum_path: Vec<String>,
variant: String,
bindings: Vec<PatternBinding>,
},
Closure {
params: Vec<ClosureParam>,
return_type: Option<TypeNode>,
body: Node,
},
VariantCtor {
enum_path: Vec<String>,
variant: String,
body: Node,
},
}
#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
pub enum RefBase {
Root,
Sibling,
Uncle,
Prev,
Next,
Index,
This,
}
#[derive(Debug, PartialEq, Clone)]
pub enum FStringPart {
Literal(String),
Interpolation(Box<Node>),
}
#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
pub enum Operator {
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
And,
Or,
Not,
Pipe,
Concat,
}
impl Expr {
pub fn kind(&self) -> &'static str {
match self {
Expr::Missing => "Missing",
Expr::Bool(_) => "Bool",
Expr::Int(_) => "Int",
Expr::Float(_) => "Float",
Expr::String(_) => "String",
Expr::List(_) => "List",
Expr::Tuple(_) => "Tuple",
Expr::Dict(_) => "Dict",
Expr::Spread(_) => "Spread",
Expr::Comprehension { .. } => "Comprehension",
Expr::Variable(_) => "Variable",
Expr::Reference { .. } => "Reference",
Expr::Binary(_, _, _) => "Binary",
Expr::Unary(_, _) => "Unary",
Expr::Ternary { .. } => "Ternary",
Expr::FnCall { .. } => "FnCall",
Expr::FString(_) => "FString",
Expr::Type(_) => "Type",
Expr::Wildcard => "Wildcard",
Expr::Where { .. } => "Where",
Expr::Match { .. } => "Match",
Expr::VariantPattern { .. } => "VariantPattern",
Expr::Closure { .. } => "Closure",
Expr::VariantCtor { .. } => "VariantCtor",
}
}
}
impl Display for Expr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Missing => write!(f, "<missing>"),
Expr::Bool(v) => write!(f, "{}", v),
Expr::Int(v) => write!(f, "{}", v),
Expr::Float(v) => write!(f, "{}", v),
Expr::String(v) => write!(f, "\"{}\"", v),
_ => write!(f, "<expr>"),
}
}
}
pub fn is_builtin_type_name(name: &str) -> bool {
matches!(
name,
"Int"
| "Float"
| "Number"
| "String"
| "Bool"
| "Any"
| "List"
| "Dict"
| "Closure"
| "Fn"
| "Tuple"
)
}
pub fn type_node_from_brand_arg(expr: &Expr, range: TokenRange) -> Option<TypeNode> {
match expr {
Expr::Type(t) => Some(t.clone()),
Expr::Variable(path) => {
let mut segs = Vec::with_capacity(path.len());
for tk in path {
match tk {
TokenKey::String(s, _, false) => segs.push(s.clone()),
_ => return None,
}
}
if segs.is_empty() {
return None;
}
Some(TypeNode {
path: segs,
generics: Vec::new(),
is_optional: false,
range,
variant_fields: None,
doc_comment: None,
})
}
Expr::String(s) => {
if s.is_empty() {
return None;
}
let segs: Vec<String> = s.split('.').map(|p| p.to_string()).collect();
if segs.iter().any(|p| p.is_empty()) {
return None;
}
Some(TypeNode {
path: segs,
generics: Vec::new(),
is_optional: false,
range,
variant_fields: None,
doc_comment: None,
})
}
_ => None,
}
}