Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-08-23 13:13:15 +02:00
padre ce4ff63d0d
commit 0bee0c799b
Se han modificado 11 ficheros con 736 adiciones y 413 borrados

Ver fichero

@@ -1,9 +1,9 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use alecc::lexer::Lexer;
use alecc::parser::Parser;
use alecc::codegen::CodeGenerator; use alecc::codegen::CodeGenerator;
use alecc::optimizer::{Optimizer, OptimizationLevel}; use alecc::lexer::Lexer;
use alecc::optimizer::{OptimizationLevel, Optimizer};
use alecc::parser::Parser;
use alecc::targets::Target; use alecc::targets::Target;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
const SIMPLE_C_CODE: &str = r#" const SIMPLE_C_CODE: &str = r#"
int main() { int main() {
@@ -119,5 +119,11 @@ fn bench_optimizer(c: &mut Criterion) {
}); });
} }
criterion_group!(benches, bench_lexer, bench_parser, bench_codegen, bench_optimizer); criterion_group!(
benches,
bench_lexer,
bench_parser,
bench_codegen,
bench_optimizer
);
criterion_main!(benches); criterion_main!(benches);

Ver fichero

@@ -1,6 +1,8 @@
use crate::parser::{Program, Function, Expression, Statement, Type, BinaryOperator, UnaryOperator};
use crate::targets::Target;
use crate::error::{AleccError, Result}; use crate::error::{AleccError, Result};
use crate::parser::{
BinaryOperator, Expression, Function, Program, Statement, Type, UnaryOperator,
};
use crate::targets::Target;
use std::collections::HashMap; use std::collections::HashMap;
pub struct CodeGenerator { pub struct CodeGenerator {
@@ -165,7 +167,8 @@ impl CodeGenerator {
let stack_offset = 8 + i as i32 * 4; // ebp + 8 + offset let stack_offset = 8 + i as i32 * 4; // ebp + 8 + offset
self.emit_line(&format!(" mov eax, DWORD PTR [ebp + {}]", stack_offset)); self.emit_line(&format!(" mov eax, DWORD PTR [ebp + {}]", stack_offset));
self.emit_line(&format!(" mov DWORD PTR [ebp + {}], eax", param_offset)); self.emit_line(&format!(" mov DWORD PTR [ebp + {}], eax", param_offset));
self.current_function_params.push((name.clone(), param_offset)); self.current_function_params
.push((name.clone(), param_offset));
} }
} }
Target::Amd64 => { Target::Amd64 => {
@@ -185,14 +188,18 @@ impl CodeGenerator {
let param_offset = -(i as i32 + 1) * 8; let param_offset = -(i as i32 + 1) * 8;
if i < param_registers.len() { if i < param_registers.len() {
// Parameter passed in register // Parameter passed in register
self.emit_line(&format!(" mov QWORD PTR [rbp + {}], {}", param_offset, param_registers[i])); self.emit_line(&format!(
" mov QWORD PTR [rbp + {}], {}",
param_offset, param_registers[i]
));
} else { } else {
// Parameter passed on stack // Parameter passed on stack
let stack_offset = 16 + (i - param_registers.len()) as i32 * 8; let stack_offset = 16 + (i - param_registers.len()) as i32 * 8;
self.emit_line(&format!(" mov rax, QWORD PTR [rbp + {}]", stack_offset)); self.emit_line(&format!(" mov rax, QWORD PTR [rbp + {}]", stack_offset));
self.emit_line(&format!(" mov QWORD PTR [rbp + {}], rax", param_offset)); self.emit_line(&format!(" mov QWORD PTR [rbp + {}], rax", param_offset));
} }
self.current_function_params.push((name.clone(), param_offset)); self.current_function_params
.push((name.clone(), param_offset));
} }
} }
Target::Arm64 => { Target::Arm64 => {
@@ -217,7 +224,8 @@ impl CodeGenerator {
self.emit_line(&format!(" ldr x9, [x29, #{}]", stack_offset)); self.emit_line(&format!(" ldr x9, [x29, #{}]", stack_offset));
self.emit_line(&format!(" str x9, [x29, #{}]", param_offset)); self.emit_line(&format!(" str x9, [x29, #{}]", param_offset));
} }
self.current_function_params.push((name.clone(), param_offset)); self.current_function_params
.push((name.clone(), param_offset));
} }
} }
} }
@@ -280,7 +288,11 @@ impl CodeGenerator {
Statement::Expression(expr) => { Statement::Expression(expr) => {
self.generate_expression(expr)?; self.generate_expression(expr)?;
} }
Statement::Declaration { name, var_type, initializer } => { Statement::Declaration {
name,
var_type,
initializer,
} => {
// Calculate space needed based on type // Calculate space needed based on type
let size = match var_type { let size = match var_type {
Type::Array(_, Some(length)) => length * 8, // Assuming 8-byte elements Type::Array(_, Some(length)) => length * 8, // Assuming 8-byte elements
@@ -300,10 +312,16 @@ impl CodeGenerator {
// Store the value in the local variable slot // Store the value in the local variable slot
match self.target { match self.target {
Target::Amd64 => { Target::Amd64 => {
self.emit_line(&format!(" mov QWORD PTR [rbp + {}], rax", var_offset)); self.emit_line(&format!(
" mov QWORD PTR [rbp + {}], rax",
var_offset
));
} }
Target::I386 => { Target::I386 => {
self.emit_line(&format!(" mov DWORD PTR [ebp + {}], eax", var_offset)); self.emit_line(&format!(
" mov DWORD PTR [ebp + {}], eax",
var_offset
));
} }
Target::Arm64 => { Target::Arm64 => {
self.emit_line(&format!(" str x0, [x29, #{}]", var_offset)); self.emit_line(&format!(" str x0, [x29, #{}]", var_offset));
@@ -335,7 +353,11 @@ impl CodeGenerator {
self.generate_statement(stmt)?; self.generate_statement(stmt)?;
} }
} }
Statement::If { condition, then_stmt, else_stmt } => { Statement::If {
condition,
then_stmt,
else_stmt,
} => {
let else_label = self.new_label("else"); let else_label = self.new_label("else");
let end_label = self.new_label("endif"); let end_label = self.new_label("endif");
@@ -372,7 +394,12 @@ impl CodeGenerator {
self.emit_line(&format!("{}:", end_label)); self.emit_line(&format!("{}:", end_label));
} }
Statement::For { init, condition, increment, body } => { Statement::For {
init,
condition,
increment,
body,
} => {
// Generate initialization // Generate initialization
if let Some(init_stmt) = init { if let Some(init_stmt) = init {
self.generate_statement(init_stmt)?; self.generate_statement(init_stmt)?;
@@ -412,8 +439,7 @@ impl CodeGenerator {
fn generate_expression(&mut self, expression: &Expression) -> Result<()> { fn generate_expression(&mut self, expression: &Expression) -> Result<()> {
match expression { match expression {
Expression::IntegerLiteral(value) => { Expression::IntegerLiteral(value) => match self.target {
match self.target {
Target::I386 => { Target::I386 => {
self.emit_line(&format!(" mov eax, {}", value)); self.emit_line(&format!(" mov eax, {}", value));
} }
@@ -423,8 +449,7 @@ impl CodeGenerator {
Target::Arm64 => { Target::Arm64 => {
self.emit_line(&format!(" mov x0, #{}", value)); self.emit_line(&format!(" mov x0, #{}", value));
} }
} },
}
Expression::StringLiteral(value) => { Expression::StringLiteral(value) => {
let label = self.get_string_literal_label(value); let label = self.get_string_literal_label(value);
match self.target { match self.target {
@@ -442,7 +467,11 @@ impl CodeGenerator {
} }
Expression::Identifier(name) => { Expression::Identifier(name) => {
// Check if it's a function parameter first // Check if it's a function parameter first
if let Some((_, offset)) = self.current_function_params.iter().find(|(param_name, _)| param_name == name) { if let Some((_, offset)) = self
.current_function_params
.iter()
.find(|(param_name, _)| param_name == name)
{
// Load parameter from stack // Load parameter from stack
match self.target { match self.target {
Target::I386 => { Target::I386 => {
@@ -485,7 +514,10 @@ impl CodeGenerator {
} }
} }
} }
Expression::Call { function, arguments } => { Expression::Call {
function,
arguments,
} => {
// Generate arguments and place in calling convention registers/stack // Generate arguments and place in calling convention registers/stack
match self.target { match self.target {
Target::I386 => { Target::I386 => {
@@ -502,7 +534,11 @@ impl CodeGenerator {
// Ensure stack alignment before function call // Ensure stack alignment before function call
// Stack must be 16-byte aligned before 'call' instruction // Stack must be 16-byte aligned before 'call' instruction
// Since 'call' pushes 8 bytes (return address), we need stack to be 8 bytes off 16-byte boundary // Since 'call' pushes 8 bytes (return address), we need stack to be 8 bytes off 16-byte boundary
let stack_args = if arguments.len() > param_registers.len() { arguments.len() - param_registers.len() } else { 0 }; let stack_args = if arguments.len() > param_registers.len() {
arguments.len() - param_registers.len()
} else {
0
};
let mut stack_cleanup_size = 0; let mut stack_cleanup_size = 0;
// Handle stack arguments if any // Handle stack arguments if any
@@ -526,7 +562,8 @@ impl CodeGenerator {
} }
// Then handle register arguments in reverse order to avoid overwriting // Then handle register arguments in reverse order to avoid overwriting
let reg_args: Vec<_> = arguments.iter().take(param_registers.len()).collect(); let reg_args: Vec<_> =
arguments.iter().take(param_registers.len()).collect();
for (i, arg) in reg_args.iter().enumerate().rev() { for (i, arg) in reg_args.iter().enumerate().rev() {
self.generate_expression(arg)?; self.generate_expression(arg)?;
self.emit_line(&format!(" mov {}, rax", param_registers[i])); self.emit_line(&format!(" mov {}, rax", param_registers[i]));
@@ -576,19 +613,30 @@ impl CodeGenerator {
Target::Amd64 => { Target::Amd64 => {
// Clean up stack using stored cleanup size // Clean up stack using stored cleanup size
if self.last_call_stack_cleanup > 0 { if self.last_call_stack_cleanup > 0 {
self.emit_line(&format!(" add rsp, {}", self.last_call_stack_cleanup)); self.emit_line(&format!(
" add rsp, {}",
self.last_call_stack_cleanup
));
} }
} }
Target::Arm64 => { Target::Arm64 => {
// Clean up stack arguments (if any) // Clean up stack arguments (if any)
let stack_args = if arguments.len() > 8 { arguments.len() - 8 } else { 0 }; let stack_args = if arguments.len() > 8 {
arguments.len() - 8
} else {
0
};
if stack_args > 0 { if stack_args > 0 {
self.emit_line(&format!(" add sp, sp, #{}", stack_args * 16)); self.emit_line(&format!(" add sp, sp, #{}", stack_args * 16));
} }
} }
} }
} }
Expression::Binary { left, operator, right } => { Expression::Binary {
left,
operator,
right,
} => {
// Generate binary operations // Generate binary operations
// First generate right operand and save it // First generate right operand and save it
self.generate_expression(right)?; self.generate_expression(right)?;
@@ -626,7 +674,10 @@ impl CodeGenerator {
} }
_ => { _ => {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: format!("Binary operator {:?} not implemented for i386", operator), message: format!(
"Binary operator {:?} not implemented for i386",
operator
),
}); });
} }
} }
@@ -722,7 +773,10 @@ impl CodeGenerator {
} }
_ => { _ => {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: format!("Binary operator {:?} not implemented for arm64", operator), message: format!(
"Binary operator {:?} not implemented for arm64",
operator
),
}); });
} }
} }
@@ -788,12 +842,24 @@ impl CodeGenerator {
if let Some(&offset) = self.local_variables.get(name) { if let Some(&offset) = self.local_variables.get(name) {
match self.target { match self.target {
Target::I386 => { Target::I386 => {
self.emit_line(&format!(" inc DWORD PTR [ebp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" mov eax, DWORD PTR [ebp + {}]", offset)); " inc DWORD PTR [ebp + {}]",
offset
));
self.emit_line(&format!(
" mov eax, DWORD PTR [ebp + {}]",
offset
));
} }
Target::Amd64 => { Target::Amd64 => {
self.emit_line(&format!(" inc QWORD PTR [rbp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" mov rax, QWORD PTR [rbp + {}]", offset)); " inc QWORD PTR [rbp + {}]",
offset
));
self.emit_line(&format!(
" mov rax, QWORD PTR [rbp + {}]",
offset
));
} }
Target::Arm64 => { Target::Arm64 => {
self.emit_line(&format!(" ldr x0, [x29, #{}]", offset)); self.emit_line(&format!(" ldr x0, [x29, #{}]", offset));
@@ -808,7 +874,8 @@ impl CodeGenerator {
} }
} else { } else {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: "Pre-increment can only be applied to variables".to_string(), message: "Pre-increment can only be applied to variables"
.to_string(),
}); });
} }
} }
@@ -818,12 +885,24 @@ impl CodeGenerator {
if let Some(&offset) = self.local_variables.get(name) { if let Some(&offset) = self.local_variables.get(name) {
match self.target { match self.target {
Target::I386 => { Target::I386 => {
self.emit_line(&format!(" mov eax, DWORD PTR [ebp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" inc DWORD PTR [ebp + {}]", offset)); " mov eax, DWORD PTR [ebp + {}]",
offset
));
self.emit_line(&format!(
" inc DWORD PTR [ebp + {}]",
offset
));
} }
Target::Amd64 => { Target::Amd64 => {
self.emit_line(&format!(" mov rax, QWORD PTR [rbp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" inc QWORD PTR [rbp + {}]", offset)); " mov rax, QWORD PTR [rbp + {}]",
offset
));
self.emit_line(&format!(
" inc QWORD PTR [rbp + {}]",
offset
));
} }
Target::Arm64 => { Target::Arm64 => {
self.emit_line(&format!(" ldr x0, [x29, #{}]", offset)); self.emit_line(&format!(" ldr x0, [x29, #{}]", offset));
@@ -839,7 +918,8 @@ impl CodeGenerator {
} }
} else { } else {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: "Post-increment can only be applied to variables".to_string(), message: "Post-increment can only be applied to variables"
.to_string(),
}); });
} }
} }
@@ -849,12 +929,24 @@ impl CodeGenerator {
if let Some(&offset) = self.local_variables.get(name) { if let Some(&offset) = self.local_variables.get(name) {
match self.target { match self.target {
Target::I386 => { Target::I386 => {
self.emit_line(&format!(" dec DWORD PTR [ebp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" mov eax, DWORD PTR [ebp + {}]", offset)); " dec DWORD PTR [ebp + {}]",
offset
));
self.emit_line(&format!(
" mov eax, DWORD PTR [ebp + {}]",
offset
));
} }
Target::Amd64 => { Target::Amd64 => {
self.emit_line(&format!(" dec QWORD PTR [rbp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" mov rax, QWORD PTR [rbp + {}]", offset)); " dec QWORD PTR [rbp + {}]",
offset
));
self.emit_line(&format!(
" mov rax, QWORD PTR [rbp + {}]",
offset
));
} }
Target::Arm64 => { Target::Arm64 => {
self.emit_line(&format!(" ldr x0, [x29, #{}]", offset)); self.emit_line(&format!(" ldr x0, [x29, #{}]", offset));
@@ -869,7 +961,8 @@ impl CodeGenerator {
} }
} else { } else {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: "Pre-decrement can only be applied to variables".to_string(), message: "Pre-decrement can only be applied to variables"
.to_string(),
}); });
} }
} }
@@ -879,12 +972,24 @@ impl CodeGenerator {
if let Some(&offset) = self.local_variables.get(name) { if let Some(&offset) = self.local_variables.get(name) {
match self.target { match self.target {
Target::I386 => { Target::I386 => {
self.emit_line(&format!(" mov eax, DWORD PTR [ebp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" dec DWORD PTR [ebp + {}]", offset)); " mov eax, DWORD PTR [ebp + {}]",
offset
));
self.emit_line(&format!(
" dec DWORD PTR [ebp + {}]",
offset
));
} }
Target::Amd64 => { Target::Amd64 => {
self.emit_line(&format!(" mov rax, QWORD PTR [rbp + {}]", offset)); self.emit_line(&format!(
self.emit_line(&format!(" dec QWORD PTR [rbp + {}]", offset)); " mov rax, QWORD PTR [rbp + {}]",
offset
));
self.emit_line(&format!(
" dec QWORD PTR [rbp + {}]",
offset
));
} }
Target::Arm64 => { Target::Arm64 => {
self.emit_line(&format!(" ldr x0, [x29, #{}]", offset)); self.emit_line(&format!(" ldr x0, [x29, #{}]", offset));
@@ -900,7 +1005,8 @@ impl CodeGenerator {
} }
} else { } else {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: "Post-decrement can only be applied to variables".to_string(), message: "Post-decrement can only be applied to variables"
.to_string(),
}); });
} }
} }
@@ -991,7 +1097,11 @@ impl CodeGenerator {
}); });
} }
} }
Expression::Assignment { target, operator, value } => { Expression::Assignment {
target,
operator,
value,
} => {
// Handle compound assignment operators // Handle compound assignment operators
match operator { match operator {
crate::parser::AssignmentOperator::Assign => { crate::parser::AssignmentOperator::Assign => {
@@ -1105,7 +1215,8 @@ impl CodeGenerator {
} }
} else { } else {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: "Complex assignment targets not supported for compound operators yet".to_string(), message: "Complex assignment targets not supported for compound operators yet"
.to_string(),
}); });
} }
Ok(()) Ok(())
@@ -1144,7 +1255,8 @@ impl CodeGenerator {
} }
} else { } else {
return Err(AleccError::CodegenError { return Err(AleccError::CodegenError {
message: "Complex assignment targets not supported for compound operators yet".to_string(), message: "Complex assignment targets not supported for compound operators yet"
.to_string(),
}); });
} }
Ok(()) Ok(())
@@ -1209,7 +1321,8 @@ impl CodeGenerator {
label.clone() label.clone()
} else { } else {
let label = format!(".LC{}", self.string_literals.len()); let label = format!(".LC{}", self.string_literals.len());
self.string_literals.insert(content.to_string(), label.clone()); self.string_literals
.insert(content.to_string(), label.clone());
label label
} }
} }
@@ -1248,7 +1361,11 @@ impl CodeGenerator {
} }
Ok(()) Ok(())
} }
Statement::If { condition, then_stmt, else_stmt } => { Statement::If {
condition,
then_stmt,
else_stmt,
} => {
self.collect_string_literals_from_expression(condition)?; self.collect_string_literals_from_expression(condition)?;
self.collect_string_literals_from_statement(then_stmt)?; self.collect_string_literals_from_statement(then_stmt)?;
if let Some(else_statement) = else_stmt { if let Some(else_statement) = else_stmt {
@@ -1261,7 +1378,12 @@ impl CodeGenerator {
self.collect_string_literals_from_statement(body)?; self.collect_string_literals_from_statement(body)?;
Ok(()) Ok(())
} }
Statement::For { init, condition, increment, body } => { Statement::For {
init,
condition,
increment,
body,
} => {
if let Some(init_stmt) = init { if let Some(init_stmt) = init {
self.collect_string_literals_from_statement(init_stmt)?; self.collect_string_literals_from_statement(init_stmt)?;
} }
@@ -1280,7 +1402,7 @@ impl CodeGenerator {
} }
Ok(()) Ok(())
} }
_ => Ok(()) // Other statement types don't have expressions we need to collect _ => Ok(()), // Other statement types don't have expressions we need to collect
} }
} }
@@ -1299,7 +1421,10 @@ impl CodeGenerator {
self.collect_string_literals_from_expression(operand)?; self.collect_string_literals_from_expression(operand)?;
Ok(()) Ok(())
} }
Expression::Call { function, arguments } => { Expression::Call {
function,
arguments,
} => {
self.collect_string_literals_from_expression(function)?; self.collect_string_literals_from_expression(function)?;
for arg in arguments { for arg in arguments {
self.collect_string_literals_from_expression(arg)?; self.collect_string_literals_from_expression(arg)?;
@@ -1311,7 +1436,7 @@ impl CodeGenerator {
self.collect_string_literals_from_expression(value)?; self.collect_string_literals_from_expression(value)?;
Ok(()) Ok(())
} }
_ => Ok(()) // Other expression types don't contain string literals _ => Ok(()), // Other expression types don't contain string literals
} }
} }
} }

