Wrapper Expression Variants
For clarity, several operations are modeled as distinct expression variants in the AST:
New(NewExpression)for object creationMemberAccess(MemberAccessExpression)forobj.MemberInvocation(InvocationExpression)for callsexpr(args)Indexing(IndexingExpression)andIndex(IndexExpression)Range(RangeExpression)forstart..endWith { target, initializers }for record-like with-expressionsCollection(Vec<CollectionElement>)for collection expressions
Expression Parsing
BSharp implements a complete expression parser that handles all C# expression types with proper operator precedence and associativity.
Expression Hierarchy
The expression parser follows C#'s operator precedence rules:
- Primary Expressions (
x,x.y,x[y],x(), etc.) - Unary Expressions (
+x,-x,!x,~x,++x,--x) - Multiplicative (
*,/,%) - Additive (
+,-) - Shift (
<<,>>) - Relational (
<,>,<=,>=,is,as) - Equality (
==,!=) - Logical AND (
&) - Logical XOR (
^) - Logical OR (
|) - Conditional AND (
&&) - Conditional OR (
||) - Null Coalescing (
??) - Conditional (
?:) - Assignment (
=,+=,-=, etc.)
Expression Types
Primary Expressions
Literals
- Numeric:
42,3.14,0x1A - String:
"hello",@"verbatim",$"interpolated {value}" - Character:
'a','\n' - Boolean:
true,false - Null:
null
Identifiers and Member Access
variable // Simple identifier
obj.property // Member access
obj.method() // Method invocation
obj[index] // Indexer access
Note: In the AST, simple identifiers are represented by the Expression::Variable(Identifier) variant. Member access, invocation, and indexing are represented by dedicated wrapper variants (MemberAccess, Invocation, Indexing).
Object Creation
new MyClass() // Constructor
new MyClass { Prop = value } // Object initializer
new[] { 1, 2, 3 } // Array initializer
new { Name = "John", Age = 30 } // Anonymous object
Lambda Expressions
The parser supports various lambda syntax forms:
x => x * 2 // Single parameter
(x, y) => x + y // Multiple parameters
() => DoSomething() // No parameters
(int x, string y) => Process(x, y) // Typed parameters
x => { return x * 2; } // Block body
async x => await ProcessAsync(x) // Async lambda
Query Expressions (LINQ)
Complete LINQ query syntax support:
from item in collection
where item.IsValid
orderby item.Name
select item.Value
Supported clauses:
from- Data sourcewhere- Filteringselect- Projectionorderby- Sortinggroup by- Groupingjoin- Joininglet- Variable introductioninto- Query continuation
Pattern Expressions
Modern C# pattern matching:
obj is int value // Type pattern
obj is not null // Negation pattern
obj is > 0 and < 100 // Relational patterns
obj is var x // Var pattern
Switch Expressions
value switch
{
1 => "one",
2 => "two",
_ => "other"
}
Operator Precedence Implementation
The expression entrypoint is spanned-first. Callers can unwrap the Spanned<Expression> when they do not need spans:
#![allow(unused)] fn main() { use bsharp_parser::parser::expressions::primary_expression_parser::parse_expression_spanned; use bsharp_syntax::span::Span; let result = parse_expression_spanned(Span::new(input)) .map(|(rest, s)| (rest, s.node)); }
Error Handling in Expressions
The expression parser provides detailed error messages:
- Operator precedence conflicts
- Missing operands
- Invalid syntax combinations
- Type compatibility issues
Advanced Features
Null-Conditional Operators
obj?.Property // Null-conditional member access
obj?[index] // Null-conditional element access
obj?.Method() // Null-conditional invocation
Throw Expressions
value ?? throw new ArgumentNullException()
Range and Index Expressions
array[^1] // Index from end
array[1..5] // Range
array[..^1] // Range to index from end
With Expressions (Records)
person with { Name = "Updated" }
The expression parser is designed to be extensible, allowing for easy addition of new expression types as the C# language evolves.
See Also
- Keywords and Tokens – keyword helpers, word boundaries, trivia handling for tokens used in expressions