feat(type-system): implement concept_comparison with pattern matching

Added complete support for the new type system syntax including:

- concept: Base type declarations
- sub_concept: Enum and record sub-type definitions
- concept_comparison: Compile-time pattern matching with conditional guards

Parser changes:
- Added VariantPattern, FieldCondition, and Condition AST nodes
- Implemented "is" keyword for pattern matching (e.g., "CupType is Glass or CupType is Plastic")
- Added Value::Any variant to support universal type matching
- Disambiguated enum-like vs record-like sub_concept syntax

LSP updates:
- Added Value::Any match arms across code_actions, completion, hover, inlay_hints, and semantic_tokens
- Type inference and formatting support for Any values

Example fixes:
- Fixed syntax error in baker-family behaviors (missing closing brace in nested if)
- Removed deprecated core_enums.sb file
This commit is contained in:
2026-02-14 09:28:20 +00:00
parent 6e3b35e68f
commit 25d59d6107
30 changed files with 8639 additions and 6536 deletions

View File

@@ -20,7 +20,9 @@ Declaration: Declaration = {
<r:Relationship> => Declaration::Relationship(r),
<loc:Location> => Declaration::Location(loc),
<sp:Species> => Declaration::Species(sp),
<e:EnumDecl> => Declaration::Enum(e),
<concept:ConceptDecl> => Declaration::Concept(concept),
<sub:SubConceptDecl> => Declaration::SubConcept(sub),
<comp:ConceptComparisonDecl> => Declaration::ConceptComparison(comp),
};
// ===== Use declarations =====
@@ -246,6 +248,7 @@ Value: Value = {
<FloatLit> => Value::Float(<>),
<StringLit> => Value::String(<>),
<BoolLit> => Value::Bool(<>),
"any" => Value::Any,
<lo:IntLit> ".." <hi:IntLit> => Value::Range(
Box::new(Value::Int(lo)),
Box::new(Value::Int(hi))
@@ -740,14 +743,120 @@ Species: Species = {
// ===== Enum =====
EnumDecl: EnumDecl = {
"enum" <name:Ident> "{" <variants:Comma<Ident>> "}" => EnumDecl {
// ===== Type System Declarations =====
ConceptDecl: ConceptDecl = {
"concept" <name:Ident> => ConceptDecl {
name,
span: Span::new(0, 0),
}
};
SubConceptDecl: SubConceptDecl = {
// Enum-like sub_concept: sub_concept PlateColor { Red, Blue, Green }
"sub_concept" <name:Ident> "{" <variants:Comma<Ident>> "}" => {
let parent = {
let mut last_cap = 0;
for (i, ch) in name.char_indices().skip(1) {
if ch.is_uppercase() {
last_cap = i;
}
}
if last_cap > 0 {
name[..last_cap].to_string()
} else {
name.clone()
}
};
SubConceptDecl {
name,
parent_concept: parent,
kind: SubConceptKind::Enum { variants },
span: Span::new(0, 0),
}
},
// Record-like sub_concept with at least one field
"sub_concept" <name:Ident> "{" <first:Ident> ":" <first_val:Value> <rest:("," <Ident> ":" <Value>)*> ","? "}" => {
let parent = {
let mut last_cap = 0;
for (i, ch) in name.char_indices().skip(1) {
if ch.is_uppercase() {
last_cap = i;
}
}
if last_cap > 0 {
name[..last_cap].to_string()
} else {
name.clone()
}
};
let mut fields = vec![Field {
name: first,
value: first_val,
span: Span::new(0, 0),
}];
for (field_name, field_val) in rest {
fields.push(Field {
name: field_name,
value: field_val,
span: Span::new(0, 0),
});
}
SubConceptDecl {
name,
parent_concept: parent,
kind: SubConceptKind::Record { fields },
span: Span::new(0, 0),
}
},
};
ConceptComparisonDecl: ConceptComparisonDecl = {
"concept_comparison" <name:Ident> "{" <variants:Comma<VariantPattern>> "}" => ConceptComparisonDecl {
name,
variants,
span: Span::new(0, 0),
}
};
VariantPattern: VariantPattern = {
<name:Ident> ":" "{" <conditions:Comma<FieldCondition>> "}" => VariantPattern {
name,
conditions,
span: Span::new(0, 0),
}
};
FieldCondition: FieldCondition = {
<field:Ident> ":" "any" => FieldCondition {
field_name: field,
condition: Condition::Any,
span: Span::new(0, 0),
},
<field:Ident> ":" <cond:IsCondition> => FieldCondition {
field_name: field,
condition: Condition::Is(cond),
span: Span::new(0, 0),
},
};
// Parse "FieldName is Value1 or FieldName is Value2" and extract the values
IsCondition: Vec<String> = {
<first:IsValue> <rest:("or" <IsValue>)*> => {
let mut values = vec![first];
values.extend(rest);
values
}
};
IsValue: String = {
<field:Ident> "is" <value:Ident> => value
};
// ===== Expressions =====
// Expression grammar with proper precedence:
// or > and > not > field_access > comparison > term
@@ -877,7 +986,10 @@ extern {
"relationship" => Token::Relationship,
"location" => Token::Location,
"species" => Token::Species,
"enum" => Token::Enum,
"concept" => Token::Concept,
"sub_concept" => Token::SubConcept,
"concept_comparison" => Token::ConceptComparison,
"any" => Token::Any,
"state" => Token::State,
"on" => Token::On,
"enter" => Token::Enter,