Starting work on IR.

This commit is contained in:
Jesse Brault 2026-02-27 16:26:37 -06:00
parent 8df46eec8d
commit aefac57b9d
24 changed files with 365 additions and 8 deletions

View File

@ -1,5 +1,8 @@
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::function::FunctionLoweringContext;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
@ -74,6 +77,19 @@ impl Call {
} }
} }
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
let function_name = match self.callee() {
Expression::Identifier(identifier) => identifier.name(),
_ => panic!("Calling things other than identifiers not yet supported."),
};
let arguments = self
.arguments()
.iter()
.map(|arg| arg.lower_to_ir(context))
.collect();
IrExpression::Call(IrCall::new(function_name, arguments))
}
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }

View File

@ -1,5 +1,6 @@
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::Ir;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
pub struct CompilationUnit { pub struct CompilationUnit {
@ -40,4 +41,12 @@ impl CompilationUnit {
} }
diagnostics diagnostics
} }
pub fn lower_to_ir(&self) -> Vec<Ir> {
let mut irs = vec![];
for function in &self.functions {
irs.append(&mut function.lower_to_ir());
}
irs
}
} }

View File

@ -1,8 +1,10 @@
use crate::ast::call::Call; use crate::ast::call::Call;
use crate::ast::function::FunctionLoweringContext;
use crate::ast::identifier::Identifier; use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::string_literal::StringLiteral; use crate::ast::string_literal::StringLiteral;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
@ -55,4 +57,13 @@ impl Expression {
Expression::Identifier(identifier) => identifier.source_range(), Expression::Identifier(identifier) => identifier.source_range(),
} }
} }
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
match self {
Expression::Call(call) => call.lower_to_ir(context),
Expression::IntegerLiteral(integer_literal) => integer_literal.lower_to_ir(context),
Expression::String(string_literal) => string_literal.lower_to_ir(context),
Expression::Identifier(identifier) => identifier.lower_to_ir(context),
}
}
} }

View File

@ -0,0 +1,38 @@
use crate::ast::expression::Expression;
use crate::ast::function::FunctionLoweringContext;
use crate::diagnostic::Diagnostic;
use crate::ir::ir_statement::IrStatement;
use crate::symbol_table::SymbolTable;
pub struct ExpressionStatement {
expression: Box<Expression>,
}
impl ExpressionStatement {
pub fn new(expression: Expression) -> Self {
Self {
expression: expression.into(),
}
}
pub fn expression(&self) -> &Expression {
&self.expression
}
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
self.expression.gather_declared_names(symbol_table)
}
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.expression.check_name_usages(symbol_table)
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
self.expression.type_check(symbol_table)
}
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
let ir_expression = self.expression.lower_to_ir(context);
context.add_statement(IrStatement::Expression(ir_expression));
}
}

15
dmc-lib/src/ast/fqn.rs Normal file
View File

@ -0,0 +1,15 @@
pub struct Fqn {
parts: Vec<String>,
}
impl Fqn {
pub fn new(parts: &[&str]) -> Self {
Self {
parts: parts.iter().map(|s| s.to_string()).collect(),
}
}
pub fn parts(&self) -> &[String] {
self.parts.as_slice()
}
}

View File

@ -1,5 +1,9 @@
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::Ir;
use crate::ir::ir_constant::IrConstant;
use crate::ir::ir_function::IrFunction;
use crate::ir::ir_statement::IrStatement;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::FunctionSymbol; use crate::symbol::FunctionSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::{SymbolInsertError, SymbolTable};
@ -75,4 +79,64 @@ impl Function {
} }
diagnostics diagnostics
} }
pub fn lower_to_ir(&self) -> Vec<Ir> {
let mut context = FunctionLoweringContext::new();
for statement in &self.statements {
statement.lower_to_ir(&mut context);
}
let mut irs = vec![];
for constant in context.take_constants() {
irs.push(Ir::Constant(constant));
}
let ir_function = IrFunction::new(context.take_statements());
irs.push(Ir::Function(ir_function));
irs
}
}
pub struct FunctionLoweringContext {
temp_variable_counter: usize,
constant_counter: usize,
constants: Vec<IrConstant>,
statements: Vec<IrStatement>,
}
impl FunctionLoweringContext {
pub fn new() -> Self {
Self {
temp_variable_counter: 0,
constant_counter: 0,
constants: vec![],
statements: vec![],
}
}
pub fn next_temp_variable(&mut self) -> String {
let temp_variable = format!("t_{}", self.temp_variable_counter);
self.temp_variable_counter += 1;
temp_variable
}
pub fn next_constant_name(&mut self) -> String {
let constant_name = format!("%const_{}", self.constant_counter);
self.constant_counter += 1;
constant_name
}
pub fn add_constant(&mut self, constant: IrConstant) {
self.constants.push(constant);
}
pub fn take_constants(&mut self) -> Vec<IrConstant> {
std::mem::take(&mut self.constants)
}
pub fn add_statement(&mut self, statement: IrStatement) {
self.statements.push(statement);
}
pub fn take_statements(&mut self) -> Vec<IrStatement> {
std::mem::take(&mut self.statements)
}
} }

