use crate::SourceLocation;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HirBodyId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HirExprId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HirStmtId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HirBlockId(pub u32);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arena<T> {
items: Vec<T>,
}
impl<T> Default for Arena<T> {
fn default() -> Self {
Self { items: Vec::new() }
}
}
impl<T> Arena<T> {
pub fn alloc(&mut self, value: T) -> u32 {
let id = self.items.len() as u32;
self.items.push(value);
id
}
pub fn get(&self, index: u32) -> Option<&T> {
self.items.get(index as usize)
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.items.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct BodySourceMap {
pub expr_ranges: Vec<SourceLocation>,
pub stmt_ranges: Vec<SourceLocation>,
pub block_ranges: Vec<SourceLocation>,
}
impl BodySourceMap {
pub fn expr_range(&self, id: HirExprId) -> Option<SourceLocation> {
self.expr_ranges.get(id.0 as usize).copied()
}
pub fn stmt_range(&self, id: HirStmtId) -> Option<SourceLocation> {
self.stmt_ranges.get(id.0 as usize).copied()
}
pub fn block_range(&self, id: HirBlockId) -> Option<SourceLocation> {
self.block_ranges.get(id.0 as usize).copied()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BodyOwnerKind {
ProgramRoot,
Subroutine {
name: Option<String>,
},
Method {
name: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BodyOwner {
pub kind: BodyOwnerKind,
pub ordinal: u32,
}
impl BodyOwner {
pub fn new(kind: BodyOwnerKind, ordinal: u32) -> Self {
Self { kind, ordinal }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Sigil {
Scalar,
Array,
Hash,
Code,
Glob,
}
impl Sigil {
fn from_str(s: &str) -> Self {
match s {
"$" => Sigil::Scalar,
"@" => Sigil::Array,
"%" => Sigil::Hash,
"&" => Sigil::Code,
"*" => Sigil::Glob,
_ => Sigil::Scalar, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AccessMode {
Read,
Write,
ReadModifyWrite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VariableKind {
Lexical,
Package,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HirVariable {
pub sigil: Sigil,
pub name: String,
pub kind: VariableKind,
pub access: AccessMode,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AssignMode {
Simple,
ReadModifyWrite,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UnaryMode {
Read,
ReadModifyWrite,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Concat,
Other(String),
}
impl BinaryOp {
fn from_str(s: &str) -> Self {
match s {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"." => BinaryOp::Concat,
other => BinaryOp::Other(other.to_string()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HirExpr {
Variable(HirVariable),
Binary {
lhs: HirExprId,
op: BinaryOp,
rhs: HirExprId,
},
Assign {
lhs: HirExprId,
rhs: HirExprId,
mode: AssignMode,
},
Unary {
operand: HirExprId,
mode: UnaryMode,
op: String,
},
Call {
args: Vec<HirExprId>,
ast_kind: String,
},
Opaque {
ast_kind: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeclStorageClass {
My,
Our,
Local,
State,
}
impl DeclStorageClass {
fn from_str(s: &str) -> Self {
match s {
"my" => DeclStorageClass::My,
"our" => DeclStorageClass::Our,
"local" => DeclStorageClass::Local,
"state" => DeclStorageClass::State,
_ => DeclStorageClass::My,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HirStmt {
Expr(HirExprId),
Let {
name: String,
sigil: Sigil,
storage: DeclStorageClass,
init: Option<HirExprId>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HirBlock {
pub stmts: Vec<HirStmtId>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HirBody {
pub exprs: Arena<HirExpr>,
pub stmts: Arena<HirStmt>,
pub blocks: Arena<HirBlock>,
pub source_map: BodySourceMap,
pub root_block: HirBlockId,
pub owner: BodyOwnerKind,
}
impl HirBody {
pub fn expr(&self, id: HirExprId) -> Option<&HirExpr> {
self.exprs.get(id.0)
}
pub fn stmt(&self, id: HirStmtId) -> Option<&HirStmt> {
self.stmts.get(id.0)
}
pub fn block(&self, id: HirBlockId) -> Option<&HirBlock> {
self.blocks.get(id.0)
}
}
struct BodyBuilder {
exprs: Arena<HirExpr>,
stmts: Arena<HirStmt>,
blocks: Arena<HirBlock>,
source_map: BodySourceMap,
}
impl BodyBuilder {
fn new() -> Self {
Self {
exprs: Arena::default(),
stmts: Arena::default(),
blocks: Arena::default(),
source_map: BodySourceMap::default(),
}
}
fn alloc_expr(&mut self, expr: HirExpr, range: SourceLocation) -> HirExprId {
let idx = self.exprs.alloc(expr);
self.source_map.expr_ranges.push(range);
HirExprId(idx)
}
fn alloc_stmt(&mut self, stmt: HirStmt, range: SourceLocation) -> HirStmtId {
let idx = self.stmts.alloc(stmt);
self.source_map.stmt_ranges.push(range);
HirStmtId(idx)
}
fn alloc_block(&mut self, block: HirBlock, range: SourceLocation) -> HirBlockId {
let idx = self.blocks.alloc(block);
self.source_map.block_ranges.push(range);
HirBlockId(idx)
}
fn finish(self, root_block: HirBlockId, owner: BodyOwnerKind) -> HirBody {
HirBody {
exprs: self.exprs,
stmts: self.stmts,
blocks: self.blocks,
source_map: self.source_map,
root_block,
owner,
}
}
}
use crate::{Node, NodeKind};
pub fn lower_body(ast: &Node) -> HirBody {
let mut builder = BodyBuilder::new();
let stmts = match &ast.kind {
NodeKind::Program { statements } => statements.as_slice(),
_ => std::slice::from_ref(ast),
};
let root_range = ast.location;
let mut root_block = HirBlock::default();
for stmt_node in stmts {
let stmt_id = lower_statement(&mut builder, stmt_node);
root_block.stmts.push(stmt_id);
}
let root_id = builder.alloc_block(root_block, root_range);
builder.finish(root_id, BodyOwnerKind::ProgramRoot)
}
fn lower_statement(builder: &mut BodyBuilder, node: &Node) -> HirStmtId {
let range = node.location;
match &node.kind {
NodeKind::VariableDeclaration { declarator, variable, initializer, .. } => {
let (sigil_str, var_name) = match &variable.kind {
NodeKind::Variable { sigil, name } => (sigil.as_str(), name.clone()),
_ => ("$", String::from("<unknown>")),
};
let sigil = Sigil::from_str(sigil_str);
let storage = DeclStorageClass::from_str(declarator);
let init_expr_id = initializer.as_ref().map(|init_node| {
let place_expr = HirExpr::Variable(HirVariable {
sigil: Sigil::from_str(sigil_str),
name: var_name.clone(),
kind: VariableKind::Lexical,
access: AccessMode::Write,
});
let place_id = builder.alloc_expr(place_expr, variable.location);
let rhs_id = lower_expr(builder, init_node);
let assign_range =
SourceLocation { start: variable.location.start, end: init_node.location.end };
let assign_expr =
HirExpr::Assign { lhs: place_id, rhs: rhs_id, mode: AssignMode::Simple };
builder.alloc_expr(assign_expr, assign_range)
});
builder.alloc_stmt(
HirStmt::Let { name: var_name, sigil, storage, init: init_expr_id },
range,
)
}
_ => {
let expr_id = lower_expr(builder, node);
builder.alloc_stmt(HirStmt::Expr(expr_id), range)
}
}
}
fn lower_expr(builder: &mut BodyBuilder, node: &Node) -> HirExprId {
let range = node.location;
match &node.kind {
NodeKind::Variable { sigil, name } => {
let var = HirVariable {
sigil: Sigil::from_str(sigil),
name: name.clone(),
kind: VariableKind::Lexical,
access: AccessMode::Read,
};
builder.alloc_expr(HirExpr::Variable(var), range)
}
NodeKind::Binary { op, left, right } => {
let lhs_id = lower_expr(builder, left);
let rhs_id = lower_expr(builder, right);
let binary_op = BinaryOp::from_str(op);
builder.alloc_expr(HirExpr::Binary { lhs: lhs_id, op: binary_op, rhs: rhs_id }, range)
}
NodeKind::Assignment { lhs, rhs, .. } => {
let lhs_id = lower_expr(builder, lhs);
let rhs_id = lower_expr(builder, rhs);
builder.alloc_expr(
HirExpr::Assign { lhs: lhs_id, rhs: rhs_id, mode: AssignMode::Simple },
range,
)
}
_ => {
let kind_name = node.kind.kind_name().to_string();
builder.alloc_expr(HirExpr::Opaque { ast_kind: kind_name }, range)
}
}
}