#![allow(dead_code)]
use crate::ast::operators::Op;
use crate::ast::program::{Program, ProgramStatement, QualifiedPath, UseTarget, Visibility};
use crate::ast::statement::{Statement, into_then_expr, stmt_from_expr_top};
use crate::ast::{Expr, Literal};
use crate::compiler::parser::cst_parser::ParserError;
use crate::compiler::parser::green::{GreenNode, GreenNodeArena, GreenNodeId, SyntaxKind};
use crate::compiler::parser::token::{Token, TokenKind};
use crate::interner::{ExprNodeId, Symbol, ToSymbol};
use crate::pattern::{Pattern, TypedId, TypedPattern};
use crate::types::Type;
use crate::utils::metadata::{Location, Span};
use std::ops::ControlFlow;
use std::path::PathBuf;
pub struct Lowerer<'a> {
source: &'a str,
tokens: &'a [Token],
arena: &'a GreenNodeArena,
file_path: PathBuf,
}
impl<'a> Lowerer<'a> {
pub fn new(
source: &'a str,
tokens: &'a [Token],
arena: &'a GreenNodeArena,
file_path: PathBuf,
) -> Self {
Self {
source,
tokens,
arena,
file_path,
}
}
fn unwrap_paren(expr: ExprNodeId) -> ExprNodeId {
match expr.to_expr() {
Expr::Paren(inner) => Self::unwrap_paren(inner),
_ => expr,
}
}
pub fn lower_program(&self, root: GreenNodeId) -> Program {
let (mut program_statements, pending_statements, pending_span) = self
.arena
.children(root)
.map_or((Vec::new(), Vec::new(), 0..0), |children| {
children
.iter()
.copied()
.filter(|child| self.arena.kind(*child) == Some(SyntaxKind::Statement))
.filter_map(|child| self.lower_statement(child))
.fold(
(Vec::new(), Vec::new(), 0..0),
|(mut program_statements, mut pending_statements, mut pending_span),
(stmt, span)| {
match &stmt {
ProgramStatement::GlobalStatement(Statement::Single(expr)) => {
let stmts = stmt_from_expr_top(*expr);
let new_pending = stmts
.into_iter()
.map(|s| (s, self.location_from_span(span.clone())));
pending_statements.extend(new_pending);
pending_span = span;
}
_ => {
if !pending_statements.is_empty() {
if let Some(merged_expr) =
into_then_expr(&pending_statements)
{
program_statements.push((
ProgramStatement::GlobalStatement(
Statement::Single(merged_expr),
),
pending_span.clone(),
));
}
pending_statements.clear();
}
program_statements.push((stmt, span));
}
}
(program_statements, pending_statements, pending_span)
},
)
});
if !pending_statements.is_empty()
&& let Some(merged_expr) = into_then_expr(&pending_statements)
{
program_statements.push((
ProgramStatement::GlobalStatement(Statement::Single(merged_expr)),
pending_span.clone(),
));
}
Program {
statements: program_statements,
}
}
fn lower_statement(&self, node: GreenNodeId) -> Option<(ProgramStatement, Span)> {
let span = self.node_span(node)?;
let stmt = match self.arena.kind(node) {
Some(SyntaxKind::Statement) => {
let visibility = self.extract_visibility(node);
let (inner_kind, inner_id) = self
.arena
.children(node)
.and_then(|children| {
children.iter().copied().find_map(|child| {
let kind = self.arena.kind(child)?;
if kind == SyntaxKind::VisibilityPub {
None
} else {
Some((kind, child))
}
})
})
.map(|(kind, id)| (Some(kind), Some(id)))
.unwrap_or((None, None));
match (inner_kind, inner_id) {
(Some(SyntaxKind::FunctionDecl), Some(id)) => self
.lower_function_decl(id, visibility)
.unwrap_or(ProgramStatement::Error),
(Some(SyntaxKind::LetDecl), Some(id)) => {
self.lower_let_decl(id).unwrap_or(ProgramStatement::Error)
}
(Some(SyntaxKind::LetRecDecl), Some(id)) => self
.lower_letrec_decl(id)
.unwrap_or(ProgramStatement::Error),
(Some(SyntaxKind::IncludeStmt), Some(id)) => {
self.lower_include(id).unwrap_or(ProgramStatement::Error)
}
(Some(SyntaxKind::StageDecl), Some(id)) => {
self.lower_stage_decl(id).unwrap_or(ProgramStatement::Error)
}
(Some(SyntaxKind::ModuleDecl), Some(id)) => self
.lower_module_decl(id, visibility)
.unwrap_or(ProgramStatement::Error),
(Some(SyntaxKind::UseStmt), Some(id)) => self
.lower_use_stmt(id, visibility)
.unwrap_or(ProgramStatement::Error),
(Some(SyntaxKind::TypeDecl), Some(id)) => self
.lower_type_decl(id, visibility)
.unwrap_or(ProgramStatement::Error),
(Some(_), _) => {
let expr_nodes = self.collect_expr_nodes(node);
let expr = self.lower_expr_sequence(&expr_nodes);
ProgramStatement::GlobalStatement(Statement::Single(expr))
}
_ => ProgramStatement::Error,
}
}
_ => ProgramStatement::Error,
};
Some((stmt, span))
}
fn extract_visibility(&self, node: GreenNodeId) -> Visibility {
self.arena
.children(node)
.and_then(|children| {
children
.iter()
.find(|child| self.arena.kind(**child) == Some(SyntaxKind::VisibilityPub))
})
.map(|_| Visibility::Public)
.unwrap_or(Visibility::Private)
}
fn lower_module_decl(
&self,
node: GreenNodeId,
visibility: Visibility,
) -> Option<ProgramStatement> {
let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
let name = self.token_text(name_idx)?.to_symbol();
let has_block = self
.find_token(node, |kind| matches!(kind, TokenKind::BlockBegin))
.is_some();
let body = if has_block {
let stmts: Vec<(ProgramStatement, Span)> = self
.arena
.children(node)
.map(|children| {
children
.iter()
.copied()
.filter(|child| self.arena.kind(*child) == Some(SyntaxKind::Statement))
.filter_map(|child| self.lower_statement(child))
.collect()
})
.unwrap_or_default();
Some(stmts)
} else {
None
};
Some(ProgramStatement::ModuleDefinition {
visibility,
name,
body,
})
}
fn lower_use_stmt(
&self,
node: GreenNodeId,
visibility: Visibility,
) -> Option<ProgramStatement> {
let path_node = self.find_child(node, |kind| kind == SyntaxKind::QualifiedPath)?;
let (path, target) = self.lower_use_path(path_node)?;
Some(ProgramStatement::UseStatement {
visibility,
path,
target,
})
}
fn lower_use_path(&self, node: GreenNodeId) -> Option<(QualifiedPath, UseTarget)> {
use crate::ast::program::UseTarget;
let mut segments: Vec<Symbol> = Vec::new();
let mut target = UseTarget::Single;
if let Some(children) = self.arena.children(node) {
for child in children.iter() {
let child_kind = self.arena.kind(*child);
match child_kind {
Some(SyntaxKind::UseTargetWildcard) => {
target = UseTarget::Wildcard;
}
Some(SyntaxKind::UseTargetMultiple) => {
let symbols = self.lower_use_target_multiple(*child);
target = UseTarget::Multiple(symbols);
}
_ => {
if let Some(token_idx) = self.get_token_index(*child)
&& let Some(token) = self.tokens.get(token_idx)
&& token.kind == TokenKind::Ident
&& let Some(text) = self.token_text(token_idx)
{
segments.push(text.to_symbol());
}
}
}
}
}
if segments.is_empty() && matches!(target, UseTarget::Single) {
None
} else {
Some((QualifiedPath::new(segments), target))
}
}
fn lower_use_target_multiple(&self, node: GreenNodeId) -> Vec<Symbol> {
let mut symbols = Vec::new();
if let Some(children) = self.arena.children(node) {
for child in children.iter() {
if let Some(token_idx) = self.get_token_index(*child)
&& let Some(token) = self.tokens.get(token_idx)
&& token.kind == TokenKind::Ident
&& let Some(text) = self.token_text(token_idx)
{
symbols.push(text.to_symbol());
}
}
}
symbols
}
fn lower_qualified_path(&self, node: GreenNodeId) -> Option<QualifiedPath> {
let segments: Vec<Symbol> = self
.arena
.children(node)
.map(|children| {
children
.iter()
.filter_map(|child| {
if let Some(token_idx) = self.get_token_index(*child)
&& let Some(token) = self.tokens.get(token_idx)
&& token.kind == TokenKind::Ident
{
self.token_text(token_idx).map(|s| s.to_symbol())
} else {
None
}
})
.collect()
})
.unwrap_or_default();
if segments.is_empty() {
None
} else {
Some(QualifiedPath::new(segments))
}
}
fn lower_type_decl(
&self,
node: GreenNodeId,
visibility: Visibility,
) -> Option<ProgramStatement> {
use crate::ast::program::VariantDef;
let is_recursive = self
.find_token(node, |kind| matches!(kind, TokenKind::Rec))
.is_some();
let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
let name = self.token_text(name_idx)?.to_symbol();
let variants: Vec<VariantDef> = self
.arena
.children(node)
.map(|children| {
children
.iter()
.copied()
.filter(|child| self.arena.kind(*child) == Some(SyntaxKind::VariantDef))
.filter_map(|child| self.lower_variant_def(child))
.collect()
})
.unwrap_or_default();
if variants.is_empty() {
let target_type = self
.arena
.children(node)
.and_then(|children| {
children
.iter()
.find(|c| {
self.arena
.kind(**c)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.copied()
})
.map(|type_node| self.lower_type(type_node))?;
Some(ProgramStatement::TypeAlias {
visibility,
name,
target_type,
})
} else {
Some(ProgramStatement::TypeDeclaration {
visibility,
name,
variants,
is_recursive,
})
}
}
fn lower_variant_def(&self, node: GreenNodeId) -> Option<crate::ast::program::VariantDef> {
use crate::ast::program::VariantDef;
let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
let name = self.token_text(name_idx)?.to_symbol();
let payload = self.arena.children(node).and_then(|children| {
let type_nodes: Vec<_> = children
.iter()
.filter(|c| {
self.arena
.kind(**c)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.collect();
match type_nodes.len() {
0 => None,
1 => {
Some(self.lower_type(*type_nodes[0]))
}
_ => {
let elem_types: Vec<_> = type_nodes
.iter()
.map(|node| self.lower_type(**node))
.collect();
let loc = Location::default();
Some(Type::Tuple(elem_types).into_id_with_location(loc))
}
}
});
Some(VariantDef::new(name, payload))
}
fn lower_let_decl(&self, node: GreenNodeId) -> Option<ProgramStatement> {
let pattern_node = self.find_child(node, Self::is_pattern_kind)?;
let (pat, pat_span) = self.lower_pattern(pattern_node)?;
let type_annotation = if let Some(type_anno_node) =
self.find_child(node, |kind| kind == SyntaxKind::TypeAnnotation)
{
if let Some(type_children) = self.arena.children(type_anno_node) {
type_children
.iter()
.find(|c| {
self.arena
.kind(**c)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.map(|type_node| self.lower_type(*type_node))
} else {
None
}
} else {
None
};
let expr_nodes = self.collect_expr_nodes_after(node, pattern_node);
let value = self.lower_expr_sequence(&expr_nodes);
let loc = self.location_from_span(pat_span.clone());
let ty =
type_annotation.unwrap_or_else(|| Type::Unknown.into_id_with_location(loc.clone()));
let typed = TypedPattern::new(pat, ty);
Some(ProgramStatement::GlobalStatement(Statement::Let(
typed, value,
)))
}
fn lower_function_decl(
&self,
node: GreenNodeId,
visibility: Visibility,
) -> Option<ProgramStatement> {
let name_idx = self.find_token(node, |kind| {
matches!(kind, TokenKind::IdentFunction | TokenKind::Ident)
})?;
let name = self.token_text(name_idx)?.to_symbol();
let (params, params_span) = self
.find_child(node, |kind| kind == SyntaxKind::ParamList)
.map(|id| self.lower_param_list(id))
.unwrap_or_else(|| (Vec::new(), self.node_span(node).unwrap_or(0..0)));
let return_type = self.arena.children(node).and_then(|children| {
children
.iter()
.find_map(|child| match self.arena.kind(*child) {
Some(SyntaxKind::TypeAnnotation) => {
self.arena.children(*child).and_then(|type_children| {
type_children
.iter()
.find(|c| {
self.arena
.kind(**c)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.map(|type_node| self.lower_type(*type_node))
})
}
Some(kind) if Self::is_type_kind(kind) => Some(self.lower_type(*child)),
_ => None,
})
});
let body_node = self
.find_child(node, |kind| kind == SyntaxKind::BlockExpr)
.or_else(|| self.find_child(node, Self::is_expr_kind))?;
let body = if self.arena.kind(body_node) == Some(SyntaxKind::BlockExpr) {
let stmts = self.lower_block_statements(body_node);
into_then_expr(&stmts).unwrap_or_else(|| Expr::Error.into_id_without_span())
} else {
self.lower_expr(body_node)
};
let arg_loc = self.location_from_span(params_span.clone());
Some(ProgramStatement::FnDefinition {
visibility,
name,
args: (params, arg_loc),
return_type,
body,
})
}
fn lower_letrec_decl(&self, node: GreenNodeId) -> Option<ProgramStatement> {
let ident_token = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
let expr_nodes = self.collect_expr_nodes(node);
let name = self.token_text(ident_token)?.to_symbol();
let span = self.node_span(node)?;
let loc = self.location_from_span(span.clone());
let ty = Type::Unknown.into_id_with_location(loc.clone());
let id = TypedId::new(name, ty);
let value = self.lower_expr_sequence(&expr_nodes);
Some(ProgramStatement::GlobalStatement(Statement::LetRec(
id, value,
)))
}
fn lower_include(&self, node: GreenNodeId) -> Option<ProgramStatement> {
let string_token = self.find_token(node, |kind| matches!(kind, TokenKind::Str))?;
let raw = self.token_text(string_token)?;
let content = raw.trim_matches('"').to_symbol();
Some(ProgramStatement::Import(content))
}
fn lower_stage_decl(&self, node: GreenNodeId) -> Option<ProgramStatement> {
use crate::ast::StageKind;
let stage_token = self.find_token(node, |kind| {
matches!(kind, TokenKind::Main | TokenKind::Macro)
})?;
let stage_kind = match self.tokens.get(stage_token)?.kind {
TokenKind::Main => StageKind::Main,
TokenKind::Macro => StageKind::Macro,
_ => StageKind::Main,
};
Some(ProgramStatement::StageDeclaration { stage: stage_kind })
}
fn lower_expr(&self, node: GreenNodeId) -> ExprNodeId {
let loc = self
.node_span(node)
.map(|span| self.location_from_span(span))
.unwrap_or_default();
match self.arena.kind(node) {
Some(SyntaxKind::IntLiteral) => {
let text = self.text_of_first_token(node).unwrap_or("0");
Expr::Literal(Literal::Float(text.to_symbol())).into_id(loc)
}
Some(SyntaxKind::FloatLiteral) => {
let text = self.text_of_first_token(node).unwrap_or("0.0");
Expr::Literal(Literal::Float(text.to_symbol())).into_id(loc)
}
Some(SyntaxKind::StringLiteral) => {
let text = self.text_of_first_token(node).unwrap_or("\"");
Expr::Literal(Literal::String(text.trim_matches('"').to_symbol())).into_id(loc)
}
Some(SyntaxKind::SelfLiteral) => Expr::Literal(Literal::SelfLit).into_id(loc),
Some(SyntaxKind::NowLiteral) => Expr::Literal(Literal::Now).into_id(loc),
Some(SyntaxKind::SampleRateLiteral) => Expr::Literal(Literal::SampleRate).into_id(loc),
Some(SyntaxKind::PlaceHolderLiteral) => {
Expr::Literal(Literal::PlaceHolder).into_id(loc)
}
Some(SyntaxKind::Identifier) => {
let text = self.text_of_first_token(node).unwrap_or("");
Expr::Var(text.to_symbol()).into_id(loc)
}
Some(SyntaxKind::QualifiedPath) => {
if let Some(path) = self.lower_qualified_path(node) {
if path.segments.len() == 1 {
Expr::Var(path.segments[0]).into_id(loc)
} else {
Expr::QualifiedVar(path).into_id(loc)
}
} else {
Expr::Error.into_id(loc)
}
}
Some(SyntaxKind::TupleExpr) => {
let elems = self.lower_expr_list(node);
Expr::Tuple(elems).into_id(loc)
}
Some(SyntaxKind::ArrayExpr) => {
let elems = self.lower_expr_list(node);
Expr::ArrayLiteral(elems).into_id(loc)
}
Some(SyntaxKind::RecordExpr) => {
if self
.find_token(node, |kind| kind == TokenKind::LeftArrow)
.is_some()
{
let base = self
.child_exprs(node)
.into_iter()
.next()
.map(|expr_node| self.lower_expr(expr_node))
.unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
let fields = self.lower_record_fields(node);
Expr::RecordUpdate(base, fields).into_id(loc)
} else {
let fields = self.lower_record_fields(node);
if self
.find_token(node, |kind| kind == TokenKind::DoubleDot)
.is_some()
{
Expr::ImcompleteRecord(fields).into_id(loc)
} else {
Expr::RecordLiteral(fields).into_id(loc)
}
}
}
Some(SyntaxKind::IfExpr) => {
let expr_children = self.child_exprs(node);
let raw_cond = expr_children
.first()
.map(|&id| self.lower_expr(id))
.unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
let cond = match raw_cond.to_expr() {
Expr::Paren(inner) => inner,
_ => raw_cond,
};
let then_expr = expr_children
.get(1)
.map(|&id| self.lower_expr(id))
.unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
let else_expr = expr_children.get(2).map(|&id| self.lower_expr(id));
Expr::If(cond, then_expr, else_expr).into_id(loc)
}
Some(SyntaxKind::MatchExpr) => self.lower_match_expr(node, loc),
Some(SyntaxKind::BlockExpr) => {
let stmts = self.lower_block_statements(node);
Expr::Block(into_then_expr(&stmts)).into_id(loc)
}
Some(SyntaxKind::LambdaExpr) => {
let (params, body) = self.lower_lambda(node);
Expr::Lambda(params, None, body).into_id(loc)
}
Some(SyntaxKind::UnaryExpr) => {
let op = self.extract_unary_op(node).unwrap_or(Op::Minus);
let rhs_nodes = self.child_exprs(node);
let rhs = if rhs_nodes.is_empty() {
Expr::Error.into_id(loc.clone())
} else {
self.lower_expr_sequence(&rhs_nodes)
};
Expr::UniOp((op, loc.span.clone()), rhs).into_id(loc)
}
Some(SyntaxKind::ParenExpr) => {
let inner_nodes = self.child_exprs(node);
if inner_nodes.is_empty() {
Expr::Error.into_id(loc.clone())
} else {
self.lower_expr_sequence(&inner_nodes)
}
}
Some(SyntaxKind::MacroExpansion) => {
let (callee, args) = self.lower_macro_expand(node);
Expr::MacroExpand(callee, args).into_id(loc)
}
Some(SyntaxKind::BinaryExpr) => self.lower_binary(node),
Some(SyntaxKind::CallExpr) => self.lower_call(node),
Some(SyntaxKind::FieldAccess) => self.lower_field_access(node),
Some(SyntaxKind::IndexExpr) => self.lower_index(node),
Some(SyntaxKind::AssignExpr) => {
Expr::Error.into_id(loc)
}
Some(SyntaxKind::BracketExpr) => {
let body_nodes = self.child_exprs(node);
let body = if body_nodes.is_empty() {
Expr::Error.into_id(loc.clone())
} else {
self.lower_expr_sequence(&body_nodes)
};
Expr::Bracket(body).into_id(loc)
}
Some(SyntaxKind::EscapeExpr) => {
let body_nodes = self.child_exprs(node);
let body = if body_nodes.is_empty() {
Expr::Error.into_id(loc.clone())
} else {
self.lower_expr_sequence(&body_nodes)
};
Expr::Escape(body).into_id(loc)
}
_ => Expr::Error.into_id(loc),
}
}
fn lower_match_expr(&self, node: GreenNodeId, loc: Location) -> ExprNodeId {
use crate::ast::MatchArm;
let children: Vec<GreenNodeId> = self
.arena
.children(node)
.map(|c| c.to_vec())
.unwrap_or_default();
let scrutinee = children
.iter()
.find(|&&c| self.arena.kind(c).map(Self::is_expr_kind) == Some(true))
.map(|&c| self.lower_expr(c))
.unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
let arm_list = children
.iter()
.find(|&&c| self.arena.kind(c) == Some(SyntaxKind::MatchArmList));
let arms: Vec<MatchArm> = arm_list
.and_then(|&list| self.arena.children(list))
.map(|arm_nodes| {
arm_nodes
.iter()
.filter(|&&c| self.arena.kind(c) == Some(SyntaxKind::MatchArm))
.map(|&arm| self.lower_match_arm(arm))
.collect()
})
.unwrap_or_default();
Expr::Match(scrutinee, arms).into_id(loc)
}
fn lower_match_arm(&self, node: GreenNodeId) -> crate::ast::MatchArm {
use crate::ast::{MatchArm, MatchPattern};
let children: Vec<GreenNodeId> = self
.arena
.children(node)
.map(|c| c.iter().copied().collect())
.unwrap_or_default();
let pattern_node = children
.iter()
.find(|&&c| self.arena.kind(c) == Some(SyntaxKind::MatchPattern));
let pattern = pattern_node
.map(|&pat| self.lower_match_pattern(pat))
.unwrap_or(MatchPattern::Wildcard);
let body = children
.iter()
.filter(|&&c| self.arena.kind(c) != Some(SyntaxKind::MatchPattern))
.find(|&&c| self.arena.kind(c).map(Self::is_expr_kind) == Some(true))
.map(|&c| self.lower_expr(c))
.unwrap_or_else(|| {
Expr::Error.into_id(self.location_from_span(self.node_span(node).unwrap_or(0..0)))
});
MatchArm { pattern, body }
}
fn lower_match_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
use crate::ast::{Literal, MatchPattern};
use crate::interner::ToSymbol;
let children: Vec<GreenNodeId> = self
.arena
.children(node)
.map(|c| c.to_vec())
.unwrap_or_default();
for &child in &children {
match self.arena.kind(child) {
Some(SyntaxKind::IntLiteral) => {
if let Some(text) = self.text_of_first_token(child) {
if let Ok(n) = text.parse::<i64>() {
return MatchPattern::Literal(Literal::Int(n));
}
}
}
Some(SyntaxKind::FloatLiteral) => {
if let Some(text) = self.text_of_first_token(child) {
return MatchPattern::Literal(Literal::Float(text.to_symbol()));
}
}
Some(SyntaxKind::PlaceHolderLiteral) => {
return MatchPattern::Wildcard;
}
Some(SyntaxKind::ConstructorPattern) => {
return self.lower_constructor_pattern(child);
}
Some(SyntaxKind::TuplePattern) => {
return self.lower_match_tuple_pattern(child);
}
Some(SyntaxKind::Identifier) => {
if let Some(text) = self.text_of_first_token(child) {
return MatchPattern::Constructor(text.to_symbol(), None);
}
}
_ => {}
}
}
MatchPattern::Wildcard
}
fn lower_constructor_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
use crate::ast::MatchPattern;
use crate::interner::ToSymbol;
let children: Vec<GreenNodeId> = self
.arena
.children(node)
.map(|c| c.to_vec())
.unwrap_or_default();
let mut constructor_name: Option<Symbol> = None;
let mut inner_pattern: Option<Box<MatchPattern>> = None;
for &child in &children {
match self.arena.kind(child) {
Some(SyntaxKind::Identifier) => {
if let Some(text) = self.text_of_first_token(child) {
if constructor_name.is_none() {
constructor_name = Some(text.to_symbol());
} else {
inner_pattern =
Some(Box::new(MatchPattern::Variable(text.to_symbol())));
}
}
}
Some(SyntaxKind::PlaceHolderLiteral) => {
inner_pattern = Some(Box::new(MatchPattern::Wildcard));
}
Some(SyntaxKind::TuplePattern) => {
inner_pattern = Some(Box::new(self.lower_tuple_pattern(child)));
}
_ => {}
}
}
if let Some(name) = constructor_name {
MatchPattern::Constructor(name, inner_pattern)
} else {
MatchPattern::Wildcard
}
}
fn lower_tuple_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
use crate::ast::MatchPattern;
use crate::interner::ToSymbol;
let children: Vec<GreenNodeId> = self
.arena
.children(node)
.map(|c| c.to_vec())
.unwrap_or_default();
let mut patterns = Vec::new();
for &child in &children {
match self.arena.kind(child) {
Some(SyntaxKind::Identifier) => {
if let Some(text) = self.text_of_first_token(child) {
patterns.push(MatchPattern::Variable(text.to_symbol()));
}
}
Some(SyntaxKind::SinglePattern) => {
if let Some(text) = self.text_of_first_token(child) {
patterns.push(MatchPattern::Variable(text.to_symbol()));
}
}
Some(SyntaxKind::PlaceHolderLiteral) => {
patterns.push(MatchPattern::Wildcard);
}
Some(SyntaxKind::TuplePattern) => {
patterns.push(self.lower_tuple_pattern(child));
}
_ => {}
}
}
if patterns.len() == 1 {
if let MatchPattern::Tuple(_) = &patterns[0] {
return patterns.pop().unwrap();
}
}
MatchPattern::Tuple(patterns)
}
fn lower_match_tuple_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
use crate::ast::MatchPattern;
let children: Vec<GreenNodeId> = self
.arena
.children(node)
.map(|c| c.to_vec())
.unwrap_or_default();
let mut patterns = Vec::new();
for &child in &children {
match self.arena.kind(child) {
Some(SyntaxKind::MatchPattern) => {
patterns.push(self.lower_match_pattern(child));
}
_ => {}
}
}
MatchPattern::Tuple(patterns)
}
fn lower_lambda(&self, node: GreenNodeId) -> (Vec<TypedId>, ExprNodeId) {
let (params, body_nodes) = self
.arena
.children(node)
.map(|children| {
(0..children.len()).fold(
(Vec::new(), Vec::new(), 0usize),
|(mut params, mut body_nodes, mut next_index), i| {
if i < next_index {
return (params, body_nodes, next_index);
}
let child = children[i];
match self.arena.kind(child) {
None => {
if let Some(token_index) = self.get_token_index(child)
&& let Some(token) = self.tokens.get(token_index)
&& matches!(
token.kind,
TokenKind::Ident | TokenKind::IdentParameter
)
{
let name = token.text(self.source).to_symbol();
let loc = self.location_from_span(token.start..token.end());
let mut ty = Type::Unknown.into_id_with_location(loc.clone());
let mut next = i + 1;
if next < children.len()
&& self.arena.kind(children[next])
== Some(SyntaxKind::TypeAnnotation)
{
ty = self
.arena
.children(children[next])
.and_then(|type_children| {
type_children
.iter()
.find(|c| {
self.arena
.kind(**c)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.copied()
})
.map(|type_node| self.lower_type(type_node))
.unwrap_or_else(|| {
Type::Unknown.into_id_with_location(loc.clone())
});
next += 1;
}
params.push(TypedId::new(name, ty));
next_index = next;
}
}
Some(kind) if Self::is_expr_kind(kind) => {
body_nodes.push(child);
}
_ => {}
}
(params, body_nodes, next_index.max(i + 1))
},
)
})
.map(|(params, body_nodes, _)| (params, body_nodes))
.unwrap_or_else(|| (Vec::new(), Vec::new()));
let body = self.lower_expr_sequence(&body_nodes);
(params, body)
}
fn lower_record_fields(&self, node: GreenNodeId) -> Vec<crate::ast::RecordField> {
let (mut fields, _) = self
.arena
.children(node)
.map_or((Vec::new(), None), |children| {
children.iter().copied().fold(
(Vec::new(), None),
|(mut fields, mut current), child| {
if let Some(token_index) = self.get_token_index(child)
&& let Some(token) = self.tokens.get(token_index)
&& matches!(token.kind, TokenKind::Ident)
{
current = Some(token.text(self.source).to_symbol());
return (fields, current);
}
if self.arena.kind(child).map(Self::is_expr_kind) == Some(true)
&& let Some(name) = current.take()
{
fields.push(crate::ast::RecordField {
name,
expr: self.lower_expr(child),
});
}
(fields, current)
},
)
});
fields.sort_by(|a, b| a.name.as_ref().cmp(b.name.as_ref()));
fields
}
fn lower_block_statements(&self, node: GreenNodeId) -> Vec<(Statement, Location)> {
self.arena
.children(node)
.into_iter()
.flatten()
.copied()
.filter(|child| self.arena.kind(*child) == Some(SyntaxKind::Statement))
.filter_map(|child| self.lower_statement(child))
.map(|(stmt, span)| {
(
match stmt {
ProgramStatement::GlobalStatement(s) => s,
ProgramStatement::StageDeclaration { stage } => {
Statement::DeclareStage(stage)
}
_ => Statement::Error,
},
self.location_from_span(span),
)
})
.collect()
}
fn lower_pattern(&self, node: GreenNodeId) -> Option<(Pattern, Span)> {
let span = self.node_span(node)?;
let pat = match self.arena.kind(node) {
Some(SyntaxKind::Pattern) => {
if let Some(child) = self.child_patterns(node).into_iter().next() {
return self.lower_pattern(child);
} else {
Pattern::Error
}
}
Some(SyntaxKind::SinglePattern) => {
let name_text = self.text_of_first_token(node).unwrap_or("");
if name_text == "_" {
Pattern::Placeholder
} else {
Pattern::Single(name_text.to_symbol())
}
}
Some(SyntaxKind::TuplePattern) => {
let elems = self
.child_patterns(node)
.into_iter()
.filter_map(|id| self.lower_pattern(id))
.map(|(p, _)| p)
.collect();
Pattern::Tuple(elems)
}
Some(SyntaxKind::RecordPattern) => {
let (items, _) = self
.arena
.children(node)
.map_or((Vec::new(), None), |children| {
children.iter().copied().fold(
(Vec::new(), None),
|(mut items, mut current), child| {
if let Some(token_index) = self.get_token_index(child)
&& let Some(token) = self.tokens.get(token_index)
&& matches!(
token.kind,
TokenKind::Ident | TokenKind::IdentParameter
)
{
current = Some(token.text(self.source).to_symbol());
return (items, current);
}
if self.arena.kind(child).map(Self::is_pattern_kind) == Some(true)
&& let Some((p, _)) = self.lower_pattern(child)
&& let Some(name) = current.take()
{
items.push((name, p));
}
(items, current)
},
)
});
Pattern::Record(items)
}
_ => Pattern::Error,
};
Some((pat, span))
}
fn lower_expr_sequence(&self, nodes: &[GreenNodeId]) -> ExprNodeId {
let result = (0..nodes.len()).try_fold(
(Option::<ExprNodeId>::None, false),
|(acc, skip_next), i| {
if skip_next {
return ControlFlow::Continue((acc, false));
}
let node = nodes[i];
match self.arena.kind(node) {
Some(SyntaxKind::BinaryExpr) => {
ControlFlow::Continue((Some(self.lower_binary(node)), false))
}
Some(SyntaxKind::CallExpr) => {
ControlFlow::Continue((Some(self.lower_call(node)), false))
}
Some(SyntaxKind::FieldAccess) => {
ControlFlow::Continue((Some(self.lower_field_access(node)), false))
}
Some(SyntaxKind::IndexExpr) => {
ControlFlow::Continue((Some(self.lower_index(node)), false))
}
Some(SyntaxKind::AssignExpr) => {
if let Some(lhs) = acc {
let assign = self.lower_assign(lhs, node);
if i + 1 < nodes.len() {
let cont = self.lower_expr_sequence(&nodes[(i + 1)..]);
let loc = self.location_from_span(merge_spans(
assign.to_span(),
cont.to_span(),
));
return ControlFlow::Break(
Expr::Then(assign, Some(cont)).into_id(loc),
);
}
ControlFlow::Continue((Some(assign), false))
} else {
ControlFlow::Continue((Some(self.lower_expr(node)), false))
}
}
Some(_) => {
if let Some(prev) = acc
&& let Expr::Then(first, None) = prev.to_expr()
{
let rhs = self.lower_expr_sequence(&nodes[i..]);
let loc =
self.location_from_span(merge_spans(prev.to_span(), rhs.to_span()));
return ControlFlow::Break(Expr::Then(first, Some(rhs)).into_id(loc));
}
ControlFlow::Continue((Some(self.lower_expr(node)), false))
}
None => ControlFlow::Continue((acc, false)),
}
},
);
match result {
ControlFlow::Break(expr) => expr,
ControlFlow::Continue((acc, _)) => {
acc.unwrap_or_else(|| Expr::Error.into_id_without_span())
}
}
}
fn lower_binary(&self, node: GreenNodeId) -> ExprNodeId {
let (op, op_span) = self
.extract_binary_op(node)
.unwrap_or((Op::Unknown("".to_string()), 0..0));
let expr_children = self.child_exprs(node);
let (lhs, rhs) = if expr_children.len() >= 2 {
let lhs_node = expr_children[0];
let rhs_node = expr_children[expr_children.len() - 1];
(self.lower_expr(lhs_node), self.lower_expr(rhs_node))
} else if expr_children.len() == 1 {
(
Expr::Error.into_id_without_span(),
self.lower_expr(expr_children[0]),
)
} else {
(
Expr::Error.into_id_without_span(),
Expr::Error.into_id_without_span(),
)
};
let loc = self.location_from_span(merge_spans(lhs.to_span(), rhs.to_span()));
Expr::BinOp(lhs, (op, op_span), rhs).into_id(loc)
}
fn lower_call(&self, node: GreenNodeId) -> ExprNodeId {
let expr_children = self.child_exprs(node);
let callee = if !expr_children.is_empty() {
self.lower_expr(expr_children[0])
} else {
Expr::Error.into_id_without_span()
};
let args = self
.lower_arg_list(node)
.into_iter()
.map(Self::unwrap_paren)
.collect();
let call_span = self.node_span(node).unwrap_or_else(|| callee.to_span());
let loc = self.location_from_span(merge_spans(callee.to_span(), call_span));
Expr::Apply(callee, args).into_id(loc)
}
fn lower_field_access(&self, node: GreenNodeId) -> ExprNodeId {
let expr_children = self.child_exprs(node);
let lhs = if !expr_children.is_empty() {
self.lower_expr(expr_children[0])
} else {
Expr::Error.into_id_without_span()
};
let lhs_span = lhs.to_span();
let field_token = self
.arena
.children(node)
.into_iter()
.flatten()
.filter_map(|&child| self.get_token_index(child))
.filter_map(|idx| self.tokens.get(idx))
.filter(|tok| matches!(tok.kind, TokenKind::Ident | TokenKind::Int))
.next_back();
let expr = field_token
.and_then(|tok| match tok.kind {
TokenKind::Ident => Some(Expr::FieldAccess(lhs, tok.text(self.source).to_symbol())),
TokenKind::Int => tok
.text(self.source)
.parse::<i64>()
.ok()
.map(|n| Expr::Proj(lhs, n)),
_ => None,
})
.unwrap_or(Expr::Error);
let span = self
.node_span(node)
.map(|s| merge_spans(lhs_span.clone(), s))
.unwrap_or(lhs_span);
let loc = self.location_from_span(span);
match expr {
Expr::FieldAccess(_, _) | Expr::Proj(_, _) => expr.into_id(loc),
_ => Expr::Error.into_id(loc),
}
}
fn lower_index(&self, node: GreenNodeId) -> ExprNodeId {
let expr_children = self.child_exprs(node);
let (lhs, index) = if expr_children.len() >= 2 {
(
self.lower_expr(expr_children[0]),
self.lower_expr(expr_children[1]),
)
} else if expr_children.len() == 1 {
(
self.lower_expr(expr_children[0]),
Expr::Error.into_id_without_span(),
)
} else {
(
Expr::Error.into_id_without_span(),
Expr::Error.into_id_without_span(),
)
};
let lhs_span = lhs.to_span();
let index_span = index.to_span();
let loc = self.location_from_span(merge_spans(lhs_span, index_span));
Expr::ArrayAccess(lhs, index).into_id(loc)
}
fn lower_assign(&self, lhs: ExprNodeId, node: GreenNodeId) -> ExprNodeId {
let rhs_nodes = self.child_exprs(node);
let rhs = self.lower_expr_sequence(&rhs_nodes);
let loc = self.location_from_span(merge_spans(lhs.to_span(), rhs.to_span()));
Expr::Assign(lhs, rhs).into_id(loc)
}
fn macro_expand_span(&self, node: GreenNodeId, base_span: Span) -> Span {
let bang_end = self
.find_token(node, |kind| matches!(kind, TokenKind::MacroExpand))
.and_then(|idx| self.tokens.get(idx).map(|t| t.end()))
.unwrap_or(base_span.end);
base_span.start..bang_end
}
fn lower_macro_expand(&self, node: GreenNodeId) -> (ExprNodeId, Vec<ExprNodeId>) {
let args = self.lower_arg_list(node);
if let Some(path_node) = self.find_child(node, |kind| kind == SyntaxKind::QualifiedPath)
&& let Some(path) = self.lower_qualified_path(path_node)
{
let path_span = self.node_span(path_node).unwrap_or(0..0);
let loc = self.location_from_span(self.macro_expand_span(node, path_span));
return (Expr::QualifiedVar(path).into_id(loc), args);
}
let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident));
let name_text = name_idx.and_then(|idx| self.token_text(idx)).unwrap_or("");
let name = name_text.to_symbol();
let ident_span = name_idx
.and_then(|idx| self.tokens.get(idx).map(|t| t.start..t.end()))
.unwrap_or(0..0);
let loc = self.location_from_span(self.macro_expand_span(node, ident_span));
(Expr::Var(name).into_id(loc), args)
}
fn lower_param_list(&self, node: GreenNodeId) -> (Vec<TypedId>, Span) {
let params = if let Some(children) = self.arena.children(node) {
let (new_params, _) =
(0..children.len()).fold((Vec::new(), 0usize), |(mut acc, mut next_index), i| {
if i < next_index {
return (acc, next_index);
}
let child = children[i];
if let GreenNode::Token { token_index, .. } = self.arena.get(child)
&& let Some(token) = self.tokens.get(*token_index)
&& matches!(token.kind, TokenKind::Ident | TokenKind::IdentParameter)
{
let name = token.text(self.source).to_symbol();
let loc = self.location_from_span(token.start..token.end());
let mut next = i + 1;
let mut ty = Type::Unknown.into_id_with_location(loc.clone());
let mut default_value = None;
if next < children.len()
&& self.arena.kind(children[next]) == Some(SyntaxKind::TypeAnnotation)
{
if let Some(type_children) = self.arena.children(children[next]) {
ty = type_children
.iter()
.find(|c| {
self.arena
.kind(**c)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.map(|type_node| self.lower_type(*type_node))
.unwrap_or_else(|| {
Type::Unknown.into_id_with_location(loc.clone())
});
}
next += 1;
}
if next < children.len()
&& self.arena.kind(children[next]) == Some(SyntaxKind::ParamDefault)
{
let expr_nodes = self.child_exprs(children[next]);
if !expr_nodes.is_empty() {
default_value = Some(self.lower_expr_sequence(&expr_nodes));
}
next += 1;
}
let tid = match default_value {
Some(default_value) => TypedId::with_default(name, ty, default_value),
None => TypedId::new(name, ty),
};
acc.push(tid);
next_index = next;
}
(acc, next_index.max(i + 1))
});
new_params
} else {
Vec::new()
};
let span = self.node_span(node).unwrap_or(0..0);
(params, span)
}
fn child_exprs(&self, node: GreenNodeId) -> Vec<GreenNodeId> {
self.arena
.children(node)
.into_iter()
.flatten()
.copied()
.filter(|child| self.arena.kind(*child).map(Self::is_expr_kind) == Some(true))
.collect()
}
fn child_patterns(&self, node: GreenNodeId) -> Vec<GreenNodeId> {
self.arena
.children(node)
.into_iter()
.flatten()
.copied()
.filter(|child| self.arena.kind(*child).map(Self::is_pattern_kind) == Some(true))
.collect()
}
fn find_child(
&self,
node: GreenNodeId,
predicate: impl Fn(SyntaxKind) -> bool,
) -> Option<GreenNodeId> {
self.arena
.children(node)?
.iter()
.copied()
.find(|child| match self.arena.kind(*child) {
Some(kind) => predicate(kind),
None => false,
})
}
fn collect_expr_nodes(&self, node: GreenNodeId) -> Vec<GreenNodeId> {
self.arena
.children(node)
.into_iter()
.flatten()
.copied()
.filter(|child| self.arena.kind(*child).map(Self::is_expr_kind) == Some(true))
.collect()
}
fn collect_expr_nodes_after(&self, node: GreenNodeId, after: GreenNodeId) -> Vec<GreenNodeId> {
self.arena
.children(node)
.into_iter()
.flatten()
.copied()
.skip_while(|child| *child != after)
.skip(1)
.filter(|child| self.arena.kind(*child).map(Self::is_expr_kind) == Some(true))
.collect()
}
fn find_token(
&self,
node: GreenNodeId,
predicate: impl Fn(TokenKind) -> bool,
) -> Option<usize> {
self.walk_tokens(node).into_iter().find(|&idx| {
self.tokens
.get(idx)
.map(|t| predicate(t.kind))
.unwrap_or(false)
})
}
fn walk_tokens(&self, node: GreenNodeId) -> Vec<usize> {
match self.arena.get(node) {
GreenNode::Token { token_index, .. } => vec![*token_index],
GreenNode::Internal { children, .. } => children
.iter()
.flat_map(|child| self.walk_tokens(*child))
.collect(),
}
}
fn text_of_first_token(&self, node: GreenNodeId) -> Option<&'a str> {
self.walk_tokens(node)
.into_iter()
.next()
.and_then(|idx| self.token_text(idx))
}
fn text_of_token_node(&self, node: GreenNodeId) -> Option<&'a str> {
match self.arena.get(node) {
GreenNode::Token { token_index, .. } => self.token_text(*token_index),
_ => None,
}
}
fn get_token_index(&self, node: GreenNodeId) -> Option<usize> {
match self.arena.get(node) {
GreenNode::Token { token_index, .. } => Some(*token_index),
_ => None,
}
}
fn token_text(&self, index: usize) -> Option<&'a str> {
self.tokens.get(index).map(|t| t.text(self.source))
}
fn node_span(&self, node: GreenNodeId) -> Option<Span> {
match self.arena.get(node) {
GreenNode::Token { token_index, .. } => {
self.tokens.get(*token_index).map(|t| t.start..t.end())
}
GreenNode::Internal { children, .. } => {
let (start, end) = children
.iter()
.filter_map(|child| self.node_span(*child))
.fold(
(Option::<usize>::None, Option::<usize>::None),
|(start, end), span| {
let next_start = Some(start.map_or(span.start, |s| s.min(span.start)));
let next_end = Some(end.map_or(span.end, |e| e.max(span.end)));
(next_start, next_end)
},
);
start.zip(end).map(|(s, e)| s..e)
}
}
}
fn location_from_span(&self, span: Span) -> Location {
Location {
span,
path: self.file_path.clone(),
}
}
fn lower_arg_list(&self, node: GreenNodeId) -> Vec<ExprNodeId> {
let collect_args = |children: &[GreenNodeId]| {
let (mut args, current) = children.iter().copied().fold(
(Vec::new(), Vec::new()),
|(mut args, mut current), child| {
match self.arena.get(child) {
GreenNode::Token { token_index, .. }
if self.tokens.get(*token_index).map(|t| t.kind)
== Some(TokenKind::Comma) =>
{
if !current.is_empty() {
args.push(self.lower_expr_sequence(¤t));
current.clear();
}
}
_ => {
if self.arena.kind(child).map(Self::is_expr_kind) == Some(true) {
current.push(child);
}
}
}
(args, current)
},
);
if !current.is_empty() {
args.push(self.lower_expr_sequence(¤t));
}
args
};
if let Some(arg_node) = self.find_child(node, |kind| kind == SyntaxKind::ArgList) {
if let Some(children) = self.arena.children(arg_node) {
return collect_args(children);
}
return Vec::new();
}
if let Some(children) = self.arena.children(node) {
return collect_args(children);
}
Vec::new()
}
fn lower_expr_list(&self, node: GreenNodeId) -> Vec<ExprNodeId> {
let collect_elems = |children: &[GreenNodeId]| {
let (mut elems, current) = children.iter().copied().fold(
(Vec::new(), Vec::new()),
|(mut elems, mut current), child| {
match self.arena.get(child) {
GreenNode::Token { token_index, .. }
if self.tokens.get(*token_index).map(|t| t.kind)
== Some(TokenKind::Comma) =>
{
if !current.is_empty() {
elems.push(self.lower_expr_sequence(¤t));
current.clear();
}
}
_ => {
if self.arena.kind(child).map(Self::is_expr_kind) == Some(true) {
current.push(child);
}
}
}
(elems, current)
},
);
if !current.is_empty() {
elems.push(self.lower_expr_sequence(¤t));
}
elems
};
self.arena
.children(node)
.map_or_else(Vec::new, collect_elems)
}
fn is_expr_kind(kind: SyntaxKind) -> bool {
matches!(
kind,
SyntaxKind::BinaryExpr
| SyntaxKind::UnaryExpr
| SyntaxKind::ParenExpr
| SyntaxKind::CallExpr
| SyntaxKind::FieldAccess
| SyntaxKind::IndexExpr
| SyntaxKind::AssignExpr
| SyntaxKind::ArrayExpr
| SyntaxKind::MacroExpansion
| SyntaxKind::BracketExpr
| SyntaxKind::EscapeExpr
| SyntaxKind::LambdaExpr
| SyntaxKind::IfExpr
| SyntaxKind::MatchExpr
| SyntaxKind::BlockExpr
| SyntaxKind::TupleExpr
| SyntaxKind::RecordExpr
| SyntaxKind::IntLiteral
| SyntaxKind::FloatLiteral
| SyntaxKind::StringLiteral
| SyntaxKind::SelfLiteral
| SyntaxKind::NowLiteral
| SyntaxKind::SampleRateLiteral
| SyntaxKind::PlaceHolderLiteral
| SyntaxKind::Identifier
| SyntaxKind::QualifiedPath
)
}
fn is_pattern_kind(kind: SyntaxKind) -> bool {
matches!(
kind,
SyntaxKind::Pattern
| SyntaxKind::SinglePattern
| SyntaxKind::TuplePattern
| SyntaxKind::RecordPattern
)
}
fn extract_unary_op(&self, node: GreenNodeId) -> Option<Op> {
self.walk_tokens(node)
.into_iter()
.find_map(|idx| match self.tokens.get(idx)?.kind {
TokenKind::OpMinus => Some(Op::Minus),
TokenKind::OpSum => Some(Op::Sum),
_ => None,
})
}
fn extract_binary_op(&self, node: GreenNodeId) -> Option<(Op, Span)> {
let children = self.arena.children(node)?;
for &child in children {
if let Some(idx) = self.get_token_index(child)
&& let Some(tok) = self.tokens.get(idx)
{
let op = match tok.kind {
TokenKind::OpSum => Some(Op::Sum),
TokenKind::OpMinus => Some(Op::Minus),
TokenKind::OpProduct => Some(Op::Product),
TokenKind::OpDivide => Some(Op::Divide),
TokenKind::OpEqual => Some(Op::Equal),
TokenKind::OpNotEqual => Some(Op::NotEqual),
TokenKind::OpLessThan => Some(Op::LessThan),
TokenKind::OpLessEqual => Some(Op::LessEqual),
TokenKind::OpGreaterThan => Some(Op::GreaterThan),
TokenKind::OpGreaterEqual => Some(Op::GreaterEqual),
TokenKind::OpModulo => Some(Op::Modulo),
TokenKind::OpExponent => Some(Op::Exponent),
TokenKind::OpAnd => Some(Op::And),
TokenKind::OpOr => Some(Op::Or),
TokenKind::OpAt => Some(Op::At),
TokenKind::OpPipe => Some(Op::Pipe),
TokenKind::OpPipeMacro => Some(Op::PipeMacro),
_ => None,
};
if let Some(op) = op {
return Some((op, tok.start..tok.end()));
}
}
}
None
}
}
pub fn parse_program(source: &str, file_path: PathBuf) -> (Program, Vec<ParserError>) {
let tokens = crate::compiler::parser::tokenize(source);
let preparsed = crate::compiler::parser::preparse(&tokens);
let (root, arena, tokens, errors) = crate::compiler::parser::parse_cst(tokens, &preparsed);
let lowerer = Lowerer::new(source, &tokens, &arena, file_path);
let program = lowerer.lower_program(root);
(program, errors)
}
pub fn parse_to_expr(
source: &str,
file_path: Option<PathBuf>,
) -> (
ExprNodeId,
crate::ast::program::ModuleInfo,
Vec<Box<dyn crate::utils::error::ReportableError>>,
) {
let path = file_path.unwrap_or_default();
let (prog, parse_errs) = parse_program(source, path.clone());
let errs =
crate::compiler::parser::parser_errors_to_reportable(source, path.clone(), parse_errs);
if prog.statements.is_empty() {
return (
Expr::Error.into_id_without_span(),
crate::ast::program::ModuleInfo::new(),
errs,
);
}
let (expr, module_info, mut new_errs) = crate::ast::program::expr_from_program(prog, path);
let mut all_errs = errs;
all_errs.append(&mut new_errs);
(expr, module_info, all_errs)
}
pub fn add_global_context(ast: ExprNodeId, file_path: PathBuf) -> ExprNodeId {
let span = ast.to_span();
let loc = crate::utils::metadata::Location {
span: span.clone(),
path: file_path,
};
let res = Expr::Let(
TypedPattern::new(
Pattern::Single(crate::utils::metadata::GLOBAL_LABEL.to_symbol()),
Type::Unknown.into_id_with_location(loc.clone()),
),
Expr::Lambda(vec![], None, ast).into_id(loc.clone()),
None,
);
res.into_id(loc)
}
impl<'a> Lowerer<'a> {
fn is_type_kind(kind: SyntaxKind) -> bool {
matches!(
kind,
SyntaxKind::PrimitiveType
| SyntaxKind::UnitType
| SyntaxKind::TupleType
| SyntaxKind::RecordType
| SyntaxKind::FunctionType
| SyntaxKind::ArrayType
| SyntaxKind::CodeType
| SyntaxKind::UnionType
| SyntaxKind::TypeIdent
)
}
fn lower_type(&self, node: GreenNodeId) -> crate::interner::TypeNodeId {
use crate::types::{PType, RecordTypeField, Type};
let span = self.node_span(node).unwrap_or(0..0);
let loc = self.location_from_span(span);
match self.arena.kind(node) {
Some(SyntaxKind::PrimitiveType) => {
let text = self.text_of_first_token(node).unwrap_or("float");
let ptype = match text {
"float" => PType::Numeric,
"int" => PType::Int,
"string" => PType::String,
_ => PType::Numeric,
};
Type::Primitive(ptype).into_id_with_location(loc)
}
Some(SyntaxKind::UnitType) => Type::Primitive(PType::Unit).into_id_with_location(loc),
Some(SyntaxKind::TupleType) => {
let elem_types = self
.arena
.children(node)
.into_iter()
.flatten()
.filter(|child| {
self.arena
.kind(**child)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.map(|child| self.lower_type(*child))
.collect::<Vec<_>>();
Type::Tuple(elem_types).into_id_with_location(loc)
}
Some(SyntaxKind::ArrayType) => {
let elem_type = self
.arena
.children(node)
.into_iter()
.flatten()
.find(|child| {
self.arena
.kind(**child)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.map(|child| self.lower_type(*child))
.unwrap_or_else(|| Type::Unknown.into_id_with_location(loc.clone()));
Type::Array(elem_type).into_id_with_location(loc)
}
Some(SyntaxKind::FunctionType) => {
let children: Vec<_> = self
.arena
.children(node)
.into_iter()
.flatten()
.filter(|child| {
self.arena
.kind(**child)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.copied()
.collect();
if children.len() >= 2 {
let lowered = children
.iter()
.map(|child| self.lower_type(*child))
.collect::<Vec<_>>();
let return_type = *lowered
.last()
.unwrap_or(&Type::Unknown.into_id_with_location(loc.clone()));
let param_type = if lowered.len() == 2 {
lowered[0]
} else {
Type::Tuple(lowered[..lowered.len() - 1].to_vec())
.into_id_with_location(loc.clone())
};
Type::Function {
arg: param_type,
ret: return_type,
}
.into_id_with_location(loc)
} else {
Type::Unknown.into_id_with_location(loc)
}
}
Some(SyntaxKind::RecordType) => {
let mut fields = Vec::new();
let mut current_field: Option<Symbol> = None;
if let Some(children) = self.arena.children(node) {
for child in children {
if let Some(token_index) = self.get_token_index(*child)
&& let Some(token) = self.tokens.get(token_index)
&& matches!(token.kind, TokenKind::Ident | TokenKind::IdentParameter)
{
current_field = Some(token.text(self.source).to_symbol());
continue;
}
if self.arena.kind(*child).map(Self::is_type_kind) == Some(true)
&& let Some(name) = current_field.take()
{
fields.push(RecordTypeField::new(name, self.lower_type(*child), false));
}
}
}
Type::Record(fields).into_id_with_location(loc)
}
Some(SyntaxKind::CodeType) => {
let inner = self
.arena
.children(node)
.into_iter()
.flatten()
.find(|child| self.arena.kind(**child).map(Self::is_type_kind) == Some(true))
.map(|child| self.lower_type(*child))
.unwrap_or_else(|| Type::Unknown.into_id_with_location(loc.clone()));
Type::Code(inner).into_id_with_location(loc)
}
Some(SyntaxKind::UnionType) => {
let elem_types = self
.arena
.children(node)
.into_iter()
.flatten()
.filter(|child| {
self.arena
.kind(**child)
.map(Self::is_type_kind)
.unwrap_or(false)
})
.map(|child| self.lower_type(*child))
.collect::<Vec<_>>();
Type::Union(elem_types).into_id_with_location(loc)
}
Some(SyntaxKind::TypeIdent) => {
if let Some(children) = self.arena.children(node) {
let mut path_segments = Vec::new();
for child in children {
if let Some(token_index) = self.get_token_index(*child)
&& let Some(token) = self.tokens.get(token_index)
{
if matches!(token.kind, TokenKind::Ident) {
path_segments.push(token.text(self.source));
}
}
}
if !path_segments.is_empty() {
let type_name = path_segments.join("$").to_symbol();
return Type::TypeAlias(type_name).into_id_with_location(loc);
}
}
Type::Unknown.into_id_with_location(loc)
}
_ => {
if let Some(token_index) = self.get_token_index(node)
&& let Some(token) = self.tokens.get(token_index)
&& matches!(token.kind, TokenKind::Ident)
{
let type_name = token.text(self.source).to_symbol();
Type::TypeAlias(type_name).into_id_with_location(loc)
} else {
Type::Unknown.into_id_with_location(loc)
}
}
}
}
}
fn merge_spans(a: Span, b: Span) -> Span {
a.start.min(b.start)..a.end.max(b.end)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compiler::parser::{parse_cst, preparse, tokenize};
fn parse_source(source: &str) -> Program {
let tokens = tokenize(source);
let preparsed = preparse(&tokens);
let (green_id, arena, tokens, _errors) = parse_cst(tokens, &preparsed);
let lowerer = Lowerer::new(source, &tokens, &arena, PathBuf::new());
lowerer.lower_program(green_id)
}
#[test]
fn test_parse_module_declaration() {
let source = "mod mymod { fn foo() { 42 } }";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::ModuleDefinition {
visibility,
name,
body,
} => {
assert_eq!(*visibility, Visibility::Private);
assert_eq!(name.as_str(), "mymod");
assert!(body.as_ref().map(|b| !b.is_empty()).unwrap_or(false));
}
_ => panic!("Expected ModuleDefinition, got {:?}", stmt),
}
}
#[test]
fn test_parse_pub_module_declaration() {
let source = "pub mod mymod { fn bar() { 1 } }";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::ModuleDefinition {
visibility,
name,
body,
} => {
assert_eq!(*visibility, Visibility::Public);
assert_eq!(name.as_str(), "mymod");
assert!(body.as_ref().map(|b| !b.is_empty()).unwrap_or(false));
}
_ => panic!("Expected ModuleDefinition, got {:?}", stmt),
}
}
#[test]
fn test_parse_use_statement() {
let source = "use modA::funcB";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::UseStatement {
visibility,
path,
target,
} => {
assert_eq!(*visibility, Visibility::Private);
assert_eq!(path.segments.len(), 2);
assert_eq!(path.segments[0].as_str(), "modA");
assert_eq!(path.segments[1].as_str(), "funcB");
assert!(matches!(target, UseTarget::Single));
}
_ => panic!("Expected UseStatement, got {:?}", stmt),
}
}
#[test]
fn test_parse_use_multiple() {
let source = "use modA::{funcB, funcC}";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::UseStatement {
visibility,
path,
target,
} => {
assert_eq!(*visibility, Visibility::Private);
assert_eq!(path.segments.len(), 1);
assert_eq!(path.segments[0].as_str(), "modA");
match target {
UseTarget::Multiple(names) => {
assert_eq!(names.len(), 2);
assert_eq!(names[0].as_str(), "funcB");
assert_eq!(names[1].as_str(), "funcC");
}
_ => panic!("Expected UseTarget::Multiple"),
}
}
_ => panic!("Expected UseStatement, got {:?}", stmt),
}
}
#[test]
fn test_parse_use_wildcard() {
let source = "use modA::*";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::UseStatement {
visibility,
path,
target,
} => {
assert_eq!(*visibility, Visibility::Private);
assert_eq!(path.segments.len(), 1);
assert_eq!(path.segments[0].as_str(), "modA");
assert!(matches!(target, UseTarget::Wildcard));
}
_ => panic!("Expected UseStatement, got {:?}", stmt),
}
}
#[test]
fn test_parse_nested_module() {
let source = "mod outer { mod inner { fn baz() { 0 } } }";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::ModuleDefinition {
visibility,
name,
body,
} => {
assert_eq!(*visibility, Visibility::Private);
assert_eq!(name.as_str(), "outer");
let body = body.as_ref().expect("Expected inline module body");
assert!(!body.is_empty());
let inner_stmt = &body[0].0;
match inner_stmt {
ProgramStatement::ModuleDefinition {
name: inner_name, ..
} => {
assert_eq!(inner_name.as_str(), "inner");
}
_ => panic!("Expected inner ModuleDefinition"),
}
}
_ => panic!("Expected ModuleDefinition, got {:?}", stmt),
}
}
#[test]
fn test_parse_use_with_long_path() {
let source = "use a::b::c::d";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::UseStatement { path, .. } => {
assert_eq!(path.segments.len(), 4);
assert_eq!(path.segments[0].as_str(), "a");
assert_eq!(path.segments[1].as_str(), "b");
assert_eq!(path.segments[2].as_str(), "c");
assert_eq!(path.segments[3].as_str(), "d");
}
_ => panic!("Expected UseStatement, got {:?}", stmt),
}
}
#[test]
fn test_module_with_multiple_items() {
let source = r#"
mod mymod {
pub fn add(x, y) { x + y }
fn private_func() { 42 }
use other_mod
}
"#;
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::ModuleDefinition { body, .. } => {
let body = body.as_ref().expect("Expected inline module body");
assert!(body.len() >= 2);
}
_ => panic!("Expected ModuleDefinition"),
}
}
#[test]
fn test_parse_external_module() {
let source = "mod external_module";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::ModuleDefinition {
visibility,
name,
body,
} => {
assert_eq!(*visibility, Visibility::Private);
assert_eq!(name.as_str(), "external_module");
assert!(body.is_none(), "External module should have None body");
}
_ => panic!("Expected ModuleDefinition, got {:?}", stmt),
}
}
#[test]
fn test_external_module_with_use_generates_correct_ast() {
use crate::ast::program::expr_from_program;
let inline_source = r#"
mod mymod {
pub fn add(x, y) { x + y }
}
use mymod::add
fn dsp() { add(1.0, 2.0) }
"#;
let inline_prog = parse_source(inline_source);
let (expr, module_info, errs) = expr_from_program(inline_prog, PathBuf::from("test.mmm"));
assert!(
module_info.use_alias_map.contains_key(&"add".to_symbol()),
"use_alias_map should contain 'add'"
);
assert_eq!(
module_info.use_alias_map.get(&"add".to_symbol()).copied(),
Some("mymod$add".to_symbol())
);
assert!(
module_info
.visibility_map
.contains_key(&"mymod$add".to_symbol()),
"visibility_map should contain 'mymod$add'"
);
assert!(
*module_info
.visibility_map
.get(&"mymod$add".to_symbol())
.unwrap(),
"mymod$add should be public"
);
let ast_string = format!("{:?}", expr.to_expr());
assert!(
ast_string.contains("mymod$add"),
"AST should contain mymod$add, but got: {}",
ast_string
);
}
#[test]
fn test_parse_pub_use() {
let source = "pub use modA::funcB";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::UseStatement {
visibility,
path,
target,
} => {
assert_eq!(*visibility, Visibility::Public);
assert_eq!(path.segments.len(), 2);
assert_eq!(path.segments[0].as_str(), "modA");
assert_eq!(path.segments[1].as_str(), "funcB");
assert!(matches!(target, UseTarget::Single));
}
_ => panic!("Expected UseStatement, got {:?}", stmt),
}
}
#[test]
fn test_parse_match_expr() {
let source = "fn test_match(num) { match num { 0 => 100, 1 => 200, _ => 300 } }";
let prog = parse_source(source);
assert!(!prog.statements.is_empty());
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::FnDefinition { body, .. } => {
let body_expr = body.to_expr();
match body_expr {
Expr::Match(scrutinee, arms) => {
match scrutinee.to_expr() {
Expr::Var(name) => assert_eq!(name.as_str(), "num"),
other => panic!("Expected Var, got {other:?}"),
}
assert_eq!(arms.len(), 3);
match &arms[0].pattern {
crate::ast::MatchPattern::Literal(crate::ast::Literal::Int(0)) => {}
other => panic!("Expected pattern Literal(Int(0)), got {:?}", other),
}
match arms[0].body.to_expr() {
Expr::Literal(crate::ast::Literal::Float(s)) => {
assert_eq!(s.as_str(), "100");
}
other => panic!("Expected body Literal(Float(100)), got {:?}", other),
}
match &arms[1].pattern {
crate::ast::MatchPattern::Literal(crate::ast::Literal::Int(1)) => {}
other => panic!("Expected pattern Literal(Int(1)), got {:?}", other),
}
match arms[1].body.to_expr() {
Expr::Literal(crate::ast::Literal::Float(s)) => {
assert_eq!(s.as_str(), "200");
}
other => panic!("Expected body Literal(Float(200)), got {:?}", other),
}
match &arms[2].pattern {
crate::ast::MatchPattern::Wildcard => {}
other => panic!("Expected Wildcard, got {:?}", other),
}
match arms[2].body.to_expr() {
Expr::Literal(crate::ast::Literal::Float(s)) => {
assert_eq!(s.as_str(), "300");
}
other => panic!("Expected body Literal(Float(300)), got {:?}", other),
}
}
other => panic!("Expected Match expr, got {:?}", other),
}
}
_ => panic!("Expected FnDefinition, got {:?}", stmt),
}
}
#[test]
fn test_parse_pipe_and_macro_pipe_share_precedence_and_associate_left() {
let source = "fn test(a, b, c) { a ||> b |> c }";
let prog = parse_source(source);
let stmt = &prog.statements[0].0;
match stmt {
ProgramStatement::FnDefinition { body, .. } => match body.to_expr() {
Expr::BinOp(lhs, (Op::Pipe, _), rhs) => {
assert!(matches!(rhs.to_expr(), Expr::Var(name) if name.as_str() == "c"));
match lhs.to_expr() {
Expr::BinOp(inner_lhs, (Op::PipeMacro, _), inner_rhs) => {
assert!(matches!(
inner_lhs.to_expr(),
Expr::Var(name) if name.as_str() == "a"
));
assert!(matches!(
inner_rhs.to_expr(),
Expr::Var(name) if name.as_str() == "b"
));
}
other => panic!("Expected nested PipeMacro on LHS, got {other:?}"),
}
}
other => panic!("Expected top-level Pipe, got {other:?}"),
},
_ => panic!("Expected FnDefinition, got {stmt:?}"),
}
}
}