View File

@ -1,4 +1,7 @@
use crate::ast::function::FunctionLoweringContext;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::ExpressibleSymbol; use crate::symbol::ExpressibleSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
@ -58,6 +61,10 @@ impl Identifier {
} }
} }
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
IrExpression::Variable(IrVariable::new(self.name()))
}
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }

View File

@ -1,3 +1,5 @@
use crate::ast::function::FunctionLoweringContext;
use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
pub struct IntegerLiteral { pub struct IntegerLiteral {
@ -17,6 +19,10 @@ impl IntegerLiteral {
self.value self.value
} }
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
IrExpression::IntegerLiteral(self.value)
}
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }

View File

@ -1,5 +1,9 @@
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::function::FunctionLoweringContext;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVariable;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::VariableSymbol; use crate::symbol::VariableSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::{SymbolInsertError, SymbolTable};
@ -68,4 +72,11 @@ impl LetStatement {
diagnostics.append(&mut self.initializer.type_check(symbol_table)); diagnostics.append(&mut self.initializer.type_check(symbol_table));
diagnostics diagnostics
} }
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
let data = self.initializer.lower_to_ir(context);
let destination = IrVariable::new(self.declared_name());
let assign_statement = IrAssign::new(destination, data);
context.add_statement(IrStatement::Assign(assign_statement));
}
} }

View File

@ -1,6 +1,8 @@
pub mod call; pub mod call;
pub mod compilation_unit; pub mod compilation_unit;
pub mod expression; pub mod expression;
pub mod expression_statement;
pub mod fqn;
pub mod function; pub mod function;
pub mod identifier; pub mod identifier;
pub mod integer_literal; pub mod integer_literal;
@ -25,6 +27,24 @@ mod name_tests {
0 0
); );
assert_eq!(compilation_unit.check_name_usages(&symbol_table).len(), 0); assert_eq!(compilation_unit.check_name_usages(&symbol_table).len(), 0);
let irs = compilation_unit.lower_to_ir();
for ir in &irs {
println!("{:#?}", ir);
}
}
#[test]
fn hello_world() {
let mut symbol_table = SymbolTable::new();
let mut compilation_unit =
parse_compilation_unit("fn println() end fn main() println(\"Hello, World!\") end");
compilation_unit.gather_declared_names(&mut symbol_table);
compilation_unit.check_name_usages(&symbol_table);
compilation_unit.type_check(&symbol_table);
let irs = compilation_unit.lower_to_ir();
for ir in &irs {
println!("{:#?}", ir);
}
} }
#[test] #[test]

View File

@ -1,32 +1,50 @@
use crate::ast::expression::Expression; use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::function::FunctionLoweringContext;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
pub enum Statement { pub enum Statement {
Let(LetStatement), Let(LetStatement),
Expression(Expression), Expression(ExpressionStatement),
} }
impl Statement { impl Statement {
pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> { pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec<Diagnostic> {
match self { match self {
Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table), Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table),
Statement::Expression(expression) => expression.gather_declared_names(symbol_table), Statement::Expression(expression_statement) => {
expression_statement.gather_declared_names(symbol_table)
}
} }
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
match self { match self {
Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table), Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table),
Statement::Expression(expression) => expression.check_name_usages(symbol_table), Statement::Expression(expression_statement) => {
expression_statement.check_name_usages(symbol_table)
}
} }
} }
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec<Diagnostic> {
match self { match self {
Statement::Let(let_statement) => let_statement.type_check(symbol_table), Statement::Let(let_statement) => let_statement.type_check(symbol_table),
Statement::Expression(expression) => expression.type_check(symbol_table), Statement::Expression(expression_statement) => {
expression_statement.type_check(symbol_table)
}
}
}
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) {
match self {
Statement::Let(let_statement) => {
let_statement.lower_to_ir(context);
}
Statement::Expression(expression) => {
expression.lower_to_ir(context);
}
} }
} }
} }

View File

@ -1,4 +1,8 @@
use crate::ast::function::FunctionLoweringContext;
use crate::ir::ir_constant::{IrConstant, IrStringConstant};
use crate::ir::ir_expression::IrExpression;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use std::rc::Rc;
pub struct StringLiteral { pub struct StringLiteral {
content: String, content: String,
@ -17,6 +21,15 @@ impl StringLiteral {
&self.content &self.content
} }
pub fn lower_to_ir(&self, context: &mut FunctionLoweringContext) -> IrExpression {
let ir_string_constant = Rc::new(IrStringConstant::new(
self.content(),
&context.next_constant_name(),
));
context.add_constant(IrConstant::String(ir_string_constant.clone()));
IrExpression::Constant(IrConstant::String(ir_string_constant))
}
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &self.source_range
} }

