Merge remote-tracking branch 'origin/main'

This commit is contained in:
flameyosflow 2025-12-16 22:08:44 +02:00
commit 21f62247f5
24 changed files with 643 additions and 131 deletions

View file

@ -240,6 +240,7 @@ pub enum ExprKind {
Return(Option<Box<Expr>>),
Break,
Continue,
Defer(Box<Expr>),
}
#[derive(Debug, Clone, PartialEq)]
@ -273,7 +274,7 @@ pub enum UnOp {
Not,
Ref,
Deref,
PreInc
PreInc,
}
#[derive(Debug, Clone)]
@ -429,6 +430,7 @@ pub enum TypedExprKind {
Return(Option<Box<TypedExpr>>),
Break,
Continue,
Defer(Box<TypedExpr>),
}
#[derive(Debug, Clone)]

View file

@ -122,7 +122,7 @@ pub enum CUnaryOp {
Not,
Ref,
Deref,
PreInc
PreInc,
}
impl CUnaryOp {
@ -132,7 +132,7 @@ impl CUnaryOp {
CUnaryOp::Not => "!",
CUnaryOp::Ref => "&",
CUnaryOp::Deref => "*",
CUnaryOp::PreInc => "++"
CUnaryOp::PreInc => "++",
}
}
}

View file

@ -1,2 +1,2 @@
pub mod declaration_transpiler;
pub mod statements_transpiler;
pub mod statements_transpiler;

View file

