From f9d03e75096e6a2fd289686226256a0b188167bb Mon Sep 17 00:00:00 2001 From: Teru Shigure Date: Fri, 30 Jan 2026 09:11:50 +0800 Subject: [PATCH 1/2] feat: create crates related to IR --- Cargo.lock | 16 ++++++++++++++++ Cargo.toml | 4 ++++ air/Cargo.toml | 7 +++++++ air/src/lib.rs | 0 analyzer/Cargo.toml | 7 +++++++ analyzer/src/lib.rs | 0 hir/Cargo.toml | 7 +++++++ hir/src/lib.rs | 0 lowerer/Cargo.toml | 7 +++++++ lowerer/src/lib.rs | 0 10 files changed, 48 insertions(+) create mode 100644 air/Cargo.toml create mode 100644 air/src/lib.rs create mode 100644 analyzer/Cargo.toml create mode 100644 analyzer/src/lib.rs create mode 100644 hir/Cargo.toml create mode 100644 hir/src/lib.rs create mode 100644 lowerer/Cargo.toml create mode 100644 lowerer/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6c550ae..5d1ab21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,14 @@ dependencies = [ "memchr", ] +[[package]] +name = "air" +version = "0.0.1" + +[[package]] +name = "analyzer" +version = "0.0.1" + [[package]] name = "annotate-snippets" version = "0.12.11" @@ -257,6 +265,10 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "hir" +version = "0.0.1" + [[package]] name = "ignore" version = "0.4.25" @@ -341,6 +353,10 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lowerer" +version = "0.0.1" + [[package]] name = "macros" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index b5accde..e6443a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,12 @@ members = [ "diag", "token", "syntax", + "hir", + "air", "lexer", "parser", + "lowerer", + "analyzer", "macros", "autofront", ] diff --git a/air/Cargo.toml b/air/Cargo.toml new file mode 100644 index 0000000..12ad5c4 --- /dev/null +++ b/air/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "air" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] diff --git a/air/src/lib.rs b/air/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/analyzer/Cargo.toml b/analyzer/Cargo.toml new file mode 100644 index 0000000..536d745 --- /dev/null +++ b/analyzer/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "analyzer" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] diff --git a/analyzer/src/lib.rs b/analyzer/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/hir/Cargo.toml b/hir/Cargo.toml new file mode 100644 index 0000000..ade17a0 --- /dev/null +++ b/hir/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "hir" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] diff --git a/hir/src/lib.rs b/hir/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/lowerer/Cargo.toml b/lowerer/Cargo.toml new file mode 100644 index 0000000..ccf321e --- /dev/null +++ b/lowerer/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "lowerer" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] diff --git a/lowerer/src/lib.rs b/lowerer/src/lib.rs new file mode 100644 index 0000000..e69de29 From a4801b9adada5b3c893ef4fa89b5a1955879c34e Mon Sep 17 00:00:00 2001 From: Teru Shigure Date: Sun, 1 Feb 2026 03:31:08 +0800 Subject: [PATCH 2/2] feat(hir): hir definitions and lowerer implement --- Cargo.lock | 22 + autofront/Cargo.toml | 1 + autofront/src/cli/cmd.rs | 1 + autofront/src/cli/help.rs | 5 + autofront/src/cli/parser.rs | 3 +- autofront/src/driver.rs | 29 + hir/Cargo.toml | 3 + hir/src/id.rs | 35 + hir/src/lib.rs | 17 + hir/src/node.rs | 84 ++ hir/src/node/constant.rs | 21 + hir/src/node/expr.rs | 188 ++++ hir/src/node/ty.rs | 37 + hir/src/print.rs | 649 ++++++++++++ lowerer/Cargo.toml | 8 + lowerer/locale/en-US/lowerer.ftl | 11 + lowerer/locale/zh-CN/lowerer.ftl | 11 + lowerer/src/context.rs | 261 +++++ lowerer/src/errors.rs | 243 +++++ lowerer/src/lib.rs | 17 + lowerer/src/lower.rs | 1570 ++++++++++++++++++++++++++++++ lowerer/src/symbol.rs | 170 ++++ parser/src/syntax/parse.rs | 13 +- syntax/src/ast.rs | 14 +- syntax/src/span.rs | 20 +- syntax/src/token/group.rs | 12 + 26 files changed, 3432 insertions(+), 13 deletions(-) create mode 100644 hir/src/id.rs create mode 100644 hir/src/node.rs create mode 100644 hir/src/node/constant.rs create mode 100644 hir/src/node/expr.rs create mode 100644 hir/src/node/ty.rs create mode 100644 hir/src/print.rs create mode 100644 lowerer/locale/en-US/lowerer.ftl create mode 100644 lowerer/locale/zh-CN/lowerer.ftl create mode 100644 lowerer/src/context.rs create mode 100644 lowerer/src/errors.rs create mode 100644 lowerer/src/lower.rs create mode 100644 lowerer/src/symbol.rs diff --git a/Cargo.lock b/Cargo.lock index 5d1ab21..d555bf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,10 +94,17 @@ dependencies = [ "diag", "lexer", "locale", + "lowerer", "parser", "unicode-width", ] +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bstr" version = "1.12.1" @@ -268,6 +275,11 @@ dependencies = [ [[package]] name = "hir" version = "0.0.1" +dependencies = [ + "bimap", + "common", + "num-bigint", +] [[package]] name = "ignore" @@ -356,6 +368,16 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lowerer" version = "0.0.1" +dependencies = [ + "annotate-snippets", + "common", + "diag", + "hir", + "locale", + "num-bigint", + "num-traits", + "syntax", +] [[package]] name = "macros" diff --git a/autofront/Cargo.toml b/autofront/Cargo.toml index fedbba9..444a3c5 100644 --- a/autofront/Cargo.toml +++ b/autofront/Cargo.toml @@ -12,5 +12,6 @@ locale = { path = "../locale" } diag = { path = "../diag" } lexer = { path = "../lexer" } parser = { path = "../parser" } +lowerer = { path = "../lowerer" } anstream = "0.6.21" unicode-width = "0.2.2" diff --git a/autofront/src/cli/cmd.rs b/autofront/src/cli/cmd.rs index d7b6b5d..1750bc0 100644 --- a/autofront/src/cli/cmd.rs +++ b/autofront/src/cli/cmd.rs @@ -17,6 +17,7 @@ pub enum Command { Lex(DebugSubcommand), Tt(DebugSubcommand), Parse(DebugSubcommand), + Hir(DebugSubcommand), } #[derive(Debug, Clone)] diff --git a/autofront/src/cli/help.rs b/autofront/src/cli/help.rs index bf8643c..7d3f378 100644 --- a/autofront/src/cli/help.rs +++ b/autofront/src/cli/help.rs @@ -9,6 +9,7 @@ pub fn print_help(topic: Option<&str>) -> Result<(), CliError> { Some("lex") => print_lex_help(), Some("tt") => print_tt_help(), Some("parse") => print_parse_help(), + Some("codegen") => print_codegen_help(), Some(cmd) => { return Err(CliError::UnknownCommand(cmd.to_string())); } @@ -39,3 +40,7 @@ fn print_tt_help() { fn print_parse_help() { anstream::println!("{}", tr!(cli_help_parse)); } + +fn print_codegen_help() { + anstream::println!("{}", tr!(cli_help_codegen)); +} diff --git a/autofront/src/cli/parser.rs b/autofront/src/cli/parser.rs index 11895f9..2ab69c1 100644 --- a/autofront/src/cli/parser.rs +++ b/autofront/src/cli/parser.rs @@ -57,7 +57,7 @@ pub fn parse_args(args: &[String]) -> Result { Some(topic) => { let topic = topic.clone(); match topic.as_str() { - "help" | "version" | "lex" | "tt" | "parse" => Some(topic), + "help" | "version" | "lex" | "tt" | "parse" | "codegen" => Some(topic), _ => Err(CliError::UnknownCommand(topic))?, } } @@ -71,6 +71,7 @@ pub fn parse_args(args: &[String]) -> Result { "lex" => Ok(Command::Lex(DebugSubcommand::parse(args)?)), "tt" => Ok(Command::Tt(DebugSubcommand::parse(args)?)), "parse" => Ok(Command::Parse(DebugSubcommand::parse(args)?)), + "hir" => Ok(Command::Hir(DebugSubcommand::parse(args)?)), other => Err(CliError::UnknownCommand(other.into())), } diff --git a/autofront/src/driver.rs b/autofront/src/driver.rs index 2fec037..ff99cc2 100644 --- a/autofront/src/driver.rs +++ b/autofront/src/driver.rs @@ -1,4 +1,5 @@ use anstream::StripStream; +use lowerer::lower; use std::{ env::args, error::Error, @@ -12,6 +13,8 @@ use unicode_width::UnicodeWidthStr; use crate::cli::{Command, DebugSubcommand, parse_args, print_help}; use common::source::Source; use diag::{DiagPrinter, DiagSink}; +// use hir::*; +// use ast2hir::lower_ast; use lexer::{lex, parse_token_tree}; use locale::tr; use parser::parse; @@ -124,6 +127,31 @@ impl Driver { Ok(()) } + fn hir(&mut self, args: DebugSubcommand) -> Result<(), Box> { + let DebugSubcommand { + file, + output, + show_recovery, + } = args; + + let mut sink = DiagSink::default(); + let src = self.load(file)?; + + let tokens = lex(&src, &mut sink); + let ts = parse_token_tree(&tokens, &mut sink); + let ast = parse(&ts, &mut sink); + let hir = lower(&ast, &mut sink); + + let printer = DiagPrinter::new(&src); + printer.print(sink, show_recovery)?; + + let output = output.unwrap_or_else(|| format!("{}.out", src.file)); + let out = get_out!(Some(output)); + write!(out, "{hir:#?}")?; + + Ok(()) + } + fn drive(mut self) -> Result<(), Box> { use Command::*; @@ -137,6 +165,7 @@ impl Driver { Lex(args) => self.lex(args)?, Tt(args) => self.tt(args)?, Parse(args) => self.parse(args)?, + Hir(args) => self.hir(args)?, } Ok(()) diff --git a/hir/Cargo.toml b/hir/Cargo.toml index ade17a0..e3dff84 100644 --- a/hir/Cargo.toml +++ b/hir/Cargo.toml @@ -5,3 +5,6 @@ edition.workspace = true license.workspace = true [dependencies] +common = { path = "../common" } +bimap = "0.6.3" +num-bigint = "0.4.6" diff --git a/hir/src/id.rs b/hir/src/id.rs new file mode 100644 index 0000000..8dd1852 --- /dev/null +++ b/hir/src/id.rs @@ -0,0 +1,35 @@ +use std::ops::{Add, AddAssign}; + +macro_rules! define_id { + ($id:ident) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $id(usize); + + impl $id { + pub fn new(value: usize) -> Self { + Self(value) + } + + pub fn as_usize(self) -> usize { + self.0 + } + } + + impl Add for $id { + type Output = Self; + fn add(self, rhs: usize) -> Self::Output { + Self(self.0 + rhs) + } + } + + impl AddAssign for $id { + fn add_assign(&mut self, rhs: usize) { + self.0 += rhs + } + } + }; +} + +define_id!(SymId); + +define_id!(DestId); diff --git a/hir/src/lib.rs b/hir/src/lib.rs index e69de29..9a3f6d1 100644 --- a/hir/src/lib.rs +++ b/hir/src/lib.rs @@ -0,0 +1,17 @@ +pub mod id; +pub mod node; +pub mod print; + +use common::span::Span; +pub use {id::*, node::*, print::*}; + +#[derive(Debug, Clone)] +pub struct Ident { + pub name: String, + pub span: Span, +} + +#[derive(Clone)] +pub struct Hir { + pub items: Vec, +} diff --git a/hir/src/node.rs b/hir/src/node.rs new file mode 100644 index 0000000..0caf2b8 --- /dev/null +++ b/hir/src/node.rs @@ -0,0 +1,84 @@ +pub mod constant; +pub mod expr; +pub mod ty; + +pub use {constant::*, expr::*, ty::*}; + +use crate::{Ident, SymId}; +use common::span::Span; + +#[derive(Debug, Clone)] +pub struct Item { + pub kind: ItemKind, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub enum ItemKind { + FnDef(FnDef), + TyDef(TyDef), + Error, +} + +#[derive(Debug, Clone)] +pub struct FnDef { + pub sym_id: SymId, + pub name: Option, + pub side_effect: bool, + pub params: Vec, + pub ret: Box, + pub body: Body, +} + +#[derive(Debug, Clone)] +pub struct Body { + pub kind: BodyKind, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub enum BodyKind { + Expr(Box), + CFfi(String), + Error, +} + +#[derive(Debug, Clone)] +pub struct TyDef { + pub sym_id: SymId, + pub name: Option, + pub ty: Box, +} + +#[derive(Debug, Clone)] +pub struct Param { + pub sym_id: SymId, + pub mutable: bool, + pub name: Ident, + pub ty: Box, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub struct Stmt { + pub kind: StmtKind, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub enum StmtKind { + LocalDef(LocalDef), + Item(Item), + Expr(Box), + Error, +} + +#[derive(Debug, Clone)] +pub struct LocalDef { + pub sym_id: SymId, + pub mutable: bool, + pub name: Option, + pub ty: Box, + pub init: Option>, + pub span: Span, +} diff --git a/hir/src/node/constant.rs b/hir/src/node/constant.rs new file mode 100644 index 0000000..80db77b --- /dev/null +++ b/hir/src/node/constant.rs @@ -0,0 +1,21 @@ +use common::span::Span; +use num_bigint::BigInt; + +use crate::Expr; + +#[derive(Debug, Clone)] +pub struct Constant { + pub kind: ConstantKind, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub enum ConstantKind { + Char(char), + Str(String), + Byte(u8), + Bytes(Vec), + Int(BigInt), + Bool(bool), + Expr(Box), +} diff --git a/hir/src/node/expr.rs b/hir/src/node/expr.rs new file mode 100644 index 0000000..44ff3da --- /dev/null +++ b/hir/src/node/expr.rs @@ -0,0 +1,188 @@ +use common::span::Span; + +use crate::{Constant, ConstantKind, DestId, Ident, Stmt, SymId, Ty}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ValCate { + L, + R, +} + +#[derive(Debug, Clone)] +pub struct Expr { + pub kind: ExprKind, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub enum ExprKind { + Use(Option, SymId), + + Const(Constant), + Array(Vec), + Repeat(Box, Constant), + Tuple(Vec), + Struct(Vec), + + Cont(DestId), + Break(DestId, Box), + Block(Block), + If(Box, Box, Box), + + Binary(BinOp, Box, Box), + Unary(UnOp, Box), + Cast(Box, Box), + Field(Box, Ident), + Index(Box, Box), + RefOf(bool, Box), + Call(Box, Vec), + + Begin(Box), + End(Box), + + Error, +} + +#[derive(Debug, Clone)] +pub struct ExprField { + pub ident: Ident, + pub expr: Box, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub struct Block { + pub dest_id: Option, + pub stmts: Vec, + pub expr: Box, + pub source: BlockSource, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ContSource { + Cont, + While, + For, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum BreakSource { + Break, + While, + For, + Else, + Return, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum BlockSource { + Block, + While, + For, + CaseArm, + Case, + FnBody, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BinOp { + pub kind: BinOpKind, + pub span: Span, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum BinOpKind { + Add, + Sub, + Mul, + Div, + Rem, + Shl, + Shr, + Eq, + Lt, + Le, + Ne, + Gt, + Ge, + Way3, + Ass, + AddAss, + SubAss, + MulAss, + DivAss, + RemAss, + ShlAss, + ShrAss, + Range, + RangeInc, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnOp { + pub kind: UnOpKind, + pub span: Span, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum UnOpKind { + Deref, + Not, + Neg, + Inc, + Dec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum UnOpPos { + Prefix, + Suffix, +} + +impl UnOpKind { + pub fn pos(self) -> UnOpPos { + use {UnOpKind::*, UnOpPos::*}; + match self { + Deref => Prefix, + Not => Prefix, + Neg => Prefix, + Inc => Suffix, + Dec => Suffix, + } + } +} + +impl Expr { + pub fn value_category(&self) -> ValCate { + use {ConstantKind::*, ExprKind::*, ValCate::*}; + match &self.kind { + Use(_, _) | Field(_, _) | Index(_, _) => L, + + Cont(_) + | Break(_, _) + | Binary(_, _, _) + | Cast(_, _) + | RefOf(_, _) + | Call(_, _) + | Array(_) + | Repeat(_, _) + | Tuple(_) + | Struct(_) + | If(_, _, _) + | Begin(_) + | End(_) + | Block(_) + | Error => R, + + Const(constant) => match &constant.kind { + Expr(expr) => expr.value_category(), + _ => R, + }, + + Unary(op, _) => match op.kind { + UnOpKind::Deref => L, + _ => R, + }, + } + } +} diff --git a/hir/src/node/ty.rs b/hir/src/node/ty.rs new file mode 100644 index 0000000..ce401a1 --- /dev/null +++ b/hir/src/node/ty.rs @@ -0,0 +1,37 @@ +use common::span::Span; + +use crate::{Constant, Ident, SymId}; + +#[derive(Debug, Clone)] +pub struct Ty { + pub kind: TyKind, + pub span: Span, +} + +#[derive(Debug, Clone)] +pub enum TyKind { + Never, + Use(Option, SymId), + Ref(Pointee), + Ptr(Pointee), + FnPtr(bool, Vec, Box), + Tuple(Vec), + Struct(Vec), + Slice(Box), + Array(Box, Constant), + Infer, + + Error, +} + +#[derive(Debug, Clone)] +pub struct Pointee { + pub mutable: bool, + pub ty: Box, +} + +#[derive(Debug, Clone)] +pub struct TyField { + pub ident: Ident, + pub ty: Box, +} diff --git a/hir/src/print.rs b/hir/src/print.rs new file mode 100644 index 0000000..7205523 --- /dev/null +++ b/hir/src/print.rs @@ -0,0 +1,649 @@ +use std::fmt::{self, Debug, Formatter, Write}; + +use num_bigint::BigInt; + +use crate::*; + +const IDENT_WIDTH: usize = 3; + +pub struct HirPrinter<'a> { + indent: usize, + written: bool, + buf: &'a mut (dyn Write + 'a), +} + +impl<'a> HirPrinter<'a> { + fn new(buf: &'a mut dyn Write) -> Self { + Self::with_indent(0, buf) + } + + fn with_indent(indent: usize, buf: &'a mut dyn Write) -> Self { + Self { + indent, + written: false, + buf, + } + } + + pub fn write_indent(&mut self) -> fmt::Result { + if self.written { + return Ok(()); + } + + self.written = true; + + let spaces = IDENT_WIDTH * self.indent; + for _ in 0..spaces { + self.buf.write_char(' ')?; + } + Ok(()) + } + + pub fn indent(&mut self) { + self.indent += 1; + } + + pub fn dedent(&mut self) { + if self.indent > 0 { + self.indent -= 1; + } + } + + pub fn print(&mut self, x: &T) -> fmt::Result { + x.print(self) + } + + pub fn println(&mut self, x: &T) -> fmt::Result { + x.print(self)?; + writeln!(self) + } +} + +impl Write for HirPrinter<'_> { + fn write_char(&mut self, c: char) -> fmt::Result { + self.write_indent()?; + self.buf.write_char(c)?; + if c == '\n' { + self.written = false; + } + Ok(()) + } + + fn write_str(&mut self, s: &str) -> fmt::Result { + for seg in s.split_inclusive('\n') { + self.write_indent()?; + self.buf.write_str(seg)?; + if seg.ends_with('\n') { + self.written = false; + } + } + Ok(()) + } + + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { + self.write_indent()?; + if let Some(s) = args.as_str() { + self.write_str(s) + } else { + self.write_str(&args.to_string()) + } + } +} + +impl HirPrint for Box { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + self.as_ref().print(f) + } +} + +impl HirPrint for u8 { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if self.is_ascii() { + let c = *self as char; + f.print(&c) + } else { + f.print(&format!("\\x{self:2X}")) + } + } +} + +impl HirPrint for bool { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + f.write_indent()?; + write!(f.buf, "{self}") + } +} + +impl HirPrint for char { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + f.write_char(*self) + } +} + +impl HirPrint for str { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + f.write_str(self) + } +} + +impl HirPrint for String { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + f.write_str(self) + } +} + +impl HirPrint for BigInt { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + f.write_indent()?; + write!(f.buf, "{self}") + } +} + +pub trait HirPrint: Debug { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + write!(f, "{self:#?}") + } +} + +impl HirPrint for Ident { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +impl HirPrint for SymId { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + write!(f, "@{}", self.as_usize()) + } +} + +impl HirPrint for DestId { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + write!(f, "'{}", self.as_usize()) + } +} + +impl HirPrint for Item { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + ItemKind::FnDef(func) => f.print(func), + ItemKind::TyDef(ty) => f.print(ty), + ItemKind::Error => f.print(""), + } + } +} + +const PAREN: (char, char) = ('(', ')'); +const BRACKET: (char, char) = ('[', ']'); +const BRACE: (char, char) = ('{', '}'); + +fn print_group( + f: &mut HirPrinter, + (l, r): (char, char), + cb: impl FnOnce(&mut HirPrinter) -> fmt::Result, +) -> fmt::Result { + f.println(&l)?; + f.indent(); + cb(f)?; + f.dedent(); + f.print(&r) +} + +fn print_list(f: &mut HirPrinter, (l, r): (char, char), xs: &[T]) -> fmt::Result { + match xs { + [] | [_] => { + f.print(&l)?; + if let Some(x) = xs.first() { + f.print(x)?; + } + f.print(&r) + } + xs => print_group(f, (l, r), |f| { + for x in xs { + f.print(x)?; + f.write_str(",\n")?; + } + Ok(()) + }), + } +} + +fn print_fn_sign( + f: &mut HirPrinter, + side_effect: bool, + params: &[Param], + ret: &Ty, +) -> fmt::Result { + f.print("fn ")?; + if side_effect { + f.print("mut ")?; + } + print_list(f, PAREN, params)?; + f.print(" -> ")?; + f.print(ret) +} + +impl HirPrint for Body { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + BodyKind::Expr(expr) => f.print(expr), + BodyKind::CFfi(symbol) => { + f.print("extern(\"C\", ")?; + f.print(&format!("{symbol:?}"))?; + f.print(")") + } + BodyKind::Error => f.print(""), + } + } +} + +impl HirPrint for FnDef { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if let Some(name) = &self.name { + f.print(name)?; + } + f.print(&self.sym_id)?; + f.print(": ")?; + print_fn_sign(f, self.side_effect, &self.params, &self.ret)?; + f.print(" = ")?; + f.print(&self.body) + } +} + +impl HirPrint for Param { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if self.mutable { + f.print("mut ")?; + } + f.print(&self.name)?; + f.print(&self.sym_id)?; + f.print(": ")?; + f.print(&self.ty) + } +} + +impl HirPrint for TyDef { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if let Some(name) = &self.name { + f.print(name)?; + } + f.print(&self.sym_id)?; + f.print(": type = ")?; + f.print(&self.ty) + } +} + +impl HirPrint for Ty { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + TyKind::Never => f.print("Never"), + TyKind::Use(ident, sym_id) => { + if let Some(ident) = ident { + f.print(ident)?; + } + f.print(sym_id) + } + TyKind::Ref(pointee) => { + f.print("&")?; + f.print(pointee) + } + TyKind::Ptr(pointee) => { + f.print("*")?; + f.print(pointee) + } + TyKind::FnPtr(side_effect, params, ret) => { + print_fn_sign(f, *side_effect, params, ret)?; + Ok(()) + } + TyKind::Tuple(tys) => print_list(f, PAREN, tys), + TyKind::Struct(fields) => { + f.print("{")?; + for (i, field) in fields.iter().enumerate() { + if i > 0 { + f.print(", ")?; + } + f.print(&field.ident)?; + f.print(": ")?; + f.print(&*field.ty)?; + } + f.print("}") + } + TyKind::Slice(ty) => { + f.print("[")?; + f.print(ty)?; + f.print("]") + } + TyKind::Array(ty, len) => { + f.print("[")?; + f.print(ty)?; + f.print("; ")?; + f.print(len)?; + f.print("]") + } + TyKind::Infer => f.print("_"), + TyKind::Error => f.print(""), + } + } +} + +impl HirPrint for Pointee { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if self.mutable { + f.print("mut ")?; + } + f.print(&self.ty) + } +} + +impl HirPrint for Expr { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + ExprKind::Use(ident, sym_id) => { + if let Some(ident) = ident { + f.print(ident)?; + } + f.print(sym_id) + } + ExprKind::Const(constant) => f.print(constant), + ExprKind::Array(arr) => print_list(f, BRACKET, arr), + ExprKind::Repeat(expr, len) => { + f.print("[")?; + expr.print_atom(f)?; + f.print("; ")?; + f.print(len)?; + f.print("]") + } + ExprKind::Tuple(tup) => print_list(f, PAREN, tup), + ExprKind::Struct(st) => print_list(f, BRACE, st), + ExprKind::Cont(dest_id) => { + f.print("cont ")?; + f.print(dest_id) + } + ExprKind::Break(dest_id, expr) => { + f.print("break ")?; + f.print(dest_id)?; + f.print(" ")?; + expr.print_atom(f) + } + ExprKind::Block(block) => f.print(block), + ExprKind::If(cond, then_branch, else_branch) => { + f.print("if ")?; + f.print(cond)?; + f.print(" ")?; + then_branch.print_branch(f)?; + f.print(" else ")?; + else_branch.print_branch(f) + } + ExprKind::Binary(op, lhs, rhs) => { + lhs.print_atom(f)?; + f.print(" ")?; + f.print(op)?; + f.print(" ")?; + rhs.print_atom(f) + } + ExprKind::Unary(op, operand) => match op.kind.pos() { + UnOpPos::Prefix => { + f.print(op)?; + operand.print_atom(f) + } + UnOpPos::Suffix => { + operand.print_atom(f)?; + f.print(op) + } + }, + ExprKind::Cast(expr, ty) => { + expr.print_atom(f)?; + f.print(" as ")?; + f.print(ty) + } + ExprKind::Field(expr, field) => { + expr.print_atom(f)?; + f.print(".")?; + f.print(field) + } + ExprKind::Index(expr, index) => { + expr.print_atom(f)?; + f.print("[")?; + f.print(index)?; + f.print("]") + } + ExprKind::RefOf(mutable, expr) => { + f.print("&")?; + if *mutable { + f.print("mut ")?; + } + expr.print_atom(f) + } + ExprKind::Call(callee, args) => { + callee.print_atom(f)?; + print_list(f, PAREN, args) + } + ExprKind::Begin(expr) => { + f.print("@begin(")?; + f.print(expr)?; + f.print(")") + } + ExprKind::End(expr) => { + f.print("@end(")?; + f.print(expr)?; + f.print(")") + } + ExprKind::Error => f.print(""), + } + } +} + +impl Expr { + pub fn is_atom(&self) -> bool { + match &self.kind { + ExprKind::Use(_, _) + | ExprKind::Array(_) + | ExprKind::Repeat(_, _) + | ExprKind::Tuple(_) + | ExprKind::Struct(_) + | ExprKind::If(_, _, _) + | ExprKind::Field(_, _) + | ExprKind::Begin(_) + | ExprKind::End(_) + | ExprKind::Error => true, + + ExprKind::Cont(_) + | ExprKind::Break(_, _) + | ExprKind::Binary(_, _, _) + | ExprKind::Unary(_, _) + | ExprKind::Cast(_, _) + | ExprKind::Index(_, _) + | ExprKind::RefOf(_, _) + | ExprKind::Call(_, _) => false, + + ExprKind::Const(constant) => constant.is_atom(), + ExprKind::Block(block) => block.dest_id.is_none(), + } + } + + pub fn print_atom(&self, f: &mut HirPrinter) -> fmt::Result { + if self.is_atom() { + f.print(self) + } else { + f.print("(")?; + f.print(self)?; + f.print(")") + } + } + + pub fn print_branch(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + ExprKind::Block(block) if block.dest_id.is_none() => f.print(self), + ExprKind::Tuple(tup) if tup.is_empty() => f.print("{ () }"), + _ => print_group(f, BRACE, |f| f.println(self)), + } + } +} + +impl HirPrint for Block { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if let Some(dest_id) = &self.dest_id { + f.print(dest_id)?; + f.print(": ")?; + } + print_group(f, BRACE, |f| { + for stmt in &self.stmts { + f.print(stmt)?; + f.println("")?; + } + f.print(&self.expr)?; + f.println("") + }) + } +} + +impl HirPrint for ExprField { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + f.print(&self.ident)?; + f.print(": ")?; + f.print(&self.expr) + } +} + +impl HirPrint for Stmt { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + StmtKind::LocalDef(def) => f.print(def)?, + StmtKind::Item(item) => f.print(item)?, + StmtKind::Expr(expr) => f.print(expr)?, + StmtKind::Error => f.print("")?, + } + f.print(";") + } +} + +impl HirPrint for LocalDef { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + if self.mutable { + f.print("mut ")?; + } + if let Some(name) = &self.name { + f.print(name)?; + } + f.print(&self.sym_id)?; + f.print(": ")?; + f.print(&self.ty)?; + if let Some(init) = &self.init { + f.print(" = ")?; + f.print(init)?; + } + Ok(()) + } +} + +impl HirPrint for BinOp { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + use BinOpKind::*; + let op = match self.kind { + Add => "+", + Sub => "-", + Mul => "*", + Div => "/", + Rem => "%", + Shl => "<<", + Shr => ">>", + Eq => "==", + Lt => "<", + Le => "<=", + Ne => "!=", + Gt => ">", + Ge => ">=", + Way3 => "<=>", + Ass => "=", + AddAss => "+=", + SubAss => "-=", + MulAss => "*=", + DivAss => "/=", + RemAss => "%=", + ShlAss => "<<=", + ShrAss => ">>=", + Range => "~", + RangeInc => "~=", + }; + f.print(op) + } +} + +impl HirPrint for UnOp { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + use UnOpKind::*; + let op = match self.kind { + Deref => "*", + Not => "!", + Neg => "-", + Inc => "++", + Dec => "--", + }; + f.print(op) + } +} + +impl HirPrint for Constant { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + match &self.kind { + ConstantKind::Char(char) => { + f.print("'")?; + f.print(char)?; + f.print("'") + } + ConstantKind::Str(str) => { + f.print("\"")?; + f.print(str)?; + f.print("\"") + } + ConstantKind::Byte(byte) => { + f.print("b\"")?; + f.print(byte)?; + f.print("\"") + } + ConstantKind::Bytes(bytes) => { + f.print("b\"")?; + for byte in bytes { + f.print(byte)?; + } + f.print("\"") + } + ConstantKind::Int(int) => f.print(int), + ConstantKind::Bool(bool) => f.print(bool), + ConstantKind::Expr(expr) => f.print(expr), + } + } +} + +impl Constant { + pub fn is_atom(&self) -> bool { + match &self.kind { + ConstantKind::Expr(expr) => expr.is_atom(), + _ => true, + } + } +} + +impl HirPrint for Vec { + fn print(&self, f: &mut HirPrinter) -> fmt::Result { + for (i, item) in self.iter().enumerate() { + if i != 0 { + f.println("")?; + } + f.print(item)?; + f.println(";")?; + } + Ok(()) + } +} + +impl Debug for Hir { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut printer = HirPrinter::new(f); + printer.print(&self.items) + } else { + Debug::fmt(&self.items, f) + } + } +} diff --git a/lowerer/Cargo.toml b/lowerer/Cargo.toml index ccf321e..191bd69 100644 --- a/lowerer/Cargo.toml +++ b/lowerer/Cargo.toml @@ -5,3 +5,11 @@ edition.workspace = true license.workspace = true [dependencies] +common = { path = "../common" } +syntax = { path = "../syntax" } +hir = { path = "../hir" } +diag = { path = "../diag" } +locale = { path = "../locale" } +annotate-snippets = "0.12.11" +num-bigint = "0.4.6" +num-traits = "0.2.19" diff --git a/lowerer/locale/en-US/lowerer.ftl b/lowerer/locale/en-US/lowerer.ftl new file mode 100644 index 0000000..28162d6 --- /dev/null +++ b/lowerer/locale/en-US/lowerer.ftl @@ -0,0 +1,11 @@ +symbol_not_found = cannot find value `{$name}` in this scope +not_a_type = `{$name}` is not a type +type_as_value = type `{$name}` cannot be used as a value +unsupported_ffi = only C FFI is supported +ret_outside_fn = return statement outside function body +break_outside_loop = break outside loop +cont_outside_loop = cont outside loop +duplicate_definition = duplicate definition of `{$name}` +previous_definition_here = previously defined here +unimplemented_global = global definitions is unimplemented +unimplemented_asm = asm is unimplemented diff --git a/lowerer/locale/zh-CN/lowerer.ftl b/lowerer/locale/zh-CN/lowerer.ftl new file mode 100644 index 0000000..f4799bb --- /dev/null +++ b/lowerer/locale/zh-CN/lowerer.ftl @@ -0,0 +1,11 @@ +symbol_not_found = 无法在此作用域中找到 `{$name}` +not_a_type = `{$name}` 不是类型 +type_as_value = 类型 `{$name}` 不能用作值 +unsupported_ffi = 仅支持 C FFI +ret_outside_fn = return 语句在函数体外 +break_outside_loop = 在循环外的 break +cont_outside_loop = 在循环外的 cont +duplicate_definition = 重复定义 `{$name}` +previous_definition_here = 先前在此定义 +unimplemented_global = 暂未实现全局变量 +unimplemented_asm = 暂未实现 asm diff --git a/lowerer/src/context.rs b/lowerer/src/context.rs new file mode 100644 index 0000000..bc35fa0 --- /dev/null +++ b/lowerer/src/context.rs @@ -0,0 +1,261 @@ +use crate::*; +use common::span::Span; +use diag::{DiagSink, Diagnostics}; +use hir::{DestId, SymId}; + +pub struct LoweringContext<'a> { + diag: &'a mut DiagSink, + scope: SymbolTable, + next_sym_id: SymId, + next_dest_id: DestId, + dest_stack: Vec>, +} + +impl<'a> LoweringContext<'a> { + pub fn new(diag: &'a mut DiagSink) -> Self { + let mut ctx = Self { + diag, + scope: SymbolTable::new(), + next_sym_id: SymId::new(0), + next_dest_id: DestId::new(0), + dest_stack: vec![Vec::new()], + }; + + ctx.register_prelude(); + + ctx + } + + pub fn diag(&mut self, diag: impl Diagnostics + 'static) { + self.diag.diag(diag); + } + + fn register_prelude(&mut self) { + let dummy_span = Span { start: 0, end: 0 }; + + let builtin_types = [ + "Never", "Bool", "Char", "Byte", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", + "UInt16", "UInt32", "UInt64", "UInt128", "ISize", "USize", + ]; + + for ty_name in builtin_types { + self.define_global(ty_name.to_string(), SymKind::Type, dummy_span); + } + } + + pub fn fresh_sym_id(&mut self) -> SymId { + let id = self.next_sym_id; + self.next_sym_id += 1; + id + } + + pub fn fresh_dest_id(&mut self) -> DestId { + let id = self.next_dest_id; + self.next_dest_id += 1; + id + } + + pub fn define_global(&mut self, name: String, kind: SymKind, span: Span) -> SymId { + let id = self.fresh_sym_id(); + let symbol = Symbol { + id, + name: Some(name.clone()), + kind, + span, + }; + self.scope.define_global(name, symbol); + id + } + + pub fn define_local(&mut self, name: Option, kind: SymKind, span: Span) -> SymId { + let id = self.fresh_sym_id(); + let symbol = Symbol { + id, + name: name.clone(), + kind, + span, + }; + + if let Some(name) = name { + match kind { + SymKind::Local => { + self.scope.define_local(name, symbol); + } + SymKind::Fn | SymKind::Type => { + if self.scope.contains_in_scope(&name) { + let prev = self.scope.get_local(&name).unwrap(); + self.diag.diag(crate::errors::DuplicateDefinition::new( + name.clone(), + span, + prev.span, + )); + } else { + self.scope.define_local(name, symbol); + } + } + } + } + + id + } + + pub fn lookup_local(&self, name: &str) -> Option<&Symbol> { + self.scope.get_local(name) + } + + pub fn lookup_across(&self, name: &str) -> Option<&Symbol> { + self.scope.get_across(name) + } + + pub fn lookup_global(&self, name: &str) -> Option<&Symbol> { + self.scope.get_global(name) + } + + pub fn current_dest(&self) -> Option { + self.dest_stack.last()?.last().map(|d| d.id) + } + + pub fn lookup_label(&self, label: &str) -> Option { + self.scope.get_label(label) + } + + pub fn find_loop_dest(&self) -> Option { + self + .dest_stack + .last()? + .iter() + .rev() + .find(|d| d.kind == DestKind::Loop) + .map(|d| d.id) + } + + pub fn find_fn_body_dest(&self) -> Option { + self + .dest_stack + .last()? + .iter() + .rev() + .find(|d| d.kind == DestKind::Body) + .map(|d| d.id) + } + + pub fn new_scope<'b>(&'b mut self) -> ScopeGuard<'b, 'a> { + self.scope.enter_scope(); + ScopeGuard { ctx: self } + } + + pub fn new_fn<'b>(&'b mut self) -> FnGuard<'b, 'a> { + self.scope.enter_fn(); + self.dest_stack.push(Vec::new()); + FnGuard { ctx: self } + } + + pub fn new_dest<'b>( + &'b mut self, + label: Option, + span: Span, + kind: DestKind, + ) -> DestGuard<'b, 'a> { + let dest_id = self.fresh_dest_id(); + let dest = Destination { + id: dest_id, + label: label.clone(), + span, + kind, + }; + + if let Some(label) = label { + self.scope.define_label(label, dest_id); + } + + if let Some(current_fn) = self.dest_stack.last_mut() { + current_fn.push(dest); + } + + DestGuard { ctx: self, dest_id } + } +} + +pub struct ScopeGuard<'b, 'a> { + ctx: &'b mut LoweringContext<'a>, +} + +impl<'b, 'a> Drop for ScopeGuard<'b, 'a> { + fn drop(&mut self) { + self.ctx.scope.exit_scope(); + } +} + +impl<'b, 'a> std::ops::Deref for ScopeGuard<'b, 'a> { + type Target = LoweringContext<'a>; + + fn deref(&self) -> &Self::Target { + self.ctx + } +} + +impl<'b, 'a> std::ops::DerefMut for ScopeGuard<'b, 'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.ctx + } +} + +pub struct FnGuard<'b, 'a> { + ctx: &'b mut LoweringContext<'a>, +} + +impl<'b, 'a> Drop for FnGuard<'b, 'a> { + fn drop(&mut self) { + self.ctx.scope.exit_fn(); + if self.ctx.dest_stack.len() > 1 { + self.ctx.dest_stack.pop(); + } + } +} + +impl<'b, 'a> std::ops::Deref for FnGuard<'b, 'a> { + type Target = LoweringContext<'a>; + + fn deref(&self) -> &Self::Target { + self.ctx + } +} + +impl<'b, 'a> std::ops::DerefMut for FnGuard<'b, 'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.ctx + } +} + +pub struct DestGuard<'b, 'a> { + ctx: &'b mut LoweringContext<'a>, + dest_id: DestId, +} + +impl<'b, 'a> DestGuard<'b, 'a> { + pub fn dest_id(&self) -> DestId { + self.dest_id + } +} + +impl<'b, 'a> Drop for DestGuard<'b, 'a> { + fn drop(&mut self) { + if let Some(current_fn) = self.ctx.dest_stack.last_mut() { + current_fn.pop(); + } + } +} + +impl<'b, 'a> std::ops::Deref for DestGuard<'b, 'a> { + type Target = LoweringContext<'a>; + + fn deref(&self) -> &Self::Target { + self.ctx + } +} + +impl<'b, 'a> std::ops::DerefMut for DestGuard<'b, 'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.ctx + } +} diff --git a/lowerer/src/errors.rs b/lowerer/src/errors.rs new file mode 100644 index 0000000..fca4f26 --- /dev/null +++ b/lowerer/src/errors.rs @@ -0,0 +1,243 @@ +use annotate_snippets::Group; +use common::{source::Source, span::Span}; +use diag::{DiagPrinter, Diagnostics, annotation_here, error}; +use locale::tre; + +#[derive(Debug, Clone)] +pub struct NotAType { + pub name: String, + pub span: Span, +} + +impl NotAType { + pub fn new(name: String, span: Span) -> Self { + Self { name, span } + } +} + +impl Diagnostics for NotAType { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(not_a_type, name = &self.name))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct NotFound { + pub name: String, + pub span: Span, +} + +impl NotFound { + pub fn new(name: String, span: Span) -> Self { + Self { name, span } + } +} + +impl Diagnostics for NotFound { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(symbol_not_found, name = &self.name))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct TypeAsValue { + pub name: String, + pub span: Span, +} + +impl TypeAsValue { + pub fn new(name: String, span: Span) -> Self { + Self { name, span } + } +} + +impl Diagnostics for TypeAsValue { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(type_as_value, name = &self.name))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct UnsupportedFfi { + pub span: Span, +} + +impl UnsupportedFfi { + pub fn new(span: Span) -> Self { + Self { span } + } +} + +impl Diagnostics for UnsupportedFfi { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(unsupported_ffi))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct UnimplementedGlobal { + pub span: Span, +} + +impl UnimplementedGlobal { + pub fn new(span: Span) -> Self { + Self { span } + } +} + +impl Diagnostics for UnimplementedGlobal { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(unimplemented_global))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct UnimplementedAsm { + pub span: Span, +} + +impl UnimplementedAsm { + pub fn new(span: Span) -> Self { + Self { span } + } +} + +impl Diagnostics for UnimplementedAsm { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(unimplemented_asm))) + .element(annotation_here!(src, self.span)), + ); + } +} + +// #[derive(Debug, Clone)] +// pub struct Unimplemented3Way { +// pub span: Span, +// } + +// impl Unimplemented3Way { +// pub fn new(span: Span) -> Self { +// Self { span } +// } +// } + +// impl Diagnostics for Unimplemented3Way{ +// fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { +// sink.error( +// Group::with_title(error().primary_title(tre!(unimplemented_3way))) +// .element(annotation_here!(src, self.span)), +// ); +// } +// } + +#[derive(Debug, Clone)] +pub struct RetOutsideFn { + pub span: Span, +} + +impl RetOutsideFn { + pub fn new(span: Span) -> Self { + Self { span } + } +} + +impl Diagnostics for RetOutsideFn { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(ret_outside_fn))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct BreakOutsideLoop { + pub span: Span, +} + +impl BreakOutsideLoop { + pub fn new(span: Span) -> Self { + Self { span } + } +} + +impl Diagnostics for BreakOutsideLoop { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(break_outside_loop))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct ContOutsideLoop { + pub span: Span, +} + +impl ContOutsideLoop { + pub fn new(span: Span) -> Self { + Self { span } + } +} + +impl Diagnostics for ContOutsideLoop { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + sink.error( + Group::with_title(error().primary_title(tre!(cont_outside_loop))) + .element(annotation_here!(src, self.span)), + ); + } +} + +#[derive(Debug, Clone)] +pub struct DuplicateDefinition { + pub name: String, + pub span: Span, + pub prev_span: Span, +} + +impl DuplicateDefinition { + pub fn new(name: String, span: Span, prev_span: Span) -> Self { + Self { + name, + span, + prev_span, + } + } +} + +impl Diagnostics for DuplicateDefinition { + fn report<'src>(&self, src: &'src Source, sink: &mut DiagPrinter<'src>) { + use annotate_snippets::AnnotationKind; + use diag::SourceSnippet; + + sink.error( + Group::with_title(error().primary_title(tre!(duplicate_definition, name = &self.name))) + .element(src.snippet().annotation(diag::here(self.span))) + .element( + src.snippet().annotation( + AnnotationKind::Context + .span(self.prev_span.into()) + .label(tre!(previous_definition_here)), + ), + ), + ); + } +} diff --git a/lowerer/src/lib.rs b/lowerer/src/lib.rs index e69de29..25851e6 100644 --- a/lowerer/src/lib.rs +++ b/lowerer/src/lib.rs @@ -0,0 +1,17 @@ +pub mod context; +pub mod errors; +pub mod lower; +pub mod symbol; + +pub use {context::*, errors::*, lower::*, symbol::*}; + +use diag::DiagSink; +use hir::Hir; +use syntax::ast; + +locale::i18n!("locale", fallback = "en-US"); + +pub fn lower(ast: &ast::Ast, diag: &mut DiagSink) -> Hir { + let mut ctx = LoweringContext::new(diag); + lower_ast(&mut ctx, ast) +} diff --git a/lowerer/src/lower.rs b/lowerer/src/lower.rs new file mode 100644 index 0000000..93232f5 --- /dev/null +++ b/lowerer/src/lower.rs @@ -0,0 +1,1570 @@ +use crate::*; +use common::span::Span; +use hir::*; +use syntax::{ast, span::Spanned, token}; + +pub fn lower_ast(ctx: &mut LoweringContext, ast: &ast::Ast) -> Hir { + let mut items = Vec::new(); + + for item in ast.root.0.vals() { + if let ast::GlobalItem::Def(def) = item { + match def.as_ref() { + ast::Def::Fn(def) => { + ctx.define_local(Some(def.name.ident.clone()), SymKind::Fn, def.name.span()); + } + ast::Def::Type(def) => { + ctx.define_local(Some(def.name.ident.clone()), SymKind::Type, def.name.span()); + } + _ => (), + } + } + } + + for item in ast.root.0.vals() { + let item = lower_global_item(ctx, item); + items.push(item); + } + + Hir { items } +} + +fn lower_global_item(ctx: &mut LoweringContext, item: &ast::GlobalItem) -> Item { + match item { + ast::GlobalItem::Def(def) => { + return lower_def_as_item(ctx, def); + } + ast::GlobalItem::Asm(asm) => { + ctx.diag(UnimplementedAsm::new(asm.span())); + } + _ => (), + } + + Item { + kind: ItemKind::Error, + span: item.span(), + } +} + +fn lower_type(ctx: &mut LoweringContext, ty: &ast::Type) -> Ty { + let span = ty.span(); + let kind = match ty { + ast::Type::Infer(_) => TyKind::Infer, + + ast::Type::Ident(ident) => { + let name = ident.0.ident.clone(); + if let Some(symbol) = ctx.lookup_across(&name) { + if symbol.kind != SymKind::Type { + ctx.diag(NotAType::new(name.clone(), span)); + TyKind::Infer + } else { + TyKind::Use(Some(Ident { name, span }), symbol.id) + } + } else { + ctx.diag(NotFound::new(name, span)); + TyKind::Infer + } + } + + ast::Type::Paren(paren) => { + return lower_type(ctx, &paren.0.inner); + } + + ast::Type::Tuple(tuple) => { + let types = tuple.0.inner.vals(); + let types = types.map(|t| lower_type(ctx, t)).collect(); + TyKind::Tuple(types) + } + + ast::Type::Ref(ref_ty) => { + let mutable = ref_ty.mut_tok.is_some(); + let pointee = lower_type(ctx, &ref_ty.pointee); + TyKind::Ref(Pointee { + mutable, + ty: Box::new(pointee), + }) + } + + ast::Type::Ptr(ptr_ty) => { + let mutable = ptr_ty.mut_tok.is_some(); + let pointee = lower_type(ctx, &ptr_ty.pointee); + TyKind::Ptr(Pointee { + mutable, + ty: Box::new(pointee), + }) + } + + ast::Type::Slice(slice) => { + let elem = lower_type(ctx, &slice.0.inner); + TyKind::Slice(Box::new(elem)) + } + + ast::Type::Array(array) => { + let inner = &array.0.inner; + let mut elem_ty = lower_type(ctx, &inner.elem); + + let lens: Vec<_> = inner.lens.vals().collect(); + for len_expr in lens.iter().rev() { + let len = lower_const_expr(ctx, len_expr); + elem_ty = Ty { + kind: TyKind::Array(Box::new(elem_ty), len), + span, + }; + } + + return elem_ty; + } + + ast::Type::Fn(fn_ty) => { + let sign = lower_fn_sign_type(ctx, fn_ty); + TyKind::FnPtr(sign.0, sign.1, sign.2) + } + + ast::Type::Struct(struct_ty) => { + let fields = struct_ty + .0 + .inner + .vals() + .map(|field| TyField { + ident: Ident { + name: field.name.ident.clone(), + span: field.name.span(), + }, + ty: Box::new(lower_type(ctx, &field.lens)), + }) + .collect(); + TyKind::Struct(fields) + } + + ast::Type::Error(_) => TyKind::Error, + }; + + Ty { kind, span } +} + +fn lower_fn_sign_type(ctx: &mut LoweringContext, fn_ty: &ast::TypeFn) -> (bool, Vec, Box) { + let side_effect = fn_ty.mut_tok.is_some(); + let params = fn_ty + .params + .0 + .inner + .vals() + .map(|p| lower_type(ctx, p)) + .collect(); + let ret = lower_type(ctx, &fn_ty.ret.ty); + + (side_effect, params, Box::new(ret)) +} + +fn lower_const_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> Constant { + let span = expr.span(); + let kind = match expr { + ast::Expr::Lit(lit) => lower_literal(&lit.lit), + _ => { + let hir_expr = lower_expr(ctx, expr); + ConstantKind::Expr(Box::new(hir_expr)) + } + }; + + Constant { kind, span } +} + +fn lower_literal(lit: &token::Lit) -> ConstantKind { + use token::Lit; + match lit { + Lit::Char(c) => ConstantKind::Char(c.char), + Lit::Str(s) => ConstantKind::Str(s.str.clone()), + Lit::Byte(b) => ConstantKind::Byte(b.byte), + Lit::Bytes(bs) => ConstantKind::Bytes(bs.bytes.clone()), + Lit::Int(i) => ConstantKind::Int(i.int.clone()), + Lit::Bool(b) => ConstantKind::Bool(b.bool), + } +} + +fn lower_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> Expr { + let span = expr.span(); + let kind = match expr { + ast::Expr::Lit(lit) => { + let constant = Constant { + kind: lower_literal(&lit.lit), + span: lit.lit.span(), + }; + ExprKind::Const(constant) + } + + ast::Expr::Ident(ident) => 'blk: { + let name = ident.0.ident.clone(); + if let Some(symbol) = ctx.lookup_across(&name) { + if symbol.kind == SymKind::Type { + ctx.diag(TypeAsValue::new(name.clone(), span)); + } else { + let ident = Ident { name, span }; + break 'blk ExprKind::Use(Some(ident), symbol.id); + } + } else { + ctx.diag(NotFound::new(name, span)); + } + ExprKind::Error + } + + ast::Expr::Paren(paren) => { + return lower_expr(ctx, &paren.0.inner); + } + + ast::Expr::Tuple(tuple) => { + let exprs = tuple.0.inner.vals(); + let exprs = exprs.map(|e| lower_expr(ctx, e)).collect(); + ExprKind::Tuple(exprs) + } + + ast::Expr::Array(array) => { + let exprs = array.0.inner.vals(); + let exprs = exprs.map(|e| lower_expr(ctx, e)).collect(); + ExprKind::Array(exprs) + } + + ast::Expr::Repeat(repeat) => { + let inner = &repeat.0.inner; + let elem = lower_expr(ctx, &inner.elem); + let first = inner.lens.vals().next(); + let len = lower_const_expr(ctx, first.unwrap()); + ExprKind::Repeat(Box::new(elem), len) + } + + ast::Expr::Struct(struct_expr) => { + let fields = struct_expr + .0 + .inner + .vals() + .map(|f| lower_field_value(ctx, f)) + .collect(); + ExprKind::Struct(fields) + } + + ast::Expr::Field(field) => { + let base = lower_expr(ctx, &field.base); + let ident = lower_member(&field.member); + ExprKind::Field(Box::new(base), ident) + } + + ast::Expr::Index(index) => { + let base = lower_expr(ctx, &index.base); + let first = index.indices.inner.vals().next(); + let idx = lower_expr(ctx, first.unwrap()); + ExprKind::Index(Box::new(base), Box::new(idx)) + } + + ast::Expr::Cast(cast) => { + let operand = lower_expr(ctx, &cast.operand); + let ty = lower_type(ctx, &cast.ty); + ExprKind::Cast(Box::new(operand), Box::new(ty)) + } + + ast::Expr::Ref(ref_expr) => { + let mutable = ref_expr.mut_tok.is_some(); + let pointee = lower_expr(ctx, &ref_expr.pointee); + ExprKind::RefOf(mutable, Box::new(pointee)) + } + + ast::Expr::Deref(deref) => { + let pointee = lower_expr(ctx, &deref.pointee); + ExprKind::Unary( + UnOp { + kind: UnOpKind::Deref, + span, + }, + Box::new(pointee), + ) + } + + ast::Expr::Prefix(prefix) => { + let operand = lower_expr(ctx, &prefix.operand); + let op = lower_prefix_op(&prefix.op); + ExprKind::Unary(op, Box::new(operand)) + } + + ast::Expr::Suffix(suffix) => { + let operand = lower_expr(ctx, &suffix.operand); + let op = lower_suffix_op(&suffix.op); + ExprKind::Unary(op, Box::new(operand)) + } + + ast::Expr::Add(add) => { + let lhs = lower_expr(ctx, &add.lhs); + let rhs = lower_expr(ctx, &add.rhs); + let op = lower_add_op(&add.op); + ExprKind::Binary(op, Box::new(lhs), Box::new(rhs)) + } + + ast::Expr::Mul(mul) => { + let lhs = lower_expr(ctx, &mul.lhs); + let rhs = lower_expr(ctx, &mul.rhs); + let op = lower_mul_op(&mul.op); + ExprKind::Binary(op, Box::new(lhs), Box::new(rhs)) + } + + ast::Expr::Shift(shift) => { + let lhs = lower_expr(ctx, &shift.lhs); + let rhs = lower_expr(ctx, &shift.rhs); + let op = lower_shift_op(&shift.op); + ExprKind::Binary(op, Box::new(lhs), Box::new(rhs)) + } + + ast::Expr::Assign(assign) => { + let lhs = lower_expr(ctx, &assign.lhs); + let rhs = lower_expr(ctx, &assign.rhs); + let op = lower_assign_op(&assign.op); + ExprKind::Binary(op, Box::new(lhs), Box::new(rhs)) + } + + ast::Expr::If(if_expr) => { + return lower_if_expr(ctx, if_expr); + } + + ast::Expr::Block(block_expr) => { + if let Some(label) = &block_expr.label { + let mut guard = ctx.new_dest( + Some(label.label.label.clone()), + block_expr.block.span(), + DestKind::Block, + ); + let dest_id = Some(guard.dest_id()); + return lower_block(&mut guard, &block_expr.block, dest_id); + } else { + return lower_block(ctx, &block_expr.block, None); + } + } + + ast::Expr::Return(ret_expr) => { + return lower_return_expr(ctx, ret_expr); + } + + ast::Expr::Break(break_expr) => { + return lower_break_expr(ctx, break_expr); + } + + ast::Expr::Cont(cont_expr) => { + return lower_cont_expr(ctx, cont_expr); + } + + ast::Expr::While(while_expr) => { + return lower_while_expr(ctx, while_expr); + } + + ast::Expr::For(for_expr) => { + return lower_for_expr(ctx, for_expr); + } + + ast::Expr::Case(case_expr) => { + return lower_case_expr(ctx, case_expr); + } + + ast::Expr::Call(call_expr) => { + let callee = lower_expr(ctx, &call_expr.callee); + let args = call_expr + .args + .0 + .inner + .vals() + .map(|e| lower_expr(ctx, e)) + .collect(); + ExprKind::Call(Box::new(callee), args) + } + + ast::Expr::Range(range_expr) => { + let lhs = lower_expr(ctx, &range_expr.lhs); + let rhs = lower_expr(ctx, &range_expr.rhs); + let op = lower_range_op(&range_expr.op); + ExprKind::Binary(op, Box::new(lhs), Box::new(rhs)) + } + + ast::Expr::Cmp(cmp_expr) => { + return lower_cmp_expr(ctx, cmp_expr); + } + + ast::Expr::And(and_expr) => { + return lower_and_expr(ctx, and_expr); + } + + ast::Expr::Fn(fn_expr) => { + return lower_fn_expr(ctx, fn_expr); + } + + ast::Expr::Error(_) => ExprKind::Error, + }; + + Expr { kind, span } +} + +fn lower_field_value(ctx: &mut LoweringContext, field: &ast::FieldValue) -> ExprField { + let expr = if let Some(init) = &field.init { + lower_expr(ctx, &init.value) + } else { + lower_expr(ctx, &ast::Expr::Ident(ast::ExprIdent(field.name.clone()))) + }; + + ExprField { + ident: Ident { + name: field.name.ident.clone(), + span: field.name.span(), + }, + expr: Box::new(expr), + span: field.span(), + } +} + +fn lower_member(member: &ast::Member) -> Ident { + let name = match member { + ast::Member::Ident(ident) => ident.ident.clone(), + ast::Member::Index(idx) => idx.int.to_string(), + }; + + Ident { + name, + span: member.span(), + } +} + +fn lower_prefix_op(op: &token::PrefixOp) -> hir::UnOp { + use token::PrefixOp; + let span = op.span(); + let kind = match op { + PrefixOp::Not(_) => hir::UnOpKind::Not, + PrefixOp::Neg(_) => hir::UnOpKind::Neg, + }; + hir::UnOp { kind, span } +} + +fn lower_suffix_op(op: &token::SuffixOp) -> hir::UnOp { + use token::SuffixOp; + let span = op.span(); + let kind = match op { + SuffixOp::Inc(_) => UnOpKind::Inc, + SuffixOp::Dec(_) => UnOpKind::Dec, + }; + hir::UnOp { kind, span } +} + +fn lower_add_op(op: &token::AddOp) -> hir::BinOp { + use token::AddOp; + let span = op.span(); + let kind = match op { + AddOp::Add(_) => BinOpKind::Add, + AddOp::Sub(_) => BinOpKind::Sub, + }; + hir::BinOp { kind, span } +} + +fn lower_mul_op(op: &token::MulOp) -> hir::BinOp { + use token::MulOp; + let span = op.span(); + let kind = match op { + MulOp::Mul(_) => BinOpKind::Mul, + MulOp::Div(_) => BinOpKind::Div, + MulOp::Rem(_) => BinOpKind::Rem, + }; + hir::BinOp { kind, span } +} + +fn lower_shift_op(op: &token::ShiftOp) -> hir::BinOp { + use token::ShiftOp; + let span = op.span(); + let kind = match op { + ShiftOp::Shl(_) => BinOpKind::Shl, + ShiftOp::Shr(_) => BinOpKind::Shr, + }; + hir::BinOp { kind, span } +} + +fn lower_assign_op(op: &token::AssignOp) -> hir::BinOp { + use token::AssignOp; + let span = op.span(); + let kind = match op { + AssignOp::Assign(_) => BinOpKind::Ass, + AssignOp::Add(_) => BinOpKind::AddAss, + AssignOp::Sub(_) => BinOpKind::SubAss, + AssignOp::Mul(_) => BinOpKind::MulAss, + AssignOp::Div(_) => BinOpKind::DivAss, + AssignOp::Rem(_) => BinOpKind::RemAss, + AssignOp::Shl(_) => BinOpKind::ShlAss, + AssignOp::Shr(_) => BinOpKind::ShrAss, + }; + hir::BinOp { kind, span } +} + +fn lower_range_op(op: &token::RangeOp) -> hir::BinOp { + use token::RangeOp; + let span = op.span(); + let kind = match op { + RangeOp::Lcro(_) => BinOpKind::Range, + RangeOp::Lcrc(_) => BinOpKind::RangeInc, + }; + hir::BinOp { kind, span } +} + +fn lower_cmp_op(op: &token::CmpOp) -> hir::BinOp { + use token::CmpOp; + let span = op.span(); + let kind = match op { + CmpOp::Lt(_) => BinOpKind::Lt, + CmpOp::Le(_) => BinOpKind::Le, + CmpOp::Gt(_) => BinOpKind::Gt, + CmpOp::Ge(_) => BinOpKind::Ge, + CmpOp::Eq(_) => BinOpKind::Eq, + CmpOp::Ne(_) => BinOpKind::Ne, + CmpOp::Way3(_) => BinOpKind::Way3, + }; + hir::BinOp { kind, span } +} + +fn lower_def_as_item(ctx: &mut LoweringContext, def: &ast::Def) -> Item { + match def { + ast::Def::Fn(def) => { + let name = def.name.ident.clone(); + let sym_id = ctx.lookup_across(&name).unwrap().id; + + let mut ctx = ctx.new_fn(); + let sign = lower_fn_sign(&mut ctx, &def.function.sign); + let body = lower_fn_body(&mut ctx, &def.function.body); + + return Item { + kind: ItemKind::FnDef(FnDef { + sym_id, + name: Some(Ident { + name, + span: def.name.span(), + }), + side_effect: sign.0, + params: sign.1, + ret: sign.2, + body, + }), + span: def.span(), + }; + } + + ast::Def::Type(def) => { + let name = def.name.ident.clone(); + let sym_id = ctx.lookup_across(&name).unwrap().id; + + return Item { + kind: ItemKind::TyDef(TyDef { + sym_id, + name: Some(Ident { + name, + span: def.name.span(), + }), + ty: Box::new(lower_type(ctx, &def.ty)), + }), + span: def.span(), + }; + } + + ast::Def::Local(local) => { + ctx.diag(UnimplementedGlobal::new(local.span())); + } + + ast::Def::Error(_) => (), + } + + Item { + kind: ItemKind::Error, + span: def.span(), + } +} + +fn lower_fn_sign(ctx: &mut LoweringContext, sign: &ast::FnSign) -> (bool, Vec, Box) { + let side_effect = sign.mut_tok.is_some(); + + let params = if let Some(params) = &sign.params { + params + .0 + .inner + .vals() + .map(|p| { + let name = p.name.ident.clone(); + let span = p.name.span(); + + let sym_id = ctx.define_local(Some(name.clone()), SymKind::Local, span); + + let ty = lower_type(ctx, &p.ty); + + Param { + sym_id, + mutable: p.mut_tok.is_some(), + name: Ident { name, span }, + ty: Box::new(ty), + span: p.span(), + } + }) + .collect() + } else { + vec![] + }; + + let ret = if let Some(r) = &sign.ret { + lower_type(ctx, &r.ty) + } else { + Ty { + kind: TyKind::Tuple(vec![]), + span: sign.span(), + } + }; + + (side_effect, params, Box::new(ret)) +} + +fn lower_fn_body(ctx: &mut LoweringContext, body: &ast::FnBody) -> Body { + let kind = match body { + ast::FnBody::Expr(expr) => { + let body_expr = match expr.as_ref() { + ast::Expr::Block(block) => { + let label = block.label.as_ref().map(|l| l.label.label.clone()); + let mut guard = ctx.new_dest(label, block.span(), DestKind::Body); + let dest_id = guard.dest_id(); + lower_block(&mut guard, &block.block, Some(dest_id)) + } + + _ => { + let mut guard = ctx.new_dest(None, expr.span(), DestKind::Body); + let dest_id = Some(guard.dest_id()); + let result_expr = lower_expr(&mut guard, expr); + + Expr { + kind: ExprKind::Block(Block { + dest_id, + stmts: vec![], + expr: Box::new(result_expr), + source: BlockSource::FnBody, + }), + span: expr.span(), + } + } + }; + + BodyKind::Expr(Box::new(body_expr)) + } + + ast::FnBody::Ffi(ffi) => { + if ffi.abi.inner.abi.str.to_uppercase() != "C" { + ctx.diag(UnsupportedFfi::new(ffi.abi.inner.abi.span)); + BodyKind::Error + } else { + let symbol = ffi.abi.inner.symbol.str.clone(); + BodyKind::CFfi(symbol) + } + } + + ast::FnBody::Asm(asm) => { + ctx.diag(UnimplementedAsm::new(asm.span())); + BodyKind::Error + } + }; + + Body { + kind, + span: body.span(), + } +} + +fn lower_if_expr(ctx: &mut LoweringContext, if_expr: &ast::ExprIf) -> Expr { + let span = if_expr.span(); + let cond = lower_expr(ctx, &if_expr.cond); + let then_branch = lower_block(ctx, &if_expr.then_branch, None); + + let else_branch = if let Some(else_br) = &if_expr.else_branch { + lower_block(ctx, &else_br.body, None) + } else { + Expr { + kind: ExprKind::Tuple(vec![]), + span, + } + }; + + Expr { + kind: ExprKind::If(Box::new(cond), Box::new(then_branch), Box::new(else_branch)), + span, + } +} + +fn lower_block(ctx: &mut LoweringContext, block: &ast::Block, dest_id: Option) -> Expr { + let mut ctx = ctx.new_scope(); + + let inner = &block.0.inner; + + for stmt in inner.vals() { + if let ast::Stmt::Def(def) = stmt { + match def { + ast::Def::Fn(def) => { + let name = def.name.ident.clone(); + ctx.define_local(Some(name), SymKind::Fn, def.span()); + } + + ast::Def::Type(def) => { + let name = def.name.ident.clone(); + ctx.define_local(Some(name), SymKind::Type, def.span()); + } + + _ => {} + } + } + } + + let mut stmts = Vec::new(); + for (stmt, _) in &inner.inner { + if let Some(hir_stmt) = lower_stmt(&mut ctx, stmt) { + stmts.push(hir_stmt); + } + } + + let expr = 'blk: { + let span = if let Some(last) = &inner.last { + match last.as_ref() { + ast::Stmt::Expr(expr) => { + break 'blk lower_expr(&mut ctx, expr); + } + _ => { + if let Some(hir_stmt) = lower_stmt(&mut ctx, last) { + stmts.push(hir_stmt); + } + last.span() + } + } + } else { + block.span() + }; + + Expr { + kind: ExprKind::Tuple(vec![]), + span, + } + }; + + Expr { + kind: ExprKind::Block(Block { + dest_id, + stmts, + expr: Box::new(expr), + source: BlockSource::Block, + }), + span: block.span(), + } +} + +fn lower_stmt(ctx: &mut LoweringContext, stmt: &ast::Stmt) -> Option { + let kind = match stmt { + ast::Stmt::Def(def) => match def { + ast::Def::Local(local) => { + let name = local.name.ident.clone(); + + let ty = match local.ty.as_ref() { + Some(ty) => lower_type(ctx, ty), + None => { + let start = local.name.span().end; + let end = local.init.as_ref().unwrap().eq_tok.span().start; + Ty { + kind: TyKind::Infer, + span: Span { start, end }, + } + } + }; + + let init = local.init.as_ref().map(|i| lower_expr(ctx, &i.value)); + + let sym_id = ctx.define_local(Some(name.clone()), SymKind::Local, local.span()); + + StmtKind::LocalDef(LocalDef { + sym_id, + mutable: local.mut_tok.is_some(), + name: Some(Ident { + name, + span: local.name.span(), + }), + ty: Box::new(ty), + init: init.map(Box::new), + span: local.span(), + }) + } + + ast::Def::Fn(def) => { + let name = def.name.ident.clone(); + + let sym_id = ctx.lookup_local(&name).unwrap().id; + + let mut ctx = ctx.new_fn(); + let sign = lower_fn_sign(&mut ctx, &def.function.sign); + let body = lower_fn_body(&mut ctx, &def.function.body); + + StmtKind::Item(Item { + kind: ItemKind::FnDef(FnDef { + sym_id, + name: Some(Ident { + name, + span: def.name.span(), + }), + side_effect: sign.0, + params: sign.1, + ret: sign.2, + body, + }), + span: def.span(), + }) + } + + ast::Def::Type(def) => { + let name_str = def.name.ident.clone(); + + let sym_id = ctx.lookup_local(&name_str).unwrap().id; + + let ty_value = lower_type(ctx, &def.ty); + + StmtKind::Item(Item { + kind: ItemKind::TyDef(TyDef { + sym_id, + name: Some(Ident { + name: name_str, + span: def.name.span(), + }), + ty: Box::new(ty_value), + }), + span: def.span(), + }) + } + + ast::Def::Error(_) => StmtKind::Error, + }, + + ast::Stmt::Expr(expr) => StmtKind::Expr(Box::new(lower_expr(ctx, expr))), + + ast::Stmt::Error(_) => StmtKind::Error, + }; + + Some(Stmt { + kind, + span: stmt.span(), + }) +} + +fn lower_return_expr(ctx: &mut LoweringContext, ret_expr: &ast::ExprReturn) -> Expr { + let span = ret_expr.span(); + + let dest_id = if let Some(dest) = ctx.find_fn_body_dest() { + dest + } else { + ctx.diag(RetOutsideFn::new(ret_expr.span())); + return Expr { + kind: ExprKind::Error, + span, + }; + }; + + let value = if let Some(val) = &ret_expr.value { + lower_expr(ctx, val) + } else { + Expr { + kind: ExprKind::Tuple(vec![]), + span, + } + }; + + Expr { + kind: ExprKind::Break(dest_id, Box::new(value)), + span, + } +} + +fn lower_break_expr(ctx: &mut LoweringContext, break_expr: &ast::ExprBreak) -> Expr { + let span = break_expr.span(); + let error = Expr { + kind: ExprKind::Error, + span, + }; + + let dest_id = if let Some(label) = &break_expr.label { + if let Some(dest) = ctx.lookup_label(&label.label) { + dest + } else { + let label_name = format!("'{}", label.label); + ctx.diag(NotFound::new(label_name, label.span())); + return error; + } + } else if let Some(dest) = ctx.find_loop_dest() { + dest + } else { + ctx.diag(BreakOutsideLoop::new(span)); + return error; + }; + + let value = if let Some(val) = &break_expr.value { + lower_expr(ctx, val) + } else { + Expr { + kind: ExprKind::Tuple(vec![]), + span, + } + }; + + Expr { + kind: ExprKind::Break(dest_id, Box::new(value)), + span, + } +} + +fn lower_cont_expr(ctx: &mut LoweringContext, cont_expr: &ast::ExprCont) -> Expr { + let span = cont_expr.span(); + let error = Expr { + kind: ExprKind::Error, + span, + }; + + let dest_id = if let Some(label) = &cont_expr.label { + if let Some(dest) = ctx.lookup_label(&label.label) { + dest + } else { + let label_name = format!("'{}", label.label); + ctx.diag(NotFound::new(label_name, label.span())); + return error; + } + } else if let Some(dest) = ctx.find_loop_dest() { + dest + } else { + ctx.diag(ContOutsideLoop::new(span)); + return error; + }; + + Expr { + kind: ExprKind::Cont(dest_id), + span, + } +} + +fn lower_while_expr(ctx: &mut LoweringContext, while_expr: &ast::ExprWhile) -> Expr { + let span = while_expr.span(); + let label = while_expr.label.as_ref().map(|l| l.label.label.clone()); + let mut ctx = ctx.new_dest(label, span, DestKind::Loop); + let dest_id = ctx.dest_id(); + + let cond = lower_expr(&mut ctx, &while_expr.cond); + + let body = lower_block(&mut ctx, &while_expr.body, None); + let body_span = while_expr.body.span(); + let body_sym_id = ctx.define_local(None, SymKind::Local, body_span); + let body = LocalDef { + sym_id: body_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Tuple(vec![]), + span: body_span, + }), + init: Some(Box::new(body)), + span: body_span, + }; + + let cont_expr = Expr { + kind: ExprKind::Cont(dest_id), + span, + }; + + let body = Expr { + kind: ExprKind::Block(Block { + dest_id: None, + stmts: vec![Stmt { + kind: StmtKind::LocalDef(body), + span: body_span, + }], + expr: Box::new(cont_expr), + source: BlockSource::While, + }), + span, + }; + + let else_branch = if let Some(else_branch) = &while_expr.exit { + let block = lower_block(&mut ctx, &else_branch.body, None); + match block.kind { + ExprKind::Block(block) if block.stmts.is_empty() => block.expr, + _ => Box::new(block), + } + } else { + Box::new(Expr { + kind: ExprKind::Tuple(vec![]), + span, + }) + }; + + let dest_id = Some(dest_id); + + Expr { + kind: ExprKind::Block(Block { + dest_id, + stmts: vec![], + expr: Box::new(Expr { + kind: ExprKind::If(Box::new(cond), Box::new(body), else_branch), + span, + }), + source: BlockSource::While, + }), + span, + } +} + +fn lower_for_expr(ctx: &mut LoweringContext, for_expr: &ast::ExprFor) -> Expr { + let span = for_expr.span(); + let label = for_expr.label.as_ref().map(|l| l.label.label.clone()); + + let range_span = for_expr.range.span(); + let range_expr = lower_expr(ctx, &for_expr.range); + let range_sym_id = ctx.define_local(None, SymKind::Local, range_span); + let range_def = LocalDef { + sym_id: range_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Infer, + span: range_span, + }), + init: Some(Box::new(range_expr)), + span: range_span, + }; + let range_use = Expr { + kind: ExprKind::Use(None, range_sym_id), + span, + }; + + let begin_init = Expr { + kind: ExprKind::Begin(Box::new(range_use.clone())), + span, + }; + let begin_sym_id = ctx.define_local(None, SymKind::Local, span); + let begin_def = LocalDef { + sym_id: begin_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Infer, + span, + }), + init: Some(Box::new(begin_init)), + span, + }; + let begin_use = Expr { + kind: ExprKind::Use(None, begin_sym_id), + span, + }; + + let end_init = Expr { + kind: ExprKind::End(Box::new(range_use.clone())), + span, + }; + let end_sym_id = ctx.define_local(None, SymKind::Local, span); + let end_def = LocalDef { + sym_id: end_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Infer, + span, + }), + init: Some(Box::new(end_init)), + span, + }; + let end_use = Expr { + kind: ExprKind::Use(None, end_sym_id), + span, + }; + + let mut ctx = ctx.new_dest(label, span, DestKind::Loop); + let dest_id = ctx.dest_id(); + + let cond = Expr { + kind: ExprKind::Binary( + BinOp { + kind: BinOpKind::Lt, + span, + }, + Box::new(begin_use.clone()), + Box::new(end_use.clone()), + ), + span, + }; + + let elem_name = for_expr.name.ident.clone(); + let elem_span = for_expr.name.span(); + let elem_init = Expr { + kind: ExprKind::Unary( + UnOp { + kind: UnOpKind::Deref, + span: elem_span, + }, + Box::new(begin_use.clone()), + ), + span: elem_span, + }; + let elem_sym_id = ctx.define_local(Some(elem_name.clone()), SymKind::Local, elem_span); + let elem_def = LocalDef { + sym_id: elem_sym_id, + mutable: for_expr.mutable.is_some(), + name: Some(Ident { + name: elem_name, + span: elem_span, + }), + ty: Box::new(Ty { + kind: TyKind::Infer, + span: elem_span, + }), + init: Some(Box::new(elem_init)), + span: elem_span, + }; + + let user_body = lower_block(&mut ctx, &for_expr.body, None); + let body_span = for_expr.body.span(); + let body_sym_id = ctx.define_local(None, SymKind::Local, body_span); + let body_def = LocalDef { + sym_id: body_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Tuple(vec![]), + span: body_span, + }), + init: Some(Box::new(user_body)), + span: body_span, + }; + + let increment = Expr { + kind: ExprKind::Unary( + UnOp { + kind: UnOpKind::Inc, + span, + }, + Box::new(begin_use.clone()), + ), + span, + }; + + let cont_expr = Expr { + kind: ExprKind::Cont(dest_id), + span, + }; + + let loop_body = Expr { + kind: ExprKind::Block(Block { + dest_id: None, + stmts: vec![ + Stmt { + kind: StmtKind::LocalDef(elem_def), + span: elem_span, + }, + Stmt { + kind: StmtKind::LocalDef(body_def), + span: body_span, + }, + Stmt { + kind: StmtKind::Expr(Box::new(increment)), + span, + }, + ], + expr: Box::new(cont_expr), + source: BlockSource::For, + }), + span, + }; + + let else_branch = if let Some(else_branch) = &for_expr.exit { + let block = lower_block(&mut ctx, &else_branch.body, None); + match block.kind { + ExprKind::Block(block) if block.stmts.is_empty() => block.expr, + _ => Box::new(block), + } + } else { + Box::new(Expr { + kind: ExprKind::Tuple(vec![]), + span, + }) + }; + + let loop_expr = Expr { + kind: ExprKind::Block(Block { + dest_id: Some(dest_id), + stmts: vec![], + expr: Box::new(Expr { + kind: ExprKind::If(Box::new(cond), Box::new(loop_body), else_branch), + span, + }), + source: BlockSource::For, + }), + span, + }; + + Expr { + kind: ExprKind::Block(Block { + dest_id: None, + stmts: vec![ + Stmt { + kind: StmtKind::LocalDef(range_def), + span: range_span, + }, + Stmt { + kind: StmtKind::LocalDef(begin_def), + span, + }, + Stmt { + kind: StmtKind::LocalDef(end_def), + span, + }, + ], + expr: Box::new(loop_expr), + source: BlockSource::For, + }), + span, + } +} + +fn lower_case_expr(ctx: &mut LoweringContext, case_expr: &ast::ExprCase) -> Expr { + let span = case_expr.span(); + + let mut ctx = ctx.new_dest(None, span, DestKind::Block); + let dest_id = ctx.dest_id(); + + let arms = &case_expr.arms.inner; + let arm_list: Vec<_> = arms.vals().collect(); + + let mut stmts = Vec::new(); + + for arm in arm_list { + // Ignore arm.label for now because it's hard to desugar. + // I am considering making case a primitive operation instead of if. + + let cond = lower_expr(&mut ctx, &arm.cond.0); + let value = lower_expr(&mut ctx, &arm.value); + let break_stmt = Expr { + kind: ExprKind::Break(dest_id, Box::new(value)), + span: arm.value.span(), + }; + + let arm_span = arm.span(); + + let if_expr = Expr { + kind: ExprKind::If( + Box::new(cond), + Box::new(break_stmt), + Box::new(Expr { + kind: ExprKind::Tuple(vec![]), + span, + }), + ), + span: arm_span, + }; + + stmts.push(Stmt { + kind: StmtKind::Expr(Box::new(if_expr)), + span: arm_span, + }); + } + + let else_expr = if let Some(else_arm) = &case_expr.else_arm { + lower_expr(&mut ctx, &else_arm.value) + } else { + Expr { + kind: ExprKind::Tuple(vec![]), + span, + } + }; + + Expr { + kind: ExprKind::Block(Block { + dest_id: Some(dest_id), + stmts, + expr: Box::new(else_expr), + source: BlockSource::Case, + }), + span, + } +} + +fn lower_cmp_expr(ctx: &mut LoweringContext, cmp_expr: &ast::ExprCmp) -> Expr { + let span = cmp_expr.span(); + + let exprs: Vec<_> = cmp_expr.0.vals().collect(); + let ops: Vec<_> = cmp_expr.0.seps().collect(); + + assert!(!ops.is_empty()); + assert_eq!(exprs.len() - 1, ops.len()); + + if exprs.len() == 2 { + let lhs_expr = lower_expr(ctx, exprs[0]); + let lhs_span = lhs_expr.span; + let lhs = Expr { + kind: ExprKind::RefOf(false, Box::new(lhs_expr)), + span: lhs_span, + }; + + let rhs_expr = lower_expr(ctx, exprs[1]); + let rhs_span = rhs_expr.span; + let rhs = Expr { + kind: ExprKind::RefOf(false, Box::new(rhs_expr)), + span: rhs_span, + }; + + let op = lower_cmp_op(ops[0]); + + return Expr { + kind: ExprKind::Binary(op, Box::new(lhs), Box::new(rhs)), + span, + }; + } + + let mut ctx = ctx.new_dest(None, span, DestKind::Block); + let dest_id = ctx.dest_id(); + + let first_node = exprs.first().unwrap(); + let first_expr = lower_expr(&mut ctx, first_node); + let first_span = first_node.span(); + let first_sym_id = ctx.define_local(None, SymKind::Local, first_span); + + let first_init = Expr { + kind: ExprKind::RefOf(false, Box::new(first_expr)), + span: first_span, + }; + + let first_def = LocalDef { + sym_id: first_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Infer, + span: first_span, + }), + init: Some(Box::new(first_init)), + span: first_span, + }; + + let mut stmts = vec![Stmt { + kind: StmtKind::LocalDef(first_def), + span: first_span, + }]; + + let mut lhs_span = first_span; + let mut lhs_use = Expr { + kind: ExprKind::Use(None, first_sym_id), + span: lhs_span, + }; + + for i in 0..ops.len() { + let rhs_node = exprs[i + 1]; + let rhs_expr = lower_expr(&mut ctx, rhs_node); + let rhs_span = rhs_node.span(); + let rhs_sym_id = ctx.define_local(None, SymKind::Local, rhs_span); + let rhs_init = Expr { + kind: ExprKind::RefOf(false, Box::new(rhs_expr)), + span: rhs_span, + }; + let rhs_def = LocalDef { + sym_id: rhs_sym_id, + mutable: false, + name: None, + ty: Box::new(Ty { + kind: TyKind::Infer, + span: rhs_span, + }), + init: Some(Box::new(rhs_init)), + span: rhs_span, + }; + let rhs_use = Expr { + kind: ExprKind::Use(None, rhs_sym_id), + span: rhs_span, + }; + + let op = lower_cmp_op(ops[i]); + let cmp_span = lhs_span.merge(rhs_span); + + let cmp = Expr { + kind: ExprKind::Binary(op, Box::new(lhs_use), Box::new(rhs_use.clone())), + span: cmp_span, + }; + + let negated = Expr { + kind: ExprKind::Unary( + UnOp { + kind: UnOpKind::Not, + span: cmp_span, + }, + Box::new(cmp), + ), + span: cmp_span, + }; + + let false_const = Expr { + kind: ExprKind::Const(Constant { + kind: ConstantKind::Bool(false), + span, + }), + span: cmp_span, + }; + + let break_false = Expr { + kind: ExprKind::Break(dest_id, Box::new(false_const)), + span: cmp_span, + }; + + let if_stmt = Expr { + kind: ExprKind::If( + Box::new(negated), + Box::new(break_false), + Box::new(Expr { + kind: ExprKind::Tuple(vec![]), + span: cmp_span, + }), + ), + span: cmp_span, + }; + + stmts.push(Stmt { + kind: StmtKind::LocalDef(rhs_def), + span: rhs_span, + }); + stmts.push(Stmt { + kind: StmtKind::Expr(Box::new(if_stmt)), + span: cmp_span, + }); + + lhs_span = rhs_span; + lhs_use = rhs_use; + } + + let true_const = Expr { + kind: ExprKind::Const(Constant { + kind: ConstantKind::Bool(true), + span, + }), + span, + }; + + Expr { + kind: ExprKind::Block(Block { + dest_id: Some(dest_id), + stmts, + expr: Box::new(true_const), + source: BlockSource::Block, + }), + span, + } +} + +fn lower_and_expr(ctx: &mut LoweringContext, and_expr: &ast::ExprLogical) -> Expr { + let span = and_expr.span(); + + let exprs: Vec<_> = and_expr.0.vals().collect(); + + assert!(exprs.len() > 1); + let short_circuit = matches!(and_expr.0.seps().next().unwrap(), token::LogicalOp::Or(_)); + + let mut ctx = ctx.new_dest(None, span, DestKind::Block); + let dest_id = ctx.dest_id(); + let mut stmts = Vec::new(); + + for expr in &exprs { + let expr_span = expr.span(); + let mut cond = lower_expr(&mut ctx, expr); + + if !short_circuit { + cond = Expr { + kind: ExprKind::Unary( + UnOp { + kind: UnOpKind::Not, + span: expr_span, + }, + Box::new(cond), + ), + span: expr_span, + }; + } + + let value = Expr { + kind: ExprKind::Const(Constant { + kind: ConstantKind::Bool(short_circuit), + span: expr_span, + }), + span: expr_span, + }; + + let break_expr = Expr { + kind: ExprKind::Break(dest_id, Box::new(value)), + span: expr_span, + }; + + let if_stmt = Expr { + kind: ExprKind::If( + Box::new(cond), + Box::new(break_expr), + Box::new(Expr { + kind: ExprKind::Tuple(vec![]), + span: expr_span, + }), + ), + span: expr_span, + }; + + stmts.push(Stmt { + kind: StmtKind::Expr(Box::new(if_stmt)), + span: expr_span, + }); + } + + let value = Expr { + kind: ExprKind::Const(Constant { + kind: ConstantKind::Bool(!short_circuit), + span, + }), + span, + }; + + Expr { + kind: ExprKind::Block(Block { + dest_id: Some(dest_id), + stmts, + expr: Box::new(value), + source: BlockSource::Block, + }), + span, + } +} + +fn lower_fn_expr(ctx: &mut LoweringContext, fn_expr: &ast::ExprFn) -> Expr { + let span = fn_expr.span(); + + let sym_id = ctx.define_local(None, SymKind::Fn, span); + + let mut ctx = ctx.new_fn(); + let sign = lower_fn_sign(&mut ctx, &fn_expr.sign); + let body = lower_fn_body(&mut ctx, &fn_expr.body); + + let fn_item = Item { + kind: ItemKind::FnDef(FnDef { + sym_id, + name: None, + side_effect: sign.0, + params: sign.1, + ret: sign.2, + body, + }), + span, + }; + + let use_expr = Expr { + kind: ExprKind::Use(None, sym_id), + span, + }; + + Expr { + kind: ExprKind::Block(Block { + dest_id: None, + stmts: vec![Stmt { + kind: StmtKind::Item(fn_item), + span, + }], + expr: Box::new(use_expr), + source: BlockSource::Block, + }), + span, + } +} diff --git a/lowerer/src/symbol.rs b/lowerer/src/symbol.rs new file mode 100644 index 0000000..63b6fdc --- /dev/null +++ b/lowerer/src/symbol.rs @@ -0,0 +1,170 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::hash::Hash; + +use common::span::Span; +use hir::{DestId, SymId}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SymKind { + Type, + Fn, + Local, +} + +#[derive(Debug, Clone)] +pub struct Symbol { + pub id: SymId, + pub name: Option, + pub kind: SymKind, + pub span: Span, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DestKind { + Block, + Loop, + Body, +} + +#[derive(Debug, Clone)] +pub struct Destination { + pub id: DestId, + pub label: Option, + pub span: Span, + pub kind: DestKind, +} + +#[derive(Debug, Clone, Default)] +struct SymbolScope(HashMap); + +#[derive(Debug, Clone, Default)] +struct LocalScope { + symbols: SymbolScope, + labels: HashMap, +} + +#[derive(Debug, Clone, Default)] +pub struct SymbolTable { + global: SymbolScope, + local: Vec>, +} + +impl SymbolTable { + pub fn new() -> Self { + Self { + global: SymbolScope::default(), + local: vec![], + } + } + + pub fn is_global(&self) -> bool { + self.local.is_empty() + } + + pub fn enter_scope(&mut self) { + if let Some(scopes) = self.local.last_mut() { + scopes.push(LocalScope::default()); + } + } + + pub fn exit_scope(&mut self) { + if let Some(scopes) = self.local.last_mut() + && scopes.len() > 1 + { + scopes.pop(); + } + } + + pub fn enter_fn(&mut self) { + self.local.push(vec![LocalScope::default()]); + } + + pub fn exit_fn(&mut self) { + self.local.pop(); + } + + pub fn contains_in_scope(&self, key: &Q) -> bool + where + String: Borrow, + Q: Hash + Eq + ?Sized, + { + if let Some(scopes) = self.local.last() { + scopes.last().unwrap().symbols.0.contains_key(key) + } else { + self.global.0.contains_key(key) + } + } + + pub fn define_label(&mut self, label: String, id: DestId) { + let f = self.local.last_mut().unwrap(); + let scope = f.last_mut().unwrap(); + scope.labels.insert(label, id); + } + + pub fn get_label(&self, label: &Q) -> Option + where + String: Borrow, + Q: Hash + Eq + ?Sized, + { + let Some(scopes) = self.local.last() else { + unreachable!("get_label called outside of any function") + }; + for scope in scopes.iter().rev() { + if let Some(id) = scope.labels.get(label) { + return Some(*id); + } + } + None + } + + pub fn define_local(&mut self, key: String, value: Symbol) { + if let Some(scopes) = self.local.last_mut() { + scopes.last_mut().unwrap().symbols.0.insert(key, value); + } else { + self.global.0.insert(key, value); + } + } + + pub fn define_global(&mut self, key: String, value: Symbol) { + self.global.0.insert(key, value); + } + + pub fn get_global(&self, key: &Q) -> Option<&Symbol> + where + String: Borrow, + Q: Hash + Eq + ?Sized, + { + self.global.0.get(key) + } + + pub fn get_local(&self, key: &Q) -> Option<&Symbol> + where + String: Borrow, + Q: Hash + Eq + ?Sized, + { + if let Some(scopes) = self.local.last() { + for scope in scopes.iter().rev() { + if let Some(value) = scope.symbols.0.get(key) { + return Some(value); + } + } + } + self.global.0.get(key) + } + + pub fn get_across(&self, key: &Q) -> Option<&Symbol> + where + String: Borrow, + Q: Hash + Eq + ?Sized, + { + for function_scopes in self.local.iter().rev() { + for scope in function_scopes.iter().rev() { + if let Some(value) = scope.symbols.0.get(key) { + return Some(value); + } + } + } + self.global.0.get(key) + } +} diff --git a/parser/src/syntax/parse.rs b/parser/src/syntax/parse.rs index 98a5c4f..495a1b5 100644 --- a/parser/src/syntax/parse.rs +++ b/parser/src/syntax/parse.rs @@ -506,9 +506,9 @@ impl_parse!(ExprLit { lit, suffix }); impl_parse!(ExprIdent(x)); impl_parse!(ExprCase { - label, case_tok, arms, + else_arm, }); impl_parse!(CaseArm { @@ -518,8 +518,14 @@ impl_parse!(CaseArm { value, }); -impl_parse!(ExprIf { +impl_parse!(ElseArm { label, + else_tok, + eq_tok, + value, +}); + +impl_parse!(ExprIf { if_tok, cond, then_branch, @@ -537,7 +543,8 @@ impl_parse!(ExprWhile { impl_parse!(ExprFor { label, for_tok, - cond, + mutable, + name, in_tok, range, body, diff --git a/syntax/src/ast.rs b/syntax/src/ast.rs index 7ac00fb..2bcd175 100644 --- a/syntax/src/ast.rs +++ b/syntax/src/ast.rs @@ -278,9 +278,9 @@ pub struct ExprIndex { #[derive(Debug, Clone, AstPrint)] pub struct ExprCase { - pub label: Option, pub case_tok: Tok![case], pub arms: Tok![{CaseArm,}], + pub else_arm: Option, } #[derive(Debug, Clone, AstPrint)] @@ -292,8 +292,15 @@ pub struct CaseArm { } #[derive(Debug, Clone, AstPrint)] -pub struct ExprIf { +pub struct ElseArm { pub label: Option, + pub else_tok: Tok![else], + pub eq_tok: Tok![=], + pub value: Box, +} + +#[derive(Debug, Clone, AstPrint)] +pub struct ExprIf { pub if_tok: Tok![if], pub cond: Box, pub then_branch: Block, @@ -313,7 +320,8 @@ pub struct ExprWhile { pub struct ExprFor { pub label: Option, pub for_tok: Tok![for], - pub cond: Box, + pub mutable: Option, + pub name: Ident, pub in_tok: Tok![in], pub range: Box, pub body: Block, diff --git a/syntax/src/span.rs b/syntax/src/span.rs index 7214dcc..5b69699 100644 --- a/syntax/src/span.rs +++ b/syntax/src/span.rs @@ -69,11 +69,11 @@ impl Spanned for Param { impl Spanned for ExprCase { fn span(&self) -> Span { - let first = match &self.label { - Some(first) => first.span(), - _ => self.case_tok.span(), + let first = self.case_tok.span(); + let last = match &self.else_arm { + Some(last) => last.span(), + _ => self.arms.span(), }; - let last = self.arms.span(); Span::merge(first, last) } } @@ -89,12 +89,20 @@ impl Spanned for CaseArm { } } -impl Spanned for ExprIf { +impl Spanned for ElseArm { fn span(&self) -> Span { let first = match &self.label { Some(first) => first.span(), - _ => self.if_tok.span(), + _ => self.else_tok.span(), }; + let last = self.value.span(); + Span::merge(first, last) + } +} + +impl Spanned for ExprIf { + fn span(&self) -> Span { + let first = self.if_tok.span(); let last = match &self.else_branch { Some(last) => last.span(), _ => self.then_branch.span(), diff --git a/syntax/src/token/group.rs b/syntax/src/token/group.rs index 034fe87..dc6cc6e 100644 --- a/syntax/src/token/group.rs +++ b/syntax/src/token/group.rs @@ -70,6 +70,18 @@ impl Separated { assert_eq!(self.len(), 1); self.last.take().unwrap() } + + pub fn vals(&self) -> impl Iterator { + self + .inner + .iter() + .map(|(val, _)| val) + .chain(self.last.iter().map(|t| t.as_ref())) + } + + pub fn seps(&self) -> impl Iterator { + self.inner.iter().map(|(_, sep)| sep) + } } fn print_with_ident(x: &impl AstPrint, f: &mut impl Write) -> fmt::Result {