Skip to content
Open
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
24 changes: 24 additions & 0 deletions examples/globals_basic.kit
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
include "stdio.h";

var globalInt: Int = 42;
const globalConstInt: Int = 100;
var globalFloat: Float = 3.14;
const globalConstFloat: Float = 2.718;
var globalBool: Bool = true;
const globalConstBool: Bool = false;

function main() {
printf("globalInt: %d\n", globalInt);
printf("globalConstInt: %d\n", globalConstInt);
printf("globalFloat: %f\n", globalFloat);
printf("globalConstFloat: %f\n", globalConstFloat);
printf("globalBool: %d\n", globalBool);
printf("globalConstBool: %d\n", globalConstBool);

globalInt = 999;
globalFloat = 1.5;
globalBool = false;
printf("modified globalInt: %d\n", globalInt);
printf("modified globalFloat: %f\n", globalFloat);
printf("modified globalBool: %d\n", globalBool);
}
9 changes: 9 additions & 0 deletions examples/globals_basic.kit.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
globalInt: 42
globalConstInt: 100
globalFloat: 3.140000
globalConstFloat: 2.718000
globalBool: 1
globalConstBool: 0
modified globalInt: 999
modified globalFloat: 1.500000
modified globalBool: 0
48 changes: 48 additions & 0 deletions examples/globals_comprehensive.kit
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
include "stdio.h";

var globalInt: Int = 42;
const globalConstInt: Int = 100;

var globalFloat: Float = 3.14;
const globalConstFloat: Float = 2.718;

var globalString = "hello";
const globalConstString = "world";

var globalBool: Bool = true;
const globalConstBool: Bool = false;

// Test global referencing another global
var globalRef: Int = 52;
const globalConstRef: Int = globalConstInt + 50;

function main() {
printf("--- Basic Types ---\n");
printf("globalInt: %d\n", globalInt);
printf("globalConstInt: %d\n", globalConstInt);

printf("globalFloat: %f\n", globalFloat);
printf("globalConstFloat: %f\n", globalConstFloat);

printf("globalString: %s\n", globalString);
printf("globalConstString: %s\n", globalConstString);

printf("globalBool: %d\n", globalBool);
printf("globalConstBool: %d\n", globalConstBool);

printf("--- References ---\n");
printf("globalRef: %d\n", globalRef);
printf("globalConstRef: %d\n", globalConstRef);

printf("--- Modification ---\n");

globalInt = 999;
globalFloat = 1.5;
globalString = "modified";
globalBool = false;

printf("modified globalInt: %d\n", globalInt);
printf("modified globalFloat: %f\n", globalFloat);
printf("modified globalString: %s\n", globalString);
printf("modified globalBool: %d\n", globalBool);
}
17 changes: 17 additions & 0 deletions examples/globals_comprehensive.kit.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--- Basic Types ---
globalInt: 42
globalConstInt: 100
globalFloat: 3.140000
globalConstFloat: 2.718000
globalString: hello
globalConstString: world
globalBool: 1
globalConstBool: 0
--- References ---
globalRef: 52
globalConstRef: 150
--- Modification ---
modified globalInt: 999
modified globalFloat: 1.500000
modified globalString: modified
modified globalBool: 0
17 changes: 17 additions & 0 deletions examples/globals_test.kit
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
include "stdio.h";

var globalVar: Int = 42;
const globalConst: Int = 100;
var globalWithInference = 123;
const constWithInference = 456;

function main() {
printf("globalVar: %d\n", globalVar);
printf("globalConst: %d\n", globalConst);
printf("globalWithInference: %d\n", globalWithInference);
printf("constWithInference: %d\n", constWithInference);

// Test modifying global variable
globalVar = 999;
printf("modified globalVar: %d\n", globalVar);
}
5 changes: 5 additions & 0 deletions examples/globals_test.kit.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
globalVar: 42
globalConst: 100
globalWithInference: 123
constWithInference: 456
modified globalVar: 999
15 changes: 15 additions & 0 deletions kitc/tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,21 @@ fn test_enum_defaults() -> Result<(), Box<dyn std::error::Error>> {
run_example_test("enum_defaults", None)
}

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

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

