Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/block_comments.kit
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ include "stdio.h";
/* Block comment at start of file */
function main() {
/* Block comment with indentation */
var x: int = 42; /* End of line block comment */
var x: Int = 42; /* End of line block comment */

/*
* Multi-line block comment
Expand Down
5 changes: 5 additions & 0 deletions examples/fail_return_type.kit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include "stdio.h";

function main(): Int {
return "hello";
}
23 changes: 23 additions & 0 deletions examples/inference_test.kit
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
include "stdio.h";

function add(a: Int, b: Int) {
return a + b;
}

function sub(a: Int, b: Int) {
return a - b;
}

function main() {
var x = 50;
var y = 20;

var z = add(x, y);
z = sub(z, 5);

var w = if z > 60 then 10 else 0;

printf("Value of w: %d\n", w);

return 0;
}
1 change: 1 addition & 0 deletions examples/inference_test.kit.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Value of w: 10
2 changes: 1 addition & 1 deletion examples/line_comments.kit
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ include "stdio.h";
// Line comment at start of file
function main() {
// Line comment with indentation
var x: int = 42; // End of line comment
var x: Int = 42; // End of line comment

// Another line comment
printf("%d", x);
Expand Down
6 changes: 3 additions & 3 deletions examples/mixed_comments.kit
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ include "stdio.h";
// Mix of line and block comments
function main() {
// Start with line comment
var x: int = 1;
var x: Int = 1;

/* Block comment in middle */
var y: int = 2; // End of line comment
var y: Int = 2; // End of line comment

/*
* Multi-line block comment
* followed by line comment
*/
// After multi-line
var z: int = x + y;
var z: Int = x + y;
printf("%d", z); /* Block comment at end */
}
4 changes: 2 additions & 2 deletions kitc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn main() -> Result<(), Error> {

fn compile(source: &PathBuf, libs: &[String], measure: bool) -> Result<PathBuf, String> {
let init = time::Instant::now();
fs::read_to_string(source).map_err(|_| format!("couldn't read {:?}", source))?;
fs::read_to_string(source).map_err(|_| format!("couldn't read {}", source.display()))?;

let ext = if cfg!(windows) { "exe" } else { "" };
let exe_path = source.with_extension(ext);
Expand All @@ -92,7 +92,7 @@ fn compile(source: &PathBuf, libs: &[String], measure: bool) -> Result<PathBuf,
fn run_executable(exe_path: &PathBuf) -> Result<(), String> {
let status = Command::new(exe_path)
.status()
.map_err(|e| format!("failed to launch executable: {}", e))?;
.map_err(|e| format!("failed to launch executable: {e}"))?;

if !status.success() {
std::process::exit(status.code().unwrap_or(1));
Expand Down
16 changes: 9 additions & 7 deletions kitc/tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ fn run_example_test(
.ok_or("couldn't get workspace root")?;

let examples_dir = workspace_root.join("examples");
let example_file = examples_dir.join(format!("{}.kit", example_name));
let expected_file = examples_dir.join(format!("{}.kit.expected", example_name));
let example_file = examples_dir.join(format!("{example_name}.kit"));
let expected_file = examples_dir.join(format!("{example_name}.kit.expected"));

assert!(
example_file.exists(),
Expand All @@ -39,8 +39,7 @@ fn run_example_test(
);

log::info!(
"Running example {} in {} (path: {}). Expected file is at {}",
example_name,
"Running example {example_name} in {} (path: {}). Expected file is at {}",
workspace_root.display(),
example_file.display(),
expected_file.display()
Expand Down Expand Up @@ -77,9 +76,7 @@ fn run_example_test(
.success();

// TODO: executable files are actually generated in the CWD, not in the examples folder.
// This explains why the executable is not actually removed. But I don't get why these tests
// passed on Linux and Mac if std::fs::remove_file is supposed to also fail when the file
// doesn't exist.
// This explains why the executable is not actually generated in the examples folder.
if let Err(err) = std::fs::remove_file(&executable_path) {
log::error!("Failed to remove executable: {err}");
}
Expand Down Expand Up @@ -175,6 +172,11 @@ fn test_mixed_comments() -> Result<(), Box<dyn std::error::Error>> {
run_example_test("mixed_comments", None)
}

#[test]
fn test_inference() -> Result<(), Box<dyn std::error::Error>> {
run_example_test("inference_test", None)
}

#[test]
fn test_nested_comments() -> Result<(), Box<dyn std::error::Error>> {
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
Expand Down
39 changes: 28 additions & 11 deletions kitlang/src/codegen/ast.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::codegen::types::*;
use crate::codegen::types::{AssignmentOperator, BinaryOperator, Type, TypeId, UnaryOperator};

use std::collections::HashSet;

/// Represents a C header inclusion.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Include {
/// Path to the header file (e.g., "stdio.h").
/// Path to header file (e.g., "stdio.h").
pub path: String,
}

Expand All @@ -16,8 +16,10 @@ pub struct Function {
pub name: String,
/// List of function parameters.
pub params: Vec<Param>,
/// Return type (`None` for void functions).
/// Return type annotation (`None` for void inference).
pub return_type: Option<Type>,
/// Inferred return type ID.
pub inferred_return: Option<TypeId>,
/// Function body as a block of statements.
pub body: Block,
}
Expand All @@ -27,8 +29,10 @@ pub struct Function {
pub struct Param {
/// Parameter name.
pub name: String,
/// Parameter type.
pub ty: Type,
/// Parameter type annotation (if specified).
pub annotation: Option<Type>,
/// Inferred parameter type ID.
pub ty: TypeId,
}

/// Represents a block of statements (e.g., function body or scope block).
Expand All @@ -46,7 +50,9 @@ pub enum Stmt {
/// Variable name.
name: String,
/// Type annotation (`None` for type inference).
ty: Option<Type>,
annotation: Option<Type>,
/// Inferred variable type ID.
inferred: TypeId,
/// Initializer expression (`None` for uninitialized).
init: Option<Expr>,
},
Expand Down Expand Up @@ -89,34 +95,42 @@ pub enum Stmt {
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
/// Variable or function identifier.
Identifier(String),
Identifier(String, TypeId),
/// Literal value.
Literal(Literal),
Literal(Literal, TypeId),
/// Function call.
Call {
/// Name of the callee function.
callee: String,
/// Arguments passed to the function.
args: Vec<Expr>,
/// Inferred return type.
ty: TypeId,
},
/// Unary operation.
UnaryOp {
/// The unary operator.
op: UnaryOperator,
/// The operand expression.
expr: Box<Expr>,
/// Inferred result type.
ty: TypeId,
},
/// Binary operation.
BinaryOp {
op: BinaryOperator,
left: Box<Expr>,
right: Box<Expr>,
/// Inferred result type.
ty: TypeId,
},
/// Assignment operation.
Assign {
op: AssignmentOperator,
left: Box<Expr>,
right: Box<Expr>,
/// Inferred result type.
ty: TypeId,
},
/// If-then-else expression.
If {
Expand All @@ -126,6 +140,8 @@ pub enum Expr {
then_branch: Box<Expr>,
/// The expression to evaluate if the condition is false.
else_branch: Box<Expr>,
/// Inferred result type.
ty: TypeId,
},
/// Range literal expression (e.g., `1...10`).
RangeLiteral {
Expand Down Expand Up @@ -153,15 +169,16 @@ pub enum Literal {

impl Literal {
/// Converts the literal to its C representation string.
#[must_use]
pub fn to_c(&self) -> String {
match self {
Literal::Int(i) => i.to_string(),
Literal::Float(f) => {
// Ensure float literals have 'f' suffix in C
if f.fract() == 0.0 {
format!("{}.0f", f)
format!("{f}.0f")
} else {
format!("{}f", f)
format!("{f}f")
}
}
Literal::String(s) => {
Expand All @@ -176,7 +193,7 @@ impl Literal {
_ => c.to_string(),
})
.collect();
format!("\"{}\"", escaped)
format!("\"{escaped}\"")
}
Literal::Bool(b) => b.to_string(),
Literal::Null => "NULL".to_string(),
Expand Down
9 changes: 5 additions & 4 deletions kitlang/src/codegen/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl Toolchain {
match self {
Toolchain::Gcc | Toolchain::Clang => {
let flags = ["-std=c99", "-Wall", "-Wextra", "-pedantic"];
flags.iter().map(|s| s.to_string()).collect()
flags.iter().map(ToString::to_string).collect()
}
#[cfg(windows)]
Toolchain::Msvc => {
Expand Down Expand Up @@ -181,7 +181,7 @@ pub struct CompilerOptions {
pub link_opts: Vec<String>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct CompilerMeta(pub Toolchain);

impl CompilerOptions {
Expand Down Expand Up @@ -256,9 +256,10 @@ impl CompilerOptions {

/// Build the compiler invocation used to spawn `Command`.
///
/// Returns (path_to_compiler_executable, args_vec).
/// Returns (`path_to_compiler_executable`, `args_vec`).
///
/// # Errors
///
/// Errors:
/// - if `sources` is empty
/// - if `output` is not set
/// - if no system compiler can be found and no `enforced_toolchain` is usable
Expand Down
Loading