Ver fichero

@@ -1,15 +1,15 @@
use crate::cli::Args; use crate::cli::Args;
use crate::lexer::Lexer;
use crate::parser::Parser;
use crate::codegen::CodeGenerator; use crate::codegen::CodeGenerator;
use crate::optimizer::{Optimizer, OptimizationLevel};
use crate::linker::Linker;
use crate::targets::Target;
use crate::error::{AleccError, Result}; use crate::error::{AleccError, Result};
use crate::lexer::Lexer;
use crate::linker::Linker;
use crate::optimizer::{OptimizationLevel, Optimizer};
use crate::parser::Parser;
use crate::targets::Target;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use tokio::fs; use tokio::fs;
use tracing::{info, debug, warn}; use tracing::{debug, info, warn};
pub struct Compiler { pub struct Compiler {
args: Args, args: Args,
@@ -19,10 +19,9 @@ pub struct Compiler {
impl Compiler { impl Compiler {
pub fn new(args: Args) -> Result<Self> { pub fn new(args: Args) -> Result<Self> {
let target = Target::from_string(&args.target).ok_or_else(|| { let target =
AleccError::UnsupportedTarget { Target::from_string(&args.target).ok_or_else(|| AleccError::UnsupportedTarget {
target: args.target.clone(), target: args.target.clone(),
}
})?; })?;
Ok(Self { Ok(Self {
@@ -39,9 +38,11 @@ impl Compiler {
}); });
} }
info!("Compiling {} files for target {}", info!(
"Compiling {} files for target {}",
self.args.input_files.len(), self.args.input_files.len(),
self.target.as_str()); self.target.as_str()
);
let mut object_files = Vec::new(); let mut object_files = Vec::new();
let input_files = self.args.input_files.clone(); // Clone to avoid borrow issues let input_files = self.args.input_files.clone(); // Clone to avoid borrow issues
@@ -50,14 +51,18 @@ impl Compiler {
for input_file in &input_files { for input_file in &input_files {
debug!("Processing file: {}", input_file.display()); debug!("Processing file: {}", input_file.display());
let extension = input_file.extension() let extension = input_file
.extension()
.and_then(|ext| ext.to_str()) .and_then(|ext| ext.to_str())
.unwrap_or(""); .unwrap_or("");
match extension { match extension {
"c" | "cpp" | "cxx" | "cc" | "C" => { "c" | "cpp" | "cxx" | "cc" | "C" => {
let obj_file = self.compile_source_file(input_file).await?; let obj_file = self.compile_source_file(input_file).await?;
if !self.args.compile_only && !self.args.assembly_only && !self.args.preprocess_only { if !self.args.compile_only
&& !self.args.assembly_only
&& !self.args.preprocess_only
{
object_files.push(obj_file); object_files.push(obj_file);
} }
} }
@@ -71,10 +76,15 @@ impl Compiler {
object_files.push(input_file.clone()); object_files.push(input_file.clone());
} }
_ => { _ => {
warn!("Unknown file extension for {}, treating as C source", warn!(
input_file.display()); "Unknown file extension for {}, treating as C source",
input_file.display()
);
let obj_file = self.compile_source_file(input_file).await?; let obj_file = self.compile_source_file(input_file).await?;
if !self.args.compile_only && !self.args.assembly_only && !self.args.preprocess_only { if !self.args.compile_only
&& !self.args.assembly_only
&& !self.args.preprocess_only
{
object_files.push(obj_file); object_files.push(obj_file);
} }
} }
@@ -96,19 +106,20 @@ impl Compiler {
info!("Compiling source file: {}", input_file.display()); info!("Compiling source file: {}", input_file.display());
// Read source file // Read source file
let source = fs::read_to_string(input_file).await.map_err(|_e| { let source =
AleccError::FileNotFound { fs::read_to_string(input_file)
.await
.map_err(|_e| AleccError::FileNotFound {
path: input_file.to_string_lossy().to_string(), path: input_file.to_string_lossy().to_string(),
}
})?; })?;
// Preprocessing // Preprocessing
let preprocessed = if self.args.preprocess_only { let preprocessed = if self.args.preprocess_only {
let output_path = self.get_output_path(input_file, "i")?; let output_path = self.get_output_path(input_file, "i")?;
let preprocessed = self.preprocess(&source, input_file).await?; let preprocessed = self.preprocess(&source, input_file).await?;
fs::write(&output_path, preprocessed).await.map_err(|e| { fs::write(&output_path, preprocessed)
AleccError::IoError(e) .await
})?; .map_err(|e| AleccError::IoError(e))?;
return Ok(output_path); return Ok(output_path);
} else { } else {
self.preprocess(&source, input_file).await? self.preprocess(&source, input_file).await?
@@ -136,17 +147,17 @@ impl Compiler {
if self.args.assembly_only { if self.args.assembly_only {
let output_path = self.get_output_path(input_file, "s")?; let output_path = self.get_output_path(input_file, "s")?;
fs::write(&output_path, assembly).await.map_err(|e| { fs::write(&output_path, assembly)
AleccError::IoError(e) .await
})?; .map_err(|e| AleccError::IoError(e))?;
return Ok(output_path); return Ok(output_path);
} }
// Write assembly to temporary file // Write assembly to temporary file
let asm_path = self.create_temp_file("s")?; let asm_path = self.create_temp_file("s")?;
fs::write(&asm_path, assembly).await.map_err(|e| { fs::write(&asm_path, assembly)
AleccError::IoError(e) .await
})?; .map_err(|e| AleccError::IoError(e))?;
// Assemble // Assemble
let obj_path = self.assemble_file(&asm_path).await?; let obj_path = self.assemble_file(&asm_path).await?;
@@ -330,8 +341,9 @@ impl Compiler {
} }
command.args(&[ command.args(&[
"-o", &obj_path.to_string_lossy(), "-o",
&asm_file.to_string_lossy() &obj_path.to_string_lossy(),
&asm_file.to_string_lossy(),
]); ]);
let output = command.output().map_err(|e| AleccError::CodegenError { let output = command.output().map_err(|e| AleccError::CodegenError {
@@ -401,20 +413,26 @@ impl Compiler {
if let Some(ref output) = self.args.output { if let Some(ref output) = self.args.output {
Ok(output.clone()) Ok(output.clone())
} else { } else {
let stem = input_file.file_stem() let stem = input_file
.file_stem()
.ok_or_else(|| AleccError::InvalidArgument { .ok_or_else(|| AleccError::InvalidArgument {
message: "Invalid input file name".to_string(), message: "Invalid input file name".to_string(),
})?; })?;
Ok(PathBuf::from(format!("{}.{}", stem.to_string_lossy(), extension))) Ok(PathBuf::from(format!(
"{}.{}",
stem.to_string_lossy(),
extension
)))
} }
} }
fn create_temp_file(&mut self, extension: &str) -> Result<PathBuf> { fn create_temp_file(&mut self, extension: &str) -> Result<PathBuf> {
let temp_path = std::env::temp_dir() let temp_path = std::env::temp_dir().join(format!(
.join(format!("alecc_{}_{}.{}", "alecc_{}_{}.{}",
std::process::id(), std::process::id(),
self.temp_files.len(), self.temp_files.len(),
extension)); extension
));
self.temp_files.push(temp_path.clone()); self.temp_files.push(temp_path.clone());
Ok(temp_path) Ok(temp_path)
} }
@@ -423,8 +441,11 @@ impl Compiler {
for temp_file in &self.temp_files { for temp_file in &self.temp_files {
if temp_file.exists() { if temp_file.exists() {
if let Err(e) = fs::remove_file(temp_file).await { if let Err(e) = fs::remove_file(temp_file).await {
warn!("Failed to remove temporary file {}: {}", warn!(
temp_file.display(), e); "Failed to remove temporary file {}: {}",
temp_file.display(),
e
);
} }
} }
} }

Ver fichero

@@ -12,36 +12,116 @@ pub enum TokenType {
Identifier(String), Identifier(String),
// Keywords // Keywords
Auto, Break, Case, Char, Const, Continue, Default, Do, Auto,
Double, Else, Enum, Extern, Float, For, Goto, If, Break,
Int, Long, Register, Return, Short, Signed, Sizeof, Static, Case,
Struct, Switch, Typedef, Union, Unsigned, Void, Volatile, While, Char,
Const,
Continue,
Default,
Do,
Double,
Else,
Enum,
Extern,
Float,
For,
Goto,
If,
Int,
Long,
Register,
Return,
Short,
Signed,
Sizeof,
Static,
Struct,
Switch,
Typedef,
Union,
Unsigned,
Void,
Volatile,
While,
// C++ Keywords // C++ Keywords
Bool, Class, Explicit, Export, False, Friend, Inline, Mutable, Bool,
Namespace, New, Operator, Private, Protected, Public, Template, Class,
This, Throw, True, Try, Typename, Using, Virtual, Explicit,
Export,
False,
Friend,
Inline,
Mutable,
Namespace,
New,
Operator,
Private,
Protected,
Public,
Template,
This,
Throw,
True,
Try,
Typename,
Using,
Virtual,
// Operators // Operators
Plus, Minus, Multiply, Divide, Modulo, Plus,
Assign, PlusAssign, MinusAssign, MultiplyAssign, DivideAssign, ModuloAssign, Minus,
Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual, Multiply,
LogicalAnd, LogicalOr, LogicalNot, Divide,
BitwiseAnd, BitwiseOr, BitwiseXor, BitwiseNot, Modulo,
LeftShift, RightShift, LeftShiftAssign, RightShiftAssign, Assign,
BitwiseAndAssign, BitwiseOrAssign, BitwiseXorAssign, PlusAssign,
Increment, Decrement, MinusAssign,
Arrow, Dot, Question, Colon, MultiplyAssign,
DivideAssign,
ModuloAssign,
Equal,
NotEqual,
Less,
Greater,
LessEqual,
GreaterEqual,
LogicalAnd,
LogicalOr,
LogicalNot,
BitwiseAnd,
BitwiseOr,
BitwiseXor,
BitwiseNot,
LeftShift,
RightShift,
LeftShiftAssign,
RightShiftAssign,
BitwiseAndAssign,
BitwiseOrAssign,
BitwiseXorAssign,
Increment,
Decrement,
Arrow,
Dot,
Question,
Colon,
// Delimiters // Delimiters
LeftParen, RightParen, LeftParen,
LeftBrace, RightBrace, RightParen,
LeftBracket, RightBracket, LeftBrace,
Semicolon, Comma, RightBrace,
LeftBracket,
RightBracket,
Semicolon,
Comma,
Ellipsis, // ... Ellipsis, // ...
// Preprocessor // Preprocessor
Hash, HashHash, Hash,
HashHash,
// Special // Special
Eof, Eof,

Ver fichero

@@ -1,9 +1,9 @@
pub mod lexer;
pub mod parser;
pub mod codegen;
pub mod targets;
pub mod compiler;
pub mod cli; pub mod cli;
pub mod codegen;
pub mod compiler;
pub mod error; pub mod error;
pub mod optimizer; pub mod lexer;
pub mod linker; pub mod linker;
pub mod optimizer;
pub mod parser;
pub mod targets;

Ver fichero

@@ -1,5 +1,5 @@
use crate::targets::Target;
use crate::error::{AleccError, Result}; use crate::error::{AleccError, Result};
use crate::targets::Target;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
@@ -238,10 +238,7 @@ impl Linker {
"/usr/lib64", "/usr/lib64",
"/lib64", "/lib64",
], ],
Target::Arm64 => vec![ Target::Arm64 => vec!["/usr/lib/aarch64-linux-gnu", "/lib/aarch64-linux-gnu"],
"/usr/lib/aarch64-linux-gnu",
"/lib/aarch64-linux-gnu",
],
}; };
for path in lib_paths { for path in lib_paths {

Ver fichero

@@ -1,19 +1,19 @@
use clap::Parser;
use anyhow::Result; use anyhow::Result;
use tracing::{info, error}; use clap::Parser;
use tracing::{error, info};
mod compiler;
mod lexer;
mod parser;
mod codegen;
mod optimizer;
mod linker;
mod targets;
mod cli; mod cli;
mod codegen;
mod compiler;
mod error; mod error;
mod lexer;
mod linker;
mod optimizer;
mod parser;
mod targets;
use compiler::Compiler;
use cli::Args; use cli::Args;
use compiler::Compiler;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {

Ver fichero

@@ -1,5 +1,5 @@
use crate::parser::Program;
use crate::error::Result; use crate::error::Result;
use crate::parser::Program;
pub struct Optimizer { pub struct Optimizer {
level: OptimizationLevel, level: OptimizationLevel,
@@ -40,9 +40,7 @@ impl Optimizer {
// No optimization // No optimization
Ok(()) Ok(())
} }
OptimizationLevel::Basic => { OptimizationLevel::Basic => self.basic_optimizations(program),
self.basic_optimizations(program)
}
OptimizationLevel::Moderate => { OptimizationLevel::Moderate => {
self.basic_optimizations(program)?; self.basic_optimizations(program)?;
self.moderate_optimizations(program) self.moderate_optimizations(program)

Ver fichero

@@ -1,5 +1,5 @@
use crate::lexer::{Token, TokenType};
use crate::error::{AleccError, Result}; use crate::error::{AleccError, Result};
use crate::lexer::{Token, TokenType};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -97,30 +97,59 @@ pub enum Expression {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum BinaryOperator { pub enum BinaryOperator {
Add, Subtract, Multiply, Divide, Modulo, Add,
Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual, Subtract,
LogicalAnd, LogicalOr, Multiply,
BitwiseAnd, BitwiseOr, BitwiseXor, Divide,
LeftShift, RightShift, Modulo,
Equal,
NotEqual,
Less,
Greater,
LessEqual,
GreaterEqual,
LogicalAnd,
LogicalOr,
BitwiseAnd,
BitwiseOr,
BitwiseXor,
LeftShift,
RightShift,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum UnaryOperator { pub enum UnaryOperator {
Plus, Minus, LogicalNot, BitwiseNot, Plus,
PreIncrement, PostIncrement, Minus,
PreDecrement, PostDecrement, LogicalNot,
AddressOf, Dereference, BitwiseNot,
PreIncrement,
PostIncrement,
PreDecrement,
PostDecrement,
AddressOf,
Dereference,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum AssignmentOperator { pub enum AssignmentOperator {
Assign, PlusAssign, MinusAssign, MultiplyAssign, DivideAssign, Assign,
#[allow(dead_code)] ModuloAssign, PlusAssign,
#[allow(dead_code)] BitwiseAndAssign, MinusAssign,
#[allow(dead_code)] BitwiseOrAssign, MultiplyAssign,
#[allow(dead_code)] BitwiseXorAssign, DivideAssign,
#[allow(dead_code)] LeftShiftAssign, #[allow(dead_code)]
#[allow(dead_code)] RightShiftAssign, ModuloAssign,
#[allow(dead_code)]
BitwiseAndAssign,
#[allow(dead_code)]
BitwiseOrAssign,
#[allow(dead_code)]
BitwiseXorAssign,
#[allow(dead_code)]
LeftShiftAssign,
#[allow(dead_code)]
RightShiftAssign,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -236,8 +265,10 @@ impl Parser {
let storage_class = self.parse_storage_class(); let storage_class = self.parse_storage_class();
let base_type = self.parse_type()?; let base_type = self.parse_type()?;
if self.check(&TokenType::LeftParen) || if self.check(&TokenType::LeftParen)
(self.check(&TokenType::Identifier("".to_string())) && self.peek_ahead(1)?.token_type == TokenType::LeftParen) { || (self.check(&TokenType::Identifier("".to_string()))
&& self.peek_ahead(1)?.token_type == TokenType::LeftParen)
{
self.parse_function_declaration(storage_class, base_type) self.parse_function_declaration(storage_class, base_type)
} else { } else {
self.parse_variable_declaration(storage_class, base_type) self.parse_variable_declaration(storage_class, base_type)
@@ -314,7 +345,10 @@ impl Parser {
}); });
}; };
self.consume(&TokenType::Semicolon, "Expected ';' after field declaration")?; self.consume(
&TokenType::Semicolon,
"Expected ';' after field declaration",
)?;
fields.push((field_name, field_type)); fields.push((field_name, field_type));
} }
@@ -351,7 +385,10 @@ impl Parser {
}); });
}; };
self.consume(&TokenType::Semicolon, "Expected ';' after field declaration")?; self.consume(
&TokenType::Semicolon,
"Expected ';' after field declaration",
)?;
fields.push((field_name, field_type)); fields.push((field_name, field_type));
} }
@@ -377,7 +414,8 @@ impl Parser {
if self.match_token(&TokenType::LeftBrace) { if self.match_token(&TokenType::LeftBrace) {
while !self.check(&TokenType::RightBrace) && !self.is_at_end() { while !self.check(&TokenType::RightBrace) && !self.is_at_end() {
let variant_name = if let TokenType::Identifier(name) = &self.advance()?.token_type { let variant_name = if let TokenType::Identifier(name) = &self.advance()?.token_type
{
name.clone() name.clone()
} else { } else {
return Err(AleccError::ParseError { return Err(AleccError::ParseError {
@@ -415,7 +453,9 @@ impl Parser {
// Helper methods // Helper methods
fn current_token(&self) -> Result<&Token> { fn current_token(&self) -> Result<&Token> {
self.tokens.get(self.current).ok_or_else(|| AleccError::ParseError { self.tokens
.get(self.current)
.ok_or_else(|| AleccError::ParseError {
line: 0, line: 0,
column: 0, column: 0,
message: "Unexpected end of input".to_string(), message: "Unexpected end of input".to_string(),
@@ -445,7 +485,9 @@ impl Parser {
} }
fn previous(&self) -> Result<&Token> { fn previous(&self) -> Result<&Token> {
self.tokens.get(self.current - 1).ok_or_else(|| AleccError::ParseError { self.tokens
.get(self.current - 1)
.ok_or_else(|| AleccError::ParseError {
line: 0, line: 0,
column: 0, column: 0,
message: "No previous token".to_string(), message: "No previous token".to_string(),
@@ -453,7 +495,9 @@ impl Parser {
} }
fn peek_ahead(&self, offset: usize) -> Result<&Token> { fn peek_ahead(&self, offset: usize) -> Result<&Token> {
self.tokens.get(self.current + offset).ok_or_else(|| AleccError::ParseError { self.tokens
.get(self.current + offset)
.ok_or_else(|| AleccError::ParseError {
line: 0, line: 0,
column: 0, column: 0,
message: "Unexpected end of input".to_string(), message: "Unexpected end of input".to_string(),
@@ -461,16 +505,19 @@ impl Parser {
} }
fn is_at_end(&self) -> bool { fn is_at_end(&self) -> bool {
self.current >= self.tokens.len() || self.current >= self.tokens.len()
matches!(self.tokens.get(self.current).map(|t| &t.token_type), Some(TokenType::Eof)) || matches!(
self.tokens.get(self.current).map(|t| &t.token_type),
Some(TokenType::Eof)
)
} }
fn check(&self, token_type: &TokenType) -> bool { fn check(&self, token_type: &TokenType) -> bool {
if self.is_at_end() { if self.is_at_end() {
false false
} else { } else {
std::mem::discriminant(&self.current_token().unwrap().token_type) == std::mem::discriminant(&self.current_token().unwrap().token_type)
std::mem::discriminant(token_type) == std::mem::discriminant(token_type)
} }
} }
@@ -526,7 +573,11 @@ impl Parser {
Ok(Declaration::TypeDef(name, base_type)) Ok(Declaration::TypeDef(name, base_type))
} }
fn parse_function_declaration(&mut self, _storage: StorageClass, return_type: Type) -> Result<Declaration> { fn parse_function_declaration(
&mut self,
_storage: StorageClass,
return_type: Type,
) -> Result<Declaration> {
let name = if let TokenType::Identifier(name) = &self.advance()?.token_type { let name = if let TokenType::Identifier(name) = &self.advance()?.token_type {
name.clone() name.clone()
} else { } else {
@@ -572,7 +623,10 @@ impl Parser {
self.advance()?; // Consume the LeftBrace self.advance()?; // Consume the LeftBrace
self.parse_block_statement()? self.parse_block_statement()?
} else { } else {
self.consume(&TokenType::Semicolon, "Expected ';' after function declaration")?; self.consume(
&TokenType::Semicolon,
"Expected ';' after function declaration",
)?;
Statement::Block(Vec::new()) // Forward declaration Statement::Block(Vec::new()) // Forward declaration
}; };
@@ -588,7 +642,11 @@ impl Parser {
})) }))
} }
fn parse_variable_declaration(&mut self, _storage: StorageClass, var_type: Type) -> Result<Declaration> { fn parse_variable_declaration(
&mut self,
_storage: StorageClass,
var_type: Type,
) -> Result<Declaration> {
let name = if let TokenType::Identifier(name) = &self.advance()?.token_type { let name = if let TokenType::Identifier(name) = &self.advance()?.token_type {
name.clone() name.clone()
} else { } else {
@@ -605,7 +663,10 @@ impl Parser {
None None
}; };
self.consume(&TokenType::Semicolon, "Expected ';' after variable declaration")?; self.consume(
&TokenType::Semicolon,
"Expected ';' after variable declaration",
)?;
Ok(Declaration::Variable(name, var_type, initializer)) Ok(Declaration::Variable(name, var_type, initializer))
} }
@@ -676,7 +737,10 @@ impl Parser {
None None
}; };
self.consume(&TokenType::Semicolon, "Expected ';' after variable declaration")?; self.consume(
&TokenType::Semicolon,
"Expected ';' after variable declaration",
)?;
Ok(Statement::Declaration { Ok(Statement::Declaration {
name, name,
@@ -757,10 +821,18 @@ impl Parser {
} }
fn is_type(&self, token_type: &TokenType) -> bool { fn is_type(&self, token_type: &TokenType) -> bool {
matches!(token_type, matches!(
TokenType::Int | TokenType::Float | TokenType::Double | token_type,
TokenType::Char | TokenType::Void | TokenType::Short | TokenType::Int
TokenType::Long | TokenType::Signed | TokenType::Unsigned) | TokenType::Float
| TokenType::Double
| TokenType::Char
| TokenType::Void
| TokenType::Short
| TokenType::Long
| TokenType::Signed
| TokenType::Unsigned
)
} }
fn parse_expression(&mut self) -> Result<Expression> { fn parse_expression(&mut self) -> Result<Expression> {
@@ -913,8 +985,12 @@ impl Parser {
fn parse_comparison(&mut self) -> Result<Expression> { fn parse_comparison(&mut self) -> Result<Expression> {
let mut expr = self.parse_shift()?; let mut expr = self.parse_shift()?;
while self.match_tokens(&[TokenType::Greater, TokenType::GreaterEqual, while self.match_tokens(&[
TokenType::Less, TokenType::LessEqual]) { TokenType::Greater,
TokenType::GreaterEqual,
TokenType::Less,
TokenType::LessEqual,
]) {
let operator = match self.previous()?.token_type { let operator = match self.previous()?.token_type {
TokenType::Greater => BinaryOperator::Greater, TokenType::Greater => BinaryOperator::Greater,
TokenType::GreaterEqual => BinaryOperator::GreaterEqual, TokenType::GreaterEqual => BinaryOperator::GreaterEqual,
@@ -995,7 +1071,16 @@ impl Parser {
} }
fn parse_unary(&mut self) -> Result<Expression> { fn parse_unary(&mut self) -> Result<Expression> {
if self.match_tokens(&[TokenType::LogicalNot, TokenType::Minus, TokenType::Plus, TokenType::Increment, TokenType::Decrement, TokenType::BitwiseAnd, TokenType::Multiply, TokenType::BitwiseNot]) { if self.match_tokens(&[
TokenType::LogicalNot,
TokenType::Minus,
TokenType::Plus,
TokenType::Increment,
TokenType::Decrement,
TokenType::BitwiseAnd,
TokenType::Multiply,
TokenType::BitwiseNot,
]) {
let operator = match self.previous()?.token_type { let operator = match self.previous()?.token_type {
TokenType::LogicalNot => UnaryOperator::LogicalNot, TokenType::LogicalNot => UnaryOperator::LogicalNot,
TokenType::Minus => UnaryOperator::Minus, TokenType::Minus => UnaryOperator::Minus,

Ver fichero

@@ -131,8 +131,15 @@ impl RegisterSet {
pub fn general_purpose_registers(&self) -> &'static [&'static str] { pub fn general_purpose_registers(&self) -> &'static [&'static str] {
match self { match self {
RegisterSet::X86_32 => &["eax", "ebx", "ecx", "edx", "esi", "edi"], RegisterSet::X86_32 => &["eax", "ebx", "ecx", "edx", "esi", "edi"],
RegisterSet::X86_64 => &["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"], RegisterSet::X86_64 => &[
RegisterSet::Aarch64 => &["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28"], "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13",
"r14", "r15",
],
RegisterSet::Aarch64 => &[
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12",
"x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24",
"x25", "x26", "x27", "x28",
],
} }
} }

Ver fichero

@@ -1,11 +1,11 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use alecc::cli::Args;
use alecc::codegen::CodeGenerator;
use alecc::compiler::Compiler;
use alecc::lexer::{Lexer, TokenType}; use alecc::lexer::{Lexer, TokenType};
use alecc::parser::Parser; use alecc::parser::Parser;
use alecc::codegen::CodeGenerator;
use alecc::targets::Target; use alecc::targets::Target;
use alecc::compiler::Compiler;
use alecc::cli::Args;
use std::path::PathBuf; use std::path::PathBuf;
#[test] #[test]
@@ -24,7 +24,10 @@ mod tests {
let mut lexer = Lexer::new(input); let mut lexer = Lexer::new(input);
let tokens = lexer.tokenize().unwrap(); let tokens = lexer.tokenize().unwrap();
assert!(matches!(tokens[0].token_type, TokenType::IntegerLiteral(42))); assert!(matches!(
tokens[0].token_type,
TokenType::IntegerLiteral(42)
));
assert!(matches!(tokens[1].token_type, TokenType::FloatLiteral(_))); assert!(matches!(tokens[1].token_type, TokenType::FloatLiteral(_)));
assert!(matches!(tokens[2].token_type, TokenType::CharLiteral('a'))); assert!(matches!(tokens[2].token_type, TokenType::CharLiteral('a')));
assert!(matches!(tokens[3].token_type, TokenType::StringLiteral(_))); assert!(matches!(tokens[3].token_type, TokenType::StringLiteral(_)));
@@ -51,7 +54,8 @@ mod tests {
let tokens = lexer.tokenize().unwrap(); let tokens = lexer.tokenize().unwrap();
// Comments should be filtered out // Comments should be filtered out
let identifier_count = tokens.iter() let identifier_count = tokens
.iter()
.filter(|t| matches!(t.token_type, TokenType::Identifier(_))) .filter(|t| matches!(t.token_type, TokenType::Identifier(_)))
.count(); .count();
assert_eq!(identifier_count, 2); // x and y assert_eq!(identifier_count, 2); // x and y