diff --git a/crates/core/src/ast.rs b/crates/core/src/ast.rs index 21098c7..06aaf6d 100644 --- a/crates/core/src/ast.rs +++ b/crates/core/src/ast.rs @@ -1,3 +1,5 @@ +pub mod bit_slices; + use tree_sitter::Node; use crate::{ @@ -6,6 +8,8 @@ use crate::{ source::{SourceFile, Span}, }; +pub use bit_slices::{BitArray, BitArraySegment, BitArraySegmentOption}; + /// Compiler-owned AST module. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Module { @@ -19,13 +23,14 @@ pub struct Module { pub enum Declaration { Import(Import), Function(Function), - Constant(RawSyntax), - ExternalFunction(RawSyntax), - ExternalType(RawSyntax), - TypeAlias(RawSyntax), - TypeDefinition(RawSyntax), - Attribute(RawSyntax), - TargetGroup(RawSyntax), + Constant(Constant), + ExternalFunction(ExternalFunction), + ExternalType(ExternalType), + TypeAlias(TypeAlias), + TypeDefinition(TypeDefinition), + Attribute(Attribute), + TargetGroup(TargetGroup), + Comment(Comment), Statement(RawSyntax), } @@ -37,6 +42,102 @@ pub struct Import { pub unqualified: Vec, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Constant { + pub span: Span, + pub public: bool, + pub name: Name, + pub type_annotation: Option, + pub value: Expression, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExternalFunction { + pub span: Span, + pub public: bool, + pub name: Name, + pub parameters: Vec, + pub return_type: TypeAnnotation, + pub body: ExternalFunctionBody, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExternalFunctionBody { + pub span: Span, + pub module: Literal, + pub function: Literal, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExternalType { + pub span: Span, + pub public: bool, + pub opaque: bool, + pub name: Name, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypeAlias { + pub span: Span, + pub public: bool, + pub opaque: bool, + pub name: Name, + pub parameters: Vec, + pub value: TypeAnnotation, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypeDefinition { + pub span: Span, + pub public: bool, + pub opaque: bool, + pub name: Name, + pub parameters: Vec, + pub constructors: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DataConstructor { + pub span: Span, + pub name: Name, + pub arguments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DataConstructorArgument { + pub span: Span, + pub label: Option, + pub type_annotation: TypeAnnotation, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Attribute { + pub span: Span, + pub name: Name, + pub arguments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TargetGroup { + pub span: Span, + pub target: Name, + pub declarations: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Comment { + pub span: Span, + pub kind: CommentKind, + pub text: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CommentKind { + Module, + Statement, + Regular, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct UnqualifiedImport { pub span: Span, @@ -177,9 +278,55 @@ pub enum Expression { FieldAccess(FieldAccess), Block(Block), Case(Case), + BinaryOperation(BinaryOperation), + Pipeline(Pipeline), + UnaryOperation(UnaryOperation), + Use(Use), + AnonymousFunction(AnonymousFunction), + Capture(Capture), + Record(Record), + RecordUpdate(RecordUpdate), + Tuple(Tuple), + TupleAccess(TupleAccess), + List(List), + BitArray(BitArray), + Panic(FailureExpression), + Todo(FailureExpression), + Assert(Assert), + Echo(Echo), Raw(RawSyntax), } +impl From<&Expression> for Span { + fn from(expression: &Expression) -> Self { + match expression { + Expression::Literal(literal) => literal.span, + Expression::Variable(name) => name.span, + Expression::Call(call) => call.span, + Expression::FieldAccess(field_access) => field_access.span, + Expression::Block(block) => block.span, + Expression::Case(case) => case.span, + Expression::BinaryOperation(operation) => operation.span, + Expression::Pipeline(pipeline) => pipeline.span, + Expression::UnaryOperation(operation) => operation.span, + Expression::Use(use_) => use_.span, + Expression::AnonymousFunction(function) => function.span, + Expression::Capture(capture) => capture.span, + Expression::Record(record) => record.span, + Expression::RecordUpdate(update) => update.span, + Expression::Tuple(tuple) => tuple.span, + Expression::TupleAccess(access) => access.span, + Expression::List(list) => list.span, + Expression::BitArray(bit_array) => bit_array.span, + Expression::Panic(panic) => panic.span, + Expression::Todo(todo) => todo.span, + Expression::Assert(assert) => assert.span, + Expression::Echo(echo) => echo.span, + Expression::Raw(raw) => raw.span, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Literal { pub span: Span, @@ -217,6 +364,144 @@ pub struct FieldAccess { pub field: Name, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BinaryOperation { + pub span: Span, + pub left: Box, + pub operator: BinaryOperator, + pub right: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BinaryOperator { + Add, + Subtract, + Multiply, + Divide, + Remainder, + FloatAdd, + FloatSubtract, + FloatMultiply, + FloatDivide, + Equal, + NotEqual, + LessThan, + LessThanEqual, + GreaterThan, + GreaterThanEqual, + FloatLessThan, + FloatLessThanEqual, + FloatGreaterThan, + FloatGreaterThanEqual, + And, + Or, + StringConcat, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Pipeline { + pub span: Span, + pub value: Box, + pub into: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnaryOperation { + pub span: Span, + pub operator: UnaryOperator, + pub value: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UnaryOperator { + BooleanNot, + IntegerNegate, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Use { + pub span: Span, + pub assignments: Vec, + pub value: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UseAssignment { + pub span: Span, + pub pattern: Pattern, + pub type_annotation: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AnonymousFunction { + pub span: Span, + pub parameters: Vec, + pub return_type: Option, + pub body: Block, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Capture { + pub span: Span, + pub function: Box, + pub arguments: Vec>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Record { + pub span: Span, + pub constructor: ConstructorName, + pub arguments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RecordUpdate { + pub span: Span, + pub constructor: ConstructorName, + pub spread: Box, + pub updates: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Tuple { + pub span: Span, + pub elements: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TupleAccess { + pub span: Span, + pub tuple: Box, + pub index: Name, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct List { + pub span: Span, + pub elements: Vec, + pub spread: Option>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FailureExpression { + pub span: Span, + pub message: Option>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Assert { + pub span: Span, + pub pattern: Pattern, + pub type_annotation: Option, + pub value: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Echo { + pub span: Span, + pub value: Box, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Case { pub span: Span, @@ -278,17 +563,180 @@ impl AstBuilder<'_> { match node.kind() { "import" => self.import(node).map(Declaration::Import), "function" => self.function(node).map(Declaration::Function), - "constant" => Ok(Declaration::Constant(self.raw(node))), - "external_function" => Ok(Declaration::ExternalFunction(self.raw(node))), - "external_type" => Ok(Declaration::ExternalType(self.raw(node))), - "type_alias" => Ok(Declaration::TypeAlias(self.raw(node))), - "type_definition" => Ok(Declaration::TypeDefinition(self.raw(node))), - "attribute" => Ok(Declaration::Attribute(self.raw(node))), - "target_group" => Ok(Declaration::TargetGroup(self.raw(node))), + "constant" => self.constant(node).map(Declaration::Constant), + "external_function" => self.external_function(node).map(Declaration::ExternalFunction), + "external_type" => self.external_type(node).map(Declaration::ExternalType), + "type_alias" => self.type_alias(node).map(Declaration::TypeAlias), + "type_definition" => self.type_definition(node).map(Declaration::TypeDefinition), + "attribute" => self.attribute(node).map(Declaration::Attribute), + "target_group" => self.target_group(node).map(Declaration::TargetGroup), + "module_comment" | "statement_comment" | "comment" => Ok(Declaration::Comment(self.comment(node))), _ => Ok(Declaration::Statement(self.raw(node))), } } + fn constant(&self, node: Node<'_>) -> Result { + let value_node = node + .child_by_field_name("value") + .ok_or_else(|| vec![self.missing(node, "constant value")])?; + Ok(Constant { + span: self.span(node), + public: self.has_child_kind(node, "visibility_modifier"), + name: self.required_name_field(node, "name")?, + type_annotation: self.type_field(node, "type")?, + value: self.expression(value_node)?, + }) + } + + fn external_function(&self, node: Node<'_>) -> Result { + let return_type = self + .type_field(node, "return_type")? + .ok_or_else(|| vec![self.missing(node, "external function return type")])?; + let body_node = node + .child_by_field_name("body") + .ok_or_else(|| vec![self.missing(node, "external function body")])?; + Ok(ExternalFunction { + span: self.span(node), + public: self.has_child_kind(node, "visibility_modifier"), + name: self.required_name_field(node, "name")?, + parameters: node + .child_by_field_name("parameters") + .map(|parameters| self.parameters(parameters)) + .transpose()? + .unwrap_or_default(), + return_type, + body: self.external_function_body(body_node)?, + }) + } + + fn external_function_body(&self, node: Node<'_>) -> Result { + let strings = self + .named_children(node) + .into_iter() + .filter(|child| child.kind() == "string") + .map(|child| self.literal(child, LiteralKind::String)) + .collect::>(); + let module = strings + .first() + .cloned() + .ok_or_else(|| vec![self.missing(node, "external module")])?; + let function = strings + .get(1) + .cloned() + .ok_or_else(|| vec![self.missing(node, "external function")])?; + Ok(ExternalFunctionBody { span: self.span(node), module, function }) + } + + fn external_type(&self, node: Node<'_>) -> Result { + Ok(ExternalType { + span: self.span(node), + public: self.has_child_kind(node, "visibility_modifier"), + opaque: self.has_child_kind(node, "opacity_modifier"), + name: self.required_named_child_as_name(node, "type_name")?, + }) + } + + fn type_alias(&self, node: Node<'_>) -> Result { + let value_node = self + .named_children(node) + .into_iter() + .find(|child| is_type_node(child.kind()) && child.kind() != "type_name") + .ok_or_else(|| vec![self.missing(node, "type alias value")])?; + let name_node = self.required_named_child(node, "type_name")?; + Ok(TypeAlias { + span: self.span(node), + public: self.has_child_kind(node, "visibility_modifier"), + opaque: self.has_child_kind(node, "opacity_modifier"), + name: self.type_decl_name(name_node), + parameters: self.type_decl_parameters(name_node), + value: TypeAnnotation { span: self.span(value_node), source: self.text(value_node).to_string() }, + }) + } + + fn type_definition(&self, node: Node<'_>) -> Result { + let constructors = self + .named_children(node) + .into_iter() + .find(|child| child.kind() == "data_constructors") + .map(|child| self.data_constructors(child)) + .transpose()? + .unwrap_or_default(); + let name_node = self.required_named_child(node, "type_name")?; + Ok(TypeDefinition { + span: self.span(node), + public: self.has_child_kind(node, "visibility_modifier"), + opaque: self.has_child_kind(node, "opacity_modifier"), + name: self.type_decl_name(name_node), + parameters: self.type_decl_parameters(name_node), + constructors, + }) + } + + fn data_constructors(&self, node: Node<'_>) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .filter(|child| child.kind() == "data_constructor") + .map(|child| self.data_constructor(child)) + .collect() + } + + fn data_constructor(&self, node: Node<'_>) -> Result { + let arguments = node + .child_by_field_name("arguments") + .map(|arguments| self.data_constructor_arguments(arguments)) + .transpose()? + .unwrap_or_default(); + Ok(DataConstructor { span: self.span(node), name: self.required_name_field(node, "name")?, arguments }) + } + + fn data_constructor_arguments(&self, node: Node<'_>) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .filter(|child| child.kind() == "data_constructor_argument") + .map(|child| { + let value = child + .child_by_field_name("value") + .ok_or_else(|| vec![self.missing(child, "constructor argument type")])?; + Ok(DataConstructorArgument { + span: self.span(child), + label: self.name_field(child, "label")?, + type_annotation: TypeAnnotation { span: self.span(value), source: self.text(value).to_string() }, + }) + }) + .collect() + } + + fn attribute(&self, node: Node<'_>) -> Result { + Ok(Attribute { + span: self.span(node), + name: self.required_name_field(node, "name")?, + arguments: node + .child_by_field_name("arguments") + .map(|arguments| self.arguments(arguments)) + .transpose()? + .unwrap_or_default(), + }) + } + + fn target_group(&self, node: Node<'_>) -> Result { + let declarations = self + .named_children(node) + .into_iter() + .filter(|child| child.kind() != "target") + .map(|child| self.declaration(child)) + .collect::, _>>()?; + Ok(TargetGroup { span: self.span(node), target: self.required_name_field(node, "target")?, declarations }) + } + + fn comment(&self, node: Node<'_>) -> Comment { + let kind = match node.kind() { + "module_comment" => CommentKind::Module, + "statement_comment" => CommentKind::Statement, + _ => CommentKind::Regular, + }; + Comment { span: self.span(node), kind, text: self.text(node).to_string() } + } + fn import(&self, node: Node<'_>) -> Result { let module = self.required_name_field(node, "module")?; let alias = self.name_field(node, "alias")?; @@ -422,30 +870,79 @@ impl AstBuilder<'_> { "float" => Ok(Expression::Literal(self.literal(node, LiteralKind::Float))), "string" => Ok(Expression::Literal(self.literal(node, LiteralKind::String))), "identifier" => Ok(Expression::Variable(self.name(node))), - "record" => self.constructor_literal(node), - "function_call" => self.call(node).map(Expression::Call), + "record" => self.record(node), + "function_call" => self.call_or_capture(node), "field_access" => self.field_access(node).map(Expression::FieldAccess), "block" => self.block_like(node).map(Expression::Block), "case" => self.case(node).map(Expression::Case), + "binary_expression" => self.binary_expression(node), + "boolean_negation" => self.unary_expression(node, UnaryOperator::BooleanNot), + "integer_negation" => self.unary_expression(node, UnaryOperator::IntegerNegate), + "use" => self.use_expression(node).map(Expression::Use), + "anonymous_function" => self.anonymous_function(node).map(Expression::AnonymousFunction), + "record_update" => self.record_update(node).map(Expression::RecordUpdate), + "tuple" => self.tuple(node).map(Expression::Tuple), + "tuple_access" => self.tuple_access(node).map(Expression::TupleAccess), + "list" => self.list(node).map(Expression::List), + "bit_string" => self.bit_array(node).map(Expression::BitArray), + "panic" => self.failure_expression(node).map(Expression::Panic), + "todo" => self.failure_expression(node).map(Expression::Todo), + "assert" => self.assert_expression(node).map(Expression::Assert), + "echo" | "pipeline_echo" => self.echo_expression(node).map(Expression::Echo), _ => Ok(Expression::Raw(self.raw(node))), } } - fn constructor_literal(&self, node: Node<'_>) -> Result { + fn record(&self, node: Node<'_>) -> Result { let text = self.text(node).to_string(); let kind = match text.as_str() { - "True" | "False" => LiteralKind::Bool, - "Nil" => LiteralKind::Nil, - _ => return Ok(Expression::Raw(self.raw(node))), + "True" | "False" => Some(LiteralKind::Bool), + "Nil" => Some(LiteralKind::Nil), + _ => None, }; + if let Some(kind) = kind { + return Ok(Expression::Literal(Literal { + span: self.span(node), + kind, + source: text, + })); + } - Ok(Expression::Literal(Literal { + let name_node = node + .child_by_field_name("name") + .ok_or_else(|| vec![self.missing(node, "record name")])?; + let arguments = node + .child_by_field_name("arguments") + .map(|arguments| self.arguments(arguments)) + .transpose()? + .unwrap_or_default(); + Ok(Expression::Record(Record { span: self.span(node), - kind, - source: text, + constructor: self.constructor_name(name_node)?, + arguments, })) } + fn call_or_capture(&self, node: Node<'_>) -> Result { + if self.text(node).contains('_') { + let call = self.call(node)?; + let arguments = call + .arguments + .into_iter() + .map(|argument| match argument.value { + Expression::Variable(Name { text, .. }) if text == "_" => None, + _ => Some(argument), + }) + .collect(); + return Ok(Expression::Capture(Capture { + span: call.span, + function: call.function, + arguments, + })); + } + self.call(node).map(Expression::Call) + } + fn call(&self, node: Node<'_>) -> Result { let function_node = node .child_by_field_name("function") @@ -488,6 +985,221 @@ impl AstBuilder<'_> { Ok(FieldAccess { span: self.span(node), record: Box::new(self.expression(record_node)?), field }) } + fn binary_expression(&self, node: Node<'_>) -> Result { + let left = node + .child_by_field_name("left") + .ok_or_else(|| vec![self.missing(node, "left operand")])?; + let right = node + .child_by_field_name("right") + .ok_or_else(|| vec![self.missing(node, "right operand")])?; + let operator = node + .child_by_field_name("operator") + .ok_or_else(|| vec![self.missing(node, "operator")])?; + if self.text(operator) == "|>" { + return Ok(Expression::Pipeline(Pipeline { + span: self.span(node), + value: Box::new(self.expression(left)?), + into: Box::new(self.expression(right)?), + })); + } + Ok(Expression::BinaryOperation(BinaryOperation { + span: self.span(node), + left: Box::new(self.expression(left)?), + operator: self.binary_operator(operator)?, + right: Box::new(self.expression(right)?), + })) + } + + fn binary_operator(&self, node: Node<'_>) -> Result { + match self.text(node) { + "+" => Ok(BinaryOperator::Add), + "-" => Ok(BinaryOperator::Subtract), + "*" => Ok(BinaryOperator::Multiply), + "/" => Ok(BinaryOperator::Divide), + "%" => Ok(BinaryOperator::Remainder), + "+." => Ok(BinaryOperator::FloatAdd), + "-." => Ok(BinaryOperator::FloatSubtract), + "*." => Ok(BinaryOperator::FloatMultiply), + "/." => Ok(BinaryOperator::FloatDivide), + "==" => Ok(BinaryOperator::Equal), + "!=" => Ok(BinaryOperator::NotEqual), + "<" => Ok(BinaryOperator::LessThan), + "<=" => Ok(BinaryOperator::LessThanEqual), + ">" => Ok(BinaryOperator::GreaterThan), + ">=" => Ok(BinaryOperator::GreaterThanEqual), + "<." => Ok(BinaryOperator::FloatLessThan), + "<=." => Ok(BinaryOperator::FloatLessThanEqual), + ">." => Ok(BinaryOperator::FloatGreaterThan), + ">=." => Ok(BinaryOperator::FloatGreaterThanEqual), + "&&" => Ok(BinaryOperator::And), + "||" => Ok(BinaryOperator::Or), + "<>" => Ok(BinaryOperator::StringConcat), + _ => Err(vec![self.unsupported(node)]), + } + } + + fn unary_expression(&self, node: Node<'_>, operator: UnaryOperator) -> Result { + let value = self + .named_children(node) + .into_iter() + .next() + .ok_or_else(|| vec![self.missing(node, "unary operand")])?; + Ok(Expression::UnaryOperation(UnaryOperation { + span: self.span(node), + operator, + value: Box::new(self.expression(value)?), + })) + } + + fn use_expression(&self, node: Node<'_>) -> Result { + let assignments = node + .child_by_field_name("assignments") + .map(|assignments| self.use_assignments(assignments)) + .transpose()? + .unwrap_or_default(); + let value = node + .child_by_field_name("value") + .ok_or_else(|| vec![self.missing(node, "use value")])?; + Ok(Use { span: self.span(node), assignments, value: Box::new(self.expression(value)?) }) + } + + fn use_assignments(&self, node: Node<'_>) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .filter(|child| child.kind() == "use_assignment") + .map(|child| { + let pattern = child + .child_by_field_name("pattern") + .ok_or_else(|| vec![self.missing(child, "use pattern")])?; + Ok(UseAssignment { + span: self.span(child), + pattern: self.pattern(pattern)?, + type_annotation: self.type_field(child, "type")?, + }) + }) + .collect() + } + + fn anonymous_function(&self, node: Node<'_>) -> Result { + let body = node + .child_by_field_name("body") + .ok_or_else(|| vec![self.missing(node, "anonymous function body")])?; + Ok(AnonymousFunction { + span: self.span(node), + parameters: node + .child_by_field_name("parameters") + .map(|parameters| self.parameters(parameters)) + .transpose()? + .unwrap_or_default(), + return_type: self.type_field(node, "return_type")?, + body: self.block_like(body)?, + }) + } + + fn record_update(&self, node: Node<'_>) -> Result { + let constructor = node + .child_by_field_name("constructor") + .ok_or_else(|| vec![self.missing(node, "record update constructor")])?; + let spread = node + .child_by_field_name("spread") + .ok_or_else(|| vec![self.missing(node, "record update spread")])?; + let updates = node + .child_by_field_name("arguments") + .map(|arguments| self.record_update_arguments(arguments)) + .transpose()? + .unwrap_or_default(); + Ok(RecordUpdate { + span: self.span(node), + constructor: self.constructor_name(constructor)?, + spread: Box::new(self.expression(spread)?), + updates, + }) + } + + fn record_update_arguments(&self, node: Node<'_>) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .filter(|child| child.kind() == "record_update_argument") + .map(|child| self.argument(child)) + .collect() + } + + fn tuple(&self, node: Node<'_>) -> Result { + Ok(Tuple { span: self.span(node), elements: self.expression_children(node)? }) + } + + fn tuple_access(&self, node: Node<'_>) -> Result { + let tuple = node + .child_by_field_name("tuple") + .or_else(|| self.named_children(node).into_iter().next()) + .ok_or_else(|| vec![self.missing(node, "tuple value")])?; + let index = node + .child_by_field_name("index") + .or_else(|| { + self.named_children(node) + .into_iter() + .find(|child| child.kind() == "integer") + }) + .ok_or_else(|| vec![self.missing(node, "tuple index")])?; + Ok(TupleAccess { span: self.span(node), tuple: Box::new(self.expression(tuple)?), index: self.name(index) }) + } + + fn list(&self, node: Node<'_>) -> Result { + let spread = node + .child_by_field_name("spread") + .map(|spread| self.expression(spread).map(Box::new)) + .transpose()?; + let spread_span = spread.as_ref().map(|spread| Span::from(spread.as_ref())); + let elements = self + .named_children(node) + .into_iter() + .filter(|child| Some(self.span(*child)) != spread_span) + .map(|child| self.expression(child)) + .collect::, _>>()?; + Ok(List { span: self.span(node), elements, spread }) + } + + fn failure_expression(&self, node: Node<'_>) -> Result { + Ok(FailureExpression { + span: self.span(node), + message: node + .child_by_field_name("message") + .map(|message| self.expression(message).map(Box::new)) + .transpose()?, + }) + } + + fn assert_expression(&self, node: Node<'_>) -> Result { + let pattern = node + .child_by_field_name("pattern") + .ok_or_else(|| vec![self.missing(node, "assert pattern")])?; + let value = node + .child_by_field_name("value") + .ok_or_else(|| vec![self.missing(node, "assert value")])?; + Ok(Assert { + span: self.span(node), + pattern: self.pattern(pattern)?, + type_annotation: self.type_field(node, "type")?, + value: Box::new(self.expression(value)?), + }) + } + + fn echo_expression(&self, node: Node<'_>) -> Result { + let value = self + .named_children(node) + .into_iter() + .next() + .ok_or_else(|| vec![self.missing(node, "echo value")])?; + Ok(Echo { span: self.span(node), value: Box::new(self.expression(value)?) }) + } + + fn expression_children(&self, node: Node<'_>) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .map(|child| self.expression(child)) + .collect() + } + fn case(&self, node: Node<'_>) -> Result { let subjects_node = node .child_by_field_name("subjects") @@ -732,14 +1444,48 @@ impl AstBuilder<'_> { fn name_field(&self, node: Node<'_>, field: &str) -> Result, Diagnostics> { node.child_by_field_name(field) .map(|child| match child.kind() { - "identifier" | "type_identifier" | "constructor_name" | "label" | "module" | "discard" => { - Ok(self.name(child)) - } + "identifier" | "type_identifier" | "constructor_name" | "label" | "module" | "discard" + | "type_name" | "target" | "integer" => Ok(self.name(child)), _ => Err(vec![self.unsupported(child)]), }) .transpose() } + fn required_named_child<'tree>(&self, node: Node<'tree>, kind: &str) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .find(|child| child.kind() == kind) + .ok_or_else(|| vec![self.missing(node, kind)]) + } + + fn required_named_child_as_name(&self, node: Node<'_>, kind: &str) -> Result { + self.required_named_child(node, kind).map(|child| self.name(child)) + } + + fn type_decl_name(&self, node: Node<'_>) -> Name { + let text = self.text(node); + let end = text.find('(').unwrap_or(text.len()); + Name { + span: Span::new(self.source.id, node.start_byte(), node.start_byte() + end), + text: text[..end].to_string(), + } + } + + fn type_decl_parameters(&self, node: Node<'_>) -> Vec { + self.text(node) + .split_once('(') + .and_then(|(_, rest)| rest.split_once(')').map(|(params, _)| params)) + .map(|params| { + params + .split(',') + .map(str::trim) + .filter(|param| !param.is_empty()) + .map(String::from) + .collect() + }) + .unwrap_or_default() + } + fn name(&self, node: Node<'_>) -> Name { Name { span: self.span(node), text: self.text(node).to_string() } } @@ -765,6 +1511,10 @@ impl AstBuilder<'_> { Span::new(self.source.id, node.start_byte(), node.end_byte()) } + fn has_child_kind(&self, node: Node<'_>, kind: &str) -> bool { + self.named_children(node).into_iter().any(|child| child.kind() == kind) + } + fn unsupported(&self, node: Node<'_>) -> Diagnostic { Diagnostic::new( DiagnosticCode::AstError, @@ -784,6 +1534,13 @@ impl AstBuilder<'_> { } } +fn is_type_node(kind: &str) -> bool { + matches!( + kind, + "function_type" | "tuple_type" | "type" | "type_hole" | "type_name" | "type_var" + ) +} + #[cfg(test)] mod tests { use crate::{parse, source::SourceFileId}; diff --git a/crates/core/src/ast/bit_slices.rs b/crates/core/src/ast/bit_slices.rs new file mode 100644 index 0000000..23a31ee --- /dev/null +++ b/crates/core/src/ast/bit_slices.rs @@ -0,0 +1,68 @@ +use tree_sitter::Node; + +use crate::{diagnostic::Diagnostics, source::Span}; + +use super::{AstBuilder, Expression}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitArray { + pub span: Span, + pub segments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitArraySegment { + pub span: Span, + pub value: Expression, + pub options: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitArraySegmentOption { + pub span: Span, + pub value: Option, + pub source: String, +} + +impl AstBuilder<'_> { + pub(super) fn bit_array(&self, node: Node<'_>) -> Result { + Ok(BitArray { + span: self.span(node), + segments: self + .named_children(node) + .into_iter() + .filter(|child| child.kind() == "bit_string_segment") + .map(|child| self.bit_array_segment(child)) + .collect::, _>>()?, + }) + } + + fn bit_array_segment(&self, node: Node<'_>) -> Result { + let value = node + .child_by_field_name("value") + .ok_or_else(|| vec![self.missing(node, "bit array segment value")])?; + let options = self + .named_children(node) + .into_iter() + .find(|child| child.kind() == "bit_string_segment_options") + .map(|options| self.bit_array_segment_options(options)) + .transpose()? + .unwrap_or_default(); + Ok(BitArraySegment { span: self.span(node), value: self.expression(value)?, options }) + } + + fn bit_array_segment_options(&self, node: Node<'_>) -> Result, Diagnostics> { + self.named_children(node) + .into_iter() + .map(|child| { + let value = self + .named_children(child) + .into_iter() + .next() + .map(|value| self.expression(value)) + .transpose()?; + Ok(BitArraySegmentOption { span: self.span(child), value, source: self.text(child).to_string() }) + }) + .collect() + } +} diff --git a/crates/core/src/ir.rs b/crates/core/src/ir.rs index 9349dc2..863b177 100644 --- a/crates/core/src/ir.rs +++ b/crates/core/src/ir.rs @@ -1,3 +1,5 @@ +pub mod bit_slices; + use std::collections::HashMap; use crate::{ @@ -8,6 +10,10 @@ use crate::{ types::{Type, TypedModule}, }; +pub use bit_slices::{BitArrayLiteral, BitArraySegment, BitSegmentOption, BitSegmentType, BitStringPatternSegment}; + +use bit_slices::{ast_bit_array_literal, bit_array_literal, bit_string_pattern_segments}; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LocalId(pub u32); @@ -329,51 +335,6 @@ pub enum ExpressionKind { Failure(FailurePath), } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BitArrayLiteral { - pub segments: Vec, - pub bit_len: u32, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BitArraySegment { - pub value: u64, - pub bit_size: u32, - pub type_: BitSegmentType, - pub options: Vec, - pub span: Span, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BitStringPatternSegment { - pub binding: Option, - pub bit_size: Option, - pub type_: BitSegmentType, - pub options: Vec, - pub span: Span, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum BitSegmentType { - Integer, - Float, - Binary, - Utf8, - Utf16, - Utf32, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum BitSegmentOption { - Size(u32), - Unit(u32), - Signed, - Unsigned, - BigEndian, - LittleEndian, - NativeEndian, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct DirectCall { pub function: String, @@ -526,6 +487,19 @@ pub enum BindingPath { }, } +impl BindingPath { + fn subject(&self) -> usize { + match self { + Self::Subject(subject) + | Self::TupleElement { subject, .. } + | Self::ListElement { subject, .. } + | Self::ListTail { subject } + | Self::ConstructorField { subject, .. } + | Self::Alias { subject } => *subject, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum IrPattern { Discard, @@ -647,15 +621,11 @@ impl Lowerer { } for declaration in &self.module.resolved.ast.declarations { match declaration { - AstDeclaration::TypeDefinition(raw) if is_public_declaration(&raw.source) => { - for name in exported_type_names(&raw.source) { - exports.push(Export { name, kind: ExportKind::Type, span: raw.span }); - } + AstDeclaration::TypeDefinition(type_) if type_.public => { + exports.push(Export { name: type_.name.text.clone(), kind: ExportKind::Type, span: type_.span }); } - AstDeclaration::TypeAlias(raw) if is_public_declaration(&raw.source) => { - if let Some(name) = declaration_name(&raw.source, "type") { - exports.push(Export { name, kind: ExportKind::Type, span: raw.span }); - } + AstDeclaration::TypeAlias(alias) if alias.public => { + exports.push(Export { name: alias.name.text.clone(), kind: ExportKind::Type, span: alias.span }); } _ => {} } @@ -769,8 +739,7 @@ impl Lowerer { if last_statement { result = value; } else { - instructions - .push(Instruction::Evaluate { expression: value, span: ast_expression_span(expression) }); + instructions.push(Instruction::Evaluate { expression: value, span: Span::from(expression) }); } } } @@ -784,7 +753,7 @@ impl Lowerer { fn lower_expression(&mut self, context: &mut FunctionContext, expression: &AstExpression) -> Option { match expression { AstExpression::Literal(literal) => Some(Expression { - type_: type_for_literal(literal.kind.clone()), + type_: Type::from(&literal.kind), span: literal.span, kind: ExpressionKind::Literal(Literal { kind: literal.kind.clone(), source: literal.source.clone() }), }), @@ -855,6 +824,61 @@ impl Lowerer { }, }) } + AstExpression::Tuple(tuple) => Some(Expression { + type_: self + .typed_expression_type(tuple.span) + .unwrap_or(Type::Tuple(Vec::new())), + span: tuple.span, + kind: ExpressionKind::Tuple( + tuple + .elements + .iter() + .map(|item| self.lower_expression(context, item)) + .collect::>>()?, + ), + }), + AstExpression::List(list) => Some(Expression { + type_: self + .typed_expression_type(list.span) + .unwrap_or(Type::List(Box::new(Type::Int))), + span: list.span, + kind: ExpressionKind::List( + list.elements + .iter() + .map(|item| self.lower_expression(context, item)) + .collect::>>()?, + ), + }), + AstExpression::Record(record) => { + let type_ = self.typed_expression_type(record.span).unwrap_or(Type::Nil); + Some(Expression { + type_, + span: record.span, + kind: ExpressionKind::Constructor(ConstructorValue { + name: constructor_name(&record.constructor), + arguments: record + .arguments + .iter() + .map(|argument| self.lower_expression(context, &argument.value)) + .collect::>>()?, + }), + }) + } + AstExpression::BitArray(bit_array) => Some(Expression { + type_: Type::BitArray, + span: bit_array.span, + kind: ExpressionKind::BitArray(ast_bit_array_literal(bit_array)), + }), + AstExpression::Panic(panic) | AstExpression::Todo(panic) => Some(Expression { + type_: self.typed_expression_type(panic.span).unwrap_or(Type::Nil), + span: panic.span, + kind: ExpressionKind::Failure(FailurePath { reason: FailureReason::Panic, span: panic.span }), + }), + AstExpression::Assert(assert) => Some(Expression { + type_: self.typed_expression_type(assert.span).unwrap_or(Type::Nil), + span: assert.span, + kind: ExpressionKind::Failure(FailurePath { reason: FailureReason::Assert, span: assert.span }), + }), AstExpression::Raw(raw) if raw.kind == "bit_string" => Some(Expression { type_: Type::BitArray, span: raw.span, @@ -895,19 +919,22 @@ impl Lowerer { span: raw.span, }), }), - AstExpression::Raw(raw) => { - self.diagnostics.push( - Diagnostic::new( - DiagnosticCode::LoweringError, - format!("expression `{}` cannot be lowered", raw.kind), - ) - .with_label(Label::primary(raw.span, "unsupported expression here")), - ); - None - } + AstExpression::Raw(raw) => self.unsupported_ast_expression(raw.kind.clone(), raw.span), + other => self.unsupported_ast_expression(ast_expression_kind(other), Span::from(other)), } } + fn unsupported_ast_expression(&mut self, kind: String, span: Span) -> Option { + self.diagnostics.push( + Diagnostic::new( + DiagnosticCode::LoweringError, + format!("expression `{kind}` cannot be lowered"), + ) + .with_label(Label::primary(span, "unsupported expression here")), + ); + None + } + fn lower_call(&mut self, context: &mut FunctionContext, call: &ast::Call) -> Option { if let AstExpression::Variable(function_name) = call.function.as_ref() && let Some(function_type) = self.function_types.get(&function_name.text).cloned() @@ -1158,15 +1185,31 @@ impl FunctionContext { } } -fn ast_expression_span(expression: &AstExpression) -> Span { +fn ast_expression_kind(expression: &AstExpression) -> String { match expression { - AstExpression::Literal(literal) => literal.span, - AstExpression::Variable(name) => name.span, - AstExpression::Call(call) => call.span, - AstExpression::FieldAccess(field_access) => field_access.span, - AstExpression::Block(block) => block.span, - AstExpression::Case(case) => case.span, - AstExpression::Raw(raw) => raw.span, + AstExpression::Literal(_) => "literal".into(), + AstExpression::Variable(_) => "variable".into(), + AstExpression::Call(_) => "call".into(), + AstExpression::FieldAccess(_) => "field_access".into(), + AstExpression::Block(_) => "block".into(), + AstExpression::Case(_) => "case".into(), + AstExpression::BinaryOperation(_) => "binary_expression".into(), + AstExpression::Pipeline(_) => "pipeline".into(), + AstExpression::UnaryOperation(_) => "unary_expression".into(), + AstExpression::Use(_) => "use".into(), + AstExpression::AnonymousFunction(_) => "anonymous_function".into(), + AstExpression::Capture(_) => "capture".into(), + AstExpression::Record(_) => "record".into(), + AstExpression::RecordUpdate(_) => "record_update".into(), + AstExpression::Tuple(_) => "tuple".into(), + AstExpression::TupleAccess(_) => "tuple_access".into(), + AstExpression::List(_) => "list".into(), + AstExpression::BitArray(_) => "bit_array".into(), + AstExpression::Panic(_) => "panic".into(), + AstExpression::Todo(_) => "todo".into(), + AstExpression::Assert(_) => "assert".into(), + AstExpression::Echo(_) => "echo".into(), + AstExpression::Raw(raw) => raw.kind.clone(), } } @@ -1196,13 +1239,13 @@ fn collect_successful_bindings( }); } IrPattern::Tuple(elements) => { - let subject = binding_subject(&path); + let subject = path.subject(); for (index, element) in elements.iter().enumerate() { collect_successful_bindings(context, element, BindingPath::TupleElement { subject, index }, bindings); } } IrPattern::List { elements, tail } => { - let subject = binding_subject(&path); + let subject = path.subject(); for (index, element) in elements.iter().enumerate() { collect_successful_bindings(context, element, BindingPath::ListElement { subject, index }, bindings); } @@ -1215,7 +1258,7 @@ fn collect_successful_bindings( } } IrPattern::Constructor { arguments, .. } => { - let subject = binding_subject(&path); + let subject = path.subject(); for (index, argument) in arguments.iter().enumerate() { collect_successful_bindings( context, @@ -1226,7 +1269,7 @@ fn collect_successful_bindings( } } IrPattern::BitString(segments) => { - let subject = binding_subject(&path); + let subject = path.subject(); for (index, segment) in segments.iter().enumerate() { if let Some(local) = segment.binding { bindings.push(SuccessfulBinding { @@ -1241,17 +1284,6 @@ fn collect_successful_bindings( } } -fn binding_subject(path: &BindingPath) -> usize { - match path { - BindingPath::Subject(subject) - | BindingPath::TupleElement { subject, .. } - | BindingPath::ListElement { subject, .. } - | BindingPath::ListTail { subject } - | BindingPath::ConstructorField { subject, .. } - | BindingPath::Alias { subject } => *subject, - } -} - fn raw_literal_arguments( raw: &ast::RawSyntax, _context: &mut FunctionContext, _lowerer: &mut Lowerer, ) -> Option> { @@ -1295,117 +1327,6 @@ fn integer_expression(source: &str, span: Span) -> Option { }) } -fn bit_array_literal(raw: &ast::RawSyntax) -> BitArrayLiteral { - let segments = bit_array_segments(raw); - let bit_len = segments.iter().map(|segment| segment.bit_size).sum(); - BitArrayLiteral { segments, bit_len } -} - -fn bit_array_segments(raw: &ast::RawSyntax) -> Vec { - let Some(inner) = raw - .source - .trim() - .strip_prefix("<<") - .and_then(|source| source.strip_suffix(">>")) - else { - return Vec::new(); - }; - inner - .split(',') - .filter_map(|segment| bit_array_segment(segment.trim(), raw.span)) - .collect() -} - -fn bit_array_segment(source: &str, span: Span) -> Option { - if source.is_empty() { - return None; - } - let (value, options) = source.split_once(':').unwrap_or((source, "")); - let value = value.trim().parse::().ok()?; - let options = bit_segment_options(options); - let bit_size = options - .iter() - .find_map(|option| match option { - BitSegmentOption::Size(size) => Some(*size), - _ => None, - }) - .unwrap_or(8); - Some(BitArraySegment { value, bit_size, type_: BitSegmentType::Integer, options, span }) -} - -fn bit_segment_options(source: &str) -> Vec { - source - .split('-') - .filter_map(|option| { - let option = option.trim(); - if option.is_empty() { - return None; - } - match option { - "signed" => Some(BitSegmentOption::Signed), - "unsigned" => Some(BitSegmentOption::Unsigned), - "big" => Some(BitSegmentOption::BigEndian), - "little" => Some(BitSegmentOption::LittleEndian), - "native" => Some(BitSegmentOption::NativeEndian), - "float" => None, - "binary" | "bytes" | "bits" | "bit_string" | "utf8" | "utf16" | "utf32" => None, - _ if let Some(size) = option.strip_prefix("size(").and_then(|value| value.strip_suffix(')')) => { - size.parse().ok().map(BitSegmentOption::Size) - } - _ if let Some(unit) = option.strip_prefix("unit(").and_then(|value| value.strip_suffix(')')) => { - unit.parse().ok().map(BitSegmentOption::Unit) - } - _ => option.parse().ok().map(BitSegmentOption::Size), - } - }) - .collect() -} - -fn bit_string_pattern_segments( - context: &mut FunctionContext, raw: &ast::RawSyntax, subject_type: &Type, -) -> Vec { - let Some(inner) = raw - .source - .trim() - .strip_prefix("<<") - .and_then(|source| source.strip_suffix(">>")) - else { - return Vec::new(); - }; - inner - .split(',') - .map(|segment| bit_string_pattern_segment(context, segment.trim(), raw.span, subject_type)) - .collect() -} - -fn bit_string_pattern_segment( - context: &mut FunctionContext, source: &str, span: Span, subject_type: &Type, -) -> BitStringPatternSegment { - let (value, options) = source.split_once(':').unwrap_or((source, "")); - let options = bit_segment_options(options); - let bit_size = options.iter().find_map(|option| match option { - BitSegmentOption::Size(size) => Some(*size), - _ => None, - }); - let binding = value.trim().chars().next().filter(|char| char.is_lowercase()).map(|_| { - let name = ast::Name { span, text: value.trim().into() }; - let local = context.allocate(&name, subject_type.clone()); - context.bind(name.text, local.id); - local.id - }); - BitStringPatternSegment { binding, bit_size, type_: BitSegmentType::Integer, options, span } -} - -fn type_for_literal(kind: LiteralKind) -> Type { - match kind { - LiteralKind::Int => Type::Int, - LiteralKind::Float => Type::Float, - LiteralKind::String => Type::String, - LiteralKind::Bool => Type::Bool, - LiteralKind::Nil => Type::Nil, - } -} - fn call_abi(type_: &Type, boundary: CallBoundary) -> CallAbi { match type_ { Type::Function { params, return_type } => { @@ -1480,13 +1401,54 @@ impl From<&AstDeclaration> for DeclarationMetadata { visibility: visibility(function.public), span: function.span, }, - AstDeclaration::Constant(raw) => raw_metadata(raw, DeclarationKind::Constant, "const"), - AstDeclaration::ExternalFunction(raw) => raw_metadata(raw, DeclarationKind::ExternalFunction, "fn"), - AstDeclaration::ExternalType(raw) => raw_metadata(raw, DeclarationKind::ExternalType, "type"), - AstDeclaration::TypeAlias(raw) => raw_metadata(raw, DeclarationKind::TypeAlias, "type"), - AstDeclaration::TypeDefinition(raw) => raw_metadata(raw, DeclarationKind::TypeDefinition, "type"), - AstDeclaration::Attribute(raw) => raw_metadata(raw, DeclarationKind::Attribute, "@"), - AstDeclaration::TargetGroup(raw) => raw_metadata(raw, DeclarationKind::TargetGroup, "target"), + AstDeclaration::Constant(constant) => Self { + name: Some(constant.name.text.clone()), + kind: DeclarationKind::Constant, + visibility: visibility(constant.public), + span: constant.span, + }, + AstDeclaration::ExternalFunction(function) => Self { + name: Some(function.name.text.clone()), + kind: DeclarationKind::ExternalFunction, + visibility: visibility(function.public), + span: function.span, + }, + AstDeclaration::ExternalType(type_) => Self { + name: Some(type_.name.text.clone()), + kind: DeclarationKind::ExternalType, + visibility: visibility(type_.public), + span: type_.span, + }, + AstDeclaration::TypeAlias(alias) => Self { + name: Some(alias.name.text.clone()), + kind: DeclarationKind::TypeAlias, + visibility: visibility(alias.public), + span: alias.span, + }, + AstDeclaration::TypeDefinition(type_) => Self { + name: Some(type_.name.text.clone()), + kind: DeclarationKind::TypeDefinition, + visibility: visibility(type_.public), + span: type_.span, + }, + AstDeclaration::Attribute(attribute) => Self { + name: Some(attribute.name.text.clone()), + kind: DeclarationKind::Attribute, + visibility: Visibility::Private, + span: attribute.span, + }, + AstDeclaration::TargetGroup(group) => Self { + name: Some(group.target.text.clone()), + kind: DeclarationKind::TargetGroup, + visibility: Visibility::Private, + span: group.span, + }, + AstDeclaration::Comment(comment) => Self { + name: None, + kind: DeclarationKind::Statement, + visibility: Visibility::Private, + span: comment.span, + }, AstDeclaration::Statement(raw) => raw_metadata(raw, DeclarationKind::Statement, ""), } } @@ -1501,37 +1463,23 @@ fn raw_metadata(raw: &ast::RawSyntax, kind: DeclarationKind, keyword: &str) -> D } } -fn lower_constant(id: ConstantId, raw: &ast::RawSyntax) -> Constant { - let name = declaration_name(&raw.source, "const").unwrap_or_else(|| format!("__constant_{}", id.0)); +fn lower_constant(id: ConstantId, constant: &ast::Constant) -> Constant { Constant { id, - name, - public: is_public_declaration(&raw.source), - value: constant_value(&raw.source), - span: raw.span, + name: constant.name.text.clone(), + public: constant.public, + value: ast_constant_value(&constant.value), + span: constant.span, } } -fn constant_value(source: &str) -> ConstantValue { - let Some(value) = source.split_once('=').map(|(_, value)| value.trim()) else { - return ConstantValue::Raw(source.trim().into()); - }; - if value == "True" || value == "False" { - return ConstantValue::Literal(Literal { kind: LiteralKind::Bool, source: value.into() }); - } - if value == "Nil" { - return ConstantValue::Literal(Literal { kind: LiteralKind::Nil, source: value.into() }); - } - if value.starts_with('"') && value.ends_with('"') { - return ConstantValue::Literal(Literal { kind: LiteralKind::String, source: value.into() }); - } - if value.parse::().is_ok() { - return ConstantValue::Literal(Literal { kind: LiteralKind::Int, source: value.into() }); - } - if value.parse::().is_ok() && value.contains('.') { - return ConstantValue::Literal(Literal { kind: LiteralKind::Float, source: value.into() }); +fn ast_constant_value(expression: &AstExpression) -> ConstantValue { + match expression { + AstExpression::Literal(literal) => { + ConstantValue::Literal(Literal { kind: literal.kind.clone(), source: literal.source.clone() }) + } + _ => ConstantValue::Raw(format!("{expression:?}")), } - ConstantValue::Raw(value.into()) } fn declaration_name(source: &str, keyword: &str) -> Option { @@ -1544,10 +1492,6 @@ fn declaration_name(source: &str, keyword: &str) -> Option { .map(str::to_string) } -fn exported_type_names(source: &str) -> Vec { - declaration_name(source, "type").into_iter().collect() -} - fn is_public_declaration(source: &str) -> bool { source.trim_start().starts_with("pub ") } diff --git a/crates/core/src/ir/bit_slices.rs b/crates/core/src/ir/bit_slices.rs new file mode 100644 index 0000000..ff0fe80 --- /dev/null +++ b/crates/core/src/ir/bit_slices.rs @@ -0,0 +1,183 @@ +use crate::{ + ast::{self, Expression as AstExpression, LiteralKind}, + source::Span, + types::Type, +}; + +use super::FunctionContext; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitArrayLiteral { + pub segments: Vec, + pub bit_len: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitArraySegment { + pub value: u64, + pub bit_size: u32, + pub type_: BitSegmentType, + pub options: Vec, + pub span: Span, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitStringPatternSegment { + pub binding: Option, + pub bit_size: Option, + pub type_: BitSegmentType, + pub options: Vec, + pub span: Span, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BitSegmentType { + Integer, + Float, + Binary, + Utf8, + Utf16, + Utf32, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BitSegmentOption { + Size(u32), + Unit(u32), + Signed, + Unsigned, + BigEndian, + LittleEndian, + NativeEndian, +} + +pub(super) fn bit_array_literal(raw: &ast::RawSyntax) -> BitArrayLiteral { + let segments = bit_array_segments(raw); + let bit_len = segments.iter().map(|segment| segment.bit_size).sum(); + BitArrayLiteral { segments, bit_len } +} + +pub(super) fn ast_bit_array_literal(bit_array: &ast::BitArray) -> BitArrayLiteral { + let segments = bit_array + .segments + .iter() + .filter_map(|segment| { + let value = match &segment.value { + AstExpression::Literal(literal) if literal.kind == LiteralKind::Int => { + literal.source.parse::().ok()? + } + _ => return None, + }; + let options = segment + .options + .iter() + .filter_map(|option| bit_segment_options(&option.source).into_iter().next()) + .collect::>(); + let bit_size = options + .iter() + .find_map(|option| match option { + BitSegmentOption::Size(size) => Some(*size), + _ => None, + }) + .unwrap_or(8); + Some(BitArraySegment { value, bit_size, type_: BitSegmentType::Integer, options, span: segment.span }) + }) + .collect::>(); + let bit_len = segments.iter().map(|segment| segment.bit_size).sum(); + BitArrayLiteral { segments, bit_len } +} + +fn bit_array_segments(raw: &ast::RawSyntax) -> Vec { + let Some(inner) = raw + .source + .trim() + .strip_prefix("<<") + .and_then(|source| source.strip_suffix(">>")) + else { + return Vec::new(); + }; + inner + .split(',') + .filter_map(|segment| bit_array_segment(segment.trim(), raw.span)) + .collect() +} + +fn bit_array_segment(source: &str, span: Span) -> Option { + if source.is_empty() { + return None; + } + let (value, options) = source.split_once(':').unwrap_or((source, "")); + let value = value.trim().parse::().ok()?; + let options = bit_segment_options(options); + let bit_size = options + .iter() + .find_map(|option| match option { + BitSegmentOption::Size(size) => Some(*size), + _ => None, + }) + .unwrap_or(8); + Some(BitArraySegment { value, bit_size, type_: BitSegmentType::Integer, options, span }) +} + +fn bit_segment_options(source: &str) -> Vec { + source + .split('-') + .filter_map(|option| { + let option = option.trim(); + if option.is_empty() { + return None; + } + match option { + "signed" => Some(BitSegmentOption::Signed), + "unsigned" => Some(BitSegmentOption::Unsigned), + "big" => Some(BitSegmentOption::BigEndian), + "little" => Some(BitSegmentOption::LittleEndian), + "native" => Some(BitSegmentOption::NativeEndian), + "float" => None, + "binary" | "bytes" | "bits" | "bit_string" | "utf8" | "utf16" | "utf32" => None, + _ if let Some(size) = option.strip_prefix("size(").and_then(|value| value.strip_suffix(')')) => { + size.parse().ok().map(BitSegmentOption::Size) + } + _ if let Some(unit) = option.strip_prefix("unit(").and_then(|value| value.strip_suffix(')')) => { + unit.parse().ok().map(BitSegmentOption::Unit) + } + _ => option.parse().ok().map(BitSegmentOption::Size), + } + }) + .collect() +} + +pub(super) fn bit_string_pattern_segments( + context: &mut FunctionContext, raw: &ast::RawSyntax, subject_type: &Type, +) -> Vec { + let Some(inner) = raw + .source + .trim() + .strip_prefix("<<") + .and_then(|source| source.strip_suffix(">>")) + else { + return Vec::new(); + }; + inner + .split(',') + .map(|segment| bit_string_pattern_segment(context, segment.trim(), raw.span, subject_type)) + .collect() +} + +fn bit_string_pattern_segment( + context: &mut FunctionContext, source: &str, span: Span, subject_type: &Type, +) -> BitStringPatternSegment { + let (value, options) = source.split_once(':').unwrap_or((source, "")); + let options = bit_segment_options(options); + let bit_size = options.iter().find_map(|option| match option { + BitSegmentOption::Size(size) => Some(*size), + _ => None, + }); + let binding = value.trim().chars().next().filter(|char| char.is_lowercase()).map(|_| { + let name = ast::Name { span, text: value.trim().into() }; + let local = context.allocate(&name, subject_type.clone()); + context.bind(name.text, local.id); + local.id + }); + BitStringPatternSegment { binding, bit_size, type_: BitSegmentType::Integer, options, span } +} diff --git a/crates/core/src/resolve.rs b/crates/core/src/resolve.rs index ffe71d6..89b7c6b 100644 --- a/crates/core/src/resolve.rs +++ b/crates/core/src/resolve.rs @@ -282,31 +282,29 @@ impl Resolver { SymbolKind::Function { public: function.public }, ); } - Declaration::TypeDefinition(raw) => self.collect_type_definition(scope, &raw), - Declaration::TypeAlias(raw) => { - if let Some(name) = type_name(&raw.source) { - self.define(scope, &raw_name(&raw, name), Namespace::Type, SymbolKind::Type); - } + Declaration::TypeDefinition(type_) => self.collect_type_definition(scope, &type_), + Declaration::TypeAlias(alias) => { + self.define(scope, &alias.name, Namespace::Type, SymbolKind::Type); } _ => {} } } } - fn collect_type_definition(&mut self, scope: ScopeId, raw: &ast::RawSyntax) { - if let Some(name) = type_name(&raw.source) { - self.define(scope, &raw_name(raw, name), Namespace::Type, SymbolKind::Type); - } - for constructor in constructors(&raw.source) { + fn collect_type_definition(&mut self, scope: ScopeId, type_: &ast::TypeDefinition) { + self.define(scope, &type_.name, Namespace::Type, SymbolKind::Type); + for constructor in &type_.constructors { self.define( scope, - &raw_name(raw, constructor), + &constructor.name, Namespace::Constructor, SymbolKind::Constructor, ); - } - for field in fields(&raw.source) { - self.define(scope, &raw_name(raw, field), Namespace::Field, SymbolKind::Field); + for argument in &constructor.arguments { + if let Some(label) = &argument.label { + self.define(scope, label, Namespace::Field, SymbolKind::Field); + } + } } } @@ -347,9 +345,7 @@ impl Resolver { Expression::Variable(name) => self.resolve_name(scope, name), Expression::Call(call) => { self.resolve_expression(scope, &call.function); - for argument in &call.arguments { - self.resolve_expression(scope, &argument.value); - } + self.resolve_arguments(scope, &call.arguments); } Expression::FieldAccess(field_access) => self.resolve_field_access(scope, field_access), Expression::Block(block) => { @@ -372,6 +368,82 @@ impl Resolver { self.resolve_expression(clause_scope, &clause.value); } } + Expression::BinaryOperation(operation) => { + self.resolve_expression(scope, &operation.left); + self.resolve_expression(scope, &operation.right); + } + Expression::Pipeline(pipeline) => { + self.resolve_expression(scope, &pipeline.value); + self.resolve_expression(scope, &pipeline.into); + } + Expression::UnaryOperation(operation) => self.resolve_expression(scope, &operation.value), + Expression::Use(use_) => { + let child = self.new_scope(Some(scope)); + for assignment in &use_.assignments { + self.bind_pattern(child, &assignment.pattern, SymbolKind::Local); + } + self.resolve_expression(child, &use_.value); + } + Expression::AnonymousFunction(function) => { + let child = self.new_scope(Some(scope)); + for parameter in &function.parameters { + if let Some(name) = ¶meter.name { + self.define(child, name, Namespace::Value, SymbolKind::Parameter); + } + } + self.resolve_block(child, &function.body); + } + Expression::Capture(capture) => { + self.resolve_expression(scope, &capture.function); + for argument in capture.arguments.iter().flatten() { + self.resolve_expression(scope, &argument.value); + } + } + Expression::Record(record) => self.resolve_arguments(scope, &record.arguments), + Expression::RecordUpdate(update) => { + self.resolve_expression(scope, &update.spread); + self.resolve_arguments(scope, &update.updates); + } + Expression::Tuple(tuple) => { + for element in &tuple.elements { + self.resolve_expression(scope, element); + } + } + Expression::TupleAccess(access) => self.resolve_expression(scope, &access.tuple), + Expression::List(list) => { + for element in &list.elements { + self.resolve_expression(scope, element); + } + if let Some(spread) = &list.spread { + self.resolve_expression(scope, spread); + } + } + Expression::BitArray(bit_array) => { + for segment in &bit_array.segments { + self.resolve_expression(scope, &segment.value); + for option in &segment.options { + if let Some(value) = &option.value { + self.resolve_expression(scope, value); + } + } + } + } + Expression::Panic(failure) | Expression::Todo(failure) => { + if let Some(message) = &failure.message { + self.resolve_expression(scope, message); + } + } + Expression::Assert(assert) => { + self.resolve_expression(scope, &assert.value); + self.bind_pattern(scope, &assert.pattern, SymbolKind::Local); + } + Expression::Echo(echo) => self.resolve_expression(scope, &echo.value), + } + } + + fn resolve_arguments(&mut self, scope: ScopeId, arguments: &[ast::Argument]) { + for argument in arguments { + self.resolve_expression(scope, &argument.value); } } @@ -614,29 +686,31 @@ fn module_interface(module: &ast::Module) -> ModuleInterface { for declaration in &module.declarations { match declaration { - Declaration::TypeDefinition(raw) => { - let public = raw.source.trim_start().starts_with("pub "); - if let Some(name) = type_name(&raw.source) { - members.insert((Namespace::Type, name.into()), ModuleMember { public, span: raw.span }); - } - for constructor in constructors(&raw.source) { - members.insert( - (Namespace::Constructor, constructor.into()), - ModuleMember { public, span: raw.span }, - ); - } - for field in fields(&raw.source) { + Declaration::TypeDefinition(type_) => { + members.insert( + (Namespace::Type, type_.name.text.clone()), + ModuleMember { public: type_.public, span: type_.span }, + ); + for constructor in &type_.constructors { members.insert( - (Namespace::Field, field.into()), - ModuleMember { public, span: raw.span }, + (Namespace::Constructor, constructor.name.text.clone()), + ModuleMember { public: type_.public, span: constructor.span }, ); + for argument in &constructor.arguments { + if let Some(label) = &argument.label { + members.insert( + (Namespace::Field, label.text.clone()), + ModuleMember { public: type_.public, span: label.span }, + ); + } + } } } - Declaration::TypeAlias(raw) => { - let public = raw.source.trim_start().starts_with("pub "); - if let Some(name) = type_name(&raw.source) { - members.insert((Namespace::Type, name.into()), ModuleMember { public, span: raw.span }); - } + Declaration::TypeAlias(alias) => { + members.insert( + (Namespace::Type, alias.name.text.clone()), + ModuleMember { public: alias.public, span: alias.span }, + ); } _ => {} } @@ -645,43 +719,6 @@ fn module_interface(module: &ast::Module) -> ModuleInterface { ModuleInterface { members } } -fn raw_name(raw: &ast::RawSyntax, name: &str) -> ast::Name { - ast::Name { span: raw.span, text: name.into() } -} - -fn type_name(source: &str) -> Option<&str> { - let mut words = source - .split_whitespace() - .filter(|word| *word != "pub" && *word != "opaque"); - if words.next()? != "type" { - return None; - } - words - .next() - .map(|word| word.split(['(', '{', '=']).next().unwrap_or(word)) -} - -fn constructors(source: &str) -> Vec<&str> { - let Some((_, body)) = source.split_once('{') else { - return Vec::new(); - }; - body.lines() - .filter_map(|line| { - let line = line.trim(); - let name = line.split(['(', ' ', '}']).next().unwrap_or(""); - name.chars().next().is_some_and(char::is_uppercase).then_some(name) - }) - .collect() -} - -fn fields(source: &str) -> Vec<&str> { - source - .split(['(', ',', ')']) - .filter_map(|part| part.trim().split_once(':').map(|(name, _)| name.trim())) - .filter(|name| !name.is_empty()) - .collect() -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/core/src/snapshots/compiler_core__ast__tests__represents_pattern_matching_syntax.snap b/crates/core/src/snapshots/compiler_core__ast__tests__represents_pattern_matching_syntax.snap index f3c816a..a8529c4 100644 --- a/crates/core/src/snapshots/compiler_core__ast__tests__represents_pattern_matching_syntax.snap +++ b/crates/core/src/snapshots/compiler_core__ast__tests__represents_pattern_matching_syntax.snap @@ -1,5 +1,6 @@ --- source: crates/core/src/ast.rs +assertion_line: 1553 expression: "ast.functions[0].body.statements" --- [ @@ -213,8 +214,8 @@ expression: "ast.functions[0].body.statements" ), ], guard: Some( - Raw( - RawSyntax { + BinaryOperation( + BinaryOperation { span: Span { file_id: SourceFileId( 0, @@ -222,8 +223,32 @@ expression: "ast.functions[0].body.statements" start: 254, end: 263, }, - kind: "binary_expression", - source: "value > 0", + left: Variable( + Name { + span: Span { + file_id: SourceFileId( + 0, + ), + start: 254, + end: 259, + }, + text: "value", + }, + ), + operator: GreaterThan, + right: Literal( + Literal { + span: Span { + file_id: SourceFileId( + 0, + ), + start: 262, + end: 263, + }, + kind: Int, + source: "0", + }, + ), }, ), ), diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index 96d08b6..9a56fc4 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -26,6 +26,24 @@ pub enum Type { Function { params: Vec, return_type: Box }, } +impl From for Type { + fn from(kind: LiteralKind) -> Self { + match kind { + LiteralKind::Int => Self::Int, + LiteralKind::Float => Self::Float, + LiteralKind::String => Self::String, + LiteralKind::Bool => Self::Bool, + LiteralKind::Nil => Self::Nil, + } + } +} + +impl From<&LiteralKind> for Type { + fn from(kind: &LiteralKind) -> Self { + kind.clone().into() + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct FieldInfo { pub name: String, @@ -174,7 +192,7 @@ impl TypeChecker { for declaration in self.module.ast.declarations.clone() { match declaration { Declaration::TypeDefinition(raw) => { - if let Some(type_declaration) = parse_type_definition(&raw) { + if let Some(type_declaration) = type_definition_from_ast(&raw) { for constructor in &type_declaration.constructors { self.constructors.insert(constructor.name.clone(), constructor.clone()); self.interface @@ -187,7 +205,7 @@ impl TypeChecker { } } Declaration::TypeAlias(raw) => { - if let Some(alias) = parse_type_alias(&raw) { + if let Some(alias) = type_alias_from_ast(&raw) { self.interface.types.insert(alias.name.clone(), alias); } } @@ -304,13 +322,7 @@ impl TypeChecker { fn check_expression(&mut self, expression: &Expression) -> Option { let type_ = match expression { - Expression::Literal(literal) => match literal.kind { - LiteralKind::Int => Type::Int, - LiteralKind::Float => Type::Float, - LiteralKind::String => Type::String, - LiteralKind::Bool => Type::Bool, - LiteralKind::Nil => Type::Nil, - }, + Expression::Literal(literal) => Type::from(&literal.kind), Expression::Variable(name) => self.lookup_name(name)?, Expression::Call(call) => { let function_type = self.check_expression(&call.function)?; @@ -358,6 +370,68 @@ impl TypeChecker { self.pop_scope(); type_ } + Expression::Tuple(tuple) => Type::Tuple( + tuple + .elements + .iter() + .map(|element| self.check_expression(element)) + .collect::>>()?, + ), + Expression::List(list) => { + for element in &list.elements { + self.check_expression(element)?; + } + if let Some(spread) = &list.spread { + self.check_expression(spread)?; + } + Type::List(Box::new(Type::Int)) + } + Expression::Record(record) => { + let name = constructor_name_text(&record.constructor); + let Some(constructor) = self.constructors.get(&name).cloned() else { + self.diagnostics.push( + Diagnostic::new(DiagnosticCode::TypeError, format!("unknown constructor `{name}`")) + .with_label(Label::primary(record.span, "unknown constructor here")), + ); + return None; + }; + constructor.return_type + } + Expression::BitArray(_) => Type::BitArray, + Expression::Panic(_) | Expression::Todo(_) | Expression::Assert(_) => Type::Nil, + Expression::BinaryOperation(operation) => { + self.check_expression(&operation.left)?; + self.check_expression(&operation.right)?; + match operation.operator { + ast::BinaryOperator::Equal + | ast::BinaryOperator::NotEqual + | ast::BinaryOperator::LessThan + | ast::BinaryOperator::LessThanEqual + | ast::BinaryOperator::GreaterThan + | ast::BinaryOperator::GreaterThanEqual + | ast::BinaryOperator::FloatLessThan + | ast::BinaryOperator::FloatLessThanEqual + | ast::BinaryOperator::FloatGreaterThan + | ast::BinaryOperator::FloatGreaterThanEqual + | ast::BinaryOperator::And + | ast::BinaryOperator::Or => Type::Bool, + ast::BinaryOperator::FloatAdd + | ast::BinaryOperator::FloatSubtract + | ast::BinaryOperator::FloatMultiply + | ast::BinaryOperator::FloatDivide => Type::Float, + _ => Type::Int, + } + } + Expression::Pipeline(pipeline) => { + self.check_expression(&pipeline.value)?; + self.check_expression(&pipeline.into)? + } + Expression::UnaryOperation(operation) => self.check_expression(&operation.value)?, + Expression::Use(use_) => self.check_expression(&use_.value)?, + Expression::AnonymousFunction(_) | Expression::Capture(_) => Type::Nil, + Expression::RecordUpdate(update) => self.check_expression(&update.spread)?, + Expression::TupleAccess(_) => Type::Int, + Expression::Echo(echo) => self.check_expression(&echo.value)?, Expression::Raw(raw) if raw.kind == "bit_string" => Type::BitArray, Expression::Raw(raw) if raw.kind == "tuple" => Type::Tuple( raw.source @@ -811,15 +885,14 @@ trait ExpressionSpan { impl ExpressionSpan for Expression { fn span(&self) -> Span { - match self { - Expression::Literal(literal) => literal.span, - Expression::Variable(name) => name.span, - Expression::Call(call) => call.span, - Expression::FieldAccess(field_access) => field_access.span, - Expression::Block(block) => block.span, - Expression::Case(case) => case.span, - Expression::Raw(raw) => raw.span, - } + Span::from(self) + } +} + +fn constructor_name_text(name: &ast::ConstructorName) -> String { + match name { + ast::ConstructorName::Local(name) => name.text.clone(), + ast::ConstructorName::Remote { name, .. } => name.text.clone(), } } @@ -940,7 +1013,7 @@ fn constructors_from_ast(module: &ast::Module) -> Vec<(String, ConstructorInfo)> .declarations .iter() .filter_map(|declaration| match declaration { - Declaration::TypeDefinition(raw) => parse_type_definition(raw), + Declaration::TypeDefinition(raw) => type_definition_from_ast(raw), _ => None, }) .flat_map(|declaration| { @@ -952,96 +1025,55 @@ fn constructors_from_ast(module: &ast::Module) -> Vec<(String, ConstructorInfo)> .collect() } -fn parse_type_definition(raw: &ast::RawSyntax) -> Option { - let header = raw.source.split('{').next()?.trim(); - let opaque = header.split_whitespace().any(|word| word == "opaque"); - let name = type_decl_name(header)?.to_string(); - let parameters = type_parameters(header); - let return_type = if opaque { - Type::Opaque { name: name.clone(), args: parameters.iter().cloned().map(Type::Generic).collect() } +fn type_definition_from_ast(type_: &ast::TypeDefinition) -> Option { + let parameters = type_.parameters.clone(); + let return_args = parameters.iter().cloned().map(Type::Generic).collect(); + let return_type = if type_.opaque { + Type::Opaque { name: type_.name.text.clone(), args: return_args } } else { - Type::Custom { name: name.clone(), args: parameters.iter().cloned().map(Type::Generic).collect() } + Type::Custom { name: type_.name.text.clone(), args: return_args } }; - let constructors = raw - .source - .split_once('{')? - .1 - .lines() - .filter_map(|line| parse_constructor(line.trim(), &return_type, raw.span)) - .collect::>(); - - Some(TypeDeclaration { name, parameters, opaque, constructors, span: raw.span }) + let constructors = type_ + .constructors + .iter() + .map(|constructor| ConstructorInfo { + name: constructor.name.text.clone(), + fields: constructor + .arguments + .iter() + .enumerate() + .map(|(index, argument)| FieldInfo { + name: argument + .label + .as_ref() + .map(|label| label.text.clone()) + .unwrap_or_else(|| format!("_{index}")), + type_: parse_type_source(&argument.type_annotation.source).unwrap_or(Type::Nil), + }) + .collect(), + return_type: return_type.clone(), + span: constructor.span, + }) + .collect(); + Some(TypeDeclaration { + name: type_.name.text.clone(), + parameters, + opaque: type_.opaque, + constructors, + span: type_.span, + }) } -fn parse_type_alias(raw: &ast::RawSyntax) -> Option { - let header = raw.source.split('=').next()?.trim(); - let name = type_decl_name(header)?.to_string(); +fn type_alias_from_ast(alias: &ast::TypeAlias) -> Option { Some(TypeDeclaration { - name, - parameters: type_parameters(header), - opaque: false, + name: alias.name.text.clone(), + parameters: alias.parameters.clone(), + opaque: alias.opaque, constructors: Vec::new(), - span: raw.span, + span: alias.span, }) } -fn type_decl_name(header: &str) -> Option<&str> { - header - .split_whitespace() - .filter(|word| *word != "pub" && *word != "opaque" && *word != "type") - .next() - .map(|word| word.split(['(', '{', '=']).next().unwrap_or(word)) -} - -fn type_parameters(header: &str) -> Vec { - let Some(params) = header - .split_once('(') - .and_then(|(_, rest)| rest.split_once(')').map(|(params, _)| params)) - else { - return Vec::new(); - }; - params - .split(',') - .map(str::trim) - .filter(|param| !param.is_empty()) - .map(String::from) - .collect() -} - -fn parse_constructor(line: &str, return_type: &Type, span: Span) -> Option { - let line = line.trim_end_matches('}').trim(); - if line.is_empty() || !line.chars().next().is_some_and(char::is_uppercase) { - return None; - } - let name = line.split(['(', ' ']).next()?.to_string(); - let fields = match line - .split_once('(') - .and_then(|(_, rest)| rest.rsplit_once(')').map(|(fields, _)| fields)) - { - Some(fields) => parse_fields(fields)?, - None => Vec::new(), - }; - Some(ConstructorInfo { name, fields, return_type: return_type.clone(), span }) -} - -fn parse_fields(source: &str) -> Option> { - if source.trim().is_empty() { - return Some(Vec::new()); - } - source - .split(',') - .enumerate() - .map(|(index, field)| { - let field = field.trim(); - if let Some((name, type_)) = field.split_once(':') { - Some(FieldInfo { name: name.trim().into(), type_: parse_type_source(type_.trim())? }) - } else { - Some(FieldInfo { name: format!("_{index}"), type_: parse_type_source(field)? }) - } - }) - .collect() -} - #[cfg(test)] mod tests { use super::*; diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 892fecb..404950f 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -34,6 +34,14 @@ TODO: - Running WebAssembly in Wasmtime and the browser - Compiler architecture from source to executable - Diagnostics and source spans +- Abstract syntax trees as the compiler's source model + - Why compilers use an AST after parsing + - What information belongs in the AST, and what stays in the CST + - Walking from tree-sitter nodes to typed Rust data structures + - Using spans to connect compiler data back to source code + - Modeling declarations separately from executable expressions + - Representing nested expressions and patterns without losing order + - How AST design shapes later resolver, type checker, and IR code - Type checking and type inference - Lowering and intermediate representations - Runtime value representation diff --git a/docs/src/internal/tasks/09_gleam_language_completeness.md b/docs/src/internal/tasks/09_gleam_language_completeness.md index f5513e3..62a0c95 100644 --- a/docs/src/internal/tasks/09_gleam_language_completeness.md +++ b/docs/src/internal/tasks/09_gleam_language_completeness.md @@ -9,13 +9,13 @@ backend target. ### Syntax and AST -- [ ] Replace raw executable syntax with structured AST nodes. -- [ ] Add structured constants, attributes, externals, target groups, and docs. -- [ ] Add structured operators, pipelines, `use`, anonymous functions, and +- [x] Replace raw executable syntax with structured AST nodes. +- [x] Add structured constants, attributes, externals, target groups, and docs. +- [x] Add structured operators, pipelines, `use`, anonymous functions, and captures. -- [ ] Add structured record construction, record updates, tuples, lists, and bit +- [x] Add structured record construction, record updates, tuples, lists, and bit arrays. -- [ ] Preserve spans and source order for every new AST node. +- [x] Preserve spans and source order for every new AST node. ### Resolution