#[test]
fn test_globals_test() -> Result<(), Box<dyn std::error::Error>> {
run_example_test("globals_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
19 changes: 18 additions & 1 deletion kitlang/src/codegen/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ pub enum Expr {
},
}

/// Represents literal values in Kit.
/// Represents a literal value in Kit.
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
/// Signed integer literal.
Expand All @@ -206,6 +206,21 @@ pub enum Literal {
Null,
}

/// Represents a global variable or constant declaration.
#[derive(Clone, Debug, PartialEq)]
pub struct GlobalDecl {
/// Variable name.
pub name: String,
/// Type annotation (`None` for type inference).
pub annotation: Option<Type>,
/// Inferred variable type ID.
pub inferred: TypeId,
/// Initializer expression (`None` for uninitialized).
pub init: Option<Expr>,
/// Whether this is a const declaration.
pub is_const: bool,
}

impl Literal {
/// Converts the literal to its C representation string.
#[must_use]
Expand Down Expand Up @@ -247,6 +262,8 @@ pub struct Program {
pub includes: Vec<Include>,
/// Kit module imports (not directly used in C generation).
pub imports: HashSet<String>,
/// Top-level global variable and constant declarations.
pub globals: Vec<GlobalDecl>,
/// Top-level function definitions.
pub functions: Vec<Function>,
/// Struct type definitions.
Expand Down
87 changes: 80 additions & 7 deletions kitlang/src/codegen/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::fmt::Write;
use std::path::{Path, PathBuf};
use std::process::Command;

use crate::codegen::ast::{Block, Expr, Function, Include, Program, Stmt};
use crate::codegen::ast::{Block, Expr, Function, GlobalDecl, Include, Program, Stmt};
use crate::codegen::compiler::{CompilerMeta, CompilerOptions, Toolchain};
use crate::codegen::inference::TypeInferencer;
use crate::codegen::parser::Parser as CodeParser;
Expand Down Expand Up @@ -39,6 +39,7 @@ impl Compiler {

fn parse(&mut self) -> CompileResult<Program> {
let mut includes = Vec::new();
let mut globals = Vec::new();
let mut functions = Vec::new();
let mut structs = Vec::new();
let mut enums = Vec::new();
Expand All @@ -65,6 +66,10 @@ impl Compiler {
includes.push(self.parser.parse_include(pair));
}

Rule::var_decl => {
globals.push(self.parser.parse_global_var_decl(pair)?);
}

Rule::function_decl => {
functions.push(self.parser.parse_function(pair)?);
}
Expand Down Expand Up @@ -95,6 +100,7 @@ impl Compiler {
Ok(Program {
includes,
imports: HashSet::new(),
globals,
functions,
structs,
enums,
Expand Down Expand Up @@ -135,16 +141,36 @@ impl Compiler {
}
};

// Emit struct declarations first
// Scan all types to gather required headers BEFORE emitting code that uses them
// Scan struct field types
for struct_def in &prog.structs {
out.push_str(&self.generate_struct_declaration(struct_def, &prog.structs));
out.push('\n');
for field in &struct_def.fields {
if let Ok(ty) = self.inferencer.store.resolve(field.ty) {
collect_from_type(&ty);
} else if let Some(ann) = &field.annotation {
collect_from_type(ann);
}
}
}

// Emit enum declarations
// Scan enum variant argument types
for enum_def in &prog.enums {
out.push_str(&self.generate_enum_declaration(enum_def));
out.push('\n');
for variant in &enum_def.variants {
for arg in &variant.args {
if let Ok(ty) = self.inferencer.store.resolve(arg.ty) {
collect_from_type(&ty);
} else if let Some(ann) = &arg.annotation {
collect_from_type(ann);
}
}
}
}

// Scan global variable types
for global in &prog.globals {
if let Ok(ty) = self.inferencer.store.resolve(global.inferred) {
collect_from_type(&ty);
}
}

// scan every function signature & body for types to gather their headers/typedefs
Expand Down Expand Up @@ -188,6 +214,24 @@ impl Compiler {
out.push('\n');
}

// Emit struct declarations
for struct_def in &prog.structs {
out.push_str(&self.generate_struct_declaration(struct_def, &prog.structs));
out.push('\n');
}

// Emit enum declarations
for enum_def in &prog.enums {
out.push_str(&self.generate_enum_declaration(enum_def));
out.push('\n');
}

// Emit global variable declarations
for global in &prog.globals {
out.push_str(&self.transpile_global(global));
out.push('\n');
}

// emit functions as before...
for func in &prog.functions {
out.push_str(&self.transpile_function(func));
Expand All @@ -196,6 +240,35 @@ impl Compiler {
out
}

fn transpile_global(&self, global: &GlobalDecl) -> String {
let ty = self.inferencer.store.resolve(global.inferred).map_or_else(
|_| "int".to_string(),
|t| {
if let Type::Named(name) = &t {
if self.inferencer.is_struct_type(name) {
format!("struct {}", name)
} else {
t.to_c_repr().name
}
} else {
t.to_c_repr().name
}
},
);

let const_prefix = if global.is_const { "const " } else { "" };

match &global.init {
Some(expr) => {
let init_str = self.transpile_expr(expr);
format!("{const_prefix}{ty} {} = {init_str};", global.name)
}
None => {
format!("{const_prefix}{ty} {};", global.name)
}
}
}

fn generate_struct_declaration(
&self,
struct_def: &StructDefinition,
Expand Down
40 changes: 38 additions & 2 deletions kitlang/src/codegen/inference.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashSet;

use super::Field;
use super::ast::{Block, Expr, Function, Literal, Program, Stmt};
use super::ast::{Block, Expr, Function, GlobalDecl, Literal, Program, Stmt};
use super::symbols::{EnumVariantInfo, SymbolTable};
use super::type_ast::{EnumDefinition, FieldInit, StructDefinition};
use super::types::{BinaryOperator, Type, TypeId, TypeStore, UnaryOperator};
Expand Down Expand Up @@ -44,12 +44,44 @@ impl TypeInferencer {
self.register_enum_types(&prog.enums)?;
self.register_struct_types(&prog.structs)?;

// Infer global variable types first (before functions)
self.infer_globals(&mut prog.globals)?;

for func in &mut prog.functions {
self.infer_function(func)?;
}
Ok(())
}

/// Infer types for global variable declarations
fn infer_globals(&mut self, globals: &mut [GlobalDecl]) -> CompileResult<()> {
for global in globals {
if let Some(init_expr) = &mut global.init {
let init_ty = self.infer_expr(init_expr)?;

global.inferred = if let Some(ann) = &global.annotation {
let ann_ty = self.store.new_known(ann.clone());
self.unify(ann_ty, init_ty)?;
ann_ty
} else {
init_ty
};

self.symbols.define_global(&global.name, global.inferred);
} else if let Some(ann) = &global.annotation {
// Declaration without initializer -> just use annotation
global.inferred = self.store.new_known(ann.clone());
self.symbols.define_global(&global.name, global.inferred);
} else {
return Err(CompilationError::TypeError(format!(
"Global variable '{}' declared without type annotation or initializer",
global.name
)));
}
}
Ok(())
}

/// Register enum types in the type store and symbol table
fn register_enum_types(&mut self, enums: &[EnumDefinition]) -> CompileResult<()> {
for enum_def in enums {
Expand Down Expand Up @@ -264,7 +296,11 @@ impl TypeInferencer {
fn infer_expr(&mut self, expr: &mut Expr) -> Result<TypeId, CompilationError> {
let ty = match expr {
Expr::Identifier(name, ty_id) => {
if let Some(var_ty) = self.symbols.lookup_var(name) {
// First check if it's a global variable
if let Some(global_ty) = self.symbols.lookup_global(name) {
*ty_id = global_ty;
global_ty
} else if let Some(var_ty) = self.symbols.lookup_var(name) {
*ty_id = var_ty;
var_ty
} else {
Expand Down
Loading