@ -187,8 +187,20 @@ impl StatementsTranspiler {
}
TypedExprKind::Do(exprs) => {
let mut stmts = Vec::new();
let mut defers = Vec::new();
for expr in exprs {
stmts.push(self.transpile_stmt(expr)?);
match &expr.kind {
TypedExprKind::Defer(defer_expr) => {
defers.push(self.transpile_stmt(defer_expr)?);
}
_ => {
stmts.push(self.transpile_stmt(expr)?);
}
}
}
// Execute defers in reverse order at the end
for defer_stmt in defers.into_iter().rev() {
stmts.push(defer_stmt);
}
Ok(CStmt::Block(stmts))
}
@ -211,10 +223,8 @@ impl StatementsTranspiler {
Box::new(end_expr),
);
let incr = CExpr::UnOp(
CUnaryOp::PreInc,
Box::new(CExpr::Var(var_name.clone())),
);
let incr =
CExpr::UnOp(CUnaryOp::PreInc, Box::new(CExpr::Var(var_name.clone())));
let body_stmts = self.expr_to_stmts(body)?;
Ok(CStmt::For(init, cond, incr, body_stmts))
@ -224,32 +234,26 @@ impl StatementsTranspiler {
TypedExprKind::Variable(var) => {
let elem_ty = match &iterable.ty {
Type::Array(inner) => self.type_to_ctype(inner)?,
_ => return Err(format!(
"Cannot iterate over non-array variable `{}`",
var
)),
_ => {
return Err(format!(
"Cannot iterate over non-array variable `{}`",
var
));
}
};
// i = 0
let (idx_name, idx_decl) = self.fresh_tmp_var(
"_i",
CType::Int,
Some(CExpr::IntLit(0)),
);
let (idx_name, idx_decl) =
self.fresh_tmp_var("_i", CType::Int, Some(CExpr::IntLit(0)));
let cond = CExpr::BinOp(
Box::new(CExpr::Var(idx_name.clone())),
CBinaryOp::Lt,
Box::new(CExpr::Dot(
Box::new(CExpr::Var(var.clone())),
"len".into(),
)),
Box::new(CExpr::Dot(Box::new(CExpr::Var(var.clone())), "len".into())),
);
let incr = CExpr::UnOp(
CUnaryOp::PreInc,
Box::new(CExpr::Var(idx_name.clone())),
);
let incr =
CExpr::UnOp(CUnaryOp::PreInc, Box::new(CExpr::Var(idx_name.clone())));
// let x = array.data[i]
let bind = CStmt::VarDecl(CVarDecl {
@ -292,11 +296,8 @@ impl StatementsTranspiler {
);
// i = 0
let (idx_name, idx_decl) = self.fresh_tmp_var(
"_i",
CType::Int,
Some(CExpr::IntLit(0)),
);
let (idx_name, idx_decl) =
self.fresh_tmp_var("_i", CType::Int, Some(CExpr::IntLit(0)));
let cond = CExpr::BinOp(
Box::new(CExpr::Var(idx_name.clone())),
@ -304,10 +305,8 @@ impl StatementsTranspiler {
Box::new(CExpr::IntLit(arr_len as i64)),
);
let incr = CExpr::UnOp(
CUnaryOp::PreInc,
Box::new(CExpr::Var(idx_name.clone())),
);
let incr =
CExpr::UnOp(CUnaryOp::PreInc, Box::new(CExpr::Var(idx_name.clone())));
let bind = CStmt::VarDecl(CVarDecl {
name: var_name.clone(),
@ -332,6 +331,7 @@ impl StatementsTranspiler {
}
TypedExprKind::Break => Ok(CStmt::Break),
TypedExprKind::Continue => Ok(CStmt::Continue),
TypedExprKind::Defer(_) => Err("Defer should be handled in Do blocks".to_string()),
_ => {
// For other expressions, treat as expression statements
let c_expr = self.transpile_expr(expr)?;
@ -344,21 +344,36 @@ impl StatementsTranspiler {
match &expr.kind {
TypedExprKind::Do(stmts) => {
let mut c_stmts = Vec::new();
let mut defers = Vec::new();
let mut last_stmt = None;
for (i, stmt) in stmts.iter().enumerate() {
if i == stmts.len() - 1 {
// Last expression in a block should be returned
match &stmt.kind {
TypedExprKind::Return(_) => {
match &stmt.kind {
TypedExprKind::Defer(defer_expr) => {
defers.push(self.transpile_stmt(defer_expr)?);
}
_ => {
if i == stmts.len() - 1 {
last_stmt = Some(stmt);
} else {
c_stmts.push(self.transpile_stmt(stmt)?);
}
_ => {
// Convert to return statement
let c_expr = self.transpile_expr(stmt)?;
c_stmts.push(CStmt::Return(Some(c_expr)));
}
}
} else {
c_stmts.push(self.transpile_stmt(stmt)?);
}
}
// Execute defers in reverse order before the last statement
for defer_stmt in defers.into_iter().rev() {
c_stmts.push(defer_stmt);
}
if let Some(last) = last_stmt {
match &last.kind {
TypedExprKind::Return(_) => {
c_stmts.push(self.transpile_stmt(last)?);
}
_ => {
// Convert to return statement
let c_expr = self.transpile_expr(last)?;
c_stmts.push(CStmt::Return(Some(c_expr)));
}
}
}
Ok(c_stmts)
@ -402,12 +417,8 @@ impl StatementsTranspiler {
Type::Bool => Ok(CType::Bool),
Type::String => Ok(CType::Ptr(Box::new(CType::Char))),
Type::Unit => Ok(CType::Void),
Type::Ptr(inner) => {
Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?)))
}
Type::Array(inner) => {
Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?)))
}
Type::Ptr(inner) => Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))),
Type::Array(inner) => Ok(CType::Ptr(Box::new(self.type_to_ctype(inner)?))),
Type::Struct(name, _) => Ok(CType::Struct(name.clone())),
Type::Enum(name, _) => Ok(CType::Struct(name.clone())),
Type::Tuple(types) => {
@ -480,8 +491,7 @@ impl StatementsTranspiler {
}
let c_ret = self.type_annot_to_ctype(ret)?;
Ok(CType::Func(c_args, Box::new(c_ret)))
}
//_ => Err(format!("Unsupported type annotation: {:?}", annot)),
} //_ => Err(format!("Unsupported type annotation: {:?}", annot)),
}
}
}

View file

@ -1 +1 @@
pub mod transpiler;
pub mod transpiler;

View file

@ -1,8 +1,8 @@
use crate::ast::*;
use crate::c_ir::*;
use std::collections::HashMap;
use crate::c_lowerer::declaration_transpiler::DeclarationTranspiler;
use crate::c_lowerer::statements_transpiler::StatementsTranspiler;
use std::collections::HashMap;
pub struct Transpiler {
structs: HashMap<String, CStructDecl>,
@ -99,7 +99,7 @@ impl Transpiler {
// But for now, skip as they're handled differently
}
TypedASTNodeKind::Load(_) => {}
TypedASTNodeKind::Trait(typed_trait ) => {}
TypedASTNodeKind::Trait(typed_trait) => {}
TypedASTNodeKind::Use(_) => {}
}
Ok(())
@ -227,19 +227,20 @@ impl Transpiler {
}
CStmt::For(init, cond, incr, body) => {
let mut output = format!("for ({}; {}; {}) {{", self.generate_var_decl(init), self.generate_expr(cond), self.generate_expr(incr));
let mut output = format!(
"for ({}; {}; {}) {{",
self.generate_var_decl(init),
self.generate_expr(cond),
self.generate_expr(incr)
);
for stmt in body {
output.push_str(&format!(" {}", self.generate_stmt(stmt)));
}
output.push_str(" }\n");
output
}
CStmt::Break => {
"break;\n".to_string()
}
CStmt::Continue => {
"continue;\n".to_string()
}
CStmt::Break => "break;\n".to_string(),
CStmt::Continue => "continue;\n".to_string(),
}
}
@ -313,7 +314,10 @@ impl Transpiler {
}
CExpr::ArrayLit(array_lit) => {
let vec = array_lit.iter().map(|expr| self.generate_expr(expr)).collect::<Vec<_>>();
let vec = array_lit
.iter()
.map(|expr| self.generate_expr(expr))
.collect::<Vec<_>>();
let len = vec.len();
format!("{}[{}]{{ {} }}", vec[0], len, vec.join(", "))
}