View File

@ -0,0 +1,17 @@
use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVariable;
#[derive(Debug)]
pub struct IrAssign {
destination: Box<IrVariable>,
value: Box<IrExpression>,
}
impl IrAssign {
pub fn new(destination: IrVariable, value: IrExpression) -> Self {
Self {
destination: destination.into(),
value: value.into(),
}
}
}

16
dmc-lib/src/ir/ir_call.rs Normal file
View File

@ -0,0 +1,16 @@
use crate::ir::ir_expression::IrExpression;
#[derive(Debug)]
pub struct IrCall {
name: String,
arguments: Vec<IrExpression>,
}
impl IrCall {
pub fn new(name: &str, arguments: Vec<IrExpression>) -> Self {
Self {
name: name.into(),
arguments,
}
}
}

View File

@ -0,0 +1,21 @@
use std::rc::Rc;
#[derive(Debug)]
pub enum IrConstant {
String(Rc<IrStringConstant>),
}
#[derive(Debug)]
pub struct IrStringConstant {
value: String,
name: String,
}
impl IrStringConstant {
pub fn new(value: &str, name: &str) -> Self {
Self {
value: value.into(),
name: name.into(),
}
}
}

View File

@ -0,0 +1,11 @@
use crate::ir::ir_call::IrCall;
use crate::ir::ir_constant::IrConstant;
use crate::ir::ir_variable::IrVariable;
#[derive(Debug)]
pub enum IrExpression {
Call(IrCall),
Constant(IrConstant),
IntegerLiteral(i64),
Variable(IrVariable),
}

View File

@ -0,0 +1,12 @@
use crate::ir::ir_statement::IrStatement;
#[derive(Debug)]
pub struct IrFunction {
statements: Vec<IrStatement>,
}
impl IrFunction {
pub fn new(statements: Vec<IrStatement>) -> Self {
Self { statements }
}
}

View File

@ -0,0 +1,6 @@
use crate::ir::ir_variable::IrVariable;
pub enum IrLhs {
Variable(IrVariable),
FunctionName(String),
}

View File

@ -0,0 +1,8 @@
use crate::ir::ir_constant::IrConstant;
use crate::ir::ir_variable::IrVariable;
pub enum IrRhs {
Constant(IrConstant),
IntegerLiteral(i64),
Variable(IrVariable),
}

View File

@ -0,0 +1,8 @@
use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_expression::IrExpression;
#[derive(Debug)]
pub enum IrStatement {
Assign(IrAssign),
Expression(IrExpression),
}

View File

@ -0,0 +1,10 @@
#[derive(Debug)]
pub struct IrVariable {
name: String,
}
impl IrVariable {
pub fn new(name: &str) -> Self {
Self { name: name.into() }
}
}

18
dmc-lib/src/ir/mod.rs Normal file
View File

@ -0,0 +1,18 @@
use crate::ir::ir_constant::IrConstant;
use crate::ir::ir_function::IrFunction;
pub mod ir_assign;
pub mod ir_call;
pub mod ir_constant;
pub mod ir_expression;
pub mod ir_function;
pub mod ir_l_value;
pub mod ir_r_value;
pub mod ir_statement;
pub mod ir_variable;
#[derive(Debug)]
pub enum Ir {
Function(IrFunction),
Constant(IrConstant),
}

View File

@ -1,5 +1,6 @@
mod ast; mod ast;
mod diagnostic; mod diagnostic;
mod ir;
mod lexer; mod lexer;
mod parser; mod parser;
mod scope; mod scope;

View File

@ -1,6 +1,7 @@
use crate::ast::call::Call; use crate::ast::call::Call;
use crate::ast::compilation_unit::CompilationUnit; use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::identifier::Identifier; use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
@ -169,7 +170,7 @@ impl<'a> Parser<'a> {
} }
fn expression_statement(&mut self) -> Statement { fn expression_statement(&mut self) -> Statement {
Statement::Expression(self.expression()) Statement::Expression(ExpressionStatement::new(self.expression()))
} }
fn expression(&mut self) -> Expression { fn expression(&mut self) -> Expression {
@ -246,8 +247,8 @@ mod smoke_tests {
assert_eq!(function.declared_name(), "main"); assert_eq!(function.declared_name(), "main");
let statements = function.statements(); let statements = function.statements();
assert_eq!(statements.len(), 1); assert_eq!(statements.len(), 1);
if let Statement::Expression(expression) = statements[0] { if let Statement::Expression(expression_statement) = statements[0] {
if let Expression::Call(call) = expression { if let Expression::Call(call) = expression_statement.expression() {
let callee = call.callee(); let callee = call.callee();
match callee { match callee {
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {