use crate::try_parse;
use dynamic::{Dynamic, Type};
use super::{Expr, Parser, Pattern, Span, expr::ExprKind, pattern::PatternKind};
use anyhow::{Result, anyhow};
use smol_str::SmolStr;
fn collect_bindings(pat: &Pattern, access: &Expr, out: &mut Vec<Stmt>, span: Span) {
use crate::expr::BinaryOp;
let mk_let_ident = |name: SmolStr, value: Expr, span: Span| {
let p = Pattern::new(PatternKind::Ident { name, ty: Type::Any }, span);
let value_stmt = Stmt::new(StmtKind::Expr(value, true), span);
Stmt::new(StmtKind::Let { pat: p, value: Box::new(value_stmt) }, span)
};
let mk_bin = |left: Expr, op: BinaryOp, right: Expr, span: Span| Expr::new(ExprKind::Binary { left: Box::new(left), op, right: Box::new(right) }, span);
let mk_idx_int = |obj: Expr, i: i32, span: Span| mk_bin(obj, BinaryOp::Idx, Expr::new(ExprKind::Value(Dynamic::I32(i)), span), span);
let mk_field = |obj: Expr, name: SmolStr, span: Span| mk_bin(obj, BinaryOp::Idx, Expr::new(ExprKind::Value(Dynamic::String(name)), span), span);
match &pat.kind {
PatternKind::Ident { name, .. } => {
out.push(mk_let_ident(name.clone(), access.clone(), span));
}
PatternKind::Tuple(items) => {
for (i, p) in items.iter().enumerate() {
let elem = mk_idx_int(access.clone(), i as i32, span);
collect_bindings(p, &elem, out, span);
}
}
PatternKind::List { elems, has_rest } => {
let prefix = if *has_rest { elems.len() - 1 } else { elems.len() };
for (i, p) in elems.iter().take(prefix).enumerate() {
let elem = mk_idx_int(access.clone(), i as i32, span);
collect_bindings(p, &elem, out, span);
}
if *has_rest {
if let PatternKind::Ident { name, .. } = &elems.last().unwrap().kind {
let from = Expr::new(ExprKind::Value((prefix as u32).into()), span);
let range = Expr::new(ExprKind::Range { start: Box::new(from), stop: Box::new(Expr::new(ExprKind::Value(Dynamic::Null), span)), inclusive: false }, span);
let slice = Expr::new(ExprKind::Binary { left: Box::new(access.clone()), op: BinaryOp::Idx, right: Box::new(range) }, span);
out.push(mk_let_ident(name.clone(), slice, span));
}
}
}
PatternKind::Struct { fields, .. } => {
for (fname, sub) in fields {
let field_access = mk_field(access.clone(), fname.clone(), span);
match sub {
None => {
out.push(mk_let_ident(fname.clone(), field_access, span));
}
Some(sub_pat) => {
collect_bindings(sub_pat, &field_access, out, span);
}
}
}
}
_ => {}
}
}
#[derive(Debug, Clone)]
pub struct Stmt {
pub kind: StmtKind,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum StmtKind {
Let { pat: Pattern, value: Box<Stmt> },
Expr(Expr, bool),
Block(Vec<Stmt>),
Break,
Continue,
Return(Option<Expr>),
While { cond: Expr, body: Box<Stmt> },
Loop(Box<Stmt>),
For { pat: Pattern, range: Expr, body: Box<Stmt> },
Fn { name: SmolStr, generic_params: Vec<Type>, args: Vec<(SmolStr, Type)>, body: Box<Stmt>, is_pub: bool },
Struct { name: SmolStr, def: Type, is_pub: bool },
Impl { target: Type, body: Box<Stmt> },
If { cond: Expr, then_body: Box<Stmt>, else_body: Option<Box<Stmt>> },
Static { name: SmolStr, ty: Type, value: Option<Expr>, is_pub: bool },
Const { name: SmolStr, ty: Type, value: Expr, is_pub: bool },
}
impl Stmt {
pub fn new(kind: StmtKind, span: Span) -> Self {
Self { kind, span }
}
pub fn expr(&self) -> Option<Expr> {
if let StmtKind::Expr(expr, _) = &self.kind { Some(expr.clone()) } else { None }
}
pub fn is_return(&self) -> bool {
matches!(self.kind, StmtKind::Return(_))
}
pub fn last_return(&mut self) -> bool {
match &mut self.kind {
StmtKind::Block(stmts) => stmts.last_mut().map(|stmt| stmt.last_return()).unwrap_or(false),
StmtKind::If { then_body, else_body, .. } => {
let then_returns = then_body.last_return();
let else_returns = else_body.as_mut().map(|body| body.last_return()).unwrap_or(false);
then_returns && else_returns
}
StmtKind::Expr(e, close) => {
if !*close {
let span = e.span;
*self = Self::new(StmtKind::Return(Some(std::mem::take(e))), span);
true
} else {
false
}
}
StmtKind::Return(_) => true,
_ => false,
}
}
pub fn get_type(&self) -> Option<Type> {
match &self.kind {
StmtKind::Expr(expr, _) => Some(expr.get_type()),
StmtKind::Block(stmts) => stmts.last().and_then(|stmt| stmt.get_type()),
StmtKind::If { then_body, .. } => then_body.get_type(),
_ => None,
}
}
fn get_assign(idx: u32, expr: Expr) -> Self {
let span = expr.span;
Self::new(StmtKind::Expr(Expr::new(ExprKind::Binary { left: Box::new(Expr::new(ExprKind::Var(idx), span)), op: crate::BinaryOp::Assign, right: Box::new(expr) }, span), true), span)
}
fn get_idx_assign(pat: Expr, idx: usize, expr: Expr) -> Self {
let span = pat.span.merge(expr.span);
let right = Expr::new(ExprKind::Binary { left: Box::new(expr), op: crate::BinaryOp::Idx, right: Box::new(Expr::new(ExprKind::Value((idx as u32).into()), span)) }, span);
Self::new(StmtKind::Expr(Expr::new(ExprKind::Binary { left: Box::new(pat), op: crate::BinaryOp::Assign, right: Box::new(right) }, span), true), span)
}
fn get_assign_expr(pat: Expr, expr: Expr) -> Self {
let span = pat.span.merge(expr.span);
Self::new(StmtKind::Expr(Expr::new(ExprKind::Binary { left: Box::new(pat), op: crate::BinaryOp::Assign, right: Box::new(expr) }, span), true), span)
}
pub fn bind_pattern(&mut self, pat: Pattern) -> Result<()> {
if let Some(expr) = self.expr() {
let stmt = match pat.kind {
PatternKind::Var { idx, ty } => {
if expr.get_type() != ty {
Self::get_assign(idx, Expr::new(ExprKind::Typed { value: Box::new(expr), ty }, pat.span))
} else {
Self::get_assign(idx, expr)
}
}
PatternKind::Tuple(list) => {
let mut stmts = Vec::new();
for (idx, p) in list.into_iter().enumerate() {
match p.expr() {
Ok(p) => stmts.push(Self::get_idx_assign(p, idx, expr.clone())),
Err(e) => return Err(e),
}
}
Self::new(StmtKind::Block(stmts), self.span)
}
PatternKind::List { elems, has_rest } => {
let mut stmts = Vec::new();
let prefix_count = if has_rest { elems.len() - 1 } else { elems.len() };
for (idx, p) in elems.iter().take(prefix_count).enumerate() {
match p.expr() {
Ok(p) => stmts.push(Self::get_idx_assign(p, idx, expr.clone())),
Err(e) => return Err(e),
}
}
if has_rest {
let rest_pat = elems.last().unwrap();
let rest_expr = match &rest_pat.kind {
PatternKind::Ident { name, .. } => Expr::new(ExprKind::Ident(name.clone()), rest_pat.span),
PatternKind::Var { idx, .. } => Expr::new(ExprKind::Var(*idx), rest_pat.span),
_ => return Err(anyhow!("..rest 后的模式必须是标识符")),
};
let from = Expr::new(ExprKind::Value((prefix_count as u32).into()), rest_pat.span);
let range = Expr::new(ExprKind::Range { start: Box::new(from), stop: Box::new(Expr::new(ExprKind::Value(Dynamic::Null), rest_pat.span)), inclusive: false }, rest_pat.span);
let slice_idx = Expr::new(ExprKind::Binary { left: Box::new(expr.clone()), op: crate::BinaryOp::Idx, right: Box::new(range) }, rest_pat.span);
stmts.push(Self::get_assign_expr(rest_expr, slice_idx));
}
Self::new(StmtKind::Block(stmts), self.span)
}
p => return Err(anyhow!("不支持的模式绑定: {:?}", p)),
};
let _ = std::mem::replace(self, stmt);
} else {
match &mut self.kind {
StmtKind::Block(stmts) => {
if let Some(stmt) = stmts.last_mut() {
stmt.bind_pattern(pat)?;
}
}
StmtKind::If { then_body, else_body, .. } => {
then_body.bind_pattern(pat.clone())?;
if let Some(e) = else_body {
e.bind_pattern(pat)?;
}
}
_ => {}
}
}
Ok(())
}
}
use std::fmt;
impl fmt::Display for Stmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
StmtKind::Let { pat, value } => writeln!(f, "let {:?} = {}", pat, value)?,
StmtKind::Block(stmts) => stmts.iter().for_each(|s| {
let _ = write!(f, "{}", s);
}),
StmtKind::Expr(expr, close) => writeln!(f, "{:?}[{}]", expr, close)?,
StmtKind::Break => writeln!(f, "break")?,
StmtKind::Continue => writeln!(f, "continue")?,
StmtKind::Return(r) => writeln!(f, "return {:?}", r)?,
StmtKind::While { cond, body } => write!(f, "while {:?}\n{}", cond, body)?,
StmtKind::Loop(body) => write!(f, "loop\n{}", body)?,
StmtKind::For { pat, range, body } => writeln!(f, "for {:?} in {:?} \n{}", pat, range, body)?,
StmtKind::If { cond, then_body, else_body } => {
write!(f, "if {:?}\nthen-> {}\n", cond, then_body)?;
if let Some(e) = else_body {
writeln!(f, "{}", e)?;
}
}
StmtKind::Fn { name, generic_params, args, body, is_pub } => {
let generic_suffix = if generic_params.is_empty() { String::new() } else { format!("<{:?}>", generic_params) };
if *is_pub {
write!(f, "pub fn {:?}{} {:?}\n", name, generic_suffix, args)?
} else {
write!(f, "fn {:?}{} {:?}\n", name, generic_suffix, args)?
}
write!(f, "{}", body)?;
}
_ => write!(f, "(todo display: {:?})", self.kind)?,
}
fmt::Result::Ok(())
}
}
impl Parser {
pub fn ident_typed(&mut self) -> Result<(SmolStr, Type)> {
let name = self.ident()?;
self.whitespace()?;
if self.take(b':').is_ok() { Ok((name, self.get_type()?)) } else { Ok((name, Type::Any)) }
}
pub fn ident_generic(&mut self) -> Result<(SmolStr, Vec<Type>)> {
self.whitespace()?;
let name = self.ident()?;
self.whitespace()?;
let params = if self.get()? == b'<' {
self.pos += 1;
crate::parse_list!(self, Vec::new(), b'>', b',', self.get_type_param()?)
} else {
Vec::new()
};
Ok((name, params))
}
pub fn block(&mut self) -> Result<Stmt> {
self.check_fatal()?;
self.whitespace()?;
let start = self.current_pos();
if self.get()? == b'{' {
self.pos += 1;
self.enter_depth()?;
self.push_decl_scope();
let result = (|| -> Result<Stmt> {
let body = crate::parse_list!(self, Vec::new(), b'}', 0, self.stmt(false)?);
Ok(Stmt::new(StmtKind::Block(body), self.span_from(start)))
})();
self.pop_decl_scope();
self.exit_depth();
result
} else {
Err(anyhow!("not code block"))
}
}
pub fn if_block(&mut self) -> Result<Stmt> {
let start = self.spans.last().copied().unwrap_or_else(|| self.current_pos());
let cond = self.get_expr_without_struct_literal()?;
let then_body = Box::new(self.block()?);
self.whitespace()?;
let else_body = if self.keyword("else").is_ok() {
self.whitespace()?;
let body = if self.keyword("if").is_ok() { self.if_block()? } else { self.block()? };
Some(Box::new(body))
} else {
None
};
Ok(Stmt::new(StmtKind::If { cond, then_body, else_body }, Span::new(start, self.current_pos())))
}
pub fn match_block(&mut self, start: usize) -> Result<Stmt> {
use crate::expr::{BinaryOp, ExprKind, UnaryOp};
use crate::pattern::{Pattern, PatternKind};
let scrut = self.get_expr_without_struct_literal()?;
self.whitespace()?;
self.take(b'{').map_err(|_| anyhow!("match 缺少 `{{`"))?;
let mut arms: Vec<(Vec<Pattern>, Option<Expr>, Expr)> = Vec::new();
loop {
self.whitespace()?;
if self.take(b'}').is_ok() {
break;
}
self.push_decl_scope();
let arm_res: Result<(Vec<Pattern>, Option<Expr>, Expr)> = (|| {
let mut pats = vec![self.pattern()?];
self.whitespace()?;
while matches!(self.get(), Ok(b'|')) && !matches!(self.ahead(), Ok(b'|')) {
self.pos += 1;
self.whitespace()?;
pats.push(self.pattern()?);
self.whitespace()?;
}
for pat in &pats[..1] {
self.declare_pattern_symbols(pat)?;
}
if pats.len() > 1 {
for p in &pats[1..] {
if !matches!(p.kind, PatternKind::Literal(_) | PatternKind::Wildcard) {
return Err(anyhow!("or-pattern 中除第一个外只支持字面量 / 通配模式 (MVP 限制)"));
}
}
}
self.whitespace()?;
let guard = if self.keyword("if").is_ok() {
self.whitespace()?;
Some(self.get_expr_no_assign()?)
} else {
None
};
self.whitespace()?;
self.just("=>").map_err(|_| anyhow!("match arm 缺少 `=>`"))?;
self.whitespace()?;
let body_start = self.current_pos();
let body = if self.get()? == b'{' {
let block_stmt = self.block()?;
Expr::new(ExprKind::Stmt(Box::new(block_stmt)), Span::new(body_start, self.current_pos()))
} else {
self.get_expr_no_assign()?
};
self.whitespace()?;
let _ = self.take(b',');
Ok((pats, guard, body))
})();
self.pop_decl_scope();
arms.push(arm_res?);
}
let span = Span::new(start, self.current_pos());
let suffix = self.match_counter;
self.match_counter += 1;
let scrut_name: SmolStr = format!("__m_scrut_{}", suffix).into();
let done_name: SmolStr = format!("__m_done_{}", suffix).into();
let out_name: SmolStr = format!("__m_out_{}", suffix).into();
self.declare_symbol_in_current_scope(&scrut_name)?;
self.declare_symbol_in_current_scope(&done_name)?;
self.declare_symbol_in_current_scope(&out_name)?;
let mk_ident = |name: &SmolStr, span: Span| Expr::new(ExprKind::Ident(name.clone()), span);
let mk_value = |v: Dynamic, span: Span| Expr::new(ExprKind::Value(v), span);
let mk_binary = |left: Expr, op: BinaryOp, right: Expr, span: Span| Expr::new(ExprKind::Binary { left: Box::new(left), op, right: Box::new(right) }, span);
let mk_let = |name: SmolStr, value: Expr, span: Span| {
let pat = Pattern::new(PatternKind::Ident { name, ty: Type::Any }, span);
let value_stmt = Stmt::new(StmtKind::Expr(value, true), span);
Stmt::new(StmtKind::Let { pat, value: Box::new(value_stmt) }, span)
};
let mk_assign_stmt = |name: &SmolStr, value: Expr, span: Span| {
let assign = mk_binary(mk_ident(name, span), BinaryOp::Assign, value, span);
Stmt::new(StmtKind::Expr(assign, true), span)
};
let mut stmts = Vec::new();
stmts.push(mk_let(scrut_name.clone(), scrut, span));
stmts.push(mk_let(done_name.clone(), mk_value(Dynamic::Bool(false), span), span));
stmts.push(mk_let(out_name.clone(), mk_value(Dynamic::Null, span), span));
for (pats, guard, body) in arms {
let arm_span = body.span;
let scrut_ident = mk_ident(&scrut_name, arm_span);
let mut test_chain = pats[0].match_test(&scrut_ident);
for p in &pats[1..] {
let t = p.match_test(&scrut_ident);
test_chain = mk_binary(test_chain, BinaryOp::Or, t, arm_span);
}
let not_done = Expr::new(ExprKind::Unary { op: UnaryOp::Not, value: Box::new(mk_ident(&done_name, arm_span)) }, arm_span);
let outer_cond = mk_binary(not_done, BinaryOp::And, test_chain, arm_span);
let mut body_stmts = Vec::new();
collect_bindings(&pats[0], &mk_ident(&scrut_name, arm_span), &mut body_stmts, arm_span);
let inner_stmts = vec![mk_assign_stmt(&out_name, body, arm_span), mk_assign_stmt(&done_name, mk_value(Dynamic::Bool(true), arm_span), arm_span)];
let inner_block = Stmt::new(StmtKind::Block(inner_stmts), arm_span);
if let Some(g) = guard {
body_stmts.push(Stmt::new(StmtKind::If { cond: g, then_body: Box::new(inner_block), else_body: None }, arm_span));
} else {
body_stmts.push(inner_block);
}
let then_body = Stmt::new(StmtKind::Block(body_stmts), arm_span);
stmts.push(Stmt::new(StmtKind::If { cond: outer_cond, then_body: Box::new(then_body), else_body: None }, arm_span));
}
stmts.push(Stmt::new(StmtKind::Expr(mk_ident(&out_name, span), false), span));
Ok(Stmt::new(StmtKind::Block(stmts), span))
}
pub fn stmt(&mut self, is_pub: bool) -> Result<Stmt> {
self.check_fatal()?;
self.whitespace()?;
self.spans.push(self.pos);
let start = self.current_pos();
if self.fn_body_depth > 0 {
for kw in &["fn", "struct", "impl", "const", "static"] {
if self.keyword(kw).is_ok() {
return Err(anyhow!("函数体内不能定义 {};请移到顶层或改用闭包", kw));
}
}
}
if self.impl_body_depth > 0 {
for kw in &["struct", "impl", "const", "static"] {
if self.keyword(kw).is_ok() {
return Err(anyhow!("impl 体内不能定义 {};请移到顶层", kw));
}
}
}
let stmt = if self.keyword("let").is_ok() {
let pat = self.pattern()?;
self.declare_pattern_symbols(&pat)?;
self.until(b'=')?;
self.whitespace()?;
let value = if self.get()? == b'{' {
if self.looks_like_dict() {
self.get_expr()?
} else {
let span = self.current_pos();
let block_stmt = self.block()?;
Expr::new(ExprKind::Stmt(Box::new(block_stmt)), Span::new(span, self.current_pos()))
}
} else {
self.get_expr()?
};
self.whitespace()?;
let close = self.take(b';').is_ok();
let stmt = Stmt::new(StmtKind::Expr(value, close), Span::new(start, self.current_pos()));
Stmt::new(StmtKind::Let { pat, value: Box::new(stmt) }, Span::new(start, self.current_pos()))
} else if self.keyword("break").is_ok() {
self.until(b';')?;
Stmt::new(StmtKind::Break, Span::new(start, self.current_pos()))
} else if self.keyword("continue").is_ok() {
self.until(b';')?;
Stmt::new(StmtKind::Continue, Span::new(start, self.current_pos()))
} else if self.keyword("return").is_ok() {
self.whitespace()?;
let expr = if matches!(self.get(), Ok(b';' | b'}')) { None } else { Some(self.get_expr()?) };
self.whitespace()?;
if self.take(b';').is_err() && !matches!(self.get(), Ok(b'}')) {
self.until(b';')?;
}
Stmt::new(StmtKind::Return(expr), Span::new(start, self.current_pos()))
} else if self.keyword("if").is_ok() {
self.if_block()?
} else if self.keyword("loop").is_ok() {
Stmt::new(StmtKind::Loop(Box::new(self.block()?)), Span::new(start, self.current_pos()))
} else if self.keyword("while").is_ok() {
self.whitespace()?;
let cond = self.get_expr()?;
let body = Box::new(self.block()?);
Stmt::new(StmtKind::While { cond, body }, Span::new(start, self.current_pos()))
} else if self.keyword("for").is_ok() {
self.whitespace()?;
let pat = self.pattern()?;
self.whitespace()?;
self.keyword("in")?;
self.whitespace()?;
let range = self.get_expr()?;
self.push_decl_scope();
let result: Result<Stmt> = (|| {
self.declare_pattern_symbols(&pat)?;
let body = Box::new(self.block()?);
Ok(Stmt::new(StmtKind::For { pat, range, body }, Span::new(start, self.current_pos())))
})();
self.pop_decl_scope();
result?
} else if self.keyword("fn").is_ok() {
self.whitespace()?;
let (name, generic_params) = self.ident_generic()?;
self.declare_function_name(&name)?;
self.until(b'(')?;
let args = crate::parse_list!(self, Vec::new(), b')', b',', self.ident_typed()?);
let body = Box::new(self.function_body(&args)?);
Stmt::new(StmtKind::Fn { name, generic_params, args, body, is_pub }, Span::new(start, self.current_pos()))
} else if self.keyword("struct").is_ok() {
let (name, params) = self.ident_generic()?;
self.declare_symbol(&name)?;
if self.until(b'{').is_ok() {
let fields = crate::parse_list!(self, Vec::new(), b'}', b',', self.ident_typed()?);
if let Some(f) = fields.iter().find(|f| f.1.is_any()) {
return Err(anyhow!("字段 {} 的类型未知", f.0));
}
Stmt::new(StmtKind::Struct { name, def: Type::Struct { params, fields }, is_pub }, Span::new(start, self.current_pos()))
} else {
self.until(b';')?;
Stmt::new(StmtKind::Struct { name, def: Type::Struct { params, fields: Vec::new() }, is_pub }, Span::new(start, self.current_pos()))
}
} else if self.keyword("const").is_ok() {
self.whitespace()?;
let (name, ty) = self.ident_typed()?;
self.declare_symbol(&name)?;
self.until(b'=')?;
let value = self.get_expr()?;
self.until(b';')?;
Stmt::new(StmtKind::Const { name, ty, value, is_pub }, Span::new(start, self.current_pos()))
} else if self.keyword("static").is_ok() {
self.whitespace()?;
let (name, ty) = self.ident_typed()?;
self.declare_symbol(&name)?;
self.whitespace()?;
if self.take(b'=').is_ok() {
let expr = self.get_expr()?;
self.until(b';')?;
Stmt::new(StmtKind::Static { name, ty, value: Some(expr), is_pub }, Span::new(start, self.current_pos()))
} else {
self.until(b';')?;
Stmt::new(StmtKind::Static { name, ty, value: None, is_pub }, Span::new(start, self.current_pos()))
}
} else if self.keyword("impl").is_ok() {
self.whitespace()?;
let target = self.get_type()?;
Stmt::new(StmtKind::Impl { target, body: Box::new(self.impl_body()?) }, Span::new(start, self.current_pos()))
} else if self.keyword("pub").is_ok() {
self.stmt(true)?
} else {
let expr = if self.get()? == b'{' {
if self.looks_like_empty_dict() {
self.dict()?
} else if let Ok(block) = try_parse!(self, self.block()) {
let _ = self.spans.pop();
return Ok(block);
} else if let Ok(dict) = try_parse!(self, self.dict()) {
dict
} else {
let block = self.block()?;
let _ = self.spans.pop();
return Ok(block);
}
} else {
self.get_expr()?
};
self.whitespace()?;
if self.is_eof() {
Stmt::new(StmtKind::Expr(expr, false), Span::new(start, self.current_pos()))
} else if self.get()? == b';' {
self.pos += 1;
Stmt::new(StmtKind::Expr(expr, true), Span::new(start, self.current_pos()))
} else if self.get()? == b'}' {
Stmt::new(StmtKind::Expr(expr, false), Span::new(start, self.current_pos()))
} else {
return Err(anyhow!("未结束的表达式"));
}
};
let _ = self.spans.pop();
Ok(stmt)
}
}