410
src/import_resolver.rs Normal file
View file

@ -0,0 +1,410 @@
use crate::ast::{ASTNode, ASTNodeKind, Span};
use crate::lexer::Token;
use crate::parser::Parser;
use logos::Logos;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::Path;
const EXTENSION: &str = ".sui";
/// Error types for import resolution
#[derive(Debug, Clone)]
pub enum ImportError {
DuplicateSymbol {
symbol: String,
locations: Vec<String>,
},
FileNotFound {
path: String,
reason: String,
},
LexError {
file: String,
position: usize,
},
ParseError {
file: String,
message: String,
span: (usize, usize),
},
CircularDependency {
files: Vec<String>,
},
}
impl std::fmt::Display for ImportError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImportError::DuplicateSymbol { symbol, locations } => {
write!(f, "Duplicate global symbol '{}' defined in:\n", symbol)?;
for (i, loc) in locations.iter().enumerate() {
write!(f, " [{}] {}", i + 1, loc)?;
if i < locations.len() - 1 {
writeln!(f)?;
}
}
Ok(())
}
ImportError::FileNotFound { path, reason } => {
write!(f, "Module not found '{}': {}", path, reason)
}
ImportError::LexError { file, position } => {
write!(f, "Lexer error at position {} in {}", position, file)
}
ImportError::ParseError {
file,
message,
span,
} => {
write!(
f,
"Parse error in {}: {} (at {}..{})",
file, message, span.0, span.1
)
}
ImportError::CircularDependency { files } => {
write!(f, "Circular dependency detected: {}", files.join(" -> "))
}
}
}
}
impl std::error::Error for ImportError {}
/// Cached parse output for a file
#[derive(Clone, Debug)]
struct ParsedFile {
/// AST nodes from this file (before import collection)
nodes: Vec<ASTNode>,
/// Hash of the file content at parse time
content_hash: u64,
}
/// Tracks global symbols and their definitions
#[derive(Debug, Default)]
struct GlobalSymbolRegistry {
/// Maps symbol name to (file, span) where it was defined
symbols: HashMap<String, Vec<(String, Span)>>,
}
impl GlobalSymbolRegistry {
fn register(&mut self, symbol: String, file: String, span: Span) -> Result<(), ImportError> {
self.symbols.entry(symbol.clone()).or_insert_with(Vec::new);
let locations = self.symbols.get_mut(&symbol).unwrap();
locations.push((file.clone(), span));
// Check for duplicates
if locations.len() > 1 {
let location_strs = locations
.iter()
.map(|(f, s)| format!("{}:{}..{}", f, s.start, s.end))
.collect();
return Err(ImportError::DuplicateSymbol {
symbol,
locations: location_strs,
});
}
Ok(())
}
}
/// Manages file dependency resolution and caching
pub struct ImportResolver {
/// Cache of parsed files keyed by path
parse_cache: HashMap<String, ParsedFile>,
/// Global symbol registry for duplicate detection
symbol_registry: GlobalSymbolRegistry,
/// Set of files currently being processed (for cycle detection)
processing_stack: HashSet<String>,
/// Dependency graph: file -> list of files it depends on
dependency_graph: HashMap<String, HashSet<String>>,
}
impl ImportResolver {
pub fn new() -> Self {
ImportResolver {
parse_cache: HashMap::new(),
symbol_registry: GlobalSymbolRegistry::default(),
processing_stack: HashSet::new(),
dependency_graph: HashMap::new(),
}
}
/// Compute a simple hash of file content
fn hash_content(content: &str) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
hasher.finish()
}
/// Lex source code into tokens
fn lex_source(source: &str, filename: &str) -> Result<Vec<(Token, logos::Span)>, ImportError> {
let mut tokens = Vec::new();
let mut lexer = Token::lexer(source);
while let Some(token_result) = lexer.next() {
match token_result {
Ok(token) => {
tokens.push((token, lexer.span()));
}
Err(()) => {
return Err(ImportError::LexError {
file: filename.to_string(),
position: lexer.span().start,
});
}
}
}
Ok(tokens)
}
/// Parse tokens into AST
fn parse_tokens(
tokens: Vec<(Token, logos::Span)>,
filename: String,
) -> Result<Vec<ASTNode>, ImportError> {
let mut parser = Parser::new(filename.clone(), tokens);
parser.parse().map_err(|e| ImportError::ParseError {
file: filename,
message: e.message.clone(),
span: (e.span.start, e.span.end),
})
}
/// Parse a file (using cache if content hash matches)
fn parse_file(&mut self, filename: &str) -> Result<Vec<ASTNode>, ImportError> {
let source = fs::read_to_string(filename).map_err(|e| ImportError::FileNotFound {
path: filename.to_string(),
reason: e.to_string(),
})?;
let content_hash = Self::hash_content(&source);
// Check if we have a valid cached version
if let Some(cached) = self.parse_cache.get(filename) {
if cached.content_hash == content_hash {
println!(" [cache hit] {}", filename);
return Ok(cached.nodes.clone());
}
}
// Parse the file
println!(" [parsing] {}", filename);
let tokens = Self::lex_source(&source, filename)?;
let nodes = Self::parse_tokens(tokens, filename.to_string())?;
// Cache the result
self.parse_cache.insert(
filename.to_string(),
ParsedFile {
nodes: nodes.clone(),
content_hash,
},
);
Ok(nodes)
}
/// Collect all dependencies from a file (via Use/Load statements)
fn collect_dependencies(&self, filename: &str, nodes: &[ASTNode]) -> Vec<String> {
let mut deps = Vec::new();
let mut seen = HashSet::new();
for node in nodes {
if let ASTNodeKind::Use(use_stmt) = &node.kind {
let path = &use_stmt.path;
if !seen.contains(path) {
seen.insert(path.clone());
deps.push(path.clone());
}
}
}
// Resolve relative paths
let importing_dir = Path::new(filename).parent().unwrap_or(Path::new(""));
deps.iter()
.filter_map(|path| {
let resolved = if path.starts_with("std/") {
Some(format!("src/{}{}", path, EXTENSION))
} else if path.starts_with("@") {
// Package manager support - TODO
None
} else if path.starts_with(".") || path.starts_with("~") {
let resolved_path = importing_dir.join(path);
Some(resolved_path.to_string_lossy().to_string() + EXTENSION)
} else {
None
};
resolved
})
.collect()
}
/// Check for circular dependencies
fn detect_cycles(&self, file: &str, target: &str) -> Result<(), ImportError> {
if file == target {
return Err(ImportError::CircularDependency {
files: vec![file.to_string()],
});
}
// Simple cycle detection: if target depends on file, we have a cycle
if let Some(target_deps) = self.dependency_graph.get(target) {
if target_deps.contains(file) {
return Err(ImportError::CircularDependency {
files: vec![file.to_string(), target.to_string()],
});
}
}
Ok(())
}
/// Recursively resolve imports for a file
fn resolve_imports_recursive(&mut self, filename: &str) -> Result<Vec<ASTNode>, ImportError> {
// Check for cycles in processing
if self.processing_stack.contains(filename) {
return Err(ImportError::CircularDependency {
files: self
.processing_stack
.iter()
.cloned()
.collect::<Vec<_>>()
.into_iter()
.chain(std::iter::once(filename.to_string()))
.collect(),
});
}
self.processing_stack.insert(filename.to_string());
// Parse the file
let nodes = self.parse_file(filename)?;
let mut result = Vec::new();
// Collect dependencies
let deps = self.collect_dependencies(filename, &nodes);
self.dependency_graph
.insert(filename.to_string(), deps.iter().cloned().collect());
// Process each node
for node in nodes {
match &node.kind {
ASTNodeKind::Use(use_stmt) => {
let path = &use_stmt.path;
// Resolve path
let dep_file = if path.starts_with("std/") {
format!("src/{}{}", path, EXTENSION)
} else if path.starts_with("@") {
// Package manager support - TODO
todo!("Package manager imports not yet supported")
} else if path.starts_with(".") || path.starts_with("~") {
let importing_dir = Path::new(filename).parent().unwrap_or(Path::new(""));
let resolved_path = importing_dir.join(path);
resolved_path.to_string_lossy().to_string() + EXTENSION
} else {
return Err(ImportError::FileNotFound {
path: path.clone(),
reason: "Invalid import path format".to_string(),
});
};
// Check for cycles
self.detect_cycles(filename, &dep_file)?;
// Recursively resolve the imported file
let imported_nodes = self.resolve_imports_recursive(&dep_file)?;
result.extend(imported_nodes);
}
ASTNodeKind::Struct(s) => {
// Register the symbol
self.symbol_registry.register(
s.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
ASTNodeKind::Enum(e) => {
self.symbol_registry.register(
e.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
ASTNodeKind::Function(f) => {
self.symbol_registry.register(
f.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
ASTNodeKind::Impl(_) => {
// Impl blocks don't have names but are still tracked
result.push(node);
}
ASTNodeKind::Extern(e) => {
self.symbol_registry.register(
e.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
ASTNodeKind::Load(_) => {
result.push(node);
}
ASTNodeKind::Trait(t) => {
self.symbol_registry.register(
t.name.clone(),
filename.to_string(),
node.span.clone(),
)?;
result.push(node);
}
}
}
self.processing_stack.remove(filename);
Ok(result)
}
/// Resolve all imports starting from the given file
pub fn resolve(&mut self, filename: &str) -> Result<Vec<ASTNode>, ImportError> {
println!("Starting import resolution...");
self.resolve_imports_recursive(filename)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_content() {
let content1 = "fn main() do end";
let content2 = "fn main() do end";
let content3 = "fn main() { x }";
assert_eq!(
ImportResolver::hash_content(content1),
ImportResolver::hash_content(content2)
);
assert_ne!(
ImportResolver::hash_content(content1),
ImportResolver::hash_content(content3)
);
}
}

View file

@ -145,6 +145,9 @@ impl LambdaLowerer {
self.collect_free_vars_expr(start, lambda_params, free_vars, local_scope);
self.collect_free_vars_expr(end, lambda_params, free_vars, local_scope);
}
ExprKind::Defer(expr) => {
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
}
// Terminal expressions don't contain variables
ExprKind::Int(_)
| ExprKind::Float(_)
@ -372,6 +375,7 @@ impl LambdaLowerer {
| ExprKind::Variable(_)
| ExprKind::Break
| ExprKind::Continue => expr.kind.clone(),
ExprKind::Defer(inner) => ExprKind::Defer(Box::new(self.lower_expr(inner)?)),
};
Ok(Expr {

View file

@ -150,6 +150,9 @@ pub enum Token {
#[token("continue")]
KeywordContinue,
#[token("defer")]
KeywordDefer,
#[token("+")]
Plus,

View file

@ -2,8 +2,9 @@ pub const EXTENSION: &str = ".sui";
pub mod ast;
pub mod c_ir;
pub mod codegen;
pub mod c_lowerer;
pub mod codegen;
pub mod import_resolver;
pub mod lambda_lower;
pub mod lexer;
pub mod monomorphize;

View file

@ -1,30 +1,40 @@
use logos::Logos;
use std::fs;
use suicmez::{
codegen::transpiler::Transpiler,
lambda_lower::LambdaLowerer,
lexer::Token,
monomorphize::{Monomorphizer, check_no_typevars},
parser::Parser,
typechecker::TypeChecker,
};
use clap::Parser;
use std::fs;
use suicmez::{
codegen::transpiler::Transpiler,
import_resolver::ImportResolver,
lambda_lower::LambdaLowerer,
monomorphize::{Monomorphizer, check_no_typevars},
typechecker::TypeChecker,
};
fn main() {
// Check if a file was provided as argument
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
// Run all test files in the tests directory
run_test_suite();
return;
}
#[derive(Parser)]
#[command(author, version, about = "A compiler for the Sui language")]
struct Args {
/// Run the test suite instead of compiling a file
#[arg(short, long)]
test: bool,
let filename = &args[1];
println!("Type checking file: {}", filename);
/// The Sui source file to compile
file: Option<String>,
}
if let Err(e) = run_file(filename) {
eprintln!("Error: {}", e);
}
}
fn main() {
let args = Args::parse();
if args.test {
run_test_suite();
} else if let Some(filename) = args.file {
println!("Type checking file: {}", filename);
if let Err(e) = run_file(&filename) {
eprintln!("Error: {}", e);
}
} else {
eprintln!("No file specified. Use --test to run the test suite or provide a Sui file to compile.");
std::process::exit(1);
}
}
fn run_test_suite() {
println!("Running test suite...\n");
@ -164,34 +174,19 @@ fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> S
}
fn run_file(filename: &str) -> Result<(), String> {
// Read the source file
// ========== IMPORT RESOLUTION PHASE ==========
println!("\n=== Import Resolution Phase ===");
let mut resolver = ImportResolver::new();
let ast_nodes = resolver
.resolve(filename)
.map_err(|e| format!("Import resolution error: {}", e))?;
println!("Import resolution complete! {} total nodes loaded", ast_nodes.len());
// Read the source file for error reporting
let source = fs::read_to_string(filename)
.map_err(|e| format!("Error reading file {}: {}", filename, e))?;
// First, we need to parse the source code
let mut tokens = Vec::new();
let mut lexer = Token::lexer(&source);
loop {
match lexer.next() {
Some(Ok(token)) => {
let span = lexer.span();
tokens.push((token, span));
}
Some(Err(_)) => {
return Err("Lexing error".to_string());
}
None => break,
}
}
let mut parser = Parser::new(filename.to_string(), tokens);
let ast_nodes = parser
.parse()
.map_err(|e| format_parse_error(&source, &e))?;
println!("Parsed {} AST nodes successfully", ast_nodes.len());
// Lower lambdas to generated functions
let lowerer = LambdaLowerer::new();
let lowered_nodes = lowerer

View file

@ -327,6 +327,11 @@ impl Monomorphizer {
| TypedExprKind::String(_)
| TypedExprKind::Break
| TypedExprKind::Continue => expr.kind.clone(),
TypedExprKind::Defer(inner) => {
let (new_inner, mut inner_needs) = self.monomorphize_expr(inner)?;
needs.append(&mut inner_needs);
TypedExprKind::Defer(Box::new(new_inner))
}
TypedExprKind::Array(elems) => {
let mut new_elems = Vec::new();

View file

@ -1720,7 +1720,18 @@ impl Parser {
self.next();
break;
}
exprs.push(self.parse_expr()?);
if matches!(self.peek(), Some(Token::KeywordDefer)) {
self.next();
let expr = self.parse_expr()?;
let end = expr.span.end;
exprs.push(Expr {
kind: ExprKind::Defer(Box::new(expr)),
span: Span::new(&(start..end), self.file.clone()),
attributes: Vec::new(),
});
} else {
exprs.push(self.parse_expr()?);
}
if matches!(self.peek(), Some(Token::Semicolon)) {
self.next();

View file

@ -1410,6 +1410,11 @@ impl TypeChecker {
ExprKind::Break => (TypedExprKind::Break, Type::Never),
ExprKind::Continue => (TypedExprKind::Continue, Type::Never),
ExprKind::Defer(expr) => {
let typed_expr = self.typecheck_expr(expr)?;
(TypedExprKind::Defer(Box::new(typed_expr)), Type::Unit)
}
ExprKind::EarlyReturn(value) => {
let typed_value = if let Some(v) = value {
Some(Box::new(self.typecheck_expr(v)?))

View file

@ -2,7 +2,6 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int main(void);

18
tests/functions.c Normal file
View file

@ -0,0 +1,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int add(int x, int y);
int main(void);
int add(int x, int y) {
return (x + y);
}
int main(void) {
int sum = add(5, 3);
struct T id = identity(42);
return sum;
}

View file

@ -0,0 +1,6 @@
use "./module_a"
use "./module_b"
fn main() -> int do
0
end

View file

@ -0,0 +1,20 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
struct Point {
int x;
int y;
};
int helper_func(void);
int main(void);
int helper_func(void) {
return 42;
}
int main(void) {
return helper_func();
}

View file

@ -0,0 +1,5 @@
use "./util"
fn main() -> int do
helper_func()
end

View file

@ -0,0 +1,3 @@
fn test_func() -> int do
42
end

View file

@ -0,0 +1,3 @@
fn test_func() -> int do
99
end

View file

@ -0,0 +1,8 @@
fn helper_func() -> int do
42
end
struct Point
x: int,
y: int,
end

View file

@ -2,12 +2,10 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
struct Point {
int x;
int y;
};
int main(void);

View file

@ -1,19 +1,21 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
struct Number {
int value;
int value;
};
char *Number_show(struct Number self);
char* Number_show(struct Number self);
int main(void);
char *Number_show(struct Number self) { return "number"; }
char* Number_show(struct Number self) {
return "number";
}
int main(void) {
struct Number number = (struct Number){.value = 40};
Number_show(number);
return 0;
struct Number number = (struct Number){ .value = 40 };
Number_show(number);
return 0;
}