From 0482e514899c093bd1a5955b12583f957cac309d Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 6 Dec 2025 19:37:11 +0530 Subject: [PATCH 01/27] Bug fixes and Feature additions --- Cargo.toml | 14 +- examples/bell_test.qc | 16 + examples/classes_example.qc | 177 ++ examples/graphics_demo.qc | 264 +++ examples/output_test.svg | 7 + src/codegen/mod.rs | 2095 ++++++++++++++----- src/doc_generator.rs | 110 +- src/environment/mod.rs | 162 +- src/error.rs | 132 ++ src/evaluator/mod.rs | 2495 +++++++++++++++++------ src/graphics/mod.rs | 434 ++++ src/graphics_runtime.rs | 260 +++ src/hardware_integration.rs | 241 ++- src/lexer/mod.rs | 862 ++++---- src/lexer/token.rs | 109 +- src/lib.rs | 25 +- src/linker.rs | 118 +- src/main.rs | 804 +++++--- src/parser/ast.rs | 123 +- src/parser/mod.rs | 1403 ++++++++----- src/quantum_backend/cirq_local.rs | 88 +- src/quantum_backend/ibm_qiskit.rs | 66 +- src/quantum_backend/mod.rs | 184 +- src/quantum_backend/native_simulator.rs | 443 ++++ src/runtime.rs | 91 +- src/type_checker.rs | 1411 +++++++++++-- stdlib/graphics.qc | 334 +++ tests/classes_test.qc | 131 ++ tests/test_draw.qc | 43 + 29 files changed, 9636 insertions(+), 3006 deletions(-) create mode 100644 examples/bell_test.qc create mode 100644 examples/classes_example.qc create mode 100644 examples/graphics_demo.qc create mode 100644 examples/output_test.svg create mode 100644 src/error.rs create mode 100644 src/graphics/mod.rs create mode 100644 src/graphics_runtime.rs create mode 100644 src/quantum_backend/native_simulator.rs create mode 100644 stdlib/graphics.qc create mode 100644 tests/classes_test.qc create mode 100644 tests/test_draw.qc diff --git a/Cargo.toml b/Cargo.toml index a00e989..f9c2585 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "quantica" version = "0.1.0" edition = "2021" - +# This new [lib] section tells Cargo to build libquantica.a [lib] name = "quantica" path = "src/lib.rs" @@ -11,7 +11,7 @@ crate-type = ["staticlib","rlib"] - +# This tells Cargo to also build the quantica.exe compiler [[bin]] name = "quantica" path = "src/main.rs" @@ -21,16 +21,18 @@ num-complex = "0.4.6" rand = "0.8.5" inkwell = { version = "0.6.0", features = ["llvm18-1"] } libc = "0.2" -actix-web = "4" -serde = { version = "1.0", features = ["derive"] } #JSON +actix-web = "4" # The web server framework +serde = { version = "1.0", features = ["derive"] } # For JSON tokio = { version = "1", features = ["macros", "rt-multi-thread"] } reqwest = { version = "0.11", features = ["json", "blocking"] } serde_json = "1.0" +lazy_static = "1.4" +rayon = "1.7" [profile.dev] debug = 0 strip = "symbols" -codegen-units = 1 -overflow-checks = false +codegen-units = 1 # Force single compilation unit +overflow-checks = false # Can sometimes interfere with code size checks lto = "fat" diff --git a/examples/bell_test.qc b/examples/bell_test.qc new file mode 100644 index 0000000..706d211 --- /dev/null +++ b/examples/bell_test.qc @@ -0,0 +1,16 @@ +// Bell State Test for IBM Quantum Hardware +func main(): + // Create a 2-qubit register + quantum q[2] + + // Create Bell state: (|00⟩ + |11⟩)/√2 + apply Hadamard(q[0]) + apply CNOT(q[0], q[1]) + + // Measure both qubits + let m0 = measure(q[0]) + let m1 = measure(q[1]) + + print("Measured qubit 0:", m0) + print("Measured qubit 1:", m1) + print("Bell state created successfully!") \ No newline at end of file diff --git a/examples/classes_example.qc b/examples/classes_example.qc new file mode 100644 index 0000000..48b0888 --- /dev/null +++ b/examples/classes_example.qc @@ -0,0 +1,177 @@ +// examples/classes_example.qc +// Demonstrating classes and objects in Quantica + +print("=== QUANTICA CLASSES & OBJECTS ===\n") + +// --- Example 1: Basic Class --- +print("--- Example 1: Basic Point Class ---") + +class Point: + let x: Float = 0.0 + let y: Float = 0.0 + + func init(x_val: Float, y_val: Float): + x = x_val + y = y_val + + func distance_from_origin() -> Float: + return sqrt(x * x + y * y) + + func move(dx: Float, dy: Float): + x = x + dx + y = y + dy + + func display(): + print("Point(" + to_string(x) + ", " + to_string(y) + ")") + +let p1 = new Point(3.0, 4.0) +p1.display() +print("Distance from origin:", p1.distance_from_origin()) + +p1.move(1.0, 1.0) +print("After moving:") +p1.display() + +// --- Example 2: Quantum Circuit Class --- +print("\n--- Example 2: Quantum Circuit Class ---") + +class QuantumCircuit: + let num_qubits: Int + let qreg: QuantumRegister + + func init(n: Int): + num_qubits = n + quantum temp[n] + qreg = temp + + func apply_hadamard(index: Int): + apply Hadamard(qreg[index]) + + func apply_cnot(control: Int, target: Int): + apply CNOT(qreg[control], qreg[target]) + + func measure_all(): + for i in 0..num_qubits: + let result = measure(qreg[i]) + print("Qubit", i, ":", result) + + func create_bell_state(): + apply_hadamard(0) + apply_cnot(0, 1) + +let circuit = new QuantumCircuit(2) +circuit.create_bell_state() +print("Bell state created!") +circuit.measure_all() + +// --- Example 3: Inheritance (if supported) --- +print("\n--- Example 3: Vector Class with Inheritance ---") + +class Vector2D: + public let x: Float + public let y: Float + + func init(x_val: Float, y_val: Float): + x = x_val + y = y_val + + func magnitude() -> Float: + return sqrt(x * x + y * y) + + func dot(other: Vector2D) -> Float: + return x * other.x + y * other.y + +class Vector3D(Vector2D): + public let z: Float + + func init(x_val: Float, y_val: Float, z_val: Float): + super.init(x_val, y_val) + z = z_val + + func magnitude() -> Float: + return sqrt(x * x + y * y + z * z) + + func cross(other: Vector3D) -> Vector3D: + let cx = y * other.z - z * other.y + let cy = z * other.x - x * other.z + let cz = x * other.y - y * other.x + return new Vector3D(cx, cy, cz) + +let v1 = new Vector3D(1.0, 0.0, 0.0) +let v2 = new Vector3D(0.0, 1.0, 0.0) +print("v1 magnitude:", v1.magnitude()) +print("v1 dot v2:", v1.dot(v2)) + +// --- Example 4: Counter Class with Static Method --- +print("\n--- Example 4: Counter Class ---") + +class Counter: + private mut count: Int = 0 + static let max_count: Int = 100 + + func increment(): + if count < max_count: + count = count + 1 + + func decrement(): + if count > 0: + count = count - 1 + + func get_value() -> Int: + return count + + func reset(): + count = 0 + +let counter = new Counter() +counter.increment() +counter.increment() +counter.increment() +print("Counter value:", counter.get_value()) + +counter.decrement() +print("After decrement:", counter.get_value()) + +counter.reset() +print("After reset:", counter.get_value()) + +// --- Example 5: Quantum State Manager --- +print("\n--- Example 5: Quantum State Manager ---") + +class QuantumStateManager: + private let states: Array + private mut current_index: Int + + func init(): + states = [] + current_index = 0 + + func add_state(name: String, qreg: QuantumRegister): + // Store state information + states[current_index] = {"name": name, "register": qreg} + current_index = current_index + 1 + + func get_state_count() -> Int: + return current_index + + func debug_all(): + for i in 0..current_index: + let state = states[i] + print("State:", state["name"]) + debug_state(state["register"]) + +let manager = new QuantumStateManager() + +quantum q1[2] +apply Hadamard(q1[0]) +manager.add_state("superposition", q1) + +quantum q2[2] +apply X(q2[0]) +apply CNOT(q2[0], q2[1]) +manager.add_state("bell_state", q2) + +print("\nStored states:", manager.get_state_count()) +manager.debug_all() + +print("\n=== CLASSES DEMONSTRATION COMPLETE ===") \ No newline at end of file diff --git a/examples/graphics_demo.qc b/examples/graphics_demo.qc new file mode 100644 index 0000000..0f73692 --- /dev/null +++ b/examples/graphics_demo.qc @@ -0,0 +1,264 @@ +// examples/graphics_demo.qc +// Comprehensive graphics demonstration + +import "stdlib/graphics.qc" as gfx +import "stdlib/math.qc" as math + +print("=== QUANTICA GRAPHICS DEMO ===\n") + +// ============================================ +// 1. BASIC SHAPES +// ============================================ +print("--- Test 1: Basic Shapes ---") +let canvas1 = gfx.Canvas(800, 600) +canvas1.set_background(gfx.COLOR_WHITE) + +// Draw shapes +canvas1.draw_rect(50.0, 50.0, 100.0, 80.0, gfx.COLOR_RED, True) +canvas1.draw_circle(400.0, 300.0, 50.0, gfx.COLOR_BLUE, False) +canvas1.draw_line(100.0, 500.0, 700.0, 500.0, gfx.COLOR_GREEN, 3.0) +canvas1.draw_text(350.0, 550.0, "Basic Shapes Demo", gfx.COLOR_BLACK, 20.0) + +canvas1.save_svg("output/basic_shapes.svg") +print("✓ Saved basic_shapes.svg") +canvas1.destroy() + +// ============================================ +// 2. GEOMETRIC PATTERNS +// ============================================ +print("\n--- Test 2: Geometric Patterns ---") +let canvas2 = gfx.Canvas(800, 800) +canvas2.set_background(gfx.COLOR_WHITE) + +// Draw concentric circles +mut i = 1 +while i <= 10: + let radius = to_float(i * 30) + let color = gfx.lerp_color(gfx.COLOR_BLUE, gfx.COLOR_RED,to_float(i) / 10.0) + canvas2.draw_circle(400.0, 400.0, radius, color, False) + i = i + 1 + +canvas2.draw_text(350.0, 750.0, "Concentric Circles",gfx.COLOR_BLACK, 20.0) + +canvas2.save_svg("output/patterns.svg") +print("✓ Saved patterns.svg") +canvas2.destroy() + +// ============================================ +// 3. STAR PATTERN +// ============================================ +print("\n--- Test 3: Star Pattern ---") +let canvas3 = gfx.Canvas(800, 800) +canvas3.set_background(gfx.COLOR_BLACK) + +// Draw multiple stars +mut j = 0 +while j < 5: + let angle = math.TAU * to_float(j) / 5.0 + let x = 400.0 + 200.0 * math.cos(angle) + let y = 400.0 + 200.0 * math.sin(angle) + gfx.draw_star(canvas3, x, y, 50.0, 20.0, 5, gfx.COLOR_YELLOW) + j = j + 1 + +// Central star +gfx.draw_star(canvas3, 400.0, 400.0, 80.0, 30.0, 5, gfx.COLOR_WHITE) + +canvas3.save_svg("output/stars.svg") +print("✓ Saved stars.svg") +canvas3.destroy() + +// ============================================ +// 4. GRID AND AXES +// ============================================ +print("\n--- Test 4: Grid and Axes ---") +let canvas4 = gfx.Canvas(800, 600) +canvas4.set_background(gfx.COLOR_WHITE) + +// Draw grid +gfx.draw_grid(canvas4, 50.0, gfx.COLOR_GRAY) + +// Draw axes +gfx.draw_axes(canvas4, 400.0, 300.0, 300.0, gfx.COLOR_BLACK) + +canvas4.draw_text(350.0, 550.0, "Grid with Axes",gfx.COLOR_BLACK, 20.0) + +canvas4.save_svg("output/grid.svg") +print("✓ Saved grid.svg") +canvas4.destroy() + +// ============================================ +// 5. MATHEMATICAL FUNCTION PLOT +// ============================================ +print("\n--- Test 5: Function Plot (Sine Wave) ---") +let canvas5 = gfx.Canvas(800, 600) +canvas5.set_background(gfx.COLOR_WHITE) + +// Generate sine wave data +mut x_data = [] +mut y_data = [] +mut k = 0 +while k <= 100: + let x = to_float(k) / 100.0 * math.TAU * 2.0 + let y = math.sin(x) + x_data = x_data + [x] + y_data = y_data + [y] + k = k + 1 + +let plot1 = gfx.Plot(gfx.PLOT_LINE) +plot1.set_data(x_data, y_data) +plot1.set_title("Sine Wave") +plot1.render(canvas5) + +canvas5.save_svg("output/sine_plot.svg") +print("✓ Saved sine_plot.svg") +canvas5.destroy() +plot1.destroy() + +// ============================================ +// 6. SCATTER PLOT +// ============================================ +print("\n--- Test 6: Scatter Plot ---") +let canvas6 = gfx.Canvas(800, 600) +canvas6.set_background(gfx.COLOR_WHITE) + +// Generate random-looking scatter data +mut x_scatter = [] +mut y_scatter = [] +mut m = 0 +while m < 50: + let x = to_float(m) / 10.0 + let y = math.sin(to_float(m) / 5.0) + to_float(m % 3) / 3.0 + x_scatter = x_scatter + [x] + y_scatter = y_scatter + [y] + m = m + 1 + +let plot2 = gfx.Plot(gfx.PLOT_SCATTER) +plot2.set_data(x_scatter, y_scatter) +plot2.set_title("Scatter Plot Example") +plot2.render(canvas6) + +canvas6.save_svg("output/scatter_plot.svg") +print("✓ Saved scatter_plot.svg") +canvas6.destroy() +plot2.destroy() + +// ============================================ +// 7. BAR CHART +// ============================================ +print("\n--- Test 7: Bar Chart ---") +let canvas7 = gfx.Canvas(800, 600) +canvas7.set_background(gfx.COLOR_WHITE) + +// Sample data +let x_bars = [1.0, 2.0, 3.0, 4.0, 5.0] +let y_bars = [10.0, 25.0, 15.0, 30.0, 20.0] + +let plot3 = gfx.Plot(gfx.PLOT_BAR) +plot3.set_data(x_bars, y_bars) +plot3.set_title("Bar Chart Example") +plot3.render(canvas7) + +canvas7.save_svg("output/bar_chart.svg") +print("✓ Saved bar_chart.svg") +canvas7.destroy() +plot3.destroy() + +// ============================================ +// 8. BLOCH SPHERE (Quantum Visualization) +// ============================================ +print("\n--- Test 8: Bloch Sphere ---") +let canvas8 = gfx.Canvas(800, 800) +canvas8.set_background(gfx.COLOR_WHITE) + +canvas8.draw_text(300.0, 50.0, "Quantum State Visualization",gfx.COLOR_BLACK, 24.0) + +gfx.draw_bloch_sphere(canvas8, 400.0, 400.0, 200.0) + +// Draw state vector +let theta = math.PI / 4.0 +let phi = 0.0 +let r = 200.0 +let x_state = 400.0 + r * math.sin(theta) * math.cos(phi) +let y_state = 400.0 + r * math.sin(theta) * math.sin(phi) + +canvas8.draw_line(400.0, 400.0, x_state, y_state,gfx.COLOR_RED, 3.0) +canvas8.draw_circle(x_state, y_state, 8.0, gfx.COLOR_RED, True) + +canvas8.save_svg("output/bloch_sphere.svg") +print("✓ Saved bloch_sphere.svg") +canvas8.destroy() + +// ============================================ +// 9. COLORFUL MANDALA +// ============================================ +print("\n--- Test 9: Colorful Mandala ---") +let canvas9 = gfx.Canvas(800, 800) +canvas9.set_background(gfx.COLOR_BLACK) + +// Draw mandala pattern +mut layer = 0 +while layer < 12: + let radius = 50.0 + to_float(layer) * 25.0 + let sides = 6 + layer + let t = to_float(layer) / 12.0 + let color = gfx.lerp_color(gfx.COLOR_CYAN, gfx.COLOR_MAGENTA, t) + gfx.draw_polygon(canvas9, 400.0, 400.0, radius, sides, color, False) + layer = layer + 1 + +canvas9.draw_text(320.0, 750.0, "Geometric Mandala",gfx.COLOR_WHITE, 24.0) + +canvas9.save_svg("output/mandala.svg") +print("✓ Saved mandala.svg") +canvas9.destroy() + +// ============================================ +// 10. COMBINED PLOT (Multiple Curves) +// ============================================ +print("\n--- Test 10: Multiple Function Plots ---") +let canvas10 = gfx.Canvas(800, 600) +canvas10.set_background(gfx.COLOR_WHITE) + +// Draw coordinate system +gfx.draw_axes(canvas10, 100.0, 500.0, 0.0, gfx.COLOR_BLACK) + +// Plot multiple functions +mut n = 0 +while n <= 100: + let x = to_float(n) / 10.0 + let x_screen = 100.0 + x * 60.0 + // sin(x) + let y1 = math.sin(x) * 100.0 + let y1_screen = 300.0 - y1 + if n > 0: + canvas10.draw_circle(x_screen, y1_screen, 2.0, gfx.COLOR_RED, True) + // cos(x) + let y2 = math.cos(x) * 100.0 + let y2_screen = 300.0 - y2 + if n > 0: + canvas10.draw_circle(x_screen, y2_screen, 2.0, gfx.COLOR_BLUE, True) + n = n + 1 + +canvas10.draw_text(300.0, 50.0, "Sin and Cos Functions",gfx.COLOR_BLACK, 20.0) +canvas10.draw_text(120.0, 100.0, "sin(x)", gfx.COLOR_RED, 16.0) +canvas10.draw_text(120.0, 130.0, "cos(x)", gfx.COLOR_BLUE, 16.0) + +canvas10.save_svg("output/multi_plot.svg") +print("✓ Saved multi_plot.svg") +canvas10.destroy() + +// ============================================ +// SUMMARY +// ============================================ +print("\n=== GRAPHICS DEMO COMPLETE ===") +print("Generated 10 SVG files in output/ directory:") +print(" 1. basic_shapes.svg - Basic geometric shapes") +print(" 2. patterns.svg - Concentric circles") +print(" 3. stars.svg - Star pattern") +print(" 4. grid.svg - Grid with axes") +print(" 5. sine_plot.svg - Sine wave plot") +print(" 6. scatter_plot.svg - Scatter plot") +print(" 7. bar_chart.svg - Bar chart") +print(" 8. bloch_sphere.svg - Quantum state visualization") +print(" 9. mandala.svg - Geometric mandala") +print(" 10. multi_plot.svg - Multiple function plots") +print("\n✓ All graphics tests passed!") \ No newline at end of file diff --git a/examples/output_test.svg b/examples/output_test.svg new file mode 100644 index 0000000..649f9db --- /dev/null +++ b/examples/output_test.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index cbe6dee..aab76bc 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,36 +1,41 @@ // src/codegen/mod.rs +use crate::lexer::Lexer; +use crate::parser::ast::ImportPath; +use crate::parser::ast::Loc; +use crate::parser::Parser; +use std::fs; +use crate::parser::ast::{ASTNode, BinaryOperator, Parameter, Type, UnaryOperator}; +use inkwell::attributes::{Attribute, AttributeLoc}; use inkwell::builder::Builder; use inkwell::context::Context; +use inkwell::execution_engine::ExecutionEngine; +use inkwell::module::Linkage; use inkwell::module::Module; -use inkwell::OptimizationLevel; use inkwell::passes::PassBuilderOptions; -use inkwell::targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetMachine,TargetTriple}; -use inkwell::attributes::{Attribute, AttributeLoc}; -use crate::parser::ast::{ASTNode, Type, Parameter, BinaryOperator, UnaryOperator}; -use inkwell::types::{BasicType, BasicTypeEnum, BasicMetadataTypeEnum}; -use inkwell::values::{BasicValue, BasicValueEnum, BasicMetadataValueEnum}; -use inkwell::{IntPredicate, FloatPredicate}; +use inkwell::targets::{ + CodeModel, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple, +}; +use inkwell::types::VectorType; +use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; -use inkwell::module::Linkage; +use inkwell::values::{BasicMetadataValueEnum, BasicValue, BasicValueEnum}; use inkwell::AddressSpace; +use inkwell::OptimizationLevel; +use inkwell::{FloatPredicate, IntPredicate}; +use std::collections::HashMap; use std::ffi::CString; use std::os::raw::{c_int, c_void}; -use inkwell::execution_engine::ExecutionEngine; -use inkwell::types::VectorType; -use std::collections::HashMap; - +// Debug info imports use inkwell::debug_info::{ - AsDIScope, DICompileUnit, DISubprogram, DIType, DebugInfoBuilder, DWARFEmissionKind, - DWARFSourceLanguage, DIScope, DILocation, DIFlags,DIFlagsConstants, + AsDIScope, DICompileUnit, DIFlags, DIFlagsConstants, DILocation, DIScope, DISubprogram, DIType, + DWARFEmissionKind, DWARFSourceLanguage, DebugInfoBuilder, }; use crate::runtime::{ - quantica_rt_new_state, - quantica_rt_debug_state, - quantica_rt_apply_gate, - quantica_rt_measure + quantica_rt_apply_gate, quantica_rt_debug_state, quantica_rt_measure, quantica_rt_new_state, + quantica_rt_print_int, quantica_rt_print_string, }; #[derive(Debug)] @@ -40,13 +45,19 @@ enum MLIRStep { LLVMIR, } - +/// The main LLVM compiler backend with debug info support. pub struct Compiler<'ctx> { context: &'ctx Context, builder: Builder<'ctx>, module: Module<'ctx>, target_machine: TargetMachine, - variables: HashMap, inkwell::types::BasicTypeEnum<'ctx>)>, + variables: HashMap< + String, + ( + inkwell::values::PointerValue<'ctx>, + inkwell::types::BasicTypeEnum<'ctx>, + ), + >, puts_function: FunctionValue<'ctx>, rt_new_state: FunctionValue<'ctx>, rt_debug_state: FunctionValue<'ctx>, @@ -58,42 +69,43 @@ pub struct Compiler<'ctx> { rt_htod_transfer: FunctionValue<'ctx>, rt_dtoh_transfer: FunctionValue<'ctx>, - + // Debug info fields debug_builder: DebugInfoBuilder<'ctx>, compile_unit: DICompileUnit<'ctx>, di_types: HashMap>, current_debug_location: Option>, + current_module_alias: Option, } impl<'ctx> Compiler<'ctx> { - + /// Creates a new Compiler instance with debug info enabled. pub fn new(context: &'ctx Context, opt_level: OptimizationLevel) -> Self { let builder = context.create_builder(); let module = context.create_module("quantica_module"); - + // Initialize debug info let (debug_builder, compile_unit) = module.create_debug_info_builder( - true, - DWARFSourceLanguage::C, - "quantica_main.qc", - ".", - "Quantica Compiler v0.1.0", - false, - "", - 0, - "", - DWARFEmissionKind::Full, - 0, - false, - false, - "", - "", + true, // allow_unresolved + DWARFSourceLanguage::C, // Use C as base language + "quantica_main.qc", // filename + ".", // directory + "Quantica Compiler v0.1.0", // producer + false, // is_optimized (set based on opt_level if needed) + "", // compiler flags + 0, // runtime version + "", // split name + DWARFEmissionKind::Full, // emission kind + 0, // dwo_id + false, // split_debug_inlining + false, // debug_info_for_profiling + "", // sysroot + "", // sdk ); Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); - let target = Target::from_triple(&target_triple) - .expect("Failed to create target from triple"); + let target = + Target::from_triple(&target_triple).expect("Failed to create target from triple"); let cpu_name = TargetMachine::get_host_cpu_name().to_string(); let cpu_features = TargetMachine::get_host_cpu_features().to_string(); let target_machine = target @@ -107,7 +119,8 @@ impl<'ctx> Compiler<'ctx> { ) .expect("Failed to create target machine"); - let execution_engine = module.create_jit_execution_engine(opt_level) + let execution_engine = module + .create_jit_execution_engine(opt_level) .expect("Failed to create JIT Execution Engine"); let _i8_type = context.i8_type(); @@ -115,11 +128,7 @@ impl<'ctx> Compiler<'ctx> { let ptr_type = context.ptr_type(AddressSpace::default()); let puts_fn_type = i32_type.fn_type(&[ptr_type.into()], false); - let puts_function = module.add_function( - "puts", - puts_fn_type, - Some(Linkage::External), - ); + let puts_function = module.add_function("puts", puts_fn_type, Some(Linkage::External)); let state_ptr_type = context.ptr_type(AddressSpace::default()); let new_state_fn_type = state_ptr_type.fn_type(&[i32_type.into()], false); @@ -170,23 +179,61 @@ impl<'ctx> Compiler<'ctx> { let size_t_type = context.i64_type(); let alloc_fn_type = i8_ptr_type.fn_type(&[size_t_type.into()], false); - let rt_device_alloc = module.add_function("quantica_rt_device_alloc", alloc_fn_type, Some(Linkage::External)); + let rt_device_alloc = module.add_function( + "quantica_rt_device_alloc", + alloc_fn_type, + Some(Linkage::External), + ); let free_fn_type = void_type.fn_type(&[i8_ptr_type.into()], false); - let rt_device_free = module.add_function("quantica_rt_device_free", free_fn_type, Some(Linkage::External)); + let rt_device_free = module.add_function( + "quantica_rt_device_free", + free_fn_type, + Some(Linkage::External), + ); + + let transfer_fn_type = i32_type.fn_type( + &[i8_ptr_type.into(), i8_ptr_type.into(), size_t_type.into()], + false, + ); + let rt_htod_transfer = module.add_function( + "quantica_rt_htod_transfer", + transfer_fn_type, + Some(Linkage::External), + ); + let rt_dtoh_transfer = module.add_function( + "quantica_rt_dtoh_transfer", + transfer_fn_type, + Some(Linkage::External), + ); - let transfer_fn_type = i32_type.fn_type(&[i8_ptr_type.into(), i8_ptr_type.into(), size_t_type.into()], false); - let rt_htod_transfer = module.add_function("quantica_rt_htod_transfer", transfer_fn_type, Some(Linkage::External)); - let rt_dtoh_transfer = module.add_function("quantica_rt_dtoh_transfer", transfer_fn_type, Some(Linkage::External)); + let print_int_type = void_type.fn_type(&[i32_type.into()], false); // Note: We'll use i32 for simplicity or i64 + // Actually, let's use i64 to match the runtime + let i64_type = context.i64_type(); + let print_int_type = void_type.fn_type(&[i64_type.into()], false); + let rt_print_int = module.add_function( + "quantica_rt_print_int", + print_int_type, + Some(Linkage::External), + ); + let print_string_type = void_type.fn_type(&[i8_ptr_type.into()], false); + let rt_print_string = module.add_function( + "quantica_rt_print_string", + print_string_type, + Some(Linkage::External), + ); unsafe { execution_engine.add_global_mapping(&rt_new_state, quantica_rt_new_state as usize); execution_engine.add_global_mapping(&rt_debug_state, quantica_rt_debug_state as usize); execution_engine.add_global_mapping(&rt_apply_gate, quantica_rt_apply_gate as usize); execution_engine.add_global_mapping(&rt_measure, quantica_rt_measure as usize); + execution_engine.add_global_mapping(&rt_print_int, quantica_rt_print_int as usize); + execution_engine + .add_global_mapping(&rt_print_string, quantica_rt_print_string as usize); } - + // Initialize debug type cache let di_types = HashMap::new(); Self { @@ -209,15 +256,16 @@ impl<'ctx> Compiler<'ctx> { compile_unit, di_types, current_debug_location: None, + current_module_alias: None, } } - + /// Finalize debug info (call after compilation) pub fn finalize_debug_info(&self) { self.debug_builder.finalize(); } - + /// Create or get a cached debug type fn get_or_create_di_type(&mut self, ty: &Type) -> DIType<'ctx> { let type_name = format!("{:?}", ty); @@ -227,94 +275,92 @@ impl<'ctx> Compiler<'ctx> { let di_type = match ty { Type::Int | Type::Int64 => { - self.debug_builder.create_basic_type( - "i64", - 64, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() - } - Type::Int8 => { - self.debug_builder.create_basic_type( - "i8", - 8, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() - } - Type::Int16 => { - self.debug_builder.create_basic_type( - "i16", - 16, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() - } - Type::Int32 => { - self.debug_builder.create_basic_type( - "i32", - 32, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() - } - Type::Int128 => { - self.debug_builder.create_basic_type( - "i128", - 128, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() + self.debug_builder + .create_basic_type( + "i64", + 64, // size in bits + 0x05, // DW_ATE_signed + DIFlags::PUBLIC, + ) + .unwrap() + .as_type() } + Type::Int8 => self + .debug_builder + .create_basic_type("i8", 8, 0x05, DIFlags::PUBLIC) + .unwrap() + .as_type(), + Type::Int16 => self + .debug_builder + .create_basic_type("i16", 16, 0x05, DIFlags::PUBLIC) + .unwrap() + .as_type(), + Type::Int32 => self + .debug_builder + .create_basic_type("i32", 32, 0x05, DIFlags::PUBLIC) + .unwrap() + .as_type(), + Type::Int128 => self + .debug_builder + .create_basic_type("i128", 128, 0x05, DIFlags::PUBLIC) + .unwrap() + .as_type(), Type::Float | Type::Float64 => { - self.debug_builder.create_basic_type( - "f64", - 64, - 0x04, - DIFlags::PUBLIC, - ).unwrap().as_type() - } - Type::Float32 => { - self.debug_builder.create_basic_type( - "f32", - 32, - 0x04, - DIFlags::PUBLIC, - ).unwrap().as_type() + self.debug_builder + .create_basic_type( + "f64", + 64, + 0x04, // DW_ATE_float + DIFlags::PUBLIC, + ) + .unwrap() + .as_type() } + Type::Float32 => self + .debug_builder + .create_basic_type("f32", 32, 0x04, DIFlags::PUBLIC) + .unwrap() + .as_type(), Type::Bool => { - self.debug_builder.create_basic_type( - "bool", - 1, - 0x02, - DIFlags::PUBLIC, - ).unwrap().as_type() + self.debug_builder + .create_basic_type( + "bool", + 1, + 0x02, // DW_ATE_boolean + DIFlags::PUBLIC, + ) + .unwrap() + .as_type() } Type::String => { - - let i8_type = self.debug_builder.create_basic_type( - "char", - 8, - 0x06, - DIFlags::PUBLIC, - ).unwrap().as_type(); - - self.debug_builder.create_pointer_type( - "string", - i8_type, - 64, - 0, - AddressSpace::default(), - ).as_type() + // Create pointer type for strings + let i8_type = self + .debug_builder + .create_basic_type( + "char", + 8, + 0x06, // DW_ATE_signed_char + DIFlags::PUBLIC, + ) + .unwrap() + .as_type(); + + self.debug_builder + .create_pointer_type( + "string", + i8_type, + 64, // pointer size in bits + 0, // alignment + AddressSpace::default(), + ) + .as_type() } _ => { - - self.debug_builder.create_basic_type( - "unknown", - 64, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() + // Default to i64 for unknown types + self.debug_builder + .create_basic_type("unknown", 64, 0x05, DIFlags::PUBLIC) + .unwrap() + .as_type() } }; @@ -322,26 +368,22 @@ impl<'ctx> Compiler<'ctx> { di_type } - + /// Set debug location for the current instruction fn set_debug_location(&mut self, line: u32, column: u32, scope: DIScope<'ctx>) { - let location = self.debug_builder.create_debug_location( - self.context, - line, - column, - scope, - None, - ); + let location = + self.debug_builder + .create_debug_location(self.context, line, column, scope, None); self.current_debug_location = Some(location); self.builder.set_current_debug_location(location); } - + /// Clear debug location fn clear_debug_location(&mut self) { self.current_debug_location = None; self.builder.unset_current_debug_location(); } - + /// Run optimization passes on the module pub fn optimize_module(&self, _opt_level: OptimizationLevel) -> Result<(), String> { let passes: &[&str] = &[ "instcombine", @@ -367,7 +409,11 @@ impl<'ctx> Compiler<'ctx> { Ok(()) } - pub fn analyze_and_fuse_tensors(&self, function_name: &str, _body_ast: &ASTNode) -> Result { + pub fn analyze_and_fuse_tensors( + &self, + function_name: &str, + _body_ast: &ASTNode, + ) -> Result { let fusion_summary = format!( "// TENSOR FUSION OPTIMIZATION APPLIED:\n\ // Kernels identified in {}:\n\ @@ -394,10 +440,14 @@ impl<'ctx> Compiler<'ctx> { return Err("(JIT Error) Compiled module failed verification.".to_string()); } - let _main_function = self.module.get_function("main") + let _main_function = self + .module + .get_function("main") .ok_or("(JIT Error) Cannot find 'func main()' entry point.".to_string())?; - let entry_fn_ptr = self.execution_engine.get_function_address("main") + let entry_fn_ptr = self + .execution_engine + .get_function_address("main") .map_err(|e| format!("(JIT Error) Failed to get function address: {}", e))?; unsafe { @@ -415,25 +465,39 @@ impl<'ctx> Compiler<'ctx> { let instr_prof_name = "llvm.instrprof.increment"; - self.module.get_function(instr_prof_name).unwrap_or_else(|| { - let func_type = void_type.fn_type(&[i64_type.into(), i64_type.into(), i32_type.into(), i32_type.into()], false); - self.module.add_function(instr_prof_name, func_type, None) - }) + self.module + .get_function(instr_prof_name) + .unwrap_or_else(|| { + let func_type = void_type.fn_type( + &[ + i64_type.into(), + i64_type.into(), + i32_type.into(), + i32_type.into(), + ], + false, + ); + self.module.add_function(instr_prof_name, func_type, None) + }) } pub fn enable_jit_profiling(&mut self, func_names: Vec) -> Result<(), String> { - println!(" -> Profiling Abstraction: Initializing {} functions for adaptive JIT.", func_names.len()); + println!( + " -> Profiling Abstraction: Initializing {} functions for adaptive JIT.", + func_names.len() + ); let i64_type = self.context.i64_type(); let zero_i64 = i64_type.const_int(0, false); - let zero_values: Vec = std::iter::repeat(zero_i64) - .take(func_names.len()) - .collect(); + let zero_values: Vec = + std::iter::repeat(zero_i64).take(func_names.len()).collect(); let zero_array_initializer = i64_type.const_array(zero_values.as_slice()); let counter_array_type = i64_type.array_type(func_names.len() as u32); - let counter_global = self.module.add_global(counter_array_type, None, "__quantica_profile_counters"); + let counter_global = + self.module + .add_global(counter_array_type, None, "__quantica_profile_counters"); counter_global.set_initializer(&zero_array_initializer.as_basic_value_enum()); @@ -441,18 +505,26 @@ impl<'ctx> Compiler<'ctx> { } pub fn trigger_adaptive_recompile(&self, hot_function_name: &str) -> Result<(), String> { - println!(" -> ADAPTIVE JIT: Function '{}' detected as hot. Recompiling...", hot_function_name); - println!(" -> ADAPTIVE JIT: Replacing old code pointer with optimized version (e.g., -O3)."); + println!( + " -> ADAPTIVE JIT: Function '{}' detected as hot. Recompiling...", + hot_function_name + ); + println!( + " -> ADAPTIVE JIT: Replacing old code pointer with optimized version (e.g., -O3)." + ); Ok(()) } - pub fn emit_vulkan_compute_path(&self, kernel_ir: &str, kernel_name: &str) -> Result { + pub fn emit_vulkan_compute_path( + &self, + kernel_ir: &str, + kernel_name: &str, + ) -> Result { let kernel_size_bytes = kernel_ir.len() * 4; let spv_binary_stub = format!( "// SPIR-V Binary Stub for {} ({} bytes)", - kernel_name, - kernel_size_bytes + kernel_name, kernel_size_bytes ); let runtime_call = format!( @@ -467,8 +539,7 @@ impl<'ctx> Compiler<'ctx> { Ok(format!( "\n{}\n\n// Final Vulkan Runtime Link:\n{}", - spv_binary_stub, - runtime_call + spv_binary_stub, runtime_call )) } @@ -517,10 +588,7 @@ impl<'ctx> Compiler<'ctx> { Ok(format!( "{}\n\n{}\n\n// LLVM IR Output:\n{}\n\n{}", - mlir_trace, - fusion_report, - raw_llvm_ir, - final_vulkan_output + mlir_trace, fusion_report, raw_llvm_ir, final_vulkan_output )) } @@ -544,11 +612,20 @@ impl<'ctx> Compiler<'ctx> { pub fn compile_jit_program(&mut self, program: &ASTNode) -> Result<(), String> { if let ASTNode::Program(statements) = program { for stmt in statements { - if let ASTNode::FunctionDeclaration { name, parameters, return_type, body } = stmt { - + if let ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + body, + } = stmt + { + // Removed doc_comment field self.compile_function(name, parameters, return_type, body)?; } else { - return Err("(Codegen Error) JIT synthesis failed: Expected FunctionDeclaration.".to_string()); + return Err( + "(Codegen Error) JIT synthesis failed: Expected FunctionDeclaration." + .to_string(), + ); } } Ok(()) @@ -557,18 +634,28 @@ impl<'ctx> Compiler<'ctx> { } } - + /// The main entry point for compiling a Quantica AST. pub fn compile_program(&mut self, program: &ASTNode) -> Result<(), String> { if let ASTNode::Program(statements) = program { for stmt in statements { match stmt { - ASTNode::FunctionDeclaration { name, parameters, return_type, body } => { - + ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + body, + } => { + // Removed doc_comment field self.compile_function(name, parameters, return_type, body)?; } - ASTNode::CircuitDeclaration { name, parameters, return_type: _, body } => { - + ASTNode::CircuitDeclaration { + name, + parameters, + return_type: _, + body, + } => { + // Removed doc_comment field println!("\n⚙️ Compiling GPU Kernel: {}", name); let kernel_ir = self.compile_gpu_kernel(name, body)?; println!(" -> Kernel IR Dump (Placeholder):\n{}", kernel_ir); @@ -594,11 +681,7 @@ impl<'ctx> Compiler<'ctx> { use std::path::Path; self.target_machine - .write_to_file( - &self.module, - FileType::Object, - Path::new(path), - ) + .write_to_file(&self.module, FileType::Object, Path::new(path)) .map_err(|e| format!("Failed to write object file: {}", e)) } @@ -606,25 +689,198 @@ impl<'ctx> Compiler<'ctx> { self.module.print_to_stderr(); } + fn compile_import( + &mut self, + path: &ImportPath, + alias: &str, + _current_function: FunctionValue<'ctx>, + ) -> Result<(), String> { + // 1. Resolve file path + let file_path = match path { + ImportPath::File(f) => { + if f.ends_with(".qc") { + f.clone() + } else { + format!("q_packages/{}/init.qc", f) + } + } + _ => return Err("(Codegen) Only file imports are supported in JIT mode.".to_string()), + }; + + // 2. Read and Parse + let source = fs::read_to_string(&file_path) + .map_err(|e| format!("Failed to read import '{}': {}", file_path, e))?; + let mut lexer = Lexer::new(&source); + let tokens = lexer.tokenize().map_err(|e| e.to_string())?; + let mut parser = Parser::new(tokens); + let ast = parser.parse().map_err(|e| e.to_string())?; + + // 3. Save current builder position + let current_block = self.builder.get_insert_block(); + + let previous_alias = self.current_module_alias.clone(); + self.current_module_alias = Some(alias.to_string()); + + if let ASTNode::Program(ref stmts) = ast { + // PASS 1: Register all global constants FIRST + for stmt in stmts { + if let ASTNode::LetDeclaration { name, value, .. } = stmt { + let mangled_name = format!("{}_{}", alias, name); + + // Helper to extract float values from literals or unary minus + let get_const_float = |node: &ASTNode| -> Option { + match node { + ASTNode::FloatLiteral(f) => Some(*f), + ASTNode::Unary { + operator: UnaryOperator::Minus, + operand, + } => { + if let ASTNode::FloatLiteral(f) = operand.as_ref() { + Some(-f) + } else { + None + } + } + _ => None, + } + }; + + // Enhanced constant folding + let init_value = match value.as_ref() { + // Direct Literals: let x = 5.0 + ASTNode::FloatLiteral(f) => Some( + self.context + .f64_type() + .const_float(*f) + .as_basic_value_enum(), + ), + ASTNode::IntLiteral(i) => Some( + self.context + .i64_type() + .const_int(*i as u64, true) + .as_basic_value_enum(), + ), + + // Unary Constants: let x = -5.0 + ASTNode::Unary { + operator: UnaryOperator::Minus, + operand, + } => { + if let Some(f) = get_const_float(operand) { + Some( + self.context + .f64_type() + .const_float(-f) + .as_basic_value_enum(), + ) + } else { + None + } + } + + // Binary Expressions: let NEG_INF = -1.0 / 0.0 + ASTNode::Binary { + operator, + left, + right, + .. + } => { + let l_val = get_const_float(left); + let r_val = get_const_float(right); + + if let (Some(l), Some(r)) = (l_val, r_val) { + let f = match operator { + BinaryOperator::Div => l / r, + BinaryOperator::Mul => l * r, + BinaryOperator::Add => l + r, + BinaryOperator::Sub => l - r, + _ => 0.0, + }; + Some(self.context.f64_type().const_float(f).as_basic_value_enum()) + } else { + None + } + } + _ => None, + }; + + // Register the global if we successfully calculated a constant value + if let Some(val) = init_value { + let global = self.module.add_global( + val.get_type(), + Some(AddressSpace::default()), + &mangled_name, + ); + global.set_initializer(&val); + } + } + } - fn compile_statement(&mut self, node: &ASTNode, current_function: FunctionValue<'ctx>) -> Result<(), String> { + // PASS 2: Now compile all functions (they can reference the constants) + for stmt in stmts { + if let ASTNode::FunctionDeclaration { + ref name, + ref parameters, + ref return_type, + ref body, + } = stmt + { + let mangled_name = format!("{}_{}", alias, name); + self.compile_function(&mangled_name, parameters, return_type, body)?; + } + } + } + + // Restore previous context + self.current_module_alias = previous_alias; + + // 5. Restore builder position so main execution continues correctly + if let Some(block) = current_block { + self.builder.position_at_end(block); + } + + Ok(()) + } + + /// Compiles a single statement. + fn compile_statement( + &mut self, + node: &ASTNode, + current_function: FunctionValue<'ctx>, + ) -> Result<(), String> { match node { - ASTNode::LetDeclaration { name, type_annotation, value, is_mutable } => { - self.compile_let_declaration(name, type_annotation, value, *is_mutable, current_function)?; + ASTNode::LetDeclaration { + name, + type_annotation, + value, + is_mutable, + } => { + // Removed doc_comment field + self.compile_let_declaration( + name, + type_annotation, + value, + *is_mutable, + current_function, + )?; + Ok(()) + } + ASTNode::Assignment { target, value } => { + self.compile_assignment(target, value, current_function)?; Ok(()) } ASTNode::If { condition, then_block, elif_blocks, - else_block + else_block, } => { self.compile_if_statement( condition, then_block, elif_blocks, else_block, - current_function + current_function, )?; Ok(()) } @@ -633,22 +889,38 @@ impl<'ctx> Compiler<'ctx> { Ok(()) } - ASTNode::FunctionCall { callee, arguments, .. } => { - self.compile_function_call(callee, arguments, current_function)?; + ASTNode::Import { path, alias } => { + self.compile_import(path, alias, current_function)?; Ok(()) } - ASTNode::QuantumDeclaration { name, size, initial_state } => { + + ASTNode::FunctionCall { + callee, + arguments, + loc, + .. + } => { + self.compile_function_call(callee, arguments, loc, current_function)?; + Ok(()) + } + ASTNode::QuantumDeclaration { + name, + size, + initial_state, + } => { self.compile_quantum_declaration(name, size, initial_state, current_function)?; Ok(()) } - ASTNode::Apply { gate_expr, arguments, .. } => { + ASTNode::Apply { + gate_expr, + arguments, + .. + } => { self.compile_apply_statement(gate_expr, arguments, current_function)?; Ok(()) } - ASTNode::Return(value_node) => { - self.compile_return(value_node, current_function) - } + ASTNode::Return(value_node) => self.compile_return(value_node, current_function), ASTNode::Block(statements) => { for stmt in statements { @@ -657,9 +929,10 @@ impl<'ctx> Compiler<'ctx> { Ok(()) } - _ => { - Err(format!("(Codegen Error) Unsupported statement type: {:?}", node)) - } + _ => Err(format!( + "(Codegen Error) Unsupported statement type: {:?}", + node + )), } } @@ -675,21 +948,30 @@ impl<'ctx> Compiler<'ctx> { v3: inkwell::values::VectorValue<'ctx>, _current_function: FunctionValue<'ctx>, ) -> Result, String> { - - let intrinsic_function = self.module.get_function("llvm.fma.v2f64") + let intrinsic_function = self + .module + .get_function("llvm.fma.v2f64") .unwrap_or_else(|| { let vector_type = self.get_vector_type(); - let fn_type = vector_type.fn_type(&[vector_type.into(), vector_type.into(), vector_type.into()], false); + let fn_type = vector_type.fn_type( + &[vector_type.into(), vector_type.into(), vector_type.into()], + false, + ); self.module.add_function("llvm.fma.v2f64", fn_type, None) }); - let call = self.builder.build_call( - intrinsic_function, - &[v1.into(), v2.into(), v3.into()], - "v_fma_tmp", - ).map_err(|e| e.to_string())?; + let call = self + .builder + .build_call( + intrinsic_function, + &[v1.into(), v2.into(), v3.into()], + "v_fma_tmp", + ) + .map_err(|e| e.to_string())?; - Ok(call.try_as_basic_value().left() + Ok(call + .try_as_basic_value() + .left() .ok_or("Failed to emit FMA intrinsic call.".to_string())? .into_vector_value()) } @@ -699,8 +981,9 @@ impl<'ctx> Compiler<'ctx> { v1: inkwell::values::VectorValue<'ctx>, v2: inkwell::values::VectorValue<'ctx>, ) -> Result, String> { - - let result = self.builder.build_float_mul(v1, v2, "vector_mul_tmp") + let result = self + .builder + .build_float_mul(v1, v2, "vector_mul_tmp") .map_err(|e| e.to_string())?; Ok(result.as_basic_value_enum().into_vector_value()) @@ -713,9 +996,11 @@ impl<'ctx> Compiler<'ctx> { initial_state_node: &Option>, current_function: FunctionValue<'ctx>, ) -> Result<(), String> { - if initial_state_node.is_some() { - return Err("(Codegen STUB) Quantum declaration with initial state is not yet supported.".to_string()); + return Err( + "(Codegen STUB) Quantum declaration with initial state is not yet supported." + .to_string(), + ); } let size_value: inkwell::values::IntValue<'ctx>; @@ -726,31 +1011,39 @@ impl<'ctx> Compiler<'ctx> { } let size_i64 = compiled_size.into_int_value(); - size_value = self.builder.build_int_truncate(size_i64, self.context.i32_type(), "size_i32") + size_value = self + .builder + .build_int_truncate(size_i64, self.context.i32_type(), "size_i32") .map_err(|e| e.to_string())?; - } else { size_value = self.context.i32_type().const_int(1, false); } - let call_site = self.builder.build_call( - self.rt_new_state, - &[size_value.into()], - "new_state_ptr", - ).map_err(|e| e.to_string())?; + let call_site = self + .builder + .build_call(self.rt_new_state, &[size_value.into()], "new_state_ptr") + .map_err(|e| e.to_string())?; - let state_ptr = call_site.try_as_basic_value().left() + let state_ptr = call_site + .try_as_basic_value() + .left() .ok_or("(Codegen Error) quantica_rt_new_state did not return a value.".to_string())? .into_pointer_value(); let state_ptr_type = self.context.ptr_type(AddressSpace::default()); - let alloca = self.builder.build_alloca(state_ptr_type, name) + let alloca = self + .builder + .build_alloca(state_ptr_type, name) .map_err(|e| e.to_string())?; - self.builder.build_store(alloca, state_ptr) + self.builder + .build_store(alloca, state_ptr) .map_err(|e| e.to_string())?; - self.variables.insert(name.to_string(), (alloca, state_ptr_type.as_basic_type_enum())); + self.variables.insert( + name.to_string(), + (alloca, state_ptr_type.as_basic_type_enum()), + ); Ok(()) } @@ -765,19 +1058,22 @@ impl<'ctx> Compiler<'ctx> { ) -> Result<(), String> { let value = self.compile_expression(value_node, current_function)?; let llvm_type = if let Some(quantica_type) = type_annotation { - self.map_type(quantica_type) + self.map_type(quantica_type) } else { value.get_type() }; - let alloca = self.builder.build_alloca(llvm_type, name).map_err(|e| e.to_string())?; + let alloca = self + .builder + .build_alloca(llvm_type, name) + .map_err(|e| e.to_string())?; let _ = self.builder.build_store(alloca, value); - + // Add debug info for local variable if let Some(di_scope) = self.get_current_di_scope(current_function) { let di_type = if let Some(quantica_type) = type_annotation { self.get_or_create_di_type(quantica_type) } else { - + // Create a debug type based on the LLVM type self.create_di_type_from_llvm(llvm_type) }; @@ -785,11 +1081,11 @@ impl<'ctx> Compiler<'ctx> { di_scope, name, self.compile_unit.get_file(), - 1, + 1, // line number - should be extracted from AST di_type, - true, + true, // always_preserve DIFlags::ZERO, - 0, + 0, // alignment ); self.debug_builder.insert_declare_at_end( @@ -797,13 +1093,8 @@ impl<'ctx> Compiler<'ctx> { Some(di_local_var), None, self.current_debug_location.unwrap_or_else(|| { - self.debug_builder.create_debug_location( - self.context, - 1, - 0, - di_scope, - None, - ) + self.debug_builder + .create_debug_location(self.context, 1, 0, di_scope, None) }), self.builder.get_insert_block().unwrap(), ); @@ -814,50 +1105,104 @@ impl<'ctx> Compiler<'ctx> { Ok(()) } + fn compile_assignment( + &mut self, + target: &ASTNode, + value_node: &ASTNode, + current_function: FunctionValue<'ctx>, + ) -> Result<(), String> { + // Get the variable name from the target + let var_name = match target { + ASTNode::Identifier { name, loc } => { + // Check if variable exists + if !self.variables.contains_key(name) { + return Err(format!( + "(Codegen Error at {}) Cannot assign to undefined variable '{}'", + loc, name + )); + } + name.clone() // Clone the name so we own it + } + _ => { + return Err( + "(Codegen Error) Assignment target must be a simple identifier.".to_string(), + ); + } + }; + + // FIX: Clone the pointer and type BEFORE the mutable borrow + let (var_ptr, var_type) = self + .variables + .get(&var_name) + .ok_or_else(|| format!("(Codegen Error) Variable '{}' not found", var_name))? + .clone(); // Clone the tuple to avoid borrow conflict + + // Now we can mutably borrow self for compile_expression + let new_value = self.compile_expression(value_node, current_function)?; + + // Type check: ensure the new value matches the variable's type + if new_value.get_type() != var_type { + return Err(format!( + "(Codegen Error) Type mismatch in assignment to '{}': expected {:?}, got {:?}", + var_name, + var_type, + new_value.get_type() + )); + } + + // Store the new value (var_ptr is now owned, not borrowed) + self.builder + .build_store(var_ptr, new_value) + .map_err(|e| e.to_string())?; + + Ok(()) + } + fn get_current_di_scope(&self, current_function: FunctionValue<'ctx>) -> Option> { - current_function.get_subprogram().map(|sp| sp.as_debug_info_scope()) + current_function + .get_subprogram() + .map(|sp| sp.as_debug_info_scope()) } fn create_di_type_from_llvm(&mut self, llvm_type: BasicTypeEnum<'ctx>) -> DIType<'ctx> { if llvm_type.is_int_type() { let int_type = llvm_type.into_int_type(); let bit_width = int_type.get_bit_width(); - self.debug_builder.create_basic_type( - &format!("i{}", bit_width), - bit_width as u64, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() + self.debug_builder + .create_basic_type( + &format!("i{}", bit_width), + bit_width as u64, + 0x05, // DW_ATE_signed + DIFlags::PUBLIC, + ) + .unwrap() + .as_type() } else if llvm_type.is_float_type() { - self.debug_builder.create_basic_type( - "f64", - 64, - 0x04, - DIFlags::PUBLIC, - ).unwrap().as_type() + self.debug_builder + .create_basic_type( + "f64", + 64, + 0x04, // DW_ATE_float + DIFlags::PUBLIC, + ) + .unwrap() + .as_type() } else if llvm_type.is_pointer_type() { - let i8_type = self.debug_builder.create_basic_type( - "char", - 8, - 0x06, - DIFlags::PUBLIC, - ).unwrap().as_type(); - - self.debug_builder.create_pointer_type( - "ptr", - i8_type, - 64, - 0, - AddressSpace::default(), - ).as_type() + let i8_type = self + .debug_builder + .create_basic_type("char", 8, 0x06, DIFlags::PUBLIC) + .unwrap() + .as_type(); + + self.debug_builder + .create_pointer_type("ptr", i8_type, 64, 0, AddressSpace::default()) + .as_type() } else { - - self.debug_builder.create_basic_type( - "unknown", - 64, - 0x05, - DIFlags::PUBLIC, - ).unwrap().as_type() + // Default + self.debug_builder + .create_basic_type("unknown", 64, 0x05, DIFlags::PUBLIC) + .unwrap() + .as_type() } } @@ -869,21 +1214,37 @@ impl<'ctx> Compiler<'ctx> { else_node: &Option>, current_function: FunctionValue<'ctx>, ) -> Result<(), String> { - let merge_block = self.context.append_basic_block(current_function, "merge"); + // 1. Compile Main If Condition let if_condition_value = self.compile_expression(condition_node, current_function)?; let if_cond = if_condition_value.into_int_value(); let if_then_block = self.context.append_basic_block(current_function, "if_then"); - let mut next_else_block = self.context.append_basic_block(current_function, "next_else"); + let mut next_else_block = self + .context + .append_basic_block(current_function, "next_else"); - let _=self.builder.build_conditional_branch(if_cond, if_then_block, next_else_block); + let _ = self + .builder + .build_conditional_branch(if_cond, if_then_block, next_else_block); + // 2. Compile 'Then' Block self.builder.position_at_end(if_then_block); self.compile_statement(then_node, current_function)?; - let _=self.builder.build_unconditional_branch(merge_block); + // FIX: Only branch to merge if the block hasn't returned yet + if self + .builder + .get_insert_block() + .unwrap() + .get_terminator() + .is_none() + { + let _ = self.builder.build_unconditional_branch(merge_block); + } + + // 3. Compile 'Elif' Blocks let mut current_else_block = next_else_block; for (i, (elif_cond_node, elif_body_node)) in elif_blocks.iter().enumerate() { @@ -892,26 +1253,53 @@ impl<'ctx> Compiler<'ctx> { let elif_cond_val = self.compile_expression(elif_cond_node, current_function)?; let elif_cond = elif_cond_val.into_int_value(); - let elif_then_block = self.context.append_basic_block(current_function, &format!("elif_then_{}", i)); - next_else_block = self.context.append_basic_block(current_function, &format!("elif_else_{}", i)); + let elif_then_block = self + .context + .append_basic_block(current_function, &format!("elif_then_{}", i)); + next_else_block = self + .context + .append_basic_block(current_function, &format!("elif_else_{}", i)); - let _=self.builder.build_conditional_branch(elif_cond, elif_then_block, next_else_block); + let _ = + self.builder + .build_conditional_branch(elif_cond, elif_then_block, next_else_block); self.builder.position_at_end(elif_then_block); self.compile_statement(elif_body_node, current_function)?; - let _=self.builder.build_unconditional_branch(merge_block); + + // FIX: Check terminator here too + if self + .builder + .get_insert_block() + .unwrap() + .get_terminator() + .is_none() + { + let _ = self.builder.build_unconditional_branch(merge_block); + } current_else_block = next_else_block; } + // 4. Compile 'Else' Block self.builder.position_at_end(current_else_block); if let Some(else_body) = else_node { self.compile_statement(else_body, current_function)?; } - let _=self.builder.build_unconditional_branch(merge_block); + // FIX: Check terminator here too + if self + .builder + .get_insert_block() + .unwrap() + .get_terminator() + .is_none() + { + let _ = self.builder.build_unconditional_branch(merge_block); + } + // 5. Move builder to merge block for subsequent instructions self.builder.position_at_end(merge_block); Ok(()) @@ -923,29 +1311,33 @@ impl<'ctx> Compiler<'ctx> { body_node: &ASTNode, current_function: FunctionValue<'ctx>, ) -> Result<(), String> { - let _parent_block = self.builder.get_insert_block().ok_or("No valid block")?; - let loop_header = self.context.append_basic_block(current_function, "loop_header"); - let loop_body = self.context.append_basic_block(current_function, "loop_body"); - let after_loop = self.context.append_basic_block(current_function, "after_loop"); + let loop_header = self + .context + .append_basic_block(current_function, "loop_header"); + let loop_body = self + .context + .append_basic_block(current_function, "loop_body"); + let after_loop = self + .context + .append_basic_block(current_function, "after_loop"); - let _=self.builder.build_unconditional_branch(loop_header); + let _ = self.builder.build_unconditional_branch(loop_header); self.builder.position_at_end(loop_header); - let condition_value = self.compile_expression(condition_node, current_function)? + let condition_value = self + .compile_expression(condition_node, current_function)? .into_int_value(); - let _=self.builder.build_conditional_branch( - condition_value, - loop_body, - after_loop, - ); + let _ = self + .builder + .build_conditional_branch(condition_value, loop_body, after_loop); self.builder.position_at_end(loop_body); self.compile_statement(body_node, current_function)?; - let _=self.builder.build_unconditional_branch(loop_header); + let _ = self.builder.build_unconditional_branch(loop_header); self.builder.position_at_end(after_loop); @@ -956,73 +1348,196 @@ impl<'ctx> Compiler<'ctx> { &mut self, callee: &ASTNode, arguments: &Vec, + loc: &Loc, current_function: FunctionValue<'ctx>, ) -> Result, String> { - + let function_name_string; let function_name = match callee { - ASTNode::Identifier { name, .. } => name, - _ => return Err("(Codegen STUB) Function calls on complex expressions are not supported.".to_string()), + ASTNode::Identifier { name, .. } => name.as_str(), + // Handle module access: math.is_prime -> math_is_prime + ASTNode::MemberAccess { object, member } => { + if let ASTNode::Identifier { name: obj_name, .. } = &**object { + function_name_string = format!("{}_{}", obj_name, member); + function_name_string.as_str() + } else { + return Err("(Codegen) Complex member access calls not supported.".to_string()); + } + } + _ => { + return Err( + "(Codegen Error) Function calls on complex expressions are not supported." + .to_string(), + ) + } }; if function_name == "print" { if arguments.len() != 1 { - return Err("(Codegen Error) 'print' currently expects 1 argument.".to_string()); + return Err("print expects 1 argument".to_string()); } + let arg_val = self.compile_expression(&arguments[0], current_function)?; + let print_fn = self + .module + .get_function("quantica_rt_print_int") + .ok_or("Runtime function 'quantica_rt_print_int' not found")?; + let print_str_fn = self + .module + .get_function("quantica_rt_print_string") + .ok_or("Runtime function 'quantica_rt_print_string' not found")?; + + // FIXED: Check type and call appropriate function + if arg_val.is_int_value() { + let int_val = self + .builder + .build_int_cast( + arg_val.into_int_value(), + self.context.i64_type(), + "cast_i64", + ) + .map_err(|e| e.to_string())?; - if !arg_val.is_pointer_value() { - return Err("(Codegen STUB) 'print' only supports string literals for now.".to_string()); - } - let string_ptr = arg_val.into_pointer_value(); + let call = self + .builder + .build_call(print_fn, &[int_val.into()], "print_int_call") + .map_err(|e| e.to_string())?; - let call_site = self.builder.build_call( - self.puts_function, - &[string_ptr.into()], - "calltmp_puts", - ).map_err(|e| e.to_string())?; + return Ok(call); + } else if arg_val.is_pointer_value() { + // This is a string + let string_ptr = arg_val.into_pointer_value(); + let call = self + .builder + .build_call(print_str_fn, &[string_ptr.into()], "print_str_call") + .map_err(|e| e.to_string())?; - return Ok(call_site); + return Ok(call); + } else if arg_val.is_float_value() { + // Convert float to string for printing + let ptr_type = self.context.ptr_type(AddressSpace::default()); + let i64_type = self.context.i64_type(); + let i32_type = self.context.i32_type(); + + let malloc_fn = self.module.get_function("malloc").unwrap_or_else(|| { + let fn_type = ptr_type.fn_type(&[i64_type.into()], false); + self.module + .add_function("malloc", fn_type, Some(Linkage::External)) + }); + + let snprintf_fn = self.module.get_function("snprintf").unwrap_or_else(|| { + let fn_type = i32_type + .fn_type(&[ptr_type.into(), i64_type.into(), ptr_type.into()], true); + self.module + .add_function("snprintf", fn_type, Some(Linkage::External)) + }); + + let buf_size = i64_type.const_int(64, false); + let buf_ptr = self + .builder + .build_call(malloc_fn, &[buf_size.into()], "alloc_str") + .map_err(|e| e.to_string())? + .try_as_basic_value() + .left() + .unwrap() + .into_pointer_value(); + + let fmt_ptr = self + .builder + .build_global_string_ptr("%.6f", "fmt_float") + .map_err(|e| e.to_string())? + .as_pointer_value(); + + self.builder + .build_call( + snprintf_fn, + &[ + buf_ptr.into(), + buf_size.into(), + fmt_ptr.into(), + arg_val.into(), + ], + "float_to_str", + ) + .map_err(|e| e.to_string())?; + let call = self + .builder + .build_call(print_str_fn, &[buf_ptr.into()], "print_float_call") + .map_err(|e| e.to_string())?; + + return Ok(call); + } else { + return Err("print() unsupported type".to_string()); + } } else if function_name == "debug_state" { if arguments.len() != 1 { - return Err("(Codegen Error) 'debug_state' expects 1 argument (a quantum register).".to_string()); + return Err( + "(Codegen Error) 'debug_state' expects 1 argument (a quantum register)." + .to_string(), + ); } let arg_val = self.compile_expression(&arguments[0], current_function)?; if !arg_val.is_pointer_value() { - return Err("(Codegen Error) 'debug_state' argument must be a quantum register.".to_string()); + return Err( + "(Codegen Error) 'debug_state' argument must be a quantum register." + .to_string(), + ); } let state_ptr = arg_val.into_pointer_value(); - let call_site = self.builder.build_call( - self.rt_debug_state, - &[state_ptr.into()], - "calltmp_debug", - ).map_err(|e| e.to_string())?; + let call_site = self + .builder + .build_call(self.rt_debug_state, &[state_ptr.into()], "calltmp_debug") + .map_err(|e| e.to_string())?; return Ok(call_site); } - let function = self.module.get_function(function_name) + let mut function_opt = self.module.get_function(function_name); + + // 2. If not found, and we are inside a module, try the mangled name (e.g. "math_max") + if function_opt.is_none() { + if let Some(alias) = &self.current_module_alias { + let mangled = format!("{}_{}", alias, function_name); + function_opt = self.module.get_function(&mangled); + } + } + + // 3. FIX: Unwrap the Option. If None, return an error. + let function = function_opt .ok_or_else(|| format!("(Codegen Error) Unknown function '{}'", function_name))?; - let mut compiled_args: Vec> = Vec::with_capacity(arguments.len()); + // 4. Compile arguments + let mut compiled_args: Vec> = + Vec::with_capacity(arguments.len()); for arg_node in arguments { let arg_value = self.compile_expression(arg_node, current_function)?; compiled_args.push(arg_value.into()); } - let call_site = self.builder.build_call( - function, - &compiled_args, - "calltmp", - ).map_err(|e| e.to_string())?; + // 5. Build the call with the UNWRAPPED 'function' + if let Some(scope) = self.get_current_di_scope(current_function) { + let location = self.debug_builder.create_debug_location( + self.context, + loc.line as u32, + loc.column as u32, + scope, + None, + ); + self.builder.set_current_debug_location(location); + } + + let call_site = self + .builder + .build_call(function, &compiled_args, "calltmp") + .map_err(|e| e.to_string())?; Ok(call_site) } - + /// Compiles a Function Declaration into an LLVM Function with debug info. fn compile_function( &mut self, name: &str, @@ -1030,20 +1545,24 @@ impl<'ctx> Compiler<'ctx> { return_type_node: &Option, body: &ASTNode, ) -> Result, String> { - - eprintln!("[DEBUG compile_function] Function '{}', body type: {}", name, match body { - ASTNode::Block(stmts) => format!("Block with {} statements", stmts.len()), - other => format!("NOT A BLOCK! Got: {:?}", format!("{:?}", other).chars().take(100).collect::()), - }); + eprintln!( + "[DEBUG compile_function] Function '{}', body type: {}", + name, + match body { + ASTNode::Block(stmts) => format!("Block with {} statements", stmts.len()), + other => format!( + "NOT A BLOCK! Got: {:?}", + format!("{:?}", other).chars().take(100).collect::() + ), + } + ); let mut param_types: Vec> = Vec::new(); for p in params { param_types.push(self.map_type(&p.param_type)); } - let param_metadata_types: Vec> = param_types - .iter() - .map(|&ty| ty.into()) - .collect(); + let param_metadata_types: Vec> = + param_types.iter().map(|&ty| ty.into()).collect(); let param_types_slice = param_metadata_types.as_slice(); let fn_return_type = match return_type_node { @@ -1054,26 +1573,24 @@ impl<'ctx> Compiler<'ctx> { let function = self.module.add_function(name, fn_return_type, None); - + // Create debug info for function let di_file = self.compile_unit.get_file(); - let line_no = 1; - + let line_no = 1; // Should be extracted from AST location info + // Create parameter types for debug info let mut di_param_types = vec![]; + // Add return type let di_return_type = match return_type_node { - Some(Type::None) | None => { - self.debug_builder.create_basic_type( - "void", - 0, - 0, - DIFlags::ZERO, - ).unwrap().as_type() - } + Some(Type::None) | None => self + .debug_builder + .create_basic_type("void", 0, 0, DIFlags::ZERO) + .unwrap() + .as_type(), Some(t) => self.get_or_create_di_type(t), }; - + // Add all parameter types (return type first, then parameters) di_param_types.push(di_return_type); for param in params { @@ -1091,15 +1608,15 @@ impl<'ctx> Compiler<'ctx> { let di_function = self.debug_builder.create_function( di_file.as_debug_info_scope(), name, - Some(name), + Some(name), // linkage name di_file, line_no, di_subroutine_type, - true, - true, + true, // is_local_to_unit + true, // is_definition line_no, DIFlags::ZERO, - false, + false, // is_optimized ); function.set_subprogram(di_function); @@ -1107,7 +1624,7 @@ impl<'ctx> Compiler<'ctx> { let entry_block = self.context.append_basic_block(function, "entry"); self.builder.position_at_end(entry_block); - + // Set debug location for function entry self.set_debug_location(line_no, 0, di_function.as_debug_info_scope()); let attribute_name = if name.starts_with("_hot_") { @@ -1119,7 +1636,7 @@ impl<'ctx> Compiler<'ctx> { }; if !attribute_name.is_empty() { - + // Get the attribute kind ID let kind_id = inkwell::attributes::Attribute::get_named_enum_kind_id(attribute_name); let attribute = self.context.create_enum_attribute(kind_id, 1); @@ -1132,20 +1649,22 @@ impl<'ctx> Compiler<'ctx> { let ast_param = ¶ms[i]; let param_type = param_types[i]; param.set_name(&ast_param.name); - let alloca = self.builder.build_alloca(param_type, &ast_param.name) + let alloca = self + .builder + .build_alloca(param_type, &ast_param.name) .map_err(|e| e.to_string())?; let _ = self.builder.build_store(alloca, param); - + // Create debug info for parameter let di_param_type = self.get_or_create_di_type(&ast_param.param_type); let di_param_var = self.debug_builder.create_parameter_variable( di_function.as_debug_info_scope(), &ast_param.name, - (i + 1) as u32, + (i + 1) as u32, // arg_no (1-indexed) di_file, line_no, di_param_type, - true, + true, // always_preserve DIFlags::ZERO, ); @@ -1157,15 +1676,18 @@ impl<'ctx> Compiler<'ctx> { entry_block, ); - self.variables.insert(ast_param.name.clone(), (alloca, param_type)); + self.variables + .insert(ast_param.name.clone(), (alloca, param_type)); } self.compile_block(body, function)?; - if function.get_last_basic_block().unwrap().get_terminator().is_none() { + let current_block = self.builder.get_insert_block().unwrap(); + + if current_block.get_terminator().is_none() { let fn_returns_void = match return_type_node { Some(Type::None) | None => true, - _ => false + _ => false, }; if fn_returns_void { @@ -1175,7 +1697,7 @@ impl<'ctx> Compiler<'ctx> { } } - + // Clear debug location after function self.clear_debug_location(); self.variables = old_variables; @@ -1183,27 +1705,43 @@ impl<'ctx> Compiler<'ctx> { if function.verify(true) { Ok(function) } else { - Err(format!("(Codegen Error) Invalid function generated: {}", name)) + Err(format!( + "(Codegen Error) Invalid function generated: {}", + name + )) } } - - fn compile_block(&mut self, node: &ASTNode, current_function: FunctionValue<'ctx>) -> Result<(), String> { + /// Compiles a Block of statements + fn compile_block( + &mut self, + node: &ASTNode, + current_function: FunctionValue<'ctx>, + ) -> Result<(), String> { if let ASTNode::Block(statements) = node { for stmt in statements { self.compile_statement(stmt, current_function)?; } Ok(()) } else { - Err(format!("(Codegen Error) Expected Block node, got: {:?}", node)) + Err(format!( + "(Codegen Error) Expected Block node, got: {:?}", + node + )) } } - - fn compile_return(&mut self, value_node: &Option>, current_function: FunctionValue<'ctx>) -> Result<(), String> { + /// Compiles a Return statement + fn compile_return( + &mut self, + value_node: &Option>, + current_function: FunctionValue<'ctx>, + ) -> Result<(), String> { if let Some(expr) = value_node { let value = self.compile_expression(expr, current_function)?; - self.builder.build_return(Some(&value)).map_err(|e| e.to_string())?; + self.builder + .build_return(Some(&value)) + .map_err(|e| e.to_string())?; } else { self.builder.build_return(None).map_err(|e| e.to_string())?; } @@ -1216,62 +1754,156 @@ impl<'ctx> Compiler<'ctx> { current_function: FunctionValue<'ctx>, ) -> Result, String> { match node { - ASTNode::IntLiteral(value) => { - Ok(self.context.i64_type().const_int(*value as u64, true).as_basic_value_enum()) - } - ASTNode::FloatLiteral(value) => { - Ok(self.context.f64_type().const_float(*value).as_basic_value_enum()) - } + ASTNode::IntLiteral(value) => Ok(self + .context + .i64_type() + .const_int(*value as u64, true) + .as_basic_value_enum()), + ASTNode::FloatLiteral(value) => Ok(self + .context + .f64_type() + .const_float(*value) + .as_basic_value_enum()), ASTNode::BoolLiteral(value) => { - let bool_val = self.context.bool_type().const_int(if *value { 1 } else { 0 }, false); + let bool_val = self + .context + .bool_type() + .const_int(if *value { 1 } else { 0 }, false); Ok(bool_val.as_basic_value_enum()) } ASTNode::StringLiteral(s) => { - let global_str = self.builder + let global_str = self + .builder .build_global_string_ptr(s.as_str(), "str_literal") .map_err(|e| e.to_string())?; Ok(global_str.as_pointer_value().as_basic_value_enum()) } ASTNode::Identifier { name, loc } => { - match self.variables.get(name) { - Some((alloca, llvm_type)) => { - let loaded_value = self.builder.build_load(*llvm_type, *alloca, name) - .map_err(|e| e.to_string())?; - Ok(loaded_value) - } - None => Err(format!("Codegen Error at {}: Undefined variable '{}'", loc, name)) + // 1. Try local variables first + if let Some((alloca, llvm_type)) = self.variables.get(name) { + let loaded_value = self + .builder + .build_load(*llvm_type, *alloca, name) + .map_err(|e| e.to_string())?; + return Ok(loaded_value); + } + + // 2. Try global variables (e.g. math_NAN) + let global_name = if let Some(alias) = &self.current_module_alias { + format!("{}_{}", alias, name) + } else { + name.clone() + }; + + // Look for mangled name first, then raw name + let global = self + .module + .get_global(&global_name) + .or_else(|| self.module.get_global(name)); + + if let Some(g) = global { + let ptr_val = g.as_pointer_value(); + let val_type = g.get_value_type(); + + // FIX: Manually convert AnyTypeEnum to BasicTypeEnum + let basic_type = if val_type.is_float_type() { + val_type.into_float_type().as_basic_type_enum() + } else if val_type.is_int_type() { + val_type.into_int_type().as_basic_type_enum() + } else { + // Default fallback + return Err(format!( + "Codegen Error: Unsupported global variable type for '{}'", + name + )); + }; + + let val = self + .builder + .build_load(basic_type, ptr_val, "load_global") + .map_err(|e| e.to_string())?; + return Ok(val); } + + Err(format!( + "Codegen Error at {}: Undefined variable '{}'", + loc, name + )) } ASTNode::Measure(qubit_expr) => { let (state_ptr, index_i32) = self.get_qubit_info(qubit_expr, current_function)?; - let call_site = self.builder.build_call( - self.rt_measure, - &[state_ptr.into(), index_i32.into()], - "measure_result", - ).map_err(|e| e.to_string())?; + let call_site = self + .builder + .build_call( + self.rt_measure, + &[state_ptr.into(), index_i32.into()], + "measure_result", + ) + .map_err(|e| e.to_string())?; - call_site.try_as_basic_value().left() + call_site + .try_as_basic_value() + .left() .ok_or("(Codegen Error) Measure call failed to return i32.".to_string()) } - ASTNode::Binary { operator, left, right, loc } => { + ASTNode::ArrayAccess { + array, + index, + loc: _, + } => { + // 1. Get the array base pointer + let array_val = self.compile_expression(array, current_function)?; + if !array_val.is_pointer_value() { + return Err("(Codegen Error) Array access target is not a pointer.".to_string()); + } + let array_ptr = array_val.into_pointer_value(); + + // 2. Get the index + let index_val = self.compile_expression(index, current_function)?; + if !index_val.is_int_value() { + return Err("(Codegen Error) Array index must be an integer.".to_string()); + } + let index_int = index_val.into_int_value(); + + // 3. Calculate address and load + // NOTE: For 'math.qc', arrays are Float[], so we use f64_type. + let element_type = self.context.f64_type(); + + let elem_ptr = unsafe { + self.builder + .build_gep(element_type, array_ptr, &[index_int], "array_gep") + .map_err(|e| e.to_string())? + }; + + let val = self + .builder + .build_load(element_type, elem_ptr, "array_load") + .map_err(|e| e.to_string())?; + + Ok(val) + } + + ASTNode::Binary { + operator, + left, + right, + loc, + } => { let left_val = self.compile_expression(left, current_function)?; let right_val = self.compile_expression(right, current_function)?; - if left_val.is_vector_value() || right_val.is_vector_value() { if left_val.is_vector_value() && right_val.is_vector_value() { - match operator { BinaryOperator::TensorProduct => { let result = self.emit_vector_mul( left_val.into_vector_value(), right_val.into_vector_value(), )?; - return Ok(result.as_basic_value_enum()); } _ => return Err(format!( @@ -1281,73 +1913,386 @@ impl<'ctx> Compiler<'ctx> { } } else { return Err(format!( - "(Codegen Error) Mixed scalar/vector operation not supported at {}", loc + "(Codegen Error) Mixed scalar/vector operation not supported at {}", + loc )); } } + let build_float_op = |op: &BinaryOperator, + l: inkwell::values::FloatValue<'ctx>, + r: inkwell::values::FloatValue<'ctx>| + -> Result, String> { + match op { + BinaryOperator::Add => Ok(self + .builder + .build_float_add(l, r, "fadd") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::Sub => Ok(self + .builder + .build_float_sub(l, r, "fsub") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::Mul => Ok(self + .builder + .build_float_mul(l, r, "fmul") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::Div => Ok(self + .builder + .build_float_div(l, r, "fdiv") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::Less => Ok(self + .builder + .build_float_compare(FloatPredicate::OLT, l, r, "flt") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::Greater => Ok(self + .builder + .build_float_compare(FloatPredicate::OGT, l, r, "fgt") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::LessEqual => Ok(self + .builder + .build_float_compare(FloatPredicate::OLE, l, r, "fle") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::GreaterEqual => Ok(self + .builder + .build_float_compare(FloatPredicate::OGE, l, r, "fge") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::Equal => Ok(self + .builder + .build_float_compare(FloatPredicate::OEQ, l, r, "feq") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::NotEqual => Ok(self + .builder + .build_float_compare(FloatPredicate::ONE, l, r, "fne") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + BinaryOperator::TensorProduct => Ok(self + .builder + .build_float_mul(l, r, "tensor_mul") + .map_err(|e| e.to_string())? + .as_basic_value_enum()), + _ => Err(format!( + "(Codegen Error) Operator {:?} not supported for floats at {}", + op, loc + )), + } + }; + match (left_val, right_val) { + // 1. Int op Int (BasicValueEnum::IntValue(left_int), BasicValueEnum::IntValue(right_int)) => { let result = match operator { - BinaryOperator::Add => self.builder.build_int_add(left_int, right_int, "iadd"), - BinaryOperator::Sub => self.builder.build_int_sub(left_int, right_int, "isub"), - BinaryOperator::Mul => self.builder.build_int_mul(left_int, right_int, "imul"), - BinaryOperator::Div => self.builder.build_int_signed_div(left_int, right_int, "idiv"), - BinaryOperator::Equal => self.builder.build_int_compare(IntPredicate::EQ, left_int, right_int, "ieq"), - BinaryOperator::NotEqual => self.builder.build_int_compare(IntPredicate::NE, left_int, right_int, "ine"), - BinaryOperator::Less => self.builder.build_int_compare(IntPredicate::SLT, left_int, right_int, "ilt"), - BinaryOperator::LessEqual => self.builder.build_int_compare(IntPredicate::SLE, left_int, right_int, "ile"), - BinaryOperator::Greater => self.builder.build_int_compare(IntPredicate::SGT, left_int, right_int, "igt"), - BinaryOperator::GreaterEqual => self.builder.build_int_compare(IntPredicate::SGE, left_int, right_int, "ige"), - BinaryOperator::And => self.builder.build_and(left_int, right_int, "and"), - BinaryOperator::Or => self.builder.build_or(left_int, right_int, "or"), - _ => return Err(format!("(Codegen STUB) Integer operator {:?} not yet implemented at {}", operator, loc)) - }.map_err(|e| e.to_string())?; - Ok(result.as_basic_value_enum()) - } - (BasicValueEnum::FloatValue(left_float), BasicValueEnum::FloatValue(right_float)) => { - match operator { - BinaryOperator::Add | BinaryOperator::Sub | BinaryOperator::Mul | BinaryOperator::Div => { - let result = match operator { - BinaryOperator::Add => self.builder.build_float_add(left_float, right_float, "fadd"), - BinaryOperator::Sub => self.builder.build_float_sub(left_float, right_float, "fsub"), - BinaryOperator::Mul => self.builder.build_float_mul(left_float, right_float, "fmul"), - BinaryOperator::Div => self.builder.build_float_div(left_float, right_float, "fdiv"), - _ => unreachable!(), - }.map_err(|e| e.to_string())?; - Ok(result.as_basic_value_enum()) + BinaryOperator::Add => { + self.builder.build_int_add(left_int, right_int, "iadd") } - BinaryOperator::Equal | BinaryOperator::NotEqual | BinaryOperator::Less | - BinaryOperator::LessEqual | BinaryOperator::Greater | BinaryOperator::GreaterEqual => { - let result = match operator { - BinaryOperator::Equal => self.builder.build_float_compare(FloatPredicate::OEQ, left_float, right_float, "feq"), - BinaryOperator::NotEqual => self.builder.build_float_compare(FloatPredicate::ONE, left_float, right_float, "fne"), - BinaryOperator::Less => self.builder.build_float_compare(FloatPredicate::OLT, left_float, right_float, "flt"), - BinaryOperator::LessEqual => self.builder.build_float_compare(FloatPredicate::OLE, left_float, right_float, "fle"), - BinaryOperator::Greater => self.builder.build_float_compare(FloatPredicate::OGT, left_float, right_float, "fgt"), - BinaryOperator::GreaterEqual => self.builder.build_float_compare(FloatPredicate::OGE, left_float, right_float, "fge"), - _ => unreachable!(), - }.map_err(|e| e.to_string())?; - Ok(result.as_basic_value_enum()) + BinaryOperator::Sub => { + self.builder.build_int_sub(left_int, right_int, "isub") } - BinaryOperator::TensorProduct => { - let result = self.builder.build_float_mul(left_float, right_float, "tensor_scalar_mul") - .map_err(|e| e.to_string())?; - return Ok(result.as_basic_value_enum()); + BinaryOperator::Mul => { + self.builder.build_int_mul(left_int, right_int, "imul") } - _ => return Err(format!("(Codegen STUB) Float operator {:?} not yet implemented at {}", operator, loc)) + BinaryOperator::Div => self + .builder + .build_int_signed_div(left_int, right_int, "idiv"), + BinaryOperator::Mod => self + .builder + .build_int_signed_rem(left_int, right_int, "imod"), + BinaryOperator::Equal => self.builder.build_int_compare( + IntPredicate::EQ, + left_int, + right_int, + "ieq", + ), + BinaryOperator::NotEqual => self.builder.build_int_compare( + IntPredicate::NE, + left_int, + right_int, + "ine", + ), + BinaryOperator::Less => self.builder.build_int_compare( + IntPredicate::SLT, + left_int, + right_int, + "ilt", + ), + BinaryOperator::LessEqual => self.builder.build_int_compare( + IntPredicate::SLE, + left_int, + right_int, + "ile", + ), + BinaryOperator::Greater => self.builder.build_int_compare( + IntPredicate::SGT, + left_int, + right_int, + "igt", + ), + BinaryOperator::GreaterEqual => self.builder.build_int_compare( + IntPredicate::SGE, + left_int, + right_int, + "ige", + ), + BinaryOperator::And => { + self.builder.build_and(left_int, right_int, "and") + } + BinaryOperator::Or => self.builder.build_or(left_int, right_int, "or"), + _ => { + return Err(format!( + "(Codegen STUB) Integer operator {:?} not yet implemented at {}", + operator, loc + )) + } + } + .map_err(|e| e.to_string())?; + Ok(result.as_basic_value_enum()) + } + + // 2. Float op Float + ( + BasicValueEnum::FloatValue(left_float), + BasicValueEnum::FloatValue(right_float), + ) => build_float_op(operator, left_float, right_float), + + // 3. Float op Int (Promote Int to Float) + ( + BasicValueEnum::FloatValue(left_float), + BasicValueEnum::IntValue(right_int), + ) => { + let right_float = self + .builder + .build_signed_int_to_float(right_int, self.context.f64_type(), "cast_r") + .map_err(|e| e.to_string())?; + build_float_op(operator, left_float, right_float) + } + + // 4. Int op Float (Promote Int to Float) + ( + BasicValueEnum::IntValue(left_int), + BasicValueEnum::FloatValue(right_float), + ) => { + let left_float = self + .builder + .build_signed_int_to_float(left_int, self.context.f64_type(), "cast_l") + .map_err(|e| e.to_string())?; + build_float_op(operator, left_float, right_float) + } + (BasicValueEnum::PointerValue(l_ptr), BasicValueEnum::PointerValue(r_ptr)) => { + if *operator == BinaryOperator::Add { + let ptr_type = self.context.ptr_type(AddressSpace::default()); + let i64_type = self.context.i64_type(); + let i32_type = self.context.i32_type(); + let i8_type = self.context.i8_type(); + + // Declare strlen + let strlen_fn = + self.module.get_function("strlen").unwrap_or_else(|| { + let fn_type = i64_type.fn_type(&[ptr_type.into()], false); + self.module.add_function( + "strlen", + fn_type, + Some(Linkage::External), + ) + }); + + // SAFER APPROACH: Use fixed-size stack buffer instead of malloc + let buf_size_val = i64_type.const_int(512, false); // 512-byte buffer + let buf_array_type = i8_type.array_type(512); + let buf = self + .builder + .build_alloca(buf_array_type, "concat_buf") + .map_err(|e| e.to_string())?; + let buf_ptr = self + .builder + .build_pointer_cast(buf, ptr_type, "buf_cast") + .map_err(|e| e.to_string())?; + + // Declare snprintf + let snprintf_fn = + self.module.get_function("snprintf").unwrap_or_else(|| { + let fn_type = i32_type.fn_type( + &[ptr_type.into(), i64_type.into(), ptr_type.into()], + true, + ); + self.module.add_function( + "snprintf", + fn_type, + Some(Linkage::External), + ) + }); + + // Format: snprintf(buf, 512, "%s%s", s1, s2) + let fmt = self + .builder + .build_global_string_ptr("%s%s", "fmt_concat") + .map_err(|e| e.to_string())? + .as_pointer_value(); + self.builder + .build_call( + snprintf_fn, + &[ + buf_ptr.into(), + buf_size_val.into(), + fmt.into(), + l_ptr.into(), + r_ptr.into(), + ], + "concat", + ) + .map_err(|e| e.to_string())?; + + Ok(buf_ptr.as_basic_value_enum()) + } else { + Err(format!( + "(Codegen Error) Operator {:?} not supported for Strings.", + operator + )) } } - _ => Err(format!("(Codegen Error) Mismatched types in binary operation at {}", loc)) + + _ => Err(format!( + "(Codegen Error) Mismatched types in binary operation at {}", + loc + )), } } - ASTNode::FunctionCall { callee, arguments, .. } => { - let call_site = self.compile_function_call(callee, arguments, current_function)?; + ASTNode::FunctionCall { + callee, + arguments, + loc, + .. + } => { + // --- ADDED: Handle built-in type conversions inline --- + if let ASTNode::Identifier { name, .. } = &**callee { + if name == "to_int" { + if arguments.len() != 1 { + return Err("to_int expects 1 argument".to_string()); + } + let val = self.compile_expression(&arguments[0], current_function)?; + + if val.is_float_value() { + // Cast Float -> Int + let int_val = self + .builder + .build_float_to_signed_int( + val.into_float_value(), + self.context.i64_type(), + "fptosi", + ) + .map_err(|e| e.to_string())?; + return Ok(int_val.as_basic_value_enum()); + } else { + // Already int? Return as is. + return Ok(val); + } + } else if name == "to_float" { + if arguments.len() != 1 { + return Err("to_float expects 1 argument".to_string()); + } + let val = self.compile_expression(&arguments[0], current_function)?; + + if val.is_int_value() { + // Cast Int -> Float + let float_val = self + .builder + .build_signed_int_to_float( + val.into_int_value(), + self.context.f64_type(), + "sitofp", + ) + .map_err(|e| e.to_string())?; + return Ok(float_val.as_basic_value_enum()); + } else { + // Already float? Return as is. + return Ok(val); + } + } + + if name == "to_string" { + if arguments.len() != 1 { + return Err("to_string expects 1 argument".to_string()); + } + let arg_val = self.compile_expression(&arguments[0], current_function)?; + + // SIMPLER APPROACH: Use a pre-allocated buffer in the stack + let ptr_type = self.context.ptr_type(AddressSpace::default()); + let i64_type = self.context.i64_type(); + let i32_type = self.context.i32_type(); + let i8_type = self.context.i8_type(); + + // Allocate 256-byte buffer on the stack (safer than malloc) + let buf_size = i64_type.const_int(256, false); + let buf_array_type = i8_type.array_type(256); + let buf = self + .builder + .build_alloca(buf_array_type, "to_string_buf") + .map_err(|e| e.to_string())?; + let buf_ptr = self + .builder + .build_pointer_cast(buf, ptr_type, "buf_cast") + .map_err(|e| e.to_string())?; + + let snprintf_fn = + self.module.get_function("snprintf").unwrap_or_else(|| { + let fn_type = i32_type.fn_type( + &[ptr_type.into(), i64_type.into(), ptr_type.into()], + true, + ); + self.module.add_function( + "snprintf", + fn_type, + Some(Linkage::External), + ) + }); + + // Select format string based on type + let fmt_str = if arg_val.is_int_value() { + "%lld" + } else if arg_val.is_float_value() { + "%.6f" + } else { + "%s" + }; + + let fmt_ptr = self + .builder + .build_global_string_ptr(fmt_str, "fmt_to_string") + .map_err(|e| e.to_string())? + .as_pointer_value(); + + self.builder + .build_call( + snprintf_fn, + &[ + buf_ptr.into(), + buf_size.into(), + fmt_ptr.into(), + arg_val.into(), + ], + "snprintf_call", + ) + .map_err(|e| e.to_string())?; + + return Ok(buf_ptr.as_basic_value_enum()); + } + } - call_site.try_as_basic_value().left() - .ok_or_else(|| format!( + let call_site = + self.compile_function_call(callee, arguments, loc, current_function)?; + + call_site.try_as_basic_value().left().ok_or_else(|| { + format!( "(Codegen Error) Function call used in expression does not return a value." - )) + ) + }) } ASTNode::Unary { operator, operand } => { let operand_val = self.compile_expression(operand, current_function)?; @@ -1356,25 +2301,39 @@ impl<'ctx> Compiler<'ctx> { UnaryOperator::Not => { let bool_type = self.context.bool_type(); let true_val = bool_type.const_int(1, false); - self.builder.build_xor(operand_val.into_int_value(), true_val, "not") + self.builder + .build_xor(operand_val.into_int_value(), true_val, "not") .map(|val| val.as_basic_value_enum()) } UnaryOperator::Minus => { if operand_val.is_int_value() { - self.builder.build_int_neg(operand_val.into_int_value(), "ineg") + self.builder + .build_int_neg(operand_val.into_int_value(), "ineg") .map(|val| val.as_basic_value_enum()) } else if operand_val.is_float_value() { - self.builder.build_float_neg(operand_val.into_float_value(), "fneg") + self.builder + .build_float_neg(operand_val.into_float_value(), "fneg") .map(|val| val.as_basic_value_enum()) } else { - return Err("(Codegen STUB) Unary '-' not supported for this type.".to_string()); + return Err( + "(Codegen STUB) Unary '-' not supported for this type.".to_string() + ); } } - _ => return Err(format!("(Codegen STUB) Unary operator {:?} not yet implemented.", operator)) - }.map_err(|e| e.to_string())?; + _ => { + return Err(format!( + "(Codegen STUB) Unary operator {:?} not yet implemented.", + operator + )) + } + } + .map_err(|e| e.to_string())?; Ok(result) } - _ => Err(format!("(Codegen STUB) Expression {:?} is not yet implemented.", node)) + _ => Err(format!( + "(Codegen STUB) Expression {:?} is not yet implemented.", + node + )), } } @@ -1383,17 +2342,27 @@ impl<'ctx> Compiler<'ctx> { Type::Int => self.context.i64_type().as_basic_type_enum(), Type::Float => self.context.f64_type().as_basic_type_enum(), Type::Bool => self.context.bool_type().as_basic_type_enum(), - Type::String => self.context.ptr_type(inkwell::AddressSpace::default()).as_basic_type_enum(), + Type::String => self + .context + .ptr_type(inkwell::AddressSpace::default()) + .as_basic_type_enum(), + Type::Array(_) => self + .context + .ptr_type(AddressSpace::default()) + .as_basic_type_enum(), Type::None => self.context.i8_type().as_basic_type_enum(), - Type::Int8 => self.context.i8_type().as_basic_type_enum(), - Type::Int16 => self.context.i16_type().as_basic_type_enum(), - Type::Int32 => self.context.i32_type().as_basic_type_enum(), - Type::Int64 => self.context.i64_type().as_basic_type_enum(), + Type::Int8 => self.context.i8_type().as_basic_type_enum(), + Type::Int16 => self.context.i16_type().as_basic_type_enum(), + Type::Int32 => self.context.i32_type().as_basic_type_enum(), + Type::Int64 => self.context.i64_type().as_basic_type_enum(), Type::Int128 => self.context.i128_type().as_basic_type_enum(), Type::Float32 => self.context.f32_type().as_basic_type_enum(), Type::Float64 => self.context.f64_type().as_basic_type_enum(), _ => { - println!("Warning: Codegen for type {:?} is not implemented, defaulting to i64.", ty); + println!( + "Warning: Codegen for type {:?} is not implemented, defaulting to i64.", + ty + ); self.context.i64_type().as_basic_type_enum() } } @@ -1405,7 +2374,6 @@ impl<'ctx> Compiler<'ctx> { arguments: &Vec, current_function: FunctionValue<'ctx>, ) -> Result<(), String> { - let (gate_name, param_ast_nodes, is_dagger, num_controls) = self.compile_gate_expression(gate_expr, current_function)?; @@ -1416,11 +2384,13 @@ impl<'ctx> Compiler<'ctx> { let param_f64 = if param_val.is_float_value() { param_val.into_float_value() } else if param_val.is_int_value() { - self.builder.build_signed_int_to_float( - param_val.into_int_value(), - self.context.f64_type(), - "param_f64" - ).map_err(|e| e.to_string())? + self.builder + .build_signed_int_to_float( + param_val.into_int_value(), + self.context.f64_type(), + "param_f64", + ) + .map_err(|e| e.to_string())? } else { return Err("(Codegen Error) Gate parameter must be a float or int.".to_string()); }; @@ -1440,16 +2410,23 @@ impl<'ctx> Compiler<'ctx> { return Err(format!("(Codegen Error) Qubit argument must be a simple register access, found: {:?}", array)); }; - let (current_var_alloca, current_var_type) = self.variables.get(array_name) - .ok_or_else(|| format!("(Codegen Error) Unknown quantum register '{}'", array_name))?; + let (current_var_alloca, current_var_type) = + self.variables.get(array_name).ok_or_else(|| { + format!("(Codegen Error) Unknown quantum register '{}'", array_name) + })?; if !current_var_type.is_pointer_type() { - return Err(format!("(Codegen Error) Variable '{}' is not a quantum register.", array_name)); + return Err(format!( + "(Codegen Error) Variable '{}' is not a quantum register.", + array_name + )); } if let Some(first_alloca) = register_alloca { if first_alloca != *current_var_alloca { - return Err(format!("(Codegen Error) 'apply' on different registers is not supported.")); + return Err(format!( + "(Codegen Error) 'apply' on different registers is not supported." + )); } } else { register_alloca = Some(*current_var_alloca); @@ -1461,54 +2438,82 @@ impl<'ctx> Compiler<'ctx> { } else { let index_val = self.compile_expression(index, current_function)?; if !index_val.is_int_value() { - return Err(format!("(Codegen Error) Qubit index must be an integer at {}", loc)); + return Err(format!( + "(Codegen Error) Qubit index must be an integer at {}", + loc + )); } - return Err(format!("(Codegen STUB) Qubit index must be an integer literal for now (at {})", loc)); + return Err(format!( + "(Codegen STUB) Qubit index must be an integer literal for now (at {})", + loc + )); } } - _ => return Err("(Codegen Error) 'apply' arguments must be qubit accesses (e.g., q[0]).".to_string()) + _ => { + return Err( + "(Codegen Error) 'apply' arguments must be qubit accesses (e.g., q[0])." + .to_string(), + ) + } } } let state_ptr = match (register_alloca, register_type) { - (Some(alloca), Some(ty)) => { - self.builder.build_load(ty, alloca, "load_state_ptr") - .map_err(|e| e.to_string())? - .into_pointer_value() - }, - _ => return Err("(Codegen Error) 'apply' called with no qubit arguments.".to_string()) + (Some(alloca), Some(ty)) => self + .builder + .build_load(ty, alloca, "load_state_ptr") + .map_err(|e| e.to_string())? + .into_pointer_value(), + _ => return Err("(Codegen Error) 'apply' called with no qubit arguments.".to_string()), }; - let gate_name_global = self.builder.build_global_string_ptr(&gate_name, "gate_name") + let gate_name_global = self + .builder + .build_global_string_ptr(&gate_name, "gate_name") .map_err(|e| e.to_string())?; let gate_name_ptr = gate_name_global.as_pointer_value(); - let is_dagger_int = self.context.i32_type().const_int(if is_dagger { 1 } else { 0 }, false); + let is_dagger_int = self + .context + .i32_type() + .const_int(if is_dagger { 1 } else { 0 }, false); let params_ptr = self.build_f64_array(&gate_params_llvm)?; - let num_params = self.context.i32_type().const_int(gate_params_llvm.len() as u64, false); + let num_params = self + .context + .i32_type() + .const_int(gate_params_llvm.len() as u64, false); let qubit_indices_ptr = self.build_i32_array(&qubit_indices)?; - let num_qubits = self.context.i32_type().const_int(qubit_indices.len() as u64, false); - - let num_controls_i32 = self.context.i32_type().const_int(num_controls as u64, false); - - let _ = self.builder.build_call( - self.rt_apply_gate, - &[ - state_ptr.into(), - gate_name_ptr.into(), - is_dagger_int.into(), - params_ptr.into(), - num_params.into(), - qubit_indices_ptr.into(), - num_qubits.into(), - num_controls_i32.into(), - ], - "call_apply", - ).map_err(|e| e.to_string())?; + let num_qubits = self + .context + .i32_type() + .const_int(qubit_indices.len() as u64, false); + + let num_controls_i32 = self + .context + .i32_type() + .const_int(num_controls as u64, false); + + let _ = self + .builder + .build_call( + self.rt_apply_gate, + &[ + state_ptr.into(), + gate_name_ptr.into(), + is_dagger_int.into(), + params_ptr.into(), + num_params.into(), + qubit_indices_ptr.into(), + num_qubits.into(), + num_controls_i32.into(), + ], + "call_apply", + ) + .map_err(|e| e.to_string())?; Ok(()) } @@ -1517,31 +2522,48 @@ impl<'ctx> Compiler<'ctx> { &mut self, qubit_expr: &ASTNode, current_function: FunctionValue<'ctx>, - ) -> Result<(inkwell::values::PointerValue<'ctx>, inkwell::values::IntValue<'ctx>), String> { - + ) -> Result< + ( + inkwell::values::PointerValue<'ctx>, + inkwell::values::IntValue<'ctx>, + ), + String, + > { if let ASTNode::ArrayAccess { array, index, loc } = qubit_expr { let array_name = if let ASTNode::Identifier { name, .. } = &**array { name } else { - return Err(format!("(Codegen Error) Measure target must be a register access, found: {:?}", array)); + return Err(format!( + "(Codegen Error) Measure target must be a register access, found: {:?}", + array + )); }; - let (var_ptr, var_type) = self.variables.get(array_name) - .ok_or_else(|| format!("(Codegen Error) Unknown quantum register '{}'", array_name))?; + let (var_ptr, var_type) = self.variables.get(array_name).ok_or_else(|| { + format!("(Codegen Error) Unknown quantum register '{}'", array_name) + })?; - let state_ptr = self.builder.build_load(*var_type, *var_ptr, "load_measure_ptr") + let state_ptr = self + .builder + .build_load(*var_type, *var_ptr, "load_measure_ptr") .map_err(|e| e.to_string())? .into_pointer_value(); let index_val = self.compile_expression(index, current_function)?; if !index_val.is_int_value() { - return Err(format!("(Codegen Error) Qubit index must be an integer at {}", loc)); + return Err(format!( + "(Codegen Error) Qubit index must be an integer at {}", + loc + )); } - let index_i32 = self.builder.build_int_truncate( - index_val.into_int_value(), - self.context.i32_type(), - "measure_idx_i32" - ).map_err(|e| e.to_string())?; + let index_i32 = self + .builder + .build_int_truncate( + index_val.into_int_value(), + self.context.i32_type(), + "measure_idx_i32", + ) + .map_err(|e| e.to_string())?; Ok((state_ptr, index_i32)) } else { @@ -1554,7 +2576,6 @@ impl<'ctx> Compiler<'ctx> { gate_expr: &ASTNode, current_function: FunctionValue<'ctx>, ) -> Result<(String, Vec, bool, i32), String> { - match gate_expr { ASTNode::Gate { name, .. } => { let gate_name = name.clone(); @@ -1564,7 +2585,9 @@ impl<'ctx> Compiler<'ctx> { Ok((gate_name, params, is_dagger, num_controls)) } - ASTNode::ParameterizedGate { name, parameters, .. } => { + ASTNode::ParameterizedGate { + name, parameters, .. + } => { let gate_name = name.clone(); let params = parameters.clone(); let is_dagger = false; @@ -1600,28 +2623,35 @@ impl<'ctx> Compiler<'ctx> { let f64_type = self.context.f64_type(); let f64_array_type = f64_type.array_type(values.len() as u32); - let array_alloca = self.builder.build_alloca(f64_array_type, "params_array") + let array_alloca = self + .builder + .build_alloca(f64_array_type, "params_array") .map_err(|e| e.to_string())?; for (i, &val) in values.iter().enumerate() { let index = self.context.i32_type().const_int(i as u64, false); let elem_ptr = unsafe { - self.builder.build_gep( - f64_array_type, - array_alloca, - &[self.context.i32_type().const_int(0, false), index], - "elem_ptr" - ).map_err(|e| e.to_string())? + self.builder + .build_gep( + f64_array_type, + array_alloca, + &[self.context.i32_type().const_int(0, false), index], + "elem_ptr", + ) + .map_err(|e| e.to_string())? }; - self.builder.build_store(elem_ptr, val) + self.builder + .build_store(elem_ptr, val) .map_err(|e| e.to_string())?; } - self.builder.build_pointer_cast( - array_alloca, - self.context.ptr_type(AddressSpace::default()), - "params_ptr" - ).map_err(|e| e.to_string()) + self.builder + .build_pointer_cast( + array_alloca, + self.context.ptr_type(AddressSpace::default()), + "params_ptr", + ) + .map_err(|e| e.to_string()) } fn build_i32_array( @@ -1631,29 +2661,34 @@ impl<'ctx> Compiler<'ctx> { let i32_type = self.context.i32_type(); let i32_array_type = i32_type.array_type(values.len() as u32); - let array_alloca = self.builder.build_alloca(i32_array_type, "indices_array") + let array_alloca = self + .builder + .build_alloca(i32_array_type, "indices_array") .map_err(|e| e.to_string())?; for (i, &val) in values.iter().enumerate() { let index = self.context.i32_type().const_int(i as u64, false); let elem_ptr = unsafe { - self.builder.build_gep( - i32_array_type, - array_alloca, - &[self.context.i32_type().const_int(0, false), index], - "elem_ptr" - ).map_err(|e| e.to_string())? + self.builder + .build_gep( + i32_array_type, + array_alloca, + &[self.context.i32_type().const_int(0, false), index], + "elem_ptr", + ) + .map_err(|e| e.to_string())? }; - self.builder.build_store(elem_ptr, i32_type.const_int(val as u64, false)) + self.builder + .build_store(elem_ptr, i32_type.const_int(val as u64, false)) .map_err(|e| e.to_string())?; } - self.builder.build_pointer_cast( - array_alloca, - self.context.ptr_type(AddressSpace::default()), - "indices_ptr" - ).map_err(|e| e.to_string()) + self.builder + .build_pointer_cast( + array_alloca, + self.context.ptr_type(AddressSpace::default()), + "indices_ptr", + ) + .map_err(|e| e.to_string()) } - } - diff --git a/src/doc_generator.rs b/src/doc_generator.rs index a7f700b..9cd5733 100644 --- a/src/doc_generator.rs +++ b/src/doc_generator.rs @@ -5,48 +5,50 @@ use std::fs; use std::io::Write; use std::path::Path; - +/// A struct responsible for generating documentation from an AST. pub struct DocGenerator { markdown_buffer: String, } impl DocGenerator { - + /// Creates a new DocGenerator. pub fn new() -> Self { DocGenerator { markdown_buffer: String::new(), } } - + /// Public entry point. + /// Generates documentation for the given program and saves it to a file. pub fn run(ast: &ASTNode, output_file: &str) -> Result<(), String> { let mut generator = Self::new(); generator.walk_program(ast)?; - - + + // Ensure the output directory (e.g., "docs/") exists if let Some(parent_dir) = Path::new(output_file).parent() { if !parent_dir.exists() { fs::create_dir_all(parent_dir) .map_err(|e| format!("Failed to create docs directory: {}", e))?; } } - - + + // Write the buffer to the file let mut file = fs::File::create(output_file) .map_err(|e| format!("Failed to create doc file: {}", e))?; - + file.write_all(generator.markdown_buffer.as_bytes()) .map_err(|e| format!("Failed to write to doc file: {}", e))?; - + Ok(()) } - + /// Walks the main program and generates docs for top-level statements. fn walk_program(&mut self, node: &ASTNode) -> Result<(), String> { if let ASTNode::Program(statements) = node { - - self.markdown_buffer.push_str("# Quantica API Reference\n\n"); - + // Add a title to the document + self.markdown_buffer + .push_str("# Quantica API Reference\n\n"); + for stmt in statements { self.generate_doc_for_node(stmt); } @@ -56,19 +58,34 @@ impl DocGenerator { } } - + /// The core logic. Checks a node for a doc comment and formats it. fn generate_doc_for_node(&mut self, node: &ASTNode) { match node { - ASTNode::FunctionDeclaration { name, parameters, return_type, .. } => { - + ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + .. + } => { + // Skip functions without documentation + // Since we removed doc comments, we can either: + // 1. Generate basic documentation from signatures + // 2. Skip documentation generation entirely + + // Option 1: Generate basic docs from signature self.add_entry( &format!("func {}", name), &self.format_params(parameters), return_type, - "No documentation available.", + "No documentation available.", // Placeholder ); } - ASTNode::CircuitDeclaration { name, parameters, return_type, .. } => { + ASTNode::CircuitDeclaration { + name, + parameters, + return_type, + .. + } => { self.add_entry( &format!("circuit {}", name), &self.format_params(parameters), @@ -76,7 +93,12 @@ impl DocGenerator { "No documentation available.", ); } - ASTNode::LetDeclaration { name, type_annotation, is_mutable, .. } => { + ASTNode::LetDeclaration { + name, + type_annotation, + is_mutable, + .. + } => { let prefix = if *is_mutable { "mut" } else { "let" }; self.add_variable_entry( &format!("{} {}", prefix, name), @@ -85,12 +107,14 @@ impl DocGenerator { ); } _ => { - + // Skip other node types } } } + // --- Helper Functions --- + /// Adds a formatted function/circuit entry to the Markdown buffer. fn add_entry( &mut self, name: &str, @@ -98,51 +122,45 @@ impl DocGenerator { return_type: &Option, comment: &str, ) { - + // 1. Add the code signature self.markdown_buffer.push_str(&format!( "### `{}({}){}`\n\n", name, params_str, self.format_return_type(return_type) )); - - + + // 2. Add the documentation self.markdown_buffer.push_str(comment); - self.markdown_buffer.push_str("\n\n---\n\n"); + self.markdown_buffer.push_str("\n\n---\n\n"); // Add horizontal rule } - - - fn add_variable_entry( - &mut self, - name: &str, - type_annotation: &Option, - comment: &str, - ) { - + + /// Adds a formatted variable entry to the Markdown buffer. + fn add_variable_entry(&mut self, name: &str, type_annotation: &Option, comment: &str) { + // 1. Add the code signature let type_str = if let Some(t) = type_annotation { format!(": {}", self.format_type(t)) } else { "".to_string() }; - self.markdown_buffer.push_str(&format!( - "### `{}`\n\n", - format!("{}{}", name, type_str) - )); - - + self.markdown_buffer + .push_str(&format!("### `{}`\n\n", format!("{}{}", name, type_str))); + + // 2. Add the documentation self.markdown_buffer.push_str(comment); - self.markdown_buffer.push_str("\n\n---\n\n"); + self.markdown_buffer.push_str("\n\n---\n\n"); // Add horizontal rule } - + /// Formats a list of parameters into a string: "p1: int, p2: float" fn format_params(&self, params: &[Parameter]) -> String { - params.iter() + params + .iter() .map(|p| format!("{}: {}", p.name, self.format_type(&p.param_type))) .collect::>() .join(", ") } - + /// Formats a return type into a string: " -> int" fn format_return_type(&self, return_type: &Option) -> String { if let Some(t) = return_type { format!(" -> {}", self.format_type(t)) @@ -150,12 +168,10 @@ impl DocGenerator { "".to_string() } } - + /// Formats a Type enum into a readable string. fn format_type(&self, t: &Type) -> String { - + // This can be as simple or complex as you need format!("{:?}", t) } - } - diff --git a/src/environment/mod.rs b/src/environment/mod.rs index 6c32074..a525d0f 100644 --- a/src/environment/mod.rs +++ b/src/environment/mod.rs @@ -1,22 +1,25 @@ // src/environment/mod.rs +use crate::parser::ast::Parameter; +use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use std::cell::RefCell; -use crate::parser::ast::Parameter; +use crate::parser::ast::ClassMethod; +use crate::parser::ast::ClassField; +use crate::parser::ast::ASTNode; #[derive(Debug, Clone)] pub struct GateDefinition { - + // The base gate name, e.g., "x", "u", "cnot" pub name: String, - + // The parameters, e.g., [theta, phi, lambda] for U gate pub params: Vec, - + // The list of control qubit indices pub controls: Vec, - + // The list of target qubit indices pub targets: Vec, - + // The total number of qubits in the register pub register_size: usize, - + // A shared pointer to the state vector pub state_rc: Rc>>, } @@ -27,49 +30,65 @@ pub enum RuntimeValue { String(String), Bool(bool), None, - + + // A handle to a specific qubit within a register + // It holds a shared pointer to the register's state vector Qubit { - - state: Rc>>, - index: usize, - size: usize, + // A shared pointer to the register's SPARSE state + state: Rc>>, + index: usize, // This qubit's index + size: usize, // The total size of its register }, - - + + // --- THIS IS THE UPDATED REGISTER DEFINITION --- QuantumRegister { size: usize, - - state: Rc>>, + // The state is now a HashMap from basis state (usize) to amplitude + state: Rc>>, }, - + Gate { - + // "s", "x", "u", etc. base_name: String, is_dagger: bool, - + // Number of controls this expression has added num_controls: usize, }, + Class { + name: String, + superclass: Option, + fields: Vec, + methods: HashMap, + constructor: Option>, + }, + + // Object instance + Instance { + class_name: String, + fields: HashMap>>, + methods: HashMap, + }, - Register(Vec>>), + Register(Vec>>), // An array Dict(HashMap>>), Range(Vec), KetState(String), - - + + // A user-defined function Function { parameters: Vec, body: Box, env: Rc>, }, - - + + // A built-in Rust function BuiltinFunction(String), Module(Rc>), - + ReturnValue(Box), Break, Continue, - + Probabilistic { value: Box, confidence: f64, @@ -77,7 +96,6 @@ pub enum RuntimeValue { } impl RuntimeValue { - pub fn type_name(&self) -> &str { match self { RuntimeValue::Int(_) => "int", @@ -86,6 +104,8 @@ impl RuntimeValue { RuntimeValue::Bool(_) => "bool", RuntimeValue::None => "none", RuntimeValue::Qubit { .. } => "qubit", + RuntimeValue::Class { name, .. } => "class", + RuntimeValue::Instance { class_name, .. } => "instance", RuntimeValue::Gate { .. } => "gate", RuntimeValue::QuantumRegister { .. } => "quantum_register", RuntimeValue::Register(_) => "array", @@ -103,7 +123,6 @@ impl RuntimeValue { } } - impl std::fmt::Display for RuntimeValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -112,29 +131,36 @@ impl std::fmt::Display for RuntimeValue { RuntimeValue::String(s) => write!(f, "{}", s), RuntimeValue::Bool(b) => write!(f, "{}", b), RuntimeValue::None => write!(f, "None"), - + RuntimeValue::Qubit { index, size, .. } => { write!(f, "", index, size) } - + RuntimeValue::QuantumRegister { size, .. } => { write!(f, "", size) } RuntimeValue::Register(elements) => { - let parts: Vec = elements.iter().map(|el| el.borrow().to_string()).collect(); + let parts: Vec = + elements.iter().map(|el| el.borrow().to_string()).collect(); write!(f, "[{}]", parts.join(", ")) } RuntimeValue::Dict(map) => { - let parts: Vec = map.iter().map(|(k, v)| format!("{}: {}", k, v.borrow())).collect(); + let parts: Vec = map + .iter() + .map(|(k, v)| format!("{}: {}", k, v.borrow())) + .collect(); write!(f, "{{{}}}", parts.join(", ")) } RuntimeValue::Module(_) => write!(f, ""), - _ => write!(f, "{:?}", self), + RuntimeValue::Class { name, .. } => write!(f, "", name), + RuntimeValue::Instance { class_name, fields, .. } => { + write!(f, "<{} instance at {:p}>", class_name, fields) + } + _ => write!(f, "{:?}", self), // Fallback } } } - #[derive(Debug, Clone)] pub struct Environment { store: HashMap>>, @@ -147,18 +173,51 @@ impl Environment { store: HashMap::new(), outer: None, }; - - env.set("print".to_string(), RuntimeValue::BuiltinFunction("print".to_string())); - env.set("echo".to_string(), RuntimeValue::BuiltinFunction("echo".to_string())); - env.set("maybe".to_string(), RuntimeValue::BuiltinFunction("maybe".to_string())); - env.set("type_of".to_string(), RuntimeValue::BuiltinFunction("type_of".to_string())); - env.set("to_string".to_string(), RuntimeValue::BuiltinFunction("to_string".to_string())); - env.set("sample".to_string(), RuntimeValue::BuiltinFunction("sample".to_string())); - env.set("to_int".to_string(), RuntimeValue::BuiltinFunction("to_int".to_string())); - env.set("to_float".to_string(), RuntimeValue::BuiltinFunction("to_float".to_string())); - env.set("len".to_string(), RuntimeValue::BuiltinFunction("len".to_string())); - env.set("debug_state".to_string(), RuntimeValue::BuiltinFunction("debug_state".to_string())); - env.set("assert".to_string(), RuntimeValue::BuiltinFunction("assert".to_string())); + + env.set( + "print".to_string(), + RuntimeValue::BuiltinFunction("print".to_string()), + ); + env.set( + "echo".to_string(), + RuntimeValue::BuiltinFunction("echo".to_string()), + ); + env.set( + "maybe".to_string(), + RuntimeValue::BuiltinFunction("maybe".to_string()), + ); + env.set( + "type_of".to_string(), + RuntimeValue::BuiltinFunction("type_of".to_string()), + ); + env.set( + "to_string".to_string(), + RuntimeValue::BuiltinFunction("to_string".to_string()), + ); + env.set( + "sample".to_string(), + RuntimeValue::BuiltinFunction("sample".to_string()), + ); + env.set( + "to_int".to_string(), + RuntimeValue::BuiltinFunction("to_int".to_string()), + ); + env.set( + "to_float".to_string(), + RuntimeValue::BuiltinFunction("to_float".to_string()), + ); + env.set( + "len".to_string(), + RuntimeValue::BuiltinFunction("len".to_string()), + ); + env.set( + "debug_state".to_string(), + RuntimeValue::BuiltinFunction("debug_state".to_string()), + ); + env.set( + "assert".to_string(), + RuntimeValue::BuiltinFunction("assert".to_string()), + ); env } @@ -182,11 +241,11 @@ impl Environment { pub fn set(&mut self, name: String, value: RuntimeValue) { self.store.insert(name, Rc::new(RefCell::new(value))); } - + pub fn get_store_clone(&self) -> HashMap>> { self.store.clone() } - + pub fn get_quantum_state(&self) -> Option { for (_, value_rc) in self.store.iter() { let value = value_rc.borrow(); @@ -194,12 +253,11 @@ impl Environment { return Some(value.clone()); } } - + if let Some(outer) = &self.outer { return outer.borrow().get_quantum_state(); } - + None } - } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..a293261 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,132 @@ +// src/error.rs +use crate::parser::ast::Loc; +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ErrorLevel { + Error, + Warning, + Note, +} + +#[derive(Debug, Clone)] +pub struct Diagnostic { + pub level: ErrorLevel, + pub message: String, + pub code: Option, // e.g. "E001" + pub loc: Loc, + pub hint: Option, +} + +impl Diagnostic { + pub fn error(loc: Loc, msg: &str) -> Self { + Self { + level: ErrorLevel::Error, + message: msg.to_string(), + code: None, + loc, + hint: None, + } + } + + pub fn with_code(mut self, code: &str) -> Self { + self.code = Some(code.to_string()); + self + } + + pub fn with_hint(mut self, hint: &str) -> Self { + self.hint = Some(hint.to_string()); + self + } +} + +pub struct ErrorReporter<'a> { + source_code: &'a str, + filename: &'a str, +} + +impl<'a> ErrorReporter<'a> { + pub fn new(source_code: &'a str, filename: &'a str) -> Self { + Self { + source_code, + filename, + } + } + + pub fn report(&self, diagnostic: &Diagnostic) { + let color_code = match diagnostic.level { + ErrorLevel::Error => "\x1b[31m", // Red + ErrorLevel::Warning => "\x1b[33m", // Yellow + ErrorLevel::Note => "\x1b[34m", // Blue + }; + let reset = "\x1b[0m"; + let bold = "\x1b[1m"; + + // Header: "error[E001]: Message" + let level_str = match diagnostic.level { + ErrorLevel::Error => "error", + ErrorLevel::Warning => "warning", + ErrorLevel::Note => "note", + }; + + let code_display = if let Some(c) = &diagnostic.code { + format!("[{}]", c) + } else { + String::new() + }; + + println!( + "{}{}{}{}: {}{}", + color_code, bold, level_str, code_display, reset, diagnostic.message + ); + + // Location: " --> src/main.qc:10:5" + println!( + " {}--> {}:{}:{}{}", + "\x1b[34m", // Blue arrow + self.filename, + diagnostic.loc.line, + diagnostic.loc.column, + reset + ); + + // Code Snippet + if let Some(line_content) = self.get_line_content(diagnostic.loc.line) { + let line_num_padding = " "; + let line_num = diagnostic.loc.line.to_string(); + + // Empty pipe line + println!("{} |", line_num_padding); + + // The actual code + println!(" {:<4} | {}", line_num, line_content); + + // The pointer line (^^^^^) + print!("{} |", line_num_padding); + + // Calculate spaces to the column + // (Note: column is 1-based, we treat it carefully) + let spaces = if diagnostic.loc.column > 0 { diagnostic.loc.column - 1 } else { 0 }; + let pointer_str = " ".repeat(spaces); + + println!(" {}{}^", pointer_str, color_code); + + // Print hint if it exists + if let Some(hint) = &diagnostic.hint { + println!( + "{} = hint: {}{}", + " ".repeat(line_num_padding.len() + 3 + spaces), + hint, + reset + ); + } + + // Reset color + println!("{}", reset); + } + } + + fn get_line_content(&self, line_number: usize) -> Option<&str> { + self.source_code.lines().nth(line_number - 1) + } +} \ No newline at end of file diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 7694490..272e41d 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -1,27 +1,37 @@ // src/evaluator/mod.rs -use std::collections::HashMap; +use crate::environment::{Environment, GateDefinition, RuntimeValue}; +use crate::lexer::Lexer; // Restored use crate::parser::ast::ASTNode; +use crate::parser::ast::BinaryOperator; +use crate::parser::ast::ImportPath; // Restored +use crate::parser::ast::ImportSpec; // Restored use crate::parser::ast::Loc; -use crate::environment::{Environment, RuntimeValue, GateDefinition}; -use std::rc::Rc; -use crate::lexer::Lexer; -use crate::parser::Parser; -use std::fs; -use std::cell::RefCell; +use crate::parser::Parser; // Restored use rand::Rng; -use crate::parser::ast::ImportSpec; -use crate::parser::ast::ImportPath; -use std::path::PathBuf; -use crate::parser::ast::BinaryOperator; - +use std::cell::RefCell; +use std::collections::HashMap; // Restored +use std::fs; // Restored +use std::path::PathBuf; // Restored +use std::rc::Rc; +use crate::parser::ast::ClassField; +use crate::parser::ast::ClassMethod; use num_complex::Complex; type C64 = Complex; - +use crate::graphics::{Canvas, Color, Plot, PlotType}; +use std::sync::Mutex; +lazy_static::lazy_static! { + static ref INTERPRETER_CANVASES: Mutex> = Mutex::new(HashMap::new()); + static ref INTERPRETER_PLOTS: Mutex> = Mutex::new(HashMap::new()); + static ref NEXT_ID: Mutex = Mutex::new(0); +} pub struct Evaluator; impl Evaluator { - pub fn evaluate_program(program: &ASTNode, env: &Rc>) -> Result { + pub fn evaluate_program( + program: &ASTNode, + env: &Rc>, + ) -> Result { if let ASTNode::Program(statements) = program { let mut last_result = RuntimeValue::None; for stmt in statements { @@ -33,8 +43,11 @@ impl Evaluator { } } - - pub fn evaluate(node: &ASTNode, env: &Rc>) -> Result { + // --- Core 'evaluate' function --- + pub fn evaluate( + node: &ASTNode, + env: &Rc>, + ) -> Result { match node { ASTNode::LetDeclaration { name, value, .. } => Self::eval_let_declaration(name, value, env), ASTNode::Assignment { target, value } => Self::eval_assignment(target, value, env), @@ -60,6 +73,21 @@ impl Evaluator { ASTNode::FromImport { path, spec } => { Self::eval_from_import(path, spec, env) } + ASTNode::ClassDeclaration { name, superclass, fields, methods, constructor, .. } => { + Self::eval_class_declaration(name, superclass, fields, methods, constructor, env) + } + + ASTNode::NewInstance { class_name, arguments, loc } => { + Self::eval_new_instance(class_name, arguments, loc, env) + } + + ASTNode::MethodCall { object, method_name, arguments, loc } => { + Self::eval_method_call(object, method_name, arguments, loc, env) + } + + ASTNode::SelfRef { loc } => { + Self::eval_self_ref(loc, env) + } ASTNode::If { condition, then_block, elif_blocks, else_block } => Self::eval_if_statement(condition, then_block, elif_blocks, else_block, env), ASTNode::While { condition, body } => @@ -81,11 +109,9 @@ impl Evaluator { state: Rc::new(RefCell::new(state_vec)) }) }, - ASTNode::Apply { gate_expr, arguments, loc } => { Self::eval_apply_statement(gate_expr, arguments, loc, env) } - ASTNode::Gate { .. } | ASTNode::Controlled { .. } | ASTNode::Dagger { .. } => { Err("Gate expressions (like 'X' or 'controlled(X)') can only be used inside an 'apply' statement.".to_string()) } @@ -133,19 +159,429 @@ impl Evaluator { _ => Err(format!("Evaluation not implemented for AST node: {:?}", node)), } } + + pub fn register_builtins(env: &Rc>) { + let mut e = env.borrow_mut(); + + // --- Standard Built-ins --- + e.set("print".to_string(), RuntimeValue::BuiltinFunction("print".to_string())); + e.set("len".to_string(), RuntimeValue::BuiltinFunction("len".to_string())); + e.set("to_string".to_string(), RuntimeValue::BuiltinFunction("to_string".to_string())); + e.set("to_int".to_string(), RuntimeValue::BuiltinFunction("to_int".to_string())); + e.set("to_float".to_string(), RuntimeValue::BuiltinFunction("to_float".to_string())); + e.set("type_of".to_string(), RuntimeValue::BuiltinFunction("type_of".to_string())); + e.set("assert".to_string(), RuntimeValue::BuiltinFunction("assert".to_string())); + e.set("echo".to_string(), RuntimeValue::BuiltinFunction("echo".to_string())); + e.set("maybe".to_string(), RuntimeValue::BuiltinFunction("maybe".to_string())); + e.set("sample".to_string(), RuntimeValue::BuiltinFunction("sample".to_string())); + e.set("debug_state".to_string(), RuntimeValue::BuiltinFunction("debug_state".to_string())); + e.set("measure".to_string(), RuntimeValue::BuiltinFunction("measure".to_string())); + + // --- Graphics Built-ins --- + e.set("_graphics_create_canvas".to_string(), RuntimeValue::BuiltinFunction("_graphics_create_canvas".to_string())); + e.set("_graphics_set_background".to_string(), RuntimeValue::BuiltinFunction("_graphics_set_background".to_string())); + e.set("_graphics_draw_rect".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_rect".to_string())); + e.set("_graphics_draw_circle".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_circle".to_string())); + e.set("_graphics_draw_line".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_line".to_string())); + e.set("_graphics_draw_text".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_text".to_string())); + e.set("_graphics_save_svg".to_string(), RuntimeValue::BuiltinFunction("_graphics_save_svg".to_string())); + e.set("_graphics_destroy_canvas".to_string(), RuntimeValue::BuiltinFunction("_graphics_destroy_canvas".to_string())); + + // --- Plot Functions --- + e.set("_graphics_create_plot".to_string(), RuntimeValue::BuiltinFunction("_graphics_create_plot".to_string())); + e.set("_graphics_plot_set_data".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_data".to_string())); + e.set("_graphics_plot_set_title".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_title".to_string())); + e.set("_graphics_plot_render".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_render".to_string())); + e.set("_graphics_destroy_plot".to_string(), RuntimeValue::BuiltinFunction("_graphics_destroy_plot".to_string())); + } fn is_truthy(val: &RuntimeValue) -> bool { - match val { - RuntimeValue::Bool(b) => *b, - RuntimeValue::Int(n) => *n != 0, - RuntimeValue::None => false, - RuntimeValue::Probabilistic { value, confidence } => { - if rand::thread_rng().gen::() < *confidence { Self::is_truthy(value) } else { false } + match val { + RuntimeValue::Bool(b) => *b, + RuntimeValue::Int(n) => *n != 0, + RuntimeValue::None => false, + RuntimeValue::Probabilistic { value, confidence } => { + if rand::thread_rng().gen::() < *confidence { + Self::is_truthy(value) + } else { + false + } + } + _ => true, } - _ => true, } -} + fn eval_class_declaration( + name: &str, + superclass: &Option, + fields: &Vec, + methods: &Vec, + constructor: &Option>, + env: &Rc>, + ) -> Result { + // Convert methods vector to HashMap + let mut method_map = HashMap::new(); + for method in methods { + method_map.insert(method.name.clone(), method.clone()); + } + + let class_value = RuntimeValue::Class { + name: name.to_string(), + superclass: superclass.clone(), + fields: fields.clone(), + methods: method_map, + constructor: constructor.clone(), + }; + + env.borrow_mut().set(name.to_string(), class_value); + Ok(RuntimeValue::None) + } + + fn eval_new_instance( + class_name: &str, + arguments: &Vec, + loc: &Loc, + env: &Rc>, + ) -> Result { + // 1. Get Class Definition + let class_value = env.borrow().get(class_name) + .ok_or_else(|| format!("Runtime Error at {}: Unknown class '{}'", loc, class_name))?; + + let class_value_borrowed = class_value.borrow(); + let (fields_def, methods, constructor) = match &*class_value_borrowed { + RuntimeValue::Class { fields, methods, constructor, .. } => { + (fields.clone(), methods.clone(), constructor.clone()) + } + _ => return Err(format!("Runtime Error at {}: '{}' is not a class", loc, class_name)), + }; + drop(class_value_borrowed); + + // 2. Initialize instance fields & Capture Initial Values + let mut instance_fields = HashMap::new(); + let mut initial_values = HashMap::new(); // Store initials to detect changes + + for field in &fields_def { + let value = if let Some(default_expr) = &field.default_value { + Self::evaluate(default_expr, env)? + } else { + RuntimeValue::None + }; + // Save initial value for change detection + initial_values.insert(field.name.clone(), value.clone()); + // Create shared storage + instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); + } + + // 3. Create Instance + let instance = RuntimeValue::Instance { + class_name: class_name.to_string(), + fields: instance_fields.clone(), + methods: methods.clone(), + }; + + // 4. Run Constructor + if let Some(constructor_node) = constructor { + if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { + // Evaluate Args + let mut evaluated_args = Vec::new(); + for arg in arguments { + evaluated_args.push(Self::evaluate(arg, env)?); + } + + if parameters.len() != evaluated_args.len() { + return Err(format!("Runtime Error: Constructor arg mismatch.")); + } + + let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); + + // A. Inject 'self' + constructor_env.borrow_mut().set("self".to_string(), instance.clone()); + + // B. Inject Implicit Fields (as local vars) + for (name, val) in &initial_values { + constructor_env.borrow_mut().set(name.clone(), val.clone()); + } + + // C. Inject Parameters (params override fields if names collide) + for (param, arg_value) in parameters.iter().zip(evaluated_args) { + constructor_env.borrow_mut().set(param.name.clone(), arg_value); + } + + // D. Execute Body + Self::evaluate(&body, &constructor_env)?; + + // E. SMART SYNC: Only update fields if the local variable CHANGED + for (field_name, field_rc) in &instance_fields { + if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { + let local_val = local_val_rc.borrow().clone(); + + // Check against the initial value we recorded + if let Some(initial_val) = initial_values.get(field_name) { + // FIX: Use the helper instead of '!=' + if !Self::are_values_equal(&local_val, initial_val) { + // Local var changed (e.g., 'x = 10'), so update the field + *field_rc.borrow_mut() = local_val; + } + } + } + } + } + } + + Ok(instance) + } + fn are_values_equal(a: &RuntimeValue, b: &RuntimeValue) -> bool { + match (a, b) { + (RuntimeValue::None, RuntimeValue::None) => true, + (RuntimeValue::Int(x), RuntimeValue::Int(y)) => x == y, + (RuntimeValue::Float(x), RuntimeValue::Float(y)) => (x - y).abs() < f64::EPSILON, + (RuntimeValue::Bool(x), RuntimeValue::Bool(y)) => x == y, + (RuntimeValue::String(x), RuntimeValue::String(y)) => x == y, + // For complex types, we assume they are different to ensure we sync them if needed. + // But crucially, None == None returns true, which fixes the bug. + _ => false, + } + } + + fn eval_method_call( + object_expr: &ASTNode, + method_name: &str, + arguments: &Vec, + loc: &Loc, + env: &Rc>, + ) -> Result { + // Evaluate object + let object = Self::evaluate(object_expr, env)?; + + match object { + // Case 1: Method call on a Class Instance + RuntimeValue::Instance { + class_name, + fields, + methods, + } => { + // Find method + let method = methods.get(method_name).ok_or_else(|| { + format!( + "Runtime Error at {}: Class '{}' has no method '{}'", + loc, class_name, method_name + ) + })?; + + // Evaluate arguments + let mut evaluated_args = Vec::new(); + for arg in arguments { + evaluated_args.push(Self::evaluate(arg, env)?); + } + + // Check parameter count + if method.parameters.len() != evaluated_args.len() { + return Err(format!( + "Runtime Error at {}: Method '{}' expected {} arguments, got {}", + loc, + method_name, + method.parameters.len(), + evaluated_args.len() + )); + } + + // Create method environment + let method_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); + + let self_instance = RuntimeValue::Instance { + class_name: class_name.clone(), + fields: fields.clone(), // Share the same Rc fields + methods: methods.clone(), + }; + method_env.borrow_mut().set("self".to_string(), self_instance); + + // 1. COPY-IN: Add instance fields to method environment + for (field_name, field_value) in &fields { + method_env + .borrow_mut() + .set(field_name.clone(), field_value.borrow().clone()); + } + + // Add parameters + for (param, arg_value) in method.parameters.iter().zip(evaluated_args) { + method_env.borrow_mut().set(param.name.clone(), arg_value); + } + + // Execute method body + let result = Self::evaluate(&method.body, &method_env)?; + + // 2. FIX START: COPY-OUT strategy + // Sync changes from method_env back to the original instance fields + for (field_name, field_rc) in &fields { + // Check if the field exists in the method environment (it should) + if let Some(env_val_rc) = method_env.borrow().get(field_name) { + // Get the potentially modified value from the environment + let new_val = env_val_rc.borrow().clone(); + + // Write it back to the Instance's shared storage + *field_rc.borrow_mut() = new_val; + } + } + // FIX END + + // Handle return value + match result { + RuntimeValue::ReturnValue(val) => Ok(*val), + other => Ok(other), + } + } + + // Case 2: Function call on a Module (Remains unchanged) + RuntimeValue::Module(module_env) => { + let member_val = module_env.borrow().get(method_name).ok_or_else(|| { + format!( + "Runtime Error at {}: Module does not have a member named '{}'", + loc, method_name + ) + })?; + + // We must clone the value to avoid borrowing conflicts with the environment + let member_val = member_val.borrow().clone(); + let evaluated_args = Self::eval_arguments(arguments, env)?; + + match member_val { + // Sub-case A: Calling a standard Function exported by the module + RuntimeValue::Function { + parameters, + body, + env: func_env, + } => { + let mut function_scope = Environment::new_enclosed(func_env); + if parameters.len() != evaluated_args.len() { + return Err(format!( + "Runtime Error at {}: Module function '{}.{}' expected {} arguments, but got {}.", + loc, "module", method_name, parameters.len(), evaluated_args.len() + )); + } + for (param, arg_val) in parameters.iter().zip(evaluated_args) { + function_scope.set(param.name.clone(), arg_val); + } + let function_scope_rc = Rc::new(RefCell::new(function_scope)); + + let statements = match &*body { + ASTNode::Block(stmts) => stmts, + _ => return Err(format!("Internal Error: Function body is not a Block.")), + }; + + let result = Self::eval_block(statements, &function_scope_rc)?; + + if let RuntimeValue::ReturnValue(val) = result { + Ok(*val) + } else { + Ok(result) + } + } + + // Sub-case B: Instantiating a CLASS exported by the module (The FIX for graphics.Canvas) + RuntimeValue::Class { name, fields, methods, constructor, .. } => { + // 1. Initialize instance fields & Capture Initial Values + let mut instance_fields = HashMap::new(); + let mut initial_values = HashMap::new(); // <--- Capture Initials + + for field in &fields { + let value = if let Some(default_expr) = &field.default_value { + Self::evaluate(default_expr, env)? + } else { + RuntimeValue::None + }; + initial_values.insert(field.name.clone(), value.clone()); + instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); + } + + // 2. Create the instance + let mut instance = RuntimeValue::Instance { + class_name: name.clone(), + fields: instance_fields.clone(), + methods: methods.clone(), + }; + + // 3. Call constructor 'init' + if let Some(constructor_node) = constructor { + if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { + if parameters.len() != evaluated_args.len() { + return Err(format!("Runtime Error: Constructor arg mismatch.")); + } + + let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(module_env.clone()))); + + // A. Inject 'self' + constructor_env.borrow_mut().set("self".to_string(), instance.clone()); + + // B. Inject Implicit Fields (as local vars) + for (name, val) in &initial_values { + constructor_env.borrow_mut().set(name.clone(), val.clone()); + } + + // C. Inject Parameters + for (param, arg_value) in parameters.iter().zip(evaluated_args) { + constructor_env.borrow_mut().set(param.name.clone(), arg_value); + } + + // D. Execute Body + Self::evaluate(&body, &constructor_env)?; + + // E. SMART SYNC: Only update fields if local var CHANGED + for (field_name, field_rc) in &instance_fields { + if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { + let local_val = local_val_rc.borrow().clone(); + + if let Some(initial_val) = initial_values.get(field_name) { + // Only overwrite if value actually changed (e.g. None -> 0) + if !Self::are_values_equal(&local_val, initial_val) { + *field_rc.borrow_mut() = local_val; + } + } + } + } + + // Return updated instance + instance = RuntimeValue::Instance { + class_name: name.clone(), + fields: instance_fields, + methods, + }; + } + } + Ok(instance) + } + + RuntimeValue::BuiltinFunction(func_name) => { + Err(format!( + "Runtime Error at {}: Built-in function '{}' cannot be called via module access yet.", + loc, func_name + )) + } + _ => Err(format!( + "Runtime Error at {}: '{}.{}' is not a function or class.", + loc, "module", method_name + )), + } + } + _ => Err(format!( + "Runtime Error at {}: Cannot call method '{}' on type {}", + loc, + method_name, + object.type_name() + )), + } + } + fn eval_self_ref( + loc: &Loc, + env: &Rc>, + ) -> Result { + // In methods, 'self' refers to the instance + // This is a simplified implementation + // In a more complete implementation, you'd store a reference to the current instance + Err(format!( + "Runtime Error at {}: 'self' can only be used inside class methods", + loc + )) + } fn eval_quantum_declaration( name: &str, @@ -157,75 +593,110 @@ impl Evaluator { let size_val = Self::evaluate(expr, env)?; let size = match size_val { RuntimeValue::Int(n) if n > 0 => n as usize, - _ => return Err(format!("Runtime Error: Quantum register size must be a positive integer, got {:?}", size_val)), + _ => { + return Err(format!( + "Runtime Error: Quantum register size must be a positive integer, got {:?}", + size_val + )) + } }; if size > 20 { - return Err(format!("Runtime Error: Register size {} is too large to simulate.", size)); + // Max size is now more about memory, but still good to limit + return Err(format!( + "Runtime Error: Register size {} is too large to simulate.", + size + )); } - + // Use the sparse state vector let state_map = Self::default_state_vector(size)?; - RuntimeValue::QuantumRegister { size, state: Rc::new(RefCell::new(state_map)) } + RuntimeValue::QuantumRegister { + size, + state: Rc::new(RefCell::new(state_map)), + } } else if let Some(expr) = initial_state_expr { let state_val = Self::evaluate(expr, env)?; match state_val { - + // Clone the sparse state RuntimeValue::QuantumRegister { size, state } => { RuntimeValue::QuantumRegister { size, state } } _ => return Err(format!("Runtime Error: Initial state for a quantum register must be a register, got {:?}", state_val.type_name())), } } else { - + // Default to a single qubit let size = 1; let state_map = Self::default_state_vector(size)?; - RuntimeValue::QuantumRegister { size, state: Rc::new(RefCell::new(state_map)) } + RuntimeValue::QuantumRegister { + size, + state: Rc::new(RefCell::new(state_map)), + } }; env.borrow_mut().set(name.to_string(), register.clone()); Ok(register) } fn eval_unary_op( - operator: &crate::parser::ast::UnaryOperator, - operand_expr: &Box, - env: &Rc>, -) -> Result { - let operand = Self::evaluate(operand_expr, env)?; - match operator { - crate::parser::ast::UnaryOperator::Not => Ok(RuntimeValue::Bool(!Self::is_truthy(&operand))), - crate::parser::ast::UnaryOperator::Minus => match operand { - RuntimeValue::Int(i) => Ok(RuntimeValue::Int(-i)), - RuntimeValue::Float(f) => Ok(RuntimeValue::Float(-f)), - _ => Err(format!("Runtime Error: Unary operator '-' not defined for type {:?}", operand.type_name())), - }, - crate::parser::ast::UnaryOperator::Plus => match operand { - RuntimeValue::Int(i) => Ok(RuntimeValue::Int(i)), - RuntimeValue::Float(f) => Ok(RuntimeValue::Float(f)), - _ => Err(format!("Runtime Error: Unary operator '+' not defined for type {:?}", operand.type_name())), - }, + operator: &crate::parser::ast::UnaryOperator, + operand_expr: &Box, + env: &Rc>, + ) -> Result { + let operand = Self::evaluate(operand_expr, env)?; + match operator { + crate::parser::ast::UnaryOperator::Not => { + Ok(RuntimeValue::Bool(!Self::is_truthy(&operand))) + } + crate::parser::ast::UnaryOperator::Minus => match operand { + RuntimeValue::Int(i) => Ok(RuntimeValue::Int(-i)), + RuntimeValue::Float(f) => Ok(RuntimeValue::Float(-f)), + _ => Err(format!( + "Runtime Error: Unary operator '-' not defined for type {:?}", + operand.type_name() + )), + }, + crate::parser::ast::UnaryOperator::Plus => match operand { + RuntimeValue::Int(i) => Ok(RuntimeValue::Int(i)), + RuntimeValue::Float(f) => Ok(RuntimeValue::Float(f)), + _ => Err(format!( + "Runtime Error: Unary operator '+' not defined for type {:?}", + operand.type_name() + )), + }, + } } -} fn default_state_vector(size: usize) -> Result, String> { - if size == 0 { return Err("Runtime Error: Quantum register size must be > 0".to_string()); } - + if size == 0 { + return Err("Runtime Error: Quantum register size must be > 0".to_string()); + } + // The |0...0> state is just one entry in the map let mut state = HashMap::new(); - state.insert(0, (1.0, 0.0)); + state.insert(0, (1.0, 0.0)); // |0> state has index 0 Ok(state) } - fn ket_to_state_vector(ket_str: &str, size: usize) -> Result, String> { + fn ket_to_state_vector( + ket_str: &str, + size: usize, + ) -> Result, String> { if size != 1 { - return Err("Runtime Error: Can only initialize single-qubit kets for now.".to_string()); + return Err( + "Runtime Error: Can only initialize single-qubit kets for now.".to_string(), + ); } let mut state = HashMap::new(); match ket_str { "0" => state.insert(0, (1.0, 0.0)), // |0> "1" => state.insert(1, (1.0, 0.0)), // |1> - _ => return Err(format!("Runtime Error: Invalid single-qubit ket state '{}'.", ket_str)), + _ => { + return Err(format!( + "Runtime Error: Invalid single-qubit ket state '{}'.", + ket_str + )) + } }; Ok(state) } - + // --- Math helpers --- fn complex_mul((a, b): (f64, f64), (c, d): (f64, f64)) -> (f64, f64) { (a * c - b * d, a * d + b * c) } @@ -243,33 +714,41 @@ impl Evaluator { a * a + b * b } - + // --- *** NEW: eval_apply_statement *** --- fn eval_apply_statement( gate_expr_node: &ASTNode, arg_nodes: &[ASTNode], loc: &Loc, env: &Rc>, ) -> Result { - - + // 1. Evaluate all qubit arguments let mut qubit_args = Vec::new(); for arg_node in arg_nodes { qubit_args.push(Self::evaluate(arg_node, env)?); } - + // 2. Evaluate the gate expression let gate_val = Self::eval_gate_expression(gate_expr_node, env)?; let (base_name, is_dagger, num_controls) = match gate_val { - RuntimeValue::Gate { base_name, is_dagger, num_controls } => (base_name, is_dagger, num_controls), - _ => return Err(format!("Runtime Error at {}: Expression is not a valid gate.", loc)), + RuntimeValue::Gate { + base_name, + is_dagger, + num_controls, + } => (base_name, is_dagger, num_controls), + _ => { + return Err(format!( + "Runtime Error at {}: Expression is not a valid gate.", + loc + )) + } }; - + // 3. Extract qubit info (state, indices, size) let mut controls = Vec::new(); let mut targets = Vec::new(); - + // --- THIS TYPE HAS CHANGED --- let mut state_rc: Option>>> = None; let mut reg_size: Option = None; @@ -281,9 +760,16 @@ impl Evaluator { for (i, qubit_val) in qubit_args.iter().enumerate() { let (q_state_rc, q_index, q_size) = match qubit_val { - + // This now matches on the sparse state RuntimeValue::Qubit { state, index, size } => (state.clone(), *index, *size), - _ => return Err(format!("Runtime Error at {}: Gate arguments must be Qubits, but argument {} was {}.", loc, i+1, qubit_val.type_name())), + _ => { + return Err(format!( + "Runtime Error at {}: Gate arguments must be Qubits, but argument {} was {}.", + loc, + i + 1, + qubit_val.type_name() + )) + } }; if i == 0 { @@ -302,11 +788,11 @@ impl Evaluator { } } - + // 4. Get the parameters let mut params = Vec::new(); Self::extract_gate_params(gate_expr_node, &mut params, env)?; - + // 5. Build the GateDefinition let gate_def = GateDefinition { name: base_name, params, @@ -316,110 +802,125 @@ impl Evaluator { state_rc: state_rc.unwrap(), }; - + // 6. Apply the gate Self::apply_multi_controlled_gate(gate_def, is_dagger) } - fn eval_gate_expression( node: &ASTNode, - env: &Rc> + env: &Rc>, ) -> Result { match node { - ASTNode::Gate { name, loc: _ } => { - Ok(RuntimeValue::Gate { - base_name: name.to_lowercase(), - is_dagger: false, - num_controls: 0, - }) - } - - ASTNode::ParameterizedGate { name, .. } => { - Ok(RuntimeValue::Gate { - base_name: name.to_lowercase(), - is_dagger: false, - num_controls: 0, - }) - } + ASTNode::Gate { name, loc: _ } => Ok(RuntimeValue::Gate { + base_name: name.to_lowercase(), + is_dagger: false, + num_controls: 0, + }), + + ASTNode::ParameterizedGate { name, .. } => Ok(RuntimeValue::Gate { + base_name: name.to_lowercase(), + is_dagger: false, + num_controls: 0, + }), ASTNode::Dagger { gate_expr, .. } => { let inner_gate = Self::eval_gate_expression(gate_expr, env)?; match inner_gate { - RuntimeValue::Gate { base_name, is_dagger, num_controls } => { + RuntimeValue::Gate { + base_name, + is_dagger, + num_controls, + } => { Ok(RuntimeValue::Gate { base_name, - is_dagger: !is_dagger, + is_dagger: !is_dagger, // Flip the flag num_controls, }) } - _ => Err("Internal Error: 'dagger' did not receive a valid gate.".to_string()) + _ => Err("Internal Error: 'dagger' did not receive a valid gate.".to_string()), } } ASTNode::Controlled { gate_expr, .. } => { let inner_gate = Self::eval_gate_expression(gate_expr, env)?; match inner_gate { - RuntimeValue::Gate { base_name, is_dagger, num_controls } => { + RuntimeValue::Gate { + base_name, + is_dagger, + num_controls, + } => { Ok(RuntimeValue::Gate { base_name, is_dagger, - num_controls: num_controls + 1, + num_controls: num_controls + 1, // Add one control }) } - _ => Err("Internal Error: 'controlled' did not receive a valid gate.".to_string()) + _ => { + Err("Internal Error: 'controlled' did not receive a valid gate." + .to_string()) + } } } - _ => Err("Internal Error: Invalid ASTNode passed to eval_gate_expression.".to_string()) + _ => Err("Internal Error: Invalid ASTNode passed to eval_gate_expression.".to_string()), } } - fn extract_gate_params( node: &ASTNode, params: &mut Vec, - env: &Rc> + env: &Rc>, ) -> Result<(), String> { match node { - ASTNode::Gate { name, .. } => { - match name.to_lowercase().as_str() { - "rx" | "ry" | "rz" | "cphase" | "u" => { - return Err(format!("Runtime Error: Parameterized gate '{}' must be called as a function (e.g., RX(theta)).", name)); - } - _ => Ok(()) + ASTNode::Gate { name, .. } => match name.to_lowercase().as_str() { + "rx" | "ry" | "rz" | "cphase" | "u" => { + return Err(format!("Runtime Error: Parameterized gate '{}' must be called as a function (e.g., RX(theta)).", name)); } + _ => Ok(()), + }, + ASTNode::ParameterizedGate { + parameters: param_exprs, + .. + } => { + for param_expr in param_exprs { + let param_val = Self::evaluate(param_expr, env)?; + let float_param = match param_val { + RuntimeValue::Float(f) => f, + RuntimeValue::Int(i) => i as f64, + _ => { + return Err(format!( + "Runtime Error: Gate parameter must be a number, got {:?}", + param_val.type_name() + )) + } + }; + params.push(float_param); + } + Ok(()) } - ASTNode::ParameterizedGate { parameters: param_exprs, .. } => { - for param_expr in param_exprs { - let param_val = Self::evaluate(param_expr, env)?; - let float_param = match param_val { - RuntimeValue::Float(f) => f, - RuntimeValue::Int(i) => i as f64, - _ => return Err(format!("Runtime Error: Gate parameter must be a number, got {:?}", param_val.type_name())) - }; - params.push(float_param); - } - Ok(()) - } - ASTNode::Dagger { gate_expr, .. } => { - Self::extract_gate_params(gate_expr, params, env) - } + ASTNode::Dagger { gate_expr, .. } => Self::extract_gate_params(gate_expr, params, env), ASTNode::Controlled { gate_expr, .. } => { Self::extract_gate_params(gate_expr, params, env) } - ASTNode::FunctionCall { callee, arguments, loc, .. } => { - + ASTNode::FunctionCall { + callee, + arguments, + loc, + .. + } => { + // The callee must be a base Gate name (like 'rx' or 'cphase') let gate_name = match &**callee { ASTNode::Identifier { name, .. } => name.to_lowercase(), _ => return Err(format!("Runtime Error at {}: Gate expression must be a simple identifier inside the call.", loc)), }; - + // --- Extract the numerical parameters only --- for arg_node in arguments.iter() { let arg_val = Self::evaluate(arg_node, env)?; let float_param = match arg_val { RuntimeValue::Float(f) => f, RuntimeValue::Int(i) => i as f64, _ => { - + // Only Qubit arguments should remain after the numerical ones + // We stop processing if it's not a number. break; } }; @@ -428,19 +929,16 @@ impl Evaluator { Ok(()) } - - _ => Ok(()) + _ => Ok(()), } } - - + // --- *** NEW: apply_multi_controlled_gate *** --- pub fn apply_multi_controlled_gate( gate: GateDefinition, - is_dagger: bool + is_dagger: bool, ) -> Result { - - + // Check for native 2-qubit gates let is_native_2qubit = matches!( gate.name.as_str(), "cphase" | "cnot" | "cz" | "cx" | "cy" | "swap" @@ -459,17 +957,25 @@ impl Evaluator { } for &target_idx in &gate.targets { if control_idx == target_idx { - return Err("Runtime Error: Control and target qubits must be different.".to_string()); + return Err( + "Runtime Error: Control and target qubits must be different.".to_string(), + ); } } control_mask |= 1 << control_idx; } if gate.targets.is_empty() { - return Err(format!("Runtime Error: Gate '{}' must have at least 1 target qubit.", gate.name)); + return Err(format!( + "Runtime Error: Gate '{}' must have at least 1 target qubit.", + gate.name + )); } if gate.targets.len() > 1 { - return Err("Runtime Error: Multi-target gates with additional controls are not yet supported.".to_string()); + return Err( + "Runtime Error: Multi-target gates with additional controls are not yet supported." + .to_string(), + ); } let target_idx = gate.targets[0]; @@ -479,62 +985,61 @@ impl Evaluator { let old_state_map = &*state_map_guard; let mut new_state_map = HashMap::new(); - + // We need to keep track of states we've already processed let mut processed = std::collections::HashSet::new(); - + // Iterate over the non-zero amplitudes for (&basis_state, &_tuple) in old_state_map.iter() { if processed.contains(&basis_state) { continue; } - + // Check if control bits are active if (basis_state & control_mask) == control_mask { - - - - + // Find the partner state by flipping the target bit let partner_state = basis_state ^ target_mask; + // Get amplitudes for both self and partner let amp_self_tuple = amp_tuple; - let amp_partner_tuple = old_state_map.get(&partner_state).cloned().unwrap_or((0.0, 0.0)); + let amp_partner_tuple = old_state_map + .get(&partner_state) + .cloned() + .unwrap_or((0.0, 0.0)); let amp_self = C64::new(amp_self_tuple.0, amp_self_tuple.1); let amp_partner = C64::new(amp_partner_tuple.0, amp_partner_tuple.1); let (amp0, amp1, idx0, idx1); - + // Determine if self is |0> or |1> if (basis_state & target_mask) == 0 { - + // Self is |0> amp0 = amp_self; amp1 = amp_partner; idx0 = basis_state; idx1 = partner_state; } else { - + // Self is |1> amp0 = amp_partner; amp1 = amp_self; idx0 = partner_state; idx1 = basis_state; } - + // Apply the 2x2 matrix let new_amp0 = matrix[0][0] * amp0 + matrix[0][1] * amp1; let new_amp1 = matrix[1][0] * amp0 + matrix[1][1] * amp1; - + // Insert into the new map only if non-zero Self::insert_if_nonzero(&mut new_state_map, idx0, new_amp0); Self::insert_if_nonzero(&mut new_state_map, idx1, new_amp1); - + // Mark both states as processed processed.insert(idx0); processed.insert(idx1); - - - } else { - + // Control bits are not active, so this state is unchanged + // We just need to copy it new_state_map.insert(basis_state, amp_tuple); processed.insert(basis_state); } @@ -546,11 +1051,13 @@ impl Evaluator { fn apply_native_2qubit_gate( gate: GateDefinition, - is_dagger: bool + is_dagger: bool, ) -> Result { - if gate.targets.len() != 2 { - return Err(format!("Runtime Error: Gate '{}' requires exactly 2 qubits.", gate.name)); + return Err(format!( + "Runtime Error: Gate '{}' requires exactly 2 qubits.", + gate.name + )); } let control_idx = gate.targets[0]; @@ -571,9 +1078,9 @@ impl Evaluator { let angle = if is_dagger { -phi } else { phi }; let phase_factor = C64::from_polar(1.0, angle); - + // This is an in-place modification for (&basis_state, amp_tuple) in state_map_guard.iter_mut() { - + // Only apply phase when both control and target are |1> if (basis_state & control_mask) != 0 && (basis_state & target_mask) != 0 { let amp = C64::new(amp_tuple.0, amp_tuple.1); let new_amp = amp * phase_factor; @@ -588,11 +1095,11 @@ impl Evaluator { let bit_b = (basis_state & target_mask) != 0; if bit_a != bit_b { - + // If bits differ, swap them let swapped_idx = basis_state ^ control_mask ^ target_mask; new_state_map.insert(swapped_idx, amp_tuple); } else { - + // Bits are the same, state is unchanged new_state_map.insert(basis_state, amp_tuple); } } @@ -601,101 +1108,125 @@ impl Evaluator { "cnot" | "cx" => { let mut new_state_map = HashMap::new(); for (&basis_state, &_tuple) in state_map_guard.iter() { - + // If control is |1> if (basis_state & control_mask) != 0 { - let swapped_idx = basis_state ^ target_mask; + let swapped_idx = basis_state ^ target_mask; // Flip target bit new_state_map.insert(swapped_idx, amp_tuple); } else { - + // Control is |0>, state is unchanged new_state_map.insert(basis_state, amp_tuple); } } *state_map_guard = new_state_map; } "cz" => { - + // In-place modification for (&basis_state, amp_tuple) in state_map_guard.iter_mut() { - + // If both are |1>, apply -1 phase if (basis_state & control_mask) != 0 && (basis_state & target_mask) != 0 { *amp_tuple = (-amp_tuple.0, -amp_tuple.1); } } } _ => { - return Err(format!("Runtime Error: Native 2-qubit gate '{}' not implemented.", gate.name)); + return Err(format!( + "Runtime Error: Native 2-qubit gate '{}' not implemented.", + gate.name + )); } } Ok(RuntimeValue::None) } - + // --- *** NEW: get_gate_matrix *** --- fn get_gate_matrix( name: &str, - params: &[f64], - is_dagger: bool + params: &[f64], // This now receives the parameters + is_dagger: bool, ) -> Result<[[C64; 2]; 2], String> { - let i = C64::new(0.0, 1.0); - let eff_dagger = if is_dagger { -1.0 } else { 1.0 }; + let eff_dagger = if is_dagger { -1.0 } else { 1.0 }; // Used for RZ, S, T, CPhase match name { - - "x" | "not" | "cnot" | "ccx" | "toffoli" => { - Ok([ [C64::new(0.0, 0.0), C64::new(1.0, 0.0)], - [C64::new(1.0, 0.0), C64::new(0.0, 0.0)] ]) - } - "y" => Ok([ [C64::new(0.0, 0.0), -i], [i, C64::new(0.0, 0.0)] ]), - "z" | "cz" => { - Ok([ [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], - [C64::new(0.0, 0.0), C64::new(-1.0, 0.0)] ]) - }, + // --- Standard Non-Parameterized Gates (Unchanged) --- + "x" | "not" | "cnot" | "ccx" | "toffoli" => Ok([ + [C64::new(0.0, 0.0), C64::new(1.0, 0.0)], + [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], + ]), + "y" => Ok([[C64::new(0.0, 0.0), -i], [i, C64::new(0.0, 0.0)]]), + "z" | "cz" => Ok([ + [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], + [C64::new(0.0, 0.0), C64::new(-1.0, 0.0)], + ]), "h" | "hadamard" => { let v = 1.0 / std::f64::consts::SQRT_2; - Ok([ [C64::new(v, 0.0), C64::new(v, 0.0)], - [C64::new(v, 0.0), C64::new(-v, 0.0)] ]) - }, + Ok([ + [C64::new(v, 0.0), C64::new(v, 0.0)], + [C64::new(v, 0.0), C64::new(-v, 0.0)], + ]) + } "s" | "cs" => { let angle = std::f64::consts::FRAC_PI_2 * eff_dagger; - Ok([ [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], - [C64::new(0.0, 0.0), C64::from_polar(1.0, angle)] ]) - }, + Ok([ + [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], + [C64::new(0.0, 0.0), C64::from_polar(1.0, angle)], + ]) + } "t" | "ct" => { let angle = std::f64::consts::FRAC_PI_4 * eff_dagger; - Ok([ [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], - [C64::new(0.0, 0.0), C64::from_polar(1.0, angle)] ]) - }, - + Ok([ + [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], + [C64::new(0.0, 0.0), C64::from_polar(1.0, angle)], + ]) + } + // --- *** PARAMETERIZED GATE LOGIC (NEW) *** --- "rx" => { let theta = params.get(0).cloned().unwrap_or(0.0) * eff_dagger; let t_2 = theta / 2.0; let (c, s) = (t_2.cos(), t_2.sin()); - Ok([ [C64::new(c, 0.0), C64::new(0.0, -s)], - [C64::new(0.0, -s), C64::new(c, 0.0)] ]) + Ok([ + [C64::new(c, 0.0), C64::new(0.0, -s)], + [C64::new(0.0, -s), C64::new(c, 0.0)], + ]) } "ry" => { let theta = params.get(0).cloned().unwrap_or(0.0) * eff_dagger; let t_2 = theta / 2.0; let (c, s) = (t_2.cos(), t_2.sin()); - Ok([ [C64::new(c, 0.0), C64::new(-s, 0.0)], - [C64::new(s, 0.0), C64::new(c, 0.0)] ]) + Ok([ + [C64::new(c, 0.0), C64::new(-s, 0.0)], + [C64::new(s, 0.0), C64::new(c, 0.0)], + ]) } "rz" => { let theta = params.get(0).cloned().unwrap_or(0.0) * eff_dagger; let t_2 = theta / 2.0; - Ok([ [C64::from_polar(1.0, -t_2), C64::new(0.0, 0.0)], - [C64::new(0.0, 0.0), C64::from_polar(1.0, t_2)] ]) + Ok([ + [C64::from_polar(1.0, -t_2), C64::new(0.0, 0.0)], + [C64::new(0.0, 0.0), C64::from_polar(1.0, t_2)], + ]) } "cphase" => { let phi = params.get(0).cloned().unwrap_or(0.0) * eff_dagger; - - Ok([ [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], - [C64::new(0.0, 0.0), C64::from_polar(1.0, phi)] ]) + // CPhase logic is applied outside the 2x2 matrix for controlled gates, + // but since we are using apply_multi_controlled, the base gate is an I gate + // with a phase applied to the |1> component. + Ok([ + [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], + [C64::new(0.0, 0.0), C64::from_polar(1.0, phi)], + ]) } "u" => { - if params.len() < 3 { return Err("U gate requires theta, phi, lambda.".to_string()); } - let (t, p, l) = (params[0] * eff_dagger, params[1] * eff_dagger, params[2] * eff_dagger); + if params.len() < 3 { + return Err("U gate requires theta, phi, lambda.".to_string()); + } + let (t, p, l) = ( + params[0] * eff_dagger, + params[1] * eff_dagger, + params[2] * eff_dagger, + ); let t_2 = t / 2.0; let c00 = C64::new(t_2.cos(), 0.0); @@ -703,49 +1234,57 @@ impl Evaluator { let c10 = C64::from_polar(t_2.sin(), p); let c11 = C64::from_polar(t_2.cos(), p + l); - Ok([ [c00, c01], [c10, c11] ]) + Ok([[c00, c01], [c10, c11]]) } - - - _ => Err(format!("Runtime Error: Unknown gate name '{}' for matrix generation.", name)) + // --- *** END PARAMETERIZED LOGIC *** --- + _ => Err(format!( + "Runtime Error: Unknown gate name '{}' for matrix generation.", + name + )), } } - fn eval_measure( target_expr: &Box, env: &Rc>, ) -> Result { - let target_val = Self::evaluate(target_expr, env)?; let (state_rc, target_index, reg_size) = match target_val { - + // This now gets the sparse state RuntimeValue::Qubit { state, index, size } => (state, index, size), - _ => return Err(format!("Runtime Error: 'measure' can only be used on a single Qubit, got {}.", target_val.type_name())), + _ => { + return Err(format!( + "Runtime Error: 'measure' can only be used on a single Qubit, got {}.", + target_val.type_name() + )) + } }; Self::perform_measurement(&state_rc, target_index, reg_size) } fn perform_measurement( - state_rc: &Rc>>, + state_rc: &Rc>>, // <-- TYPE CHANGED target_index: usize, total_size: usize, ) -> Result { - let mut state_map_guard = state_rc.borrow_mut(); let old_state_map = &*state_map_guard; if target_index >= total_size { - return Err(format!("Runtime Error: Qubit index {} is out of bounds for size {}.", target_index, total_size)); + return Err(format!( + "Runtime Error: Qubit index {} is out of bounds for size {}.", + target_index, total_size + )); } let target_mask = 1 << target_index; let mut prob0 = 0.0; - + // Iterate over the sparse map to calculate probability for (&basis_state, &_tuple) in old_state_map.iter() { if (basis_state & target_mask) == 0 { + // If target bit is 0 prob0 += amp_tuple.0.powi(2) + amp_tuple.1.powi(2); } } @@ -763,29 +1302,36 @@ impl Evaluator { probability_of_outcome = 1.0 - prob0; } - let norm_factor = if probability_of_outcome.abs() < 1e-9 { 1.0 } else { 1.0 / probability_of_outcome.sqrt() }; + let norm_factor = if probability_of_outcome.abs() < 1e-9 { + 1.0 + } else { + 1.0 / probability_of_outcome.sqrt() + }; let mut new_state_map = HashMap::new(); - + // Iterate and build the new sparse state for (&basis_state, &_tuple) in old_state_map.iter() { let bit_at_index = (basis_state >> target_index) & 1; - + // If this state matches the measurement, keep it if bit_at_index as i64 == measured_result { let (real, imag) = amp_tuple; let normalized_amp = Self::complex_scalar_mul((real, imag), norm_factor); new_state_map.insert(basis_state, normalized_amp); } - + // If it doesn't match, we drop it (by not inserting it) } - + // Replace the old state with the new collapsed state *state_map_guard = new_state_map; Ok(RuntimeValue::Int(measured_result)) } + // --- + // --- Other Builtins / Helpers (Restored) + // --- fn eval_array_access( collection_expr: &Box, @@ -793,7 +1339,6 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - let collection_val = Self::evaluate(collection_expr, env)?; let index_val = Self::evaluate(index_expr, env)?; @@ -801,15 +1346,33 @@ impl Evaluator { RuntimeValue::Register(elements) => { let index = match index_val { RuntimeValue::Int(i) => i as usize, - _ => return Err(format!("Runtime Error at {}: Array index must be an integer.", loc)), + _ => { + return Err(format!( + "Runtime Error at {}: Array index must be an integer.", + loc + )) + } }; - elements.get(index).map(|e| e.borrow().clone()).ok_or(format!("Runtime Error at {}: Array index {} out of bounds for array of size {}.", loc, index, elements.len())) - } - + elements + .get(index) + .map(|e| e.borrow().clone()) + .ok_or(format!( + "Runtime Error at {}: Array index {} out of bounds for array of size {}.", + loc, + index, + elements.len() + )) + } + // --- UPDATED LOGIC --- RuntimeValue::QuantumRegister { size, state } => { let index = match index_val { RuntimeValue::Int(i) => i, - _ => return Err(format!("Runtime Error at {}: Qubit index must be an integer.", loc)), + _ => { + return Err(format!( + "Runtime Error at {}: Qubit index must be an integer.", + loc + )) + } }; if index < 0 || index as usize >= size { return Err(format!( @@ -818,184 +1381,222 @@ impl Evaluator { )); } Ok(RuntimeValue::Qubit { - state: state.clone(), + state: state.clone(), // Pass the sparse state Rc index: index as usize, - size, + size, // Pass the total register size }) } - + // --- END UPDATE --- RuntimeValue::Dict(map) => { let key = Self::value_to_string_key(index_val)?; - map.get(&key).map(|v| v.borrow().clone()).ok_or(format!("Runtime Error at {}: Key '{}' not found in dictionary.", loc, key)) + map.get(&key).map(|v| v.borrow().clone()).ok_or(format!( + "Runtime Error at {}: Key '{}' not found in dictionary.", + loc, key + )) } _ => Err(format!( "Runtime Error at {}: Subscript '[]' is not supported for type {:?}", - loc, collection_val.type_name() + loc, + collection_val.type_name() )), } } fn eval_member_access( - object_expr: &ASTNode, - member: &str, - env: &Rc>, -) -> Result { - let object = Self::evaluate(object_expr, env)?; - match object { - - RuntimeValue::Module(module_env) => { - - match module_env.borrow().get(member) { - Some(value_rc) => Ok(value_rc.borrow().clone()), - None => Err(format!( - "Runtime Error: Module does not have a member named '{}'", - member - )) + object_expr: &ASTNode, + member: &str, + env: &Rc>, + ) -> Result { + let object = Self::evaluate(object_expr, env)?; + match object { + // *** NEW: Handle Module member access *** + RuntimeValue::Module(module_env) => { + // Look up the member in the module's environment + match module_env.borrow().get(member) { + Some(value_rc) => Ok(value_rc.borrow().clone()), + None => Err(format!( + "Runtime Error: Module does not have a member named '{}'", + member + )), + } + } + RuntimeValue::Instance { fields, .. } => { + match fields.get(member) { + Some(value_rc) => Ok(value_rc.borrow().clone()), + None => Err(format!("Runtime Error: Instance has no field named '{}'", member)) + } } - } - RuntimeValue::QuantumRegister { size, .. } => { - match member { + RuntimeValue::QuantumRegister { size, .. } => match member { "length" => Ok(RuntimeValue::Int(size as i64)), - _ => Err(format!("Runtime Error: QuantumRegister does not have a member named '{}'", member)), - } - } - RuntimeValue::Register(rc_register) => { - match member { + _ => Err(format!( + "Runtime Error: QuantumRegister does not have a member named '{}'", + member + )), + }, + RuntimeValue::Register(rc_register) => match member { "length" => Ok(RuntimeValue::Int(rc_register.len() as i64)), - _ => Err(format!("Runtime Error: Array does not have a member named '{}'", member)), - } - } - RuntimeValue::String(s) => { - match member { + _ => Err(format!( + "Runtime Error: Array does not have a member named '{}'", + member + )), + }, + RuntimeValue::String(s) => match member { "length" => Ok(RuntimeValue::Int(s.len() as i64)), - _ => Err(format!("Runtime Error: String does not have a member named '{}'", member)), - } - } - RuntimeValue::Dict(map) => { - map.get(member).map(|v| v.borrow().clone()).ok_or(format!("Runtime Error: Value does not have a member named '{}'", member)) + _ => Err(format!( + "Runtime Error: String does not have a member named '{}'", + member + )), + }, + RuntimeValue::Dict(map) => map.get(member).map(|v| v.borrow().clone()).ok_or(format!( + "Runtime Error: Value does not have a member named '{}'", + member + )), + _ => Err(format!( + "Runtime Error: Member access (.) is not supported for type {:?}", + object.type_name() + )), } - _ => Err(format!("Runtime Error: Member access (.) is not supported for type {:?}", object.type_name())), } -} - - fn insert_if_nonzero( - state_map: &mut HashMap, - index: usize, - amp: C64, -) { - - if amp.norm_sqr() > 1e-12 { - state_map.insert(index, (amp.re, amp.im)); + fn insert_if_nonzero(state_map: &mut HashMap, index: usize, amp: C64) { + // Use a small epsilon to avoid floating point issues + if amp.norm_sqr() > 1e-12 { + state_map.insert(index, (amp.re, amp.im)); + } } -} - - fn eval_range_expression( - start_expr: &Box, - end_expr: &Box, - inclusive: bool, - env: &Rc>, -) -> Result { - let start_val = Self::evaluate(start_expr, env)?; - let end_val = Self::evaluate(end_expr, env)?; - match (start_val, end_val) { - (RuntimeValue::Int(start), RuntimeValue::Int(end)) => { - let range: Vec = if inclusive { (start..=end).collect() } else { (start..end).collect() }; - Ok(RuntimeValue::Range(range)) - } - _ => Err("Runtime Error: Range boundaries must be integers.".to_string()), + start_expr: &Box, + end_expr: &Box, + inclusive: bool, + env: &Rc>, + ) -> Result { + let start_val = Self::evaluate(start_expr, env)?; + let end_val = Self::evaluate(end_expr, env)?; + match (start_val, end_val) { + (RuntimeValue::Int(start), RuntimeValue::Int(end)) => { + let range: Vec = if inclusive { + (start..=end).collect() + } else { + (start..end).collect() + }; + Ok(RuntimeValue::Range(range)) + } + _ => Err("Runtime Error: Range boundaries must be integers.".to_string()), + } } -} - - fn eval_from_import( - path: &ImportPath, - spec: &ImportSpec, - env: &Rc>, -) -> Result { - let file_path = Self::resolve_import_path(path)?; - let source = fs::read_to_string(&file_path).map_err(|e| format!("Runtime Error: Failed to import file '{}': {}", file_path, e))?; - let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize().map_err(|e| format!("Import Lexer Error: {}", e))?; - let mut parser = Parser::new(tokens); - let ast = parser.parse().map_err(|e| format!("Import Parser Error: {}", e))?; - let module_env = Rc::new(RefCell::new(Environment::new())); - Self::evaluate_program(&ast, &module_env)?; - let module_store = module_env.borrow().get_store_clone(); - match spec { - ImportSpec::List(names) => { - for name in names { - match module_store.get(name) { - Some(value_rc) => env.borrow_mut().set(name.clone(), value_rc.borrow().clone()), - None => return Err(format!("Runtime Error: Cannot import name '{}' from file '{}'.", name, file_path)), - } - } - } - ImportSpec::All => { - for (name, value_rc) in module_store.iter() { - env.borrow_mut().set(name.clone(), value_rc.borrow().clone()); - } - } - } - Ok(RuntimeValue::None) -} + path: &ImportPath, + spec: &ImportSpec, + env: &Rc>, + ) -> Result { + let file_path = Self::resolve_import_path(path)?; + let source = fs::read_to_string(&file_path).map_err(|e| { + format!( + "Runtime Error: Failed to import file '{}': {}", + file_path, e + ) + })?; + let mut lexer = Lexer::new(&source); + let tokens = lexer + .tokenize() + .map_err(|e| format!("Import Lexer Error: {}", e))?; + let mut parser = Parser::new(tokens); + let ast = parser + .parse() + .map_err(|e| format!("Import Parser Error: {}", e))?; + let module_env = Rc::new(RefCell::new(Environment::new())); + Self::evaluate_program(&ast, &module_env)?; + let module_store = module_env.borrow().get_store_clone(); + match spec { + ImportSpec::List(names) => { + for name in names { + match module_store.get(name) { + Some(value_rc) => env + .borrow_mut() + .set(name.clone(), value_rc.borrow().clone()), + None => { + return Err(format!( + "Runtime Error: Cannot import name '{}' from file '{}'.", + name, file_path + )) + } + } + } + } + ImportSpec::All => { + for (name, value_rc) in module_store.iter() { + env.borrow_mut() + .set(name.clone(), value_rc.borrow().clone()); + } + } + } + Ok(RuntimeValue::None) + } fn eval_try_catch( - try_block: &ASTNode, - error_variable: &Option, - catch_block: &ASTNode, - env: &Rc>, -) -> Result { - match Self::evaluate(try_block, env) { - Ok(value) => Ok(value), - Err(error_message) => { - let mut catch_env = Environment::new_enclosed(env.clone()); - if let Some(var_name) = error_variable { - catch_env.set(var_name.clone(), RuntimeValue::String(error_message)); - } - let catch_env_rc = Rc::new(RefCell::new(catch_env)); - Self::evaluate(catch_block, &catch_env_rc) + try_block: &ASTNode, + error_variable: &Option, + catch_block: &ASTNode, + env: &Rc>, + ) -> Result { + match Self::evaluate(try_block, env) { + Ok(value) => Ok(value), + Err(error_message) => { + let mut catch_env = Environment::new_enclosed(env.clone()); + if let Some(var_name) = error_variable { + catch_env.set(var_name.clone(), RuntimeValue::String(error_message)); + } + let catch_env_rc = Rc::new(RefCell::new(catch_env)); + Self::evaluate(catch_block, &catch_env_rc) + } } } -} fn eval_for_statement( - variable_name: &str, - iterator_expr: &Box, - body: &Box, - env: &Rc>, -) -> Result { - let iterator_val = Self::evaluate(iterator_expr, env)?; - let iterable: Vec = match iterator_val { - RuntimeValue::Range(range_vec) => range_vec.into_iter().map(RuntimeValue::Int).collect(), - RuntimeValue::Register(elements) => elements.iter().map(|rc_cell| rc_cell.borrow().clone()).collect(), - _ => return Err(format!("Runtime Error: For loop iterator must be a range or an array, not {:?}.", iterator_val.type_name())), - }; - for item in iterable { - env.borrow_mut().set(variable_name.to_string(), item); - let body_result = Self::evaluate(body, env)?; - match body_result { - RuntimeValue::Break => break, - RuntimeValue::Continue => continue, - RuntimeValue::ReturnValue(val) => return Ok(RuntimeValue::ReturnValue(val)), - _ => {} + variable_name: &str, + iterator_expr: &Box, + body: &Box, + env: &Rc>, + ) -> Result { + let iterator_val = Self::evaluate(iterator_expr, env)?; + let iterable: Vec = match iterator_val { + RuntimeValue::Range(range_vec) => { + range_vec.into_iter().map(RuntimeValue::Int).collect() + } + RuntimeValue::Register(elements) => elements + .iter() + .map(|rc_cell| rc_cell.borrow().clone()) + .collect(), + _ => { + return Err(format!( + "Runtime Error: For loop iterator must be a range or an array, not {:?}.", + iterator_val.type_name() + )) + } + }; + for item in iterable { + env.borrow_mut().set(variable_name.to_string(), item); + let body_result = Self::evaluate(body, env)?; + match body_result { + RuntimeValue::Break => break, + RuntimeValue::Continue => continue, + RuntimeValue::ReturnValue(val) => return Ok(RuntimeValue::ReturnValue(val)), + _ => {} + } } + Ok(RuntimeValue::None) } - Ok(RuntimeValue::None) -} fn eval_function_call( callee_expr: &ASTNode, arguments: &[ASTNode], loc: &Loc, env: &Rc>, - is_dagger: bool + is_dagger: bool, ) -> Result { - let evaluated_args = Self::eval_arguments(arguments, env)?; let function = Self::evaluate(callee_expr, env)?; let name = format!("{:?}", callee_expr); @@ -1003,7 +1604,10 @@ impl Evaluator { match function { RuntimeValue::BuiltinFunction(func_name) => { if is_dagger { - return Err(format!("Runtime Error at {}: Dagger is not supported for built-in function '{}'.", loc, func_name)); + return Err(format!( + "Runtime Error at {}: Dagger is not supported for built-in function '{}'.", + loc, func_name + )); } match func_name.as_str() { @@ -1018,16 +1622,37 @@ impl Evaluator { "len" => Self::builtin_len(evaluated_args), "debug_state" => Self::builtin_debug_state(evaluated_args), "assert" => Self::builtin_assert(evaluated_args), - _ => Err(format!("Runtime Error at {}: Unknown built-in function '{}'.", loc, func_name)), + "_graphics_create_canvas" => Self::builtin_graphics_create_canvas(evaluated_args), + "_graphics_set_background" => Self::builtin_graphics_set_background(evaluated_args), + "_graphics_draw_rect" => Self::builtin_graphics_draw_rect(evaluated_args), + "_graphics_draw_circle" => Self::builtin_graphics_draw_circle(evaluated_args), + "_graphics_draw_line" => Self::builtin_graphics_draw_line(evaluated_args), + "_graphics_save_svg" => Self::builtin_graphics_save_svg(evaluated_args), + "_graphics_destroy_canvas" => Ok(RuntimeValue::None), + "_graphics_create_plot" => Self::builtin_graphics_create_plot(evaluated_args), + "_graphics_plot_set_data" => Self::builtin_graphics_plot_set_data(evaluated_args), + "_graphics_plot_set_title" => Self::builtin_graphics_plot_set_title(evaluated_args), + "_graphics_plot_render" => Self::builtin_graphics_plot_render(evaluated_args), + "_graphics_destroy_plot" => Self::builtin_graphics_destroy_plot(evaluated_args), + _ => Err(format!( + "Runtime Error at {}: Unknown built-in function '{}'.", + loc, func_name + )), } } - RuntimeValue::Function { parameters, body, env: func_env } => { - + RuntimeValue::Function { + parameters, + body, + env: func_env, + } => { let mut function_scope = Environment::new_enclosed(func_env); if parameters.len() != evaluated_args.len() { return Err(format!( "Runtime Error at {}: Function '{}' expected {} arguments, but got {}.", - loc, name, parameters.len(), evaluated_args.len() + loc, + name, + parameters.len(), + evaluated_args.len() )); } for (param, arg_val) in parameters.iter().zip(evaluated_args) { @@ -1037,7 +1662,12 @@ impl Evaluator { let statements = match &*body { ASTNode::Block(stmts) => stmts, - _ => return Err(format!("Internal Error: Function body for '{}' is not a Block.", name)), + _ => { + return Err(format!( + "Internal Error: Function body for '{}' is not a Block.", + name + )) + } }; let result = if is_dagger { @@ -1052,67 +1682,148 @@ impl Evaluator { Ok(result) } } - _ => Err(format!("Runtime Error at {}: '{}' is not a callable function.", loc, name)), + + RuntimeValue::Class { name, fields, methods, constructor, .. } => { + // 1. Initialize instance fields & Capture Initial Values + let mut instance_fields = HashMap::new(); + let mut initial_values = HashMap::new(); // <--- Capture Initials + + for field in &fields { + let value = if let Some(default_expr) = &field.default_value { + Self::evaluate(default_expr, env)? + } else { + RuntimeValue::None + }; + initial_values.insert(field.name.clone(), value.clone()); + instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); + } + + // 2. Create the instance + let mut instance = RuntimeValue::Instance { + class_name: name.clone(), + fields: instance_fields.clone(), + methods: methods.clone(), + }; + + // 3. Call constructor 'init' + if let Some(constructor_node) = constructor { + if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { + if parameters.len() != evaluated_args.len() { + return Err(format!("Runtime Error: Constructor arg mismatch.")); + } + + let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); + + // A. Inject 'self' + constructor_env.borrow_mut().set("self".to_string(), instance.clone()); + + // B. Inject Implicit Fields (as local vars) + for (name, val) in &initial_values { + constructor_env.borrow_mut().set(name.clone(), val.clone()); + } + + // C. Inject Parameters + for (param, arg_value) in parameters.iter().zip(evaluated_args) { + constructor_env.borrow_mut().set(param.name.clone(), arg_value); + } + + // D. Execute Body + Self::evaluate(&body, &constructor_env)?; + + // E. SMART SYNC: Only update fields if local var CHANGED + for (field_name, field_rc) in &instance_fields { + if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { + let local_val = local_val_rc.borrow().clone(); + + if let Some(initial_val) = initial_values.get(field_name) { + // Only overwrite if value actually changed (e.g. None -> 0) + if !Self::are_values_equal(&local_val, initial_val) { + *field_rc.borrow_mut() = local_val; + } + } + } + } + + // Return updated instance + instance = RuntimeValue::Instance { + class_name: name.clone(), + fields: instance_fields, + methods, + }; + } + } + Ok(instance) + } + _ => Err(format!( + "Runtime Error at {}: '{}' is not a callable function.", + loc, name + )), } } fn eval_dict_literal( - pairs: &Vec<(ASTNode, ASTNode)>, - env: &Rc>, -) -> Result { - let mut map = HashMap::new(); - for (key_node, value_node) in pairs { - let key_str = match key_node { - ASTNode::Identifier { name, .. } => name.clone(), - ASTNode::StringLiteral(s) => s.clone(), - _ => { - let key_val = Self::evaluate(key_node, env)?; - Self::value_to_string_key(key_val)? - } - }; - let value_val = Self::evaluate(value_node, env)?; - map.insert(key_str, Rc::new(RefCell::new(value_val))); + pairs: &Vec<(ASTNode, ASTNode)>, + env: &Rc>, + ) -> Result { + let mut map = HashMap::new(); + for (key_node, value_node) in pairs { + let key_str = match key_node { + ASTNode::Identifier { name, .. } => name.clone(), + ASTNode::StringLiteral(s) => s.clone(), + _ => { + let key_val = Self::evaluate(key_node, env)?; + Self::value_to_string_key(key_val)? + } + }; + let value_val = Self::evaluate(value_node, env)?; + map.insert(key_str, Rc::new(RefCell::new(value_val))); + } + Ok(RuntimeValue::Dict(map)) } - Ok(RuntimeValue::Dict(map)) -} fn resolve_import_path(path: &ImportPath) -> Result { - match path { - ImportPath::File(file_path) => Ok(file_path.clone()), - ImportPath::Module(segments) => { - let mut pbuf = PathBuf::new(); - for segment in segments { pbuf.push(segment); } - pbuf.set_extension("qc"); - pbuf.to_str().map(|s| s.to_string()).ok_or("Runtime Error: Invalid non-UTF8 module path.".to_string()) + match path { + ImportPath::File(file_path) => Ok(file_path.clone()), + ImportPath::Module(segments) => { + let mut pbuf = PathBuf::new(); + for segment in segments { + pbuf.push(segment); + } + pbuf.set_extension("qc"); + pbuf.to_str() + .map(|s| s.to_string()) + .ok_or("Runtime Error: Invalid non-UTF8 module path.".to_string()) + } } } -} fn eval_daggered_block( - statements: &Vec, - env: &Rc> -) -> Result { - - let mut last_result = RuntimeValue::None; - - for stmt in statements.iter().rev() { - let flipped_node = match stmt { - ASTNode::Apply { gate_expr, arguments, loc } => { - let daggered_gate_expr = match &**gate_expr { + statements: &Vec, + env: &Rc>, + ) -> Result { + let mut last_result = RuntimeValue::None; - ASTNode::Gate { name, loc, .. } => { - ASTNode::Dagger { - gate_expr: Box::new(ASTNode::Gate { name: name.clone(), loc: *loc }), + for stmt in statements.iter().rev() { + let flipped_node = match stmt { + ASTNode::Apply { + gate_expr, + arguments, + loc, + } => { + let daggered_gate_expr = match &**gate_expr { + // Simple gate: X -> dagger(X) + ASTNode::Gate { name, loc, .. } => ASTNode::Dagger { + gate_expr: Box::new(ASTNode::Gate { + name: name.clone(), + loc: *loc, + }), loc: *loc, - } - } - - ASTNode::Dagger { gate_expr, .. } => { - *gate_expr.clone() - } - - ASTNode::Controlled { gate_expr, loc } => { - let inner_gate = match &**gate_expr { + }, + // Already daggered: dagger(X) -> X + ASTNode::Dagger { gate_expr, .. } => *gate_expr.clone(), + // Controlled gate: controlled(X) -> controlled(dagger(X)) + ASTNode::Controlled { gate_expr, loc } => { + let inner_gate = match &**gate_expr { ASTNode::Gate { name, loc, .. } => { ASTNode::Dagger { gate_expr: Box::new(ASTNode::Gate { name: name.clone(), loc: *loc }), @@ -1134,64 +1845,76 @@ impl Evaluator { } _ => return Err("Daggering nested complex gate expressions is not yet supported.".to_string()) }; - ASTNode::Controlled { - gate_expr: Box::new(inner_gate), - loc: *loc, + ASTNode::Controlled { + gate_expr: Box::new(inner_gate), + loc: *loc, + } } - } - - ASTNode::ParameterizedGate { name, parameters, loc } => { - ASTNode::Dagger { + // *** NEW: Parameterized gate: RX(theta) -> dagger(RX(theta)) *** + ASTNode::ParameterizedGate { + name, + parameters, + loc, + } => ASTNode::Dagger { gate_expr: Box::new(ASTNode::ParameterizedGate { name: name.clone(), parameters: parameters.clone(), - loc: *loc + loc: *loc, }), - loc: *loc + loc: *loc, + }, + _ => { + return Err("Daggering complex gate expressions is not yet supported." + .to_string()) } - } - _ => return Err("Daggering complex gate expressions is not yet supported.".to_string()) - }; + }; - ASTNode::Apply { - gate_expr: Box::new(daggered_gate_expr), - arguments: arguments.clone(), - loc: *loc, + ASTNode::Apply { + gate_expr: Box::new(daggered_gate_expr), + arguments: arguments.clone(), + loc: *loc, + } } - } - ASTNode::FunctionCall { callee, arguments, loc, is_dagger: original_is_dagger } => { ASTNode::FunctionCall { + callee, + arguments, + loc, + is_dagger: original_is_dagger, + } => ASTNode::FunctionCall { callee: callee.clone(), arguments: arguments.clone(), loc: *loc, is_dagger: !*original_is_dagger, - } - } - _ => stmt.clone(), - }; + }, + _ => stmt.clone(), + }; - last_result = Self::evaluate(&flipped_node, env)?; + last_result = Self::evaluate(&flipped_node, env)?; - match last_result { - RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => { - return Ok(last_result); + match last_result { + RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => { + return Ok(last_result); + } + _ => {} } - _ => {} } + Ok(last_result) } - Ok(last_result) -} - + // --- (Rest of builtins) --- pub fn print_quantum_state( state: &Rc>>, size: usize, - max_entries: usize + max_entries: usize, ) { let num_qubits = size; let amplitudes_map = state.borrow(); - println!("--- Quantum State ({} qubits, {} non-zero amplitudes) ---", num_qubits, amplitudes_map.len()); + println!( + "--- Quantum State ({} qubits, {} non-zero amplitudes) ---", + num_qubits, + amplitudes_map.len() + ); let mut sorted_keys: Vec = amplitudes_map.keys().cloned().collect(); sorted_keys.sort(); @@ -1209,19 +1932,24 @@ impl Evaluator { } fn builtin_debug_state(args: Vec) -> Result { if args.len() != 1 { - return Err("Runtime Error: 'debug_state' expects exactly 1 argument (a quantum register).".to_string()); + return Err( + "Runtime Error: 'debug_state' expects exactly 1 argument (a quantum register)." + .to_string(), + ); } match &args[0] { RuntimeValue::QuantumRegister { size, state } => { - - Self::print_quantum_state(state, *size, 10); + // Call the new, shared print function + Self::print_quantum_state(state, *size, 10); // 10 is max_entries Ok(RuntimeValue::None) } - _ => Err(format!("Runtime Error: 'debug_state' argument must be a quantum register, got {}.", args[0].type_name())), + _ => Err(format!( + "Runtime Error: 'debug_state' argument must be a quantum register, got {}.", + args[0].type_name() + )), } } - pub fn builtin_measure(args: Vec) -> Result { if args.len() != 1 { return Err("Runtime Error: 'measure' expects exactly one qubit argument.".to_string()); @@ -1231,46 +1959,64 @@ impl Evaluator { let (state_rc, target_index, reg_size) = match target_val { RuntimeValue::Qubit { state, index, size } => (state, index, size), - _ => return Err(format!("Runtime Error: 'measure' can only be used on a single Qubit, got {}.", target_val.type_name())), + _ => { + return Err(format!( + "Runtime Error: 'measure' can only be used on a single Qubit, got {}.", + target_val.type_name() + )) + } }; - + // This calls the core logic inside the Evaluator Self::perform_measurement(&state_rc, target_index, reg_size) } fn builtin_assert(args: Vec) -> Result { - if args.len() != 2 { - return Err("Runtime Error: 'assert' expects 2 arguments (condition, message).".to_string()); - } - - let condition = match &args[0] { - RuntimeValue::Bool(b) => *b, - _ => return Err(format!("Runtime Error: 'assert' argument 1 must be a Bool, got {}.", args[0].type_name())), - }; - - let message = match &args[1] { - RuntimeValue::String(s) => s.clone(), - _ => return Err(format!("Runtime Error: 'assert' argument 2 must be a String, got {}.", args[1].type_name())), - }; + if args.len() != 2 { + return Err( + "Runtime Error: 'assert' expects 2 arguments (condition, message).".to_string(), + ); + } - if condition { + let condition = match &args[0] { + RuntimeValue::Bool(b) => *b, + _ => { + return Err(format!( + "Runtime Error: 'assert' argument 1 must be a Bool, got {}.", + args[0].type_name() + )) + } + }; - Ok(RuntimeValue::None) - } else { + let message = match &args[1] { + RuntimeValue::String(s) => s.clone(), + _ => { + return Err(format!( + "Runtime Error: 'assert' argument 2 must be a String, got {}.", + args[1].type_name() + )) + } + }; - Err(format!("Assertion Failed: {}", message)) + if condition { + // Assertion passed, do nothing. + Ok(RuntimeValue::None) + } else { + // Assertion failed. Return an error, which will stop execution. + Err(format!("Assertion Failed: {}", message)) + } } -} fn builtin_print(args: Vec) -> Result { - let output: Vec = args.into_iter() + let output: Vec = args + .into_iter() .map(|val| { match val { - RuntimeValue::String(s) => s, + RuntimeValue::String(s) => s, // Just the string content RuntimeValue::Int(n) => n.to_string(), RuntimeValue::Float(f) => f.to_string(), RuntimeValue::Bool(b) => b.to_string(), RuntimeValue::None => "None".to_string(), - _ => format!("{}", val), + _ => format!("{}", val), // Uses Display trait } }) .collect(); @@ -1279,97 +2025,152 @@ impl Evaluator { } fn builtin_to_int(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'to_int' expects exactly one argument.".to_string()); } + if args.len() != 1 { + return Err("Runtime Error: 'to_int' expects exactly one argument.".to_string()); + } let val = &args[0]; match val { RuntimeValue::Int(i) => Ok(RuntimeValue::Int(*i)), RuntimeValue::Float(f) => Ok(RuntimeValue::Int(*f as i64)), - RuntimeValue::String(s) => s.parse::().map(RuntimeValue::Int).map_err(|_| format!("Runtime Error: Could not parse string '{}' as int.", s)), + RuntimeValue::String(s) => s + .parse::() + .map(RuntimeValue::Int) + .map_err(|_| format!("Runtime Error: Could not parse string '{}' as int.", s)), RuntimeValue::Bool(b) => Ok(RuntimeValue::Int(if *b { 1 } else { 0 })), - _ => Err(format!("Runtime Error: Cannot convert type {} to int.", val.type_name())), + _ => Err(format!( + "Runtime Error: Cannot convert type {} to int.", + val.type_name() + )), } } fn builtin_len(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'len' expects exactly one argument.".to_string()); } + if args.len() != 1 { + return Err("Runtime Error: 'len' expects exactly one argument.".to_string()); + } let val = &args[0]; match val { RuntimeValue::String(s) => Ok(RuntimeValue::Int(s.len() as i64)), RuntimeValue::Register(arr) => Ok(RuntimeValue::Int(arr.len() as i64)), RuntimeValue::Dict(map) => Ok(RuntimeValue::Int(map.len() as i64)), RuntimeValue::QuantumRegister { size, .. } => Ok(RuntimeValue::Int(*size as i64)), - _ => Err(format!("Runtime Error: len() is not supported for type {}.", val.type_name())), + _ => Err(format!( + "Runtime Error: len() is not supported for type {}.", + val.type_name() + )), } } fn builtin_to_float(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'to_float' expects exactly one argument.".to_string()); } + if args.len() != 1 { + return Err("Runtime Error: 'to_float' expects exactly one argument.".to_string()); + } let val = &args[0]; match val { RuntimeValue::Int(i) => Ok(RuntimeValue::Float(*i as f64)), RuntimeValue::Float(f) => Ok(RuntimeValue::Float(*f)), - RuntimeValue::String(s) => s.parse::().map(RuntimeValue::Float).map_err(|_| format!("Runtime Error: Could not parse string '{}' as float.", s)), - _ => Err(format!("Runtime Error: Cannot convert type {} to float.", val.type_name())), + RuntimeValue::String(s) => s + .parse::() + .map(RuntimeValue::Float) + .map_err(|_| format!("Runtime Error: Could not parse string '{}' as float.", s)), + _ => Err(format!( + "Runtime Error: Cannot convert type {} to float.", + val.type_name() + )), } } fn builtin_echo(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'echo' expects exactly one argument.".to_string()); } - let value=args.into_iter().next().unwrap(); + if args.len() != 1 { + return Err("Runtime Error: 'echo' expects exactly one argument.".to_string()); + } + let value = args.into_iter().next().unwrap(); println!("{}", value); Ok(value) } fn builtin_type_of(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'type_of' expects exactly one argument.".to_string()); } + if args.len() != 1 { + return Err("Runtime Error: 'type_of' expects exactly one argument.".to_string()); + } Ok(RuntimeValue::String(args[0].type_name().to_string())) } fn builtin_to_string(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'to_string' expects exactly one argument.".to_string()); } + if args.len() != 1 { + return Err("Runtime Error: 'to_string' expects exactly one argument.".to_string()); + } Ok(RuntimeValue::String(args[0].to_string())) } fn builtin_maybe(args: Vec) -> Result { - if args.len() != 2 { return Err("Runtime Error: 'maybe' expects two arguments: (value, confidence).".to_string()); } + if args.len() != 2 { + return Err( + "Runtime Error: 'maybe' expects two arguments: (value, confidence).".to_string(), + ); + } let value = args.clone().into_iter().next().unwrap(); let confidence_val = args.into_iter().nth(1).unwrap(); let confidence = match confidence_val { RuntimeValue::Float(p) => { - if !(0.0..=1.0).contains(&p) { return Err("Runtime Error: Confidence must be between 0.0 and 1.0.".to_string()); } + if !(0.0..=1.0).contains(&p) { + return Err( + "Runtime Error: Confidence must be between 0.0 and 1.0.".to_string() + ); + } p - }, + } _ => return Err("Runtime Error: Confidence (arg 2) must be a Float.".to_string()), }; - Ok(RuntimeValue::Probabilistic { value: Box::new(value), confidence }) + Ok(RuntimeValue::Probabilistic { + value: Box::new(value), + confidence, + }) } fn builtin_sample(args: Vec) -> Result { - if args.len() != 1 { return Err("Runtime Error: 'sample' expects exactly one argument.".to_string()); } + if args.len() != 1 { + return Err("Runtime Error: 'sample' expects exactly one argument.".to_string()); + } let prob_value = args.into_iter().next().unwrap(); match prob_value { RuntimeValue::Probabilistic { value, confidence } => { - if rand::thread_rng().gen::() < confidence { Ok(*value) } else { Ok(RuntimeValue::None) } + if rand::thread_rng().gen::() < confidence { + Ok(*value) + } else { + Ok(RuntimeValue::None) + } } other_value => Ok(other_value), } } - - fn eval_let_declaration(name: &str, value_expr: &ASTNode, env: &Rc>) -> Result { + // (Restored eval_let_declaration, etc.) + fn eval_let_declaration( + name: &str, + value_expr: &ASTNode, + env: &Rc>, + ) -> Result { let value = Self::evaluate(value_expr, env)?; env.borrow_mut().set(name.to_string(), value); Ok(RuntimeValue::None) } - fn eval_assignment(target: &ASTNode, value_expr: &ASTNode, env: &Rc>) -> Result { + fn eval_assignment( + target: &ASTNode, + value_expr: &ASTNode, + env: &Rc>, + ) -> Result { let new_value = Self::evaluate(value_expr, env)?; match target { - + // Simple identifier assignment ASTNode::Identifier { name, .. } => { if let Some(var_rc) = env.borrow().get(name) { *std::cell::RefCell::<_>::borrow_mut(&var_rc) = new_value; Ok(RuntimeValue::None) } else { - Err(format!("Runtime Error: Cannot assign to undefined variable '{}'.", name)) + Err(format!( + "Runtime Error: Cannot assign to undefined variable '{}'.", + name + )) } } - + // Subscript assignment: dict["key"] = value or array[0] = value ASTNode::ArrayAccess { array, index, loc } => { let collection_val = Self::evaluate(array, env)?; let index_val = Self::evaluate(index, env)?; @@ -1383,7 +2184,12 @@ impl Evaluator { RuntimeValue::Register(elements) => { let index = match index_val { RuntimeValue::Int(i) => i as usize, - _ => return Err(format!("Runtime Error at {}: Array index must be an integer.", loc)), + _ => { + return Err(format!( + "Runtime Error at {}: Array index must be an integer.", + loc + )) + } }; if index >= elements.len() { @@ -1393,107 +2199,153 @@ impl Evaluator { *elements[index].borrow_mut() = new_value; Ok(RuntimeValue::None) } - _ => Err(format!("Runtime Error at {}: Cannot perform subscript assignment on type {:?}", loc, collection_val.type_name())) + _ => Err(format!( + "Runtime Error at {}: Cannot perform subscript assignment on type {:?}", + loc, + collection_val.type_name() + )), } } - _ => Err("Runtime Error: Assignment target must be an identifier or subscript expression.".to_string()) + ASTNode::MemberAccess { object, member } => { + // Evaluate the object (e.g., 'self' or 'canvas') + let object_val = Self::evaluate(object, env)?; + + match object_val { + RuntimeValue::Instance { fields, .. } => { + if let Some(field_rc) = fields.get(member) { + // Update the field value + *field_rc.borrow_mut() = new_value; + Ok(RuntimeValue::None) + } else { + // Field doesn't exist on the instance + Err(format!("Runtime Error: Instance has no field named '{}'.", member)) + } + } + _ => Err(format!( + "Runtime Error: Cannot assign to member '{}' of non-instance type {}.", + member, object_val.type_name() + )), + } + } + + _ => Err( + "Runtime Error: Assignment target must be an identifier or subscript expression." + .to_string(), + ), } } - fn eval_identifier(name: &str, loc: &Loc, env: &Rc>) -> Result { + fn eval_identifier( + name: &str, + loc: &Loc, + env: &Rc>, + ) -> Result { if let Some(val_rc) = env.borrow().get(name) { Ok(val_rc.borrow().clone()) } else { - Err(format!("Runtime Error at {}: Undefined variable '{}'", loc, name)) + Err(format!( + "Runtime Error at {}: Undefined variable '{}'", + loc, name + )) } } - - - fn eval_import_statement( path: &ImportPath, alias: &str, - env: &Rc> + env: &Rc>, ) -> Result { - - + // --- THIS IS THE NEW LOGIC --- let file_path = match path { ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - + // It's a file path f.clone() } else { - + // It's a package name format!("q_packages/{}/init.qc", f) } } - ImportPath::Module(m) => { - m.join("/") + ".qc" - } + ImportPath::Module(m) => m.join("/") + ".qc", }; - - - let source = fs::read_to_string(&file_path) - .map_err(|e| format!("Runtime Error: Failed to read module '{}': {}", file_path, e))?; + // The rest of the function is the same, just using 'file_path' + let source = fs::read_to_string(&file_path).map_err(|e| { + format!( + "Runtime Error: Failed to read module '{}': {}", + file_path, e + ) + })?; let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize().map_err(|e| format!("Module Lexer Error: {}", e))?; + let tokens = lexer + .tokenize() + .map_err(|e| format!("Module Lexer Error: {}", e))?; let mut parser = Parser::new(tokens); - let ast = parser.parse().map_err(|e| format!("Module Parser Error: {}", e))?; - + let ast = parser + .parse() + .map_err(|e| format!("Module Parser Error: {}", e))?; + // Each module gets its own new, clean environment let module_env = Rc::new(RefCell::new(Environment::new())); + Self::register_builtins(&module_env); Self::evaluate_program(&ast, &module_env)?; - + // Create a Module runtime value let module_val = RuntimeValue::Module(module_env); - + // Set the alias in the *current* environment env.borrow_mut().set(alias.to_string(), module_val); Ok(RuntimeValue::None) } fn eval_if_statement( - condition: &Box, - then_block: &Box, - elif_blocks: &Vec<(ASTNode, ASTNode)>, - else_block: &Option>, - env: &Rc>, -) -> Result { - let cond_val = Self::evaluate(condition, env)?; - if Self::is_truthy(&cond_val) { - return Self::evaluate(then_block, env); - } - for (elif_cond_node, elif_body_node) in elif_blocks { - let elif_cond_val = Self::evaluate(elif_cond_node, env)?; - if Self::is_truthy(&elif_cond_val) { - return Self::evaluate(elif_body_node, env); - } - } - if let Some(else_body) = else_block { - return Self::evaluate(else_body, env); - } - Ok(RuntimeValue::None) -} + condition: &Box, + then_block: &Box, + elif_blocks: &Vec<(ASTNode, ASTNode)>, + else_block: &Option>, + env: &Rc>, + ) -> Result { + let cond_val = Self::evaluate(condition, env)?; + if Self::is_truthy(&cond_val) { + return Self::evaluate(then_block, env); + } + for (elif_cond_node, elif_body_node) in elif_blocks { + let elif_cond_val = Self::evaluate(elif_cond_node, env)?; + if Self::is_truthy(&elif_cond_val) { + return Self::evaluate(elif_body_node, env); + } + } + if let Some(else_body) = else_block { + return Self::evaluate(else_body, env); + } + Ok(RuntimeValue::None) + } - fn eval_block(statements: &Vec, env: &Rc>) -> Result { - let mut last_result = RuntimeValue::None; - for stmt in statements { - last_result = Self::evaluate(stmt, env)?; - match last_result { - RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => return Ok(last_result), - _ => {} + fn eval_block( + statements: &Vec, + env: &Rc>, + ) -> Result { + let mut last_result = RuntimeValue::None; + for stmt in statements { + last_result = Self::evaluate(stmt, env)?; + match last_result { + RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => { + return Ok(last_result) + } + _ => {} + } } + Ok(last_result) } - Ok(last_result) -} - fn eval_arguments(args: &[ASTNode], env: &Rc>) -> Result, String> { + fn eval_arguments( + args: &[ASTNode], + env: &Rc>, + ) -> Result, String> { let mut evaluated_args = Vec::new(); for arg_expr in args { let arg_value = Self::evaluate(arg_expr, env)?; @@ -1503,31 +2355,33 @@ impl Evaluator { } fn value_to_string_key(value: RuntimeValue) -> Result { - match value { + match value { RuntimeValue::String(s) => Ok(s), RuntimeValue::Int(i) => Ok(i.to_string()), RuntimeValue::Bool(b) => Ok(b.to_string()), _ => Err(format!("Runtime Error: Invalid key type for dictionary. Must be String, Int, or Bool. Found {:?}", value.type_name())), } -} + } fn eval_while_statement( - condition: &Box, - body: &Box, - env: &Rc>, -) -> Result { - loop { - let cond_val = Self::evaluate(condition, env)?; - if !Self::is_truthy(&cond_val) { break; } - let body_result = Self::evaluate(body, env)?; - match body_result { - RuntimeValue::Break => break, - RuntimeValue::Continue => continue, - RuntimeValue::ReturnValue(val) => return Ok(RuntimeValue::ReturnValue(val)), - _ => {} + condition: &Box, + body: &Box, + env: &Rc>, + ) -> Result { + loop { + let cond_val = Self::evaluate(condition, env)?; + if !Self::is_truthy(&cond_val) { + break; + } + let body_result = Self::evaluate(body, env)?; + match body_result { + RuntimeValue::Break => break, + RuntimeValue::Continue => continue, + RuntimeValue::ReturnValue(val) => return Ok(RuntimeValue::ReturnValue(val)), + _ => {} + } } + Ok(RuntimeValue::None) } - Ok(RuntimeValue::None) -} fn eval_binary_op( operator: &crate::parser::ast::BinaryOperator, @@ -1539,13 +2393,17 @@ impl Evaluator { match operator { BinaryOperator::And => { let left_val = Self::evaluate(left_expr, env)?; - if !Self::is_truthy(&left_val) { return Ok(RuntimeValue::Bool(false)); } + if !Self::is_truthy(&left_val) { + return Ok(RuntimeValue::Bool(false)); + } let right_val = Self::evaluate(right_expr, env)?; return Ok(RuntimeValue::Bool(Self::is_truthy(&right_val))); } BinaryOperator::Or => { let left_val = Self::evaluate(left_expr, env)?; - if Self::is_truthy(&left_val) { return Ok(RuntimeValue::Bool(true)); } + if Self::is_truthy(&left_val) { + return Ok(RuntimeValue::Bool(true)); + } let right_val = Self::evaluate(right_expr, env)?; return Ok(RuntimeValue::Bool(Self::is_truthy(&right_val))); } @@ -1554,21 +2412,49 @@ impl Evaluator { let left_val = Self::evaluate(left_expr, env)?; let right_val = Self::evaluate(right_expr, env)?; match (left_val.clone(), right_val.clone()) { - (RuntimeValue::Probabilistic { value: v1, confidence: c1 }, RuntimeValue::Probabilistic { value: v2, confidence: c2 }) => { + ( + RuntimeValue::Probabilistic { + value: v1, + confidence: c1, + }, + RuntimeValue::Probabilistic { + value: v2, + confidence: c2, + }, + ) => { let inner_result = Self::eval_binary_op_runtime(operator, *v1, *v2, loc, env)?; - Ok(RuntimeValue::Probabilistic { value: Box::new(inner_result), confidence: c1 * c2 }) - }, - (RuntimeValue::Probabilistic { value: v1, confidence: c1 }, r_val) => { + Ok(RuntimeValue::Probabilistic { + value: Box::new(inner_result), + confidence: c1 * c2, + }) + } + ( + RuntimeValue::Probabilistic { + value: v1, + confidence: c1, + }, + r_val, + ) => { let inner_result = Self::eval_binary_op_runtime(operator, *v1, r_val, loc, env)?; - Ok(RuntimeValue::Probabilistic { value: Box::new(inner_result), confidence: c1 }) - }, - (l_val, RuntimeValue::Probabilistic { value: v2, confidence: c2 }) => { + Ok(RuntimeValue::Probabilistic { + value: Box::new(inner_result), + confidence: c1, + }) + } + ( + l_val, + RuntimeValue::Probabilistic { + value: v2, + confidence: c2, + }, + ) => { let inner_result = Self::eval_binary_op_runtime(operator, l_val, *v2, loc, env)?; - Ok(RuntimeValue::Probabilistic { value: Box::new(inner_result), confidence: c2 }) - }, - (l_val, r_val) => { - Self::eval_binary_op_runtime(operator, l_val, r_val, loc, env) + Ok(RuntimeValue::Probabilistic { + value: Box::new(inner_result), + confidence: c2, + }) } + (l_val, r_val) => Self::eval_binary_op_runtime(operator, l_val, r_val, loc, env), } } @@ -1576,47 +2462,73 @@ impl Evaluator { operator: &crate::parser::ast::BinaryOperator, left_val: RuntimeValue, right_val: RuntimeValue, - loc:&Loc, + loc: &Loc, _env: &Rc>, ) -> Result { use crate::parser::ast::BinaryOperator::*; - + // --- SPARSE TENSOR PRODUCT --- if matches!(operator, TensorProduct) { match (left_val.clone(), right_val.clone()) { - (RuntimeValue::QuantumRegister { size: size_a, state: state_a_rc }, RuntimeValue::QuantumRegister { size: size_b, state: state_b_rc }) => { + ( + RuntimeValue::QuantumRegister { + size: size_a, + state: state_a_rc, + }, + RuntimeValue::QuantumRegister { + size: size_b, + state: state_b_rc, + }, + ) => { let state_a = state_a_rc.borrow(); let state_b = state_b_rc.borrow(); let new_size = size_a + size_b; let mut new_state = HashMap::new(); - + // Iterate over non-zero amplitudes of A for (&i, &_a) in state_a.iter() { - + // Iterate over non-zero amplitudes of B for (&j, &_b) in state_b.iter() { let new_index = (i << size_b) | j; let new_amp_tuple = Self::complex_mul(amp_a, amp_b); - + // Check for zero is implicit, as amp_a and amp_b are non-zero new_state.insert(new_index, new_amp_tuple); } } - return Ok(RuntimeValue::QuantumRegister { size: new_size, state: Rc::new(RefCell::new(new_state)) }); + return Ok(RuntimeValue::QuantumRegister { + size: new_size, + state: Rc::new(RefCell::new(new_state)), + }); + } + (l, r) => { + return Err(format!( + "Runtime Error at {}: Operator {:?} not defined for types {:?} and {:?}", + loc, + operator, + l.type_name(), + r.type_name() + )) } - (l, r) => return Err(format!("Runtime Error at {}: Operator {:?} not defined for types {:?} and {:?}", loc, operator, l.type_name(), r.type_name())), } } - + // --- END SPARSE TENSOR PRODUCT --- match operator { Equal => { let result = match (&left_val, &right_val) { (RuntimeValue::Int(l), RuntimeValue::Int(r)) => l == r, - (RuntimeValue::Float(l), RuntimeValue::Float(r)) => (l - r).abs() < f64::EPSILON, - (RuntimeValue::Float(l), RuntimeValue::Int(r)) => (l - (*r as f64)).abs() < f64::EPSILON, - (RuntimeValue::Int(l), RuntimeValue::Float(r)) => ((*l as f64) - r).abs() < f64::EPSILON, + (RuntimeValue::Float(l), RuntimeValue::Float(r)) => { + (l - r).abs() < f64::EPSILON + } + (RuntimeValue::Float(l), RuntimeValue::Int(r)) => { + (l - (*r as f64)).abs() < f64::EPSILON + } + (RuntimeValue::Int(l), RuntimeValue::Float(r)) => { + ((*l as f64) - r).abs() < f64::EPSILON + } (RuntimeValue::String(l), RuntimeValue::String(r)) => l == r, (RuntimeValue::Bool(l), RuntimeValue::Bool(r)) => l == r, (RuntimeValue::None, RuntimeValue::None) => true, @@ -1627,9 +2539,15 @@ impl Evaluator { NotEqual => { let result = match (&left_val, &right_val) { (RuntimeValue::Int(l), RuntimeValue::Int(r)) => l != r, - (RuntimeValue::Float(l), RuntimeValue::Float(r)) => (l - r).abs() > f64::EPSILON, - (RuntimeValue::Float(l), RuntimeValue::Int(r)) => (l - (*r as f64)).abs() > f64::EPSILON, - (RuntimeValue::Int(l), RuntimeValue::Float(r)) => ((*l as f64) - r).abs() > f64::EPSILON, + (RuntimeValue::Float(l), RuntimeValue::Float(r)) => { + (l - r).abs() > f64::EPSILON + } + (RuntimeValue::Float(l), RuntimeValue::Int(r)) => { + (l - (*r as f64)).abs() > f64::EPSILON + } + (RuntimeValue::Int(l), RuntimeValue::Float(r)) => { + ((*l as f64) - r).abs() > f64::EPSILON + } (RuntimeValue::String(l), RuntimeValue::String(r)) => l != r, (RuntimeValue::Bool(l), RuntimeValue::Bool(r)) => l != r, (RuntimeValue::None, RuntimeValue::None) => false, @@ -1637,69 +2555,360 @@ impl Evaluator { }; return Ok(RuntimeValue::Bool(result)); } - Power => { - match (left_val, right_val) { - (RuntimeValue::Int(base), RuntimeValue::Int(exp)) if exp >= 0 => { - match base.checked_pow(exp as u32) { - Some(result) => Ok(RuntimeValue::Int(result)), - None => Err(format!("Runtime Error at {}: Integer overflow in power operation", loc)) - } - } - (RuntimeValue::Int(base), RuntimeValue::Int(exp)) if exp < 0 => { - Ok(RuntimeValue::Float((base as f64).powf(exp as f64))) - } - (RuntimeValue::Float(base), RuntimeValue::Float(exp)) => { - Ok(RuntimeValue::Float(base.powf(exp))) - } - (RuntimeValue::Int(base), RuntimeValue::Float(exp)) => { - Ok(RuntimeValue::Float((base as f64).powf(exp))) - } - (RuntimeValue::Float(base), RuntimeValue::Int(exp)) => { - Ok(RuntimeValue::Float(base.powf(exp as f64))) - } - (l, r) => { - Err(format!( - "Runtime Error at {}: Power operator (^) not defined for types {:?} and {:?}", - loc, l.type_name(), r.type_name() - )) + Power => match (left_val, right_val) { + (RuntimeValue::Int(base), RuntimeValue::Int(exp)) if exp >= 0 => { + match base.checked_pow(exp as u32) { + Some(result) => Ok(RuntimeValue::Int(result)), + None => Err(format!( + "Runtime Error at {}: Integer overflow in power operation", + loc + )), } } - } + (RuntimeValue::Int(base), RuntimeValue::Int(exp)) if exp < 0 => { + Ok(RuntimeValue::Float((base as f64).powf(exp as f64))) + } + (RuntimeValue::Float(base), RuntimeValue::Float(exp)) => { + Ok(RuntimeValue::Float(base.powf(exp))) + } + (RuntimeValue::Int(base), RuntimeValue::Float(exp)) => { + Ok(RuntimeValue::Float((base as f64).powf(exp))) + } + (RuntimeValue::Float(base), RuntimeValue::Int(exp)) => { + Ok(RuntimeValue::Float(base.powf(exp as f64))) + } + (l, r) => Err(format!( + "Runtime Error at {}: Power operator (^) not defined for types {:?} and {:?}", + loc, + l.type_name(), + r.type_name() + )), + }, _ => { - + // (This block is for Add, Sub, Mul, Div, Mod, etc.) let (op, l, r) = match (operator, left_val, right_val) { - (Div, RuntimeValue::Int(l), RuntimeValue::Int(r)) => (operator, RuntimeValue::Float(l as f64), RuntimeValue::Float(r as f64)), - (op, RuntimeValue::Int(l), RuntimeValue::Float(r)) if matches!(op, Add | Sub | Mul | Div | Less | Greater | LessEqual | GreaterEqual) => (op, RuntimeValue::Float(l as f64), RuntimeValue::Float(r)), - (op, RuntimeValue::Float(l), RuntimeValue::Int(r)) if matches!(op, Add | Sub | Mul | Div | Less | Greater | LessEqual | GreaterEqual) => (op, RuntimeValue::Float(l), RuntimeValue::Float(r as f64)), + (Div, RuntimeValue::Int(l), RuntimeValue::Int(r)) => ( + operator, + RuntimeValue::Float(l as f64), + RuntimeValue::Float(r as f64), + ), + (op, RuntimeValue::Int(l), RuntimeValue::Float(r)) + if matches!( + op, + Add | Sub | Mul | Div | Less | Greater | LessEqual | GreaterEqual + ) => + { + (op, RuntimeValue::Float(l as f64), RuntimeValue::Float(r)) + } + (op, RuntimeValue::Float(l), RuntimeValue::Int(r)) + if matches!( + op, + Add | Sub | Mul | Div | Less | Greater | LessEqual | GreaterEqual + ) => + { + (op, RuntimeValue::Float(l), RuntimeValue::Float(r as f64)) + } (op, l, r) => (op, l, r), }; match (op, l, r) { - (Add, RuntimeValue::Int(l), RuntimeValue::Int(r)) => Ok(RuntimeValue::Int(l + r)), - (Add, RuntimeValue::String(l), RuntimeValue::String(r)) => Ok(RuntimeValue::String(format!("{}{}", l, r))), - (Sub, RuntimeValue::Int(l), RuntimeValue::Int(r)) => Ok(RuntimeValue::Int(l - r)), - (Mul, RuntimeValue::Int(l), RuntimeValue::Int(r)) => Ok(RuntimeValue::Int(l * r)), - (Mod, RuntimeValue::Int(l), RuntimeValue::Int(r)) => Ok(RuntimeValue::Int(l % r)), - (op @ (Add | Sub | Mul | Div), RuntimeValue::Float(l), RuntimeValue::Float(r)) => match op { - Div => if r == 0.0 { Ok(RuntimeValue::Float(f64::NAN)) } else { Ok(RuntimeValue::Float(l / r)) }, + (Add, RuntimeValue::Int(l), RuntimeValue::Int(r)) => { + Ok(RuntimeValue::Int(l + r)) + } + (Add, RuntimeValue::String(l), RuntimeValue::String(r)) => { + Ok(RuntimeValue::String(format!("{}{}", l, r))) + } + (Sub, RuntimeValue::Int(l), RuntimeValue::Int(r)) => { + Ok(RuntimeValue::Int(l - r)) + } + (Mul, RuntimeValue::Int(l), RuntimeValue::Int(r)) => { + Ok(RuntimeValue::Int(l * r)) + } + (Mod, RuntimeValue::Int(l), RuntimeValue::Int(r)) => { + Ok(RuntimeValue::Int(l % r)) + } + ( + op @ (Add | Sub | Mul | Div), + RuntimeValue::Float(l), + RuntimeValue::Float(r), + ) => match op { + Div => { + if r == 0.0 { + Ok(RuntimeValue::Float(f64::NAN)) + } else { + Ok(RuntimeValue::Float(l / r)) + } + } Add => Ok(RuntimeValue::Float(l + r)), Sub => Ok(RuntimeValue::Float(l - r)), Mul => Ok(RuntimeValue::Float(l * r)), _ => unreachable!(), }, - (op @ (Less | Greater | LessEqual | GreaterEqual), RuntimeValue::Int(l), RuntimeValue::Int(r)) => { - let result = match op { Less => l < r, Greater => l > r, LessEqual => l <= r, GreaterEqual => l >= r, _ => unreachable!() }; + ( + op @ (Less | Greater | LessEqual | GreaterEqual), + RuntimeValue::Int(l), + RuntimeValue::Int(r), + ) => { + let result = match op { + Less => l < r, + Greater => l > r, + LessEqual => l <= r, + GreaterEqual => l >= r, + _ => unreachable!(), + }; Ok(RuntimeValue::Bool(result)) - }, - (op @ (Less | Greater | LessEqual | GreaterEqual), RuntimeValue::Float(l), RuntimeValue::Float(r)) => { - let result = match op { Less => l < r, Greater => l > r, LessEqual => l <= r, GreaterEqual => l >= r, _ => unreachable!() }; + } + ( + op @ (Less | Greater | LessEqual | GreaterEqual), + RuntimeValue::Float(l), + RuntimeValue::Float(r), + ) => { + let result = match op { + Less => l < r, + Greater => l > r, + LessEqual => l <= r, + GreaterEqual => l >= r, + _ => unreachable!(), + }; Ok(RuntimeValue::Bool(result)) - }, - (And, RuntimeValue::Bool(l), RuntimeValue::Bool(r)) => Ok(RuntimeValue::Bool(l && r)), - (Or, RuntimeValue::Bool(l), RuntimeValue::Bool(r)) => Ok(RuntimeValue::Bool(l || r)), - (op, l, r) => Err(format!("Runtime Error at {}: Operator {:?} not defined for types {:?} and {:?}", loc, op, l.type_name(), r.type_name())), + } + (And, RuntimeValue::Bool(l), RuntimeValue::Bool(r)) => { + Ok(RuntimeValue::Bool(l && r)) + } + (Or, RuntimeValue::Bool(l), RuntimeValue::Bool(r)) => { + Ok(RuntimeValue::Bool(l || r)) + } + (op, l, r) => Err(format!( + "Runtime Error at {}: Operator {:?} not defined for types {:?} and {:?}", + loc, + op, + l.type_name(), + r.type_name() + )), } } } } + fn builtin_graphics_create_canvas(args: Vec) -> Result { + let width = match args.get(0) { Some(RuntimeValue::Int(i)) => *i as u32, _ => return Err("Width must be Int".to_string()) }; + let height = match args.get(1) { Some(RuntimeValue::Int(i)) => *i as u32, _ => return Err("Height must be Int".to_string()) }; + + let mut id_guard = NEXT_ID.lock().unwrap(); + let id = *id_guard; + *id_guard += 1; + + let canvas = Canvas::new(width, height); + INTERPRETER_CANVASES.lock().unwrap().insert(id, canvas); + + Ok(RuntimeValue::Int(id)) + } + + fn builtin_graphics_set_background(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let r = match args.get(1) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let g = match args.get(2) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let b = match args.get(3) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let a = match args.get(4) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 255 }; + + let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&id) { + canvas.set_background(Color::new(r, g, b, a)); + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_draw_rect(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + + let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; + let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + + let x = to_f(args.get(1)); + let y = to_f(args.get(2)); + let w = to_f(args.get(3)); + let h = to_f(args.get(4)); + let r = to_u8(args.get(5)); + let g = to_u8(args.get(6)); + let b = to_u8(args.get(7)); + let a = to_u8(args.get(8)); + let filled = match args.get(9) { Some(RuntimeValue::Int(i)) => *i != 0, _ => false }; + + let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&id) { + canvas.draw_rectangle(x, y, w, h, Color::new(r, g, b, a), filled); + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_draw_circle(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + + let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; + let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + + let x = to_f(args.get(1)); + let y = to_f(args.get(2)); + let radius = to_f(args.get(3)); + let r = to_u8(args.get(4)); + let g = to_u8(args.get(5)); + let b = to_u8(args.get(6)); + let a = to_u8(args.get(7)); + let filled = match args.get(8) { Some(RuntimeValue::Int(i)) => *i != 0, _ => false }; + let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&id) { + canvas.draw_circle(x, y, radius, Color::new(r, g, b, a), filled); + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_draw_line(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + + let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; + let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + + let x1 = to_f(args.get(1)); + let y1 = to_f(args.get(2)); + let x2 = to_f(args.get(3)); + let y2 = to_f(args.get(4)); + let r = to_u8(args.get(5)); + let g = to_u8(args.get(6)); + let b = to_u8(args.get(7)); + let a = to_u8(args.get(8)); + let width = to_f(args.get(9)) as f32; + + let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&id) { + canvas.draw_line(x1, y1, x2, y2, Color::new(r, g, b, a), width); + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_draw_text(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + + let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; + let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + + let x = to_f(args.get(1)); + let y = to_f(args.get(2)); + let text = match args.get(3) { Some(RuntimeValue::String(s)) => s.clone(), _ => "".to_string() }; + let r = to_u8(args.get(4)); + let g = to_u8(args.get(5)); + let b = to_u8(args.get(6)); + let a = to_u8(args.get(7)); + let size = to_f(args.get(8)) as f32; + + let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&id) { + canvas.draw_text(x, y, text, Color::new(r, g, b, a), size); + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_save_svg(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let filename = match args.get(1) { Some(RuntimeValue::String(s)) => s.clone(), _ => return Err("Filename error".to_string()) }; + + let canvases = INTERPRETER_CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get(&id) { + match canvas.save_svg(&filename) { + Ok(_) => Ok(RuntimeValue::Int(0)), + Err(e) => Err(format!("SVG Save Error: {}", e)), + } + } else { + Err("Canvas ID not found".to_string()) + } + } + + fn builtin_graphics_destroy_canvas(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + INTERPRETER_CANVASES.lock().unwrap().remove(&id); + Ok(RuntimeValue::None) + } + + // --- PLOT FUNCTIONS --- + + fn builtin_graphics_create_plot(args: Vec) -> Result { + let ptype_int = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => 0 }; + let ptype = match ptype_int { + 0 => PlotType::Line, + 1 => PlotType::Scatter, + 2 => PlotType::Bar, + 3 => PlotType::Histogram, + 4 => PlotType::Heatmap, + _ => PlotType::Line, + }; + + let mut id_guard = NEXT_ID.lock().unwrap(); + let id = *id_guard; + *id_guard += 1; + + let plot = Plot::new(ptype); + INTERPRETER_PLOTS.lock().unwrap().insert(id, plot); + Ok(RuntimeValue::Int(id)) + } + + fn builtin_graphics_plot_set_data(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + + let extract_vec = |val: Option<&RuntimeValue>| -> Vec { + match val { + Some(RuntimeValue::Register(arr_rc)) => { + let mut res = Vec::new(); + // Fix: Borrow the Register elements + for item in arr_rc.iter() { + let inner = item.borrow(); + if let RuntimeValue::Float(f) = *inner { res.push(f); } + else if let RuntimeValue::Int(i) = *inner { res.push(i as f64); } + } + res + } + _ => Vec::new() + } + }; + + let x_data = extract_vec(args.get(1)); + let y_data = extract_vec(args.get(2)); + + let mut plots = INTERPRETER_PLOTS.lock().unwrap(); + if let Some(plot) = plots.get_mut(&id) { + plot.set_data(x_data, y_data); + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_plot_set_title(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let title = match args.get(1) { Some(RuntimeValue::String(s)) => s.clone(), _ => "".to_string() }; + + let mut plots = INTERPRETER_PLOTS.lock().unwrap(); + if let Some(plot) = plots.get_mut(&id) { + plot.title = title; + } + Ok(RuntimeValue::None) + } + + fn builtin_graphics_plot_render(args: Vec) -> Result { + let plot_id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let canvas_id = match args.get(1) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + + let plots = INTERPRETER_PLOTS.lock().unwrap(); + let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); + + if let (Some(plot), Some(canvas)) = (plots.get(&plot_id), canvases.get_mut(&canvas_id)) { + match plot.render_to_canvas(canvas) { + Ok(_) => Ok(RuntimeValue::Int(0)), + Err(e) => Err(format!("Plot Render Error: {}", e)), + } + } else { + Err("Plot or Canvas not found".to_string()) + } + } + + fn builtin_graphics_destroy_plot(args: Vec) -> Result { + let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + INTERPRETER_PLOTS.lock().unwrap().remove(&id); + Ok(RuntimeValue::None) + } } diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs new file mode 100644 index 0000000..f6ae264 --- /dev/null +++ b/src/graphics/mod.rs @@ -0,0 +1,434 @@ +// src/graphics/mod.rs + +use std::collections::HashMap; +use serde::{Deserialize, Serialize}; + + +/// Color representation +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +impl Color { + pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + Color { r, g, b, a } + } + + pub fn rgb(r: u8, g: u8, b: u8) -> Self { + Color { r, g, b, a: 255 } + } + + pub fn from_hex(hex: &str) -> Result { + let hex = hex.trim_start_matches('#'); + if hex.len() != 6 && hex.len() != 8 { + return Err("Invalid hex color format".to_string()); + } + + let r = u8::from_str_radix(&hex[0..2], 16).map_err(|e| e.to_string())?; + let g = u8::from_str_radix(&hex[2..4], 16).map_err(|e| e.to_string())?; + let b = u8::from_str_radix(&hex[4..6], 16).map_err(|e| e.to_string())?; + let a = if hex.len() == 8 { + u8::from_str_radix(&hex[6..8], 16).map_err(|e| e.to_string())? + } else { + 255 + }; + + Ok(Color { r, g, b, a }) + } + + // Predefined colors + pub const BLACK: Color = Color { r: 0, g: 0, b: 0, a: 255 }; + pub const WHITE: Color = Color { r: 255, g: 255, b: 255, a: 255 }; + pub const RED: Color = Color { r: 255, g: 0, b: 0, a: 255 }; + pub const GREEN: Color = Color { r: 0, g: 255, b: 0, a: 255 }; + pub const BLUE: Color = Color { r: 0, g: 0, b: 255, a: 255 }; + pub const YELLOW: Color = Color { r: 255, g: 255, b: 0, a: 255 }; + pub const CYAN: Color = Color { r: 0, g: 255, b: 255, a: 255 }; + pub const MAGENTA: Color = Color { r: 255, g: 0, b: 255, a: 255 }; +} + +/// 2D Point +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Point2D { + pub x: f64, + pub y: f64, +} + +/// 3D Point +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Point3D { + pub x: f64, + pub y: f64, + pub z: f64, +} + +/// Graphics Window +pub struct Window { + pub width: u32, + pub height: u32, + pub title: String, + pub background: Color, +} + +impl Window { + pub fn new(width: u32, height: u32, title: String) -> Self { + Window { + width, + height, + title, + background: Color::WHITE, + } + } +} + +/// Shape types +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Shape { + Line { + start: Point2D, + end: Point2D, + color: Color, + width: f32, + }, + Rectangle { + x: f64, + y: f64, + width: f64, + height: f64, + color: Color, + filled: bool, + }, + Circle { + center: Point2D, + radius: f64, + color: Color, + filled: bool, + }, + Polygon { + points: Vec, + color: Color, + filled: bool, + }, + Text { + position: Point2D, + text: String, + color: Color, + size: f32, + }, +} + +/// Canvas for 2D drawing +pub struct Canvas { + pub width: u32, + pub height: u32, + shapes: Vec, + background: Color, +} + +impl Canvas { + pub fn new(width: u32, height: u32) -> Self { + Canvas { + width, + height, + shapes: Vec::new(), + background: Color::WHITE, + } + } + + pub fn set_background(&mut self, color: Color) { + self.background = color; + } + + pub fn clear(&mut self) { + self.shapes.clear(); + } + + pub fn draw_line(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, color: Color, width: f32) { + self.shapes.push(Shape::Line { + start: Point2D { x: x1, y: y1 }, + end: Point2D { x: x2, y: y2 }, + color, + width, + }); + } + + pub fn draw_rectangle(&mut self, x: f64, y: f64, width: f64, height: f64, color: Color, filled: bool) { + self.shapes.push(Shape::Rectangle { + x, y, width, height, color, filled, + }); + } + + pub fn draw_circle(&mut self, x: f64, y: f64, radius: f64, color: Color, filled: bool) { + self.shapes.push(Shape::Circle { + center: Point2D { x, y }, + radius, + color, + filled, + }); + } + + pub fn draw_polygon(&mut self, points: Vec, color: Color, filled: bool) { + self.shapes.push(Shape::Polygon { + points, + color, + filled, + }); + } + + pub fn draw_text(&mut self, x: f64, y: f64, text: String, color: Color, size: f32) { + self.shapes.push(Shape::Text { + position: Point2D { x, y }, + text, + color, + size, + }); + } + + pub fn save_svg(&self, filename: &str) -> Result<(), String> { + use std::fs::File; + use std::io::Write; + + let mut file = File::create(filename).map_err(|e| e.to_string())?; + + writeln!(file, r#""#).map_err(|e| e.to_string())?; + writeln!(file, r#""#, + self.width, self.height).map_err(|e| e.to_string())?; + + // Background + writeln!(file, r#" "#, + self.background.r, self.background.g, self.background.b).map_err(|e| e.to_string())?; + + // Draw shapes + for shape in &self.shapes { + match shape { + Shape::Line { start, end, color, width } => { + writeln!(file, r#" "#, + start.x, start.y, end.x, end.y, color.r, color.g, color.b, width).map_err(|e| e.to_string())?; + } + Shape::Rectangle { x, y, width, height, color, filled } => { + if *filled { + writeln!(file, r#" "#, + x, y, width, height, color.r, color.g, color.b).map_err(|e| e.to_string())?; + } else { + writeln!(file, r#" "#, + x, y, width, height, color.r, color.g, color.b).map_err(|e| e.to_string())?; + } + } + Shape::Circle { center, radius, color, filled } => { + if *filled { + writeln!(file, r#" "#, + center.x, center.y, radius, color.r, color.g, color.b).map_err(|e| e.to_string())?; + } else { + writeln!(file, r#" "#, + center.x, center.y, radius, color.r, color.g, color.b).map_err(|e| e.to_string())?; + } + } + Shape::Polygon { points, color, filled } => { + let points_str: Vec = points.iter().map(|p| format!("{},{}", p.x, p.y)).collect(); + if *filled { + writeln!(file, r#" "#, + points_str.join(" "), color.r, color.g, color.b).map_err(|e| e.to_string())?; + } else { + writeln!(file, r#" "#, + points_str.join(" "), color.r, color.g, color.b).map_err(|e| e.to_string())?; + } + } + Shape::Text { position, text, color, size } => { + writeln!(file, r#" {}"#, + position.x, position.y, size, color.r, color.g, color.b, text).map_err(|e| e.to_string())?; + } + } + } + + writeln!(file, "").map_err(|e| e.to_string())?; + Ok(()) + } + + pub fn save_png(&self, filename: &str) -> Result<(), String> { + // For PNG export, we'd use a library like image or cairo + // This is a placeholder that generates a simple PPM format + use std::fs::File; + use std::io::Write; + + let mut file = File::create(filename).map_err(|e| e.to_string())?; + + // Simple PPM format (ASCII) + writeln!(file, "P3").map_err(|e| e.to_string())?; + writeln!(file, "{} {}", self.width, self.height).map_err(|e| e.to_string())?; + writeln!(file, "255").map_err(|e| e.to_string())?; + + // Create pixel buffer + let mut pixels = vec![self.background; (self.width * self.height) as usize]; + + // Rasterize shapes (simple implementation) + for shape in &self.shapes { + match shape { + Shape::Circle { center, radius, color, filled } => { + for y in 0..self.height { + for x in 0..self.width { + let dx = x as f64 - center.x; + let dy = y as f64 - center.y; + let dist = (dx * dx + dy * dy).sqrt(); + + if *filled && dist <= *radius { + pixels[(y * self.width + x) as usize] = *color; + } else if !*filled && (dist - *radius).abs() < 1.0 { + pixels[(y * self.width + x) as usize] = *color; + } + } + } + } + _ => {} // Implement other shapes as needed + } + } + + // Write pixels + for pixel in pixels { + writeln!(file, "{} {} {}", pixel.r, pixel.g, pixel.b).map_err(|e| e.to_string())?; + } + + Ok(()) + } +} + +/// Plot types +#[derive(Debug, Clone)] +pub enum PlotType { + Line, + Scatter, + Bar, + Histogram, + Heatmap, +} + +/// Plot data +pub struct Plot { + pub plot_type: PlotType, + pub x_data: Vec, + pub y_data: Vec, + pub title: String, + pub x_label: String, + pub y_label: String, + pub color: Color, +} + +impl Plot { + pub fn new(plot_type: PlotType) -> Self { + Plot { + plot_type, + x_data: Vec::new(), + y_data: Vec::new(), + title: String::new(), + x_label: String::new(), + y_label: String::new(), + color: Color::BLUE, + } + } + + pub fn set_data(&mut self, x: Vec, y: Vec) { + self.x_data = x; + self.y_data = y; + } + + pub fn render_to_canvas(&self, canvas: &mut Canvas) -> Result<(), String> { + if self.x_data.len() != self.y_data.len() { + return Err("X and Y data must have same length".to_string()); + } + + if self.x_data.is_empty() { + return Err("No data to plot".to_string()); + } + + // Calculate bounds + let margin = 50.0; + let plot_width = canvas.width as f64 - 2.0 * margin; + let plot_height = canvas.height as f64 - 2.0 * margin; + + let x_min = self.x_data.iter().cloned().fold(f64::INFINITY, f64::min); + let x_max = self.x_data.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + let y_min = self.y_data.iter().cloned().fold(f64::INFINITY, f64::min); + let y_max = self.y_data.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + + let x_range = x_max - x_min; + let y_range = y_max - y_min; + + // Draw axes + canvas.draw_line(margin, canvas.height as f64 - margin, + canvas.width as f64 - margin, canvas.height as f64 - margin, + Color::BLACK, 2.0); + canvas.draw_line(margin, margin, margin, canvas.height as f64 - margin, + Color::BLACK, 2.0); + + // Draw title + if !self.title.is_empty() { + canvas.draw_text(canvas.width as f64 / 2.0, 30.0, + self.title.clone(), Color::BLACK, 20.0); + } + + // Draw labels + if !self.x_label.is_empty() { + canvas.draw_text(canvas.width as f64 / 2.0, canvas.height as f64 - 10.0, + self.x_label.clone(), Color::BLACK, 14.0); + } + + // Plot data + match self.plot_type { + PlotType::Line => { + for i in 0..self.x_data.len() - 1 { + let x1 = margin + (self.x_data[i] - x_min) / x_range * plot_width; + let y1 = canvas.height as f64 - margin - (self.y_data[i] - y_min) / y_range * plot_height; + let x2 = margin + (self.x_data[i + 1] - x_min) / x_range * plot_width; + let y2 = canvas.height as f64 - margin - (self.y_data[i + 1] - y_min) / y_range * plot_height; + + canvas.draw_line(x1, y1, x2, y2, self.color, 2.0); + } + } + PlotType::Scatter => { + for i in 0..self.x_data.len() { + let x = margin + (self.x_data[i] - x_min) / x_range * plot_width; + let y = canvas.height as f64 - margin - (self.y_data[i] - y_min) / y_range * plot_height; + + canvas.draw_circle(x, y, 3.0, self.color, true); + } + } + PlotType::Bar => { + let bar_width = plot_width / self.x_data.len() as f64 * 0.8; + for i in 0..self.x_data.len() { + let x = margin + (self.x_data[i] - x_min) / x_range * plot_width - bar_width / 2.0; + let height = (self.y_data[i] - y_min) / y_range * plot_height; + let y = canvas.height as f64 - margin - height; + + canvas.draw_rectangle(x, y, bar_width, height, self.color, true); + } + } + _ => return Err(format!("Plot type {:?} not yet implemented", self.plot_type)), + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_color_creation() { + let c = Color::rgb(255, 0, 0); + assert_eq!(c.r, 255); + assert_eq!(c.g, 0); + assert_eq!(c.b, 0); + } + + #[test] + fn test_canvas_drawing() { + let mut canvas = Canvas::new(800, 600); + canvas.draw_line(0.0, 0.0, 100.0, 100.0, Color::RED, 2.0); + canvas.draw_circle(400.0, 300.0, 50.0, Color::BLUE, true); + assert_eq!(canvas.shapes.len(), 2); + } +} \ No newline at end of file diff --git a/src/graphics_runtime.rs b/src/graphics_runtime.rs new file mode 100644 index 0000000..059b7d1 --- /dev/null +++ b/src/graphics_runtime.rs @@ -0,0 +1,260 @@ +// src/graphics_runtime.rs + +use crate::graphics::{Canvas, Color, Plot, PlotType, Point2D}; +use std::collections::HashMap; +use std::sync::Mutex; +use std::os::raw::{c_char, c_int}; +use std::ffi::CStr; + +lazy_static::lazy_static! { + static ref CANVASES: Mutex> = Mutex::new(HashMap::new()); + static ref PLOTS: Mutex> = Mutex::new(HashMap::new()); + static ref NEXT_CANVAS_ID: Mutex = Mutex::new(0); + static ref NEXT_PLOT_ID: Mutex = Mutex::new(0); +} + +// Canvas functions +#[no_mangle] +pub extern "C" fn quantica_graphics_create_canvas(width: c_int, height: c_int) -> c_int { + let canvas = Canvas::new(width as u32, height as u32); + + let mut canvases = CANVASES.lock().unwrap(); + let mut next_id = NEXT_CANVAS_ID.lock().unwrap(); + + let id = *next_id; + *next_id += 1; + + canvases.insert(id, canvas); + id +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_set_background( + canvas_id: c_int, + r: c_int, + g: c_int, + b: c_int, + a: c_int +) { + let mut canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&canvas_id) { + canvas.set_background(Color::new(r as u8, g as u8, b as u8, a as u8)); + } +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_clear(canvas_id: c_int) { + let mut canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&canvas_id) { + canvas.clear(); + } +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_draw_line( + canvas_id: c_int, + x1: f64, y1: f64, + x2: f64, y2: f64, + r: c_int, g: c_int, b: c_int, a: c_int, + width: f32 +) { + let mut canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&canvas_id) { + let color = Color::new(r as u8, g as u8, b as u8, a as u8); + canvas.draw_line(x1, y1, x2, y2, color, width); + } +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_draw_rect( + canvas_id: c_int, + x: f64, y: f64, + width: f64, height: f64, + r: c_int, g: c_int, b: c_int, a: c_int, + filled: c_int +) { + let mut canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&canvas_id) { + let color = Color::new(r as u8, g as u8, b as u8, a as u8); + canvas.draw_rectangle(x, y, width, height, color, filled != 0); + } +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_draw_circle( + canvas_id: c_int, + x: f64, y: f64, + radius: f64, + r: c_int, g: c_int, b: c_int, a: c_int, + filled: c_int +) { + let mut canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&canvas_id) { + let color = Color::new(r as u8, g as u8, b as u8, a as u8); + canvas.draw_circle(x, y, radius, color, filled != 0); + } +} + +#[no_mangle] +pub unsafe extern "C" fn quantica_graphics_draw_text( + canvas_id: c_int, + x: f64, y: f64, + text: *const c_char, + r: c_int, g: c_int, b: c_int, a: c_int, + size: f32 +) { + if text.is_null() { + return; + } + + let text_str = unsafe { + CStr::from_ptr(text).to_string_lossy().into_owned() + }; + + let mut canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get_mut(&canvas_id) { + let color = Color::new(r as u8, g as u8, b as u8, a as u8); + canvas.draw_text(x, y, text_str, color, size); + } +} + +#[no_mangle] +pub unsafe extern "C" fn quantica_graphics_save_svg( + canvas_id: c_int, + filename: *const c_char +) -> c_int { + if filename.is_null() { + return -1; + } + + let filename_str = unsafe { + CStr::from_ptr(filename).to_string_lossy().into_owned() + }; + + let canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get(&canvas_id) { + match canvas.save_svg(&filename_str) { + Ok(_) => 0, + Err(_) => -1, + } + } else { + -1 + } +} + +#[no_mangle] +pub unsafe extern "C" fn quantica_graphics_save_png( + canvas_id: c_int, + filename: *const c_char +) -> c_int { + if filename.is_null() { + return -1; + } + + let filename_str = unsafe { + CStr::from_ptr(filename).to_string_lossy().into_owned() + }; + + let canvases = CANVASES.lock().unwrap(); + if let Some(canvas) = canvases.get(&canvas_id) { + match canvas.save_png(&filename_str) { + Ok(_) => 0, + Err(_) => -1, + } + } else { + -1 + } +} + +// Plot functions +#[no_mangle] +pub extern "C" fn quantica_graphics_create_plot(plot_type: c_int) -> c_int { + let ptype = match plot_type { + 0 => PlotType::Line, + 1 => PlotType::Scatter, + 2 => PlotType::Bar, + 3 => PlotType::Histogram, + 4 => PlotType::Heatmap, + _ => PlotType::Line, + }; + + let plot = Plot::new(ptype); + + let mut plots = PLOTS.lock().unwrap(); + let mut next_id = NEXT_PLOT_ID.lock().unwrap(); + + let id = *next_id; + *next_id += 1; + + plots.insert(id, plot); + id +} + +#[no_mangle] +pub unsafe extern "C" fn quantica_graphics_plot_set_data( + plot_id: c_int, + x_data: *const f64, + y_data: *const f64, + len: c_int +) { + if x_data.is_null() || y_data.is_null() || len <= 0 { + return; + } + + let x_slice = unsafe { std::slice::from_raw_parts(x_data, len as usize) }; + let y_slice = unsafe { std::slice::from_raw_parts(y_data, len as usize) }; + + let mut plots = PLOTS.lock().unwrap(); + if let Some(plot) = plots.get_mut(&plot_id) { + plot.set_data(x_slice.to_vec(), y_slice.to_vec()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn quantica_graphics_plot_set_title( + plot_id: c_int, + title: *const c_char +) { + if title.is_null() { + return; + } + + let title_str = unsafe { + CStr::from_ptr(title).to_string_lossy().into_owned() + }; + + let mut plots = PLOTS.lock().unwrap(); + if let Some(plot) = plots.get_mut(&plot_id) { + plot.title = title_str; + } +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_plot_render( + plot_id: c_int, + canvas_id: c_int +) -> c_int { + let plots = PLOTS.lock().unwrap(); + let mut canvases = CANVASES.lock().unwrap(); + + if let (Some(plot), Some(canvas)) = (plots.get(&plot_id), canvases.get_mut(&canvas_id)) { + match plot.render_to_canvas(canvas) { + Ok(_) => 0, + Err(_) => -1, + } + } else { + -1 + } +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_destroy_canvas(canvas_id: c_int) { + let mut canvases = CANVASES.lock().unwrap(); + canvases.remove(&canvas_id); +} + +#[no_mangle] +pub extern "C" fn quantica_graphics_destroy_plot(plot_id: c_int) { + let mut plots = PLOTS.lock().unwrap(); + plots.remove(&plot_id); +} \ No newline at end of file diff --git a/src/hardware_integration.rs b/src/hardware_integration.rs index acb7be5..442e09f 100644 --- a/src/hardware_integration.rs +++ b/src/hardware_integration.rs @@ -1,11 +1,13 @@ // src/hardware_integration.rs -use crate::quantum_backend::{HardwareCircuit, HardwareGate, QuantumConfig, BackendManager, QuantumResult}; -use crate::parser::ast::ASTNode; use crate::environment::{Environment, RuntimeValue}; -use std::rc::Rc; +use crate::parser::ast::ASTNode; +use crate::quantum_backend::{ + BackendManager, HardwareCircuit, HardwareGate, QuantumConfig, QuantumResult, +}; use std::cell::RefCell; use std::collections::HashMap; +use std::rc::Rc; /// Circuit Recorder - captures quantum operations for hardware execution pub struct CircuitRecorder { @@ -24,7 +26,7 @@ impl CircuitRecorder { qubit_mapping: HashMap::new(), } } - + /// Register a quantum register pub fn register_qubits(&mut self, var_name: &str, size: usize) -> usize { let start_idx = self.num_qubits; @@ -32,9 +34,15 @@ impl CircuitRecorder { self.num_qubits += size; start_idx } - + /// Record a gate operation - pub fn record_gate(&mut self, gate_name: &str, qubits: Vec, params: Vec, is_dagger: bool) { + pub fn record_gate( + &mut self, + gate_name: &str, + qubits: Vec, + params: Vec, + is_dagger: bool, + ) { self.gates.push(HardwareGate { name: gate_name.to_string(), qubits, @@ -42,14 +50,14 @@ impl CircuitRecorder { is_dagger, }); } - + /// Record a measurement pub fn record_measurement(&mut self, qubit: usize) { if !self.measurements.contains(&qubit) { self.measurements.push(qubit); } } - + /// Build the final hardware circuit pub fn build_circuit(&self) -> HardwareCircuit { HardwareCircuit { @@ -73,30 +81,42 @@ impl HardwareExecutor { config, } } - + /// Execute a Quantica program on real hardware - pub fn execute_on_hardware(&mut self, program: &ASTNode, env: &Rc>) -> Result { - // Record all quantum operations + pub fn execute_on_hardware( + &mut self, + program: &ASTNode, + env: &Rc>, + ) -> Result { + // Step 1: Record all quantum operations self.record_program(program, env)?; - - // Build the circuit + + // Step 2: Build the circuit let circuit = self.recorder.build_circuit(); - - println!("📡 Submitting to {:?} with {} qubits and {} gates", - self.config.provider, circuit.num_qubits, circuit.gates.len()); - - // Submit to hardware backend + + println!( + "📡 Submitting to {:?} with {} qubits and {} gates", + self.config.provider, + circuit.num_qubits, + circuit.gates.len() + ); + + // Step 3: Submit to hardware backend let backend_manager = BackendManager::new(self.config.clone()); let result = backend_manager.execute_circuit(&circuit)?; - - // Process results + + // Step 4: Process results self.process_results(&result); - + Ok(result) } - + /// Record quantum operations from AST - fn record_program(&mut self, node: &ASTNode, env: &Rc>) -> Result<(), String> { + fn record_program( + &mut self, + node: &ASTNode, + env: &Rc>, + ) -> Result<(), String> { match node { ASTNode::Program(statements) => { for stmt in statements { @@ -104,11 +124,15 @@ impl HardwareExecutor { } Ok(()) } - _ => Err("Expected Program node".to_string()) + _ => Err("Expected Program node".to_string()), } } - - fn record_statement(&mut self, node: &ASTNode, env: &Rc>) -> Result<(), String> { + + fn record_statement( + &mut self, + node: &ASTNode, + env: &Rc>, + ) -> Result<(), String> { match node { ASTNode::QuantumDeclaration { name, size, .. } => { let qsize = if let Some(size_expr) = size { @@ -120,50 +144,60 @@ impl HardwareExecutor { } else { 1 }; - + self.recorder.register_qubits(name, qsize); Ok(()) } - - ASTNode::Apply { gate_expr, arguments, .. } => { + + ASTNode::Apply { + gate_expr, + arguments, + .. + } => { self.record_gate_application(gate_expr, arguments)?; Ok(()) } - + ASTNode::Measure(qubit_expr) => { let qubit_idx = self.extract_qubit_index(qubit_expr)?; self.recorder.record_measurement(qubit_idx); Ok(()) } - + ASTNode::Block(statements) => { for stmt in statements { self.record_statement(stmt, env)?; } Ok(()) } - - ASTNode::FunctionDeclaration { body, .. } | ASTNode::CircuitDeclaration { body, .. } => { - self.record_statement(body, env) - } - - _ => Ok(()) // Skip non-quantum statements + + ASTNode::FunctionDeclaration { body, .. } + | ASTNode::CircuitDeclaration { body, .. } => self.record_statement(body, env), + + _ => Ok(()), // Skip non-quantum statements } } - - fn record_gate_application(&mut self, gate_expr: &ASTNode, arguments: &[ASTNode]) -> Result<(), String> { + + fn record_gate_application( + &mut self, + gate_expr: &ASTNode, + arguments: &[ASTNode], + ) -> Result<(), String> { let (gate_name, params, is_dagger) = self.parse_gate_expression(gate_expr)?; let qubit_indices = self.extract_qubit_indices(arguments)?; - - self.recorder.record_gate(&gate_name, qubit_indices, params, is_dagger); + + self.recorder + .record_gate(&gate_name, qubit_indices, params, is_dagger); Ok(()) } - + fn parse_gate_expression(&self, node: &ASTNode) -> Result<(String, Vec, bool), String> { match node { ASTNode::Gate { name, .. } => Ok((name.to_lowercase(), vec![], false)), - - ASTNode::ParameterizedGate { name, parameters, .. } => { + + ASTNode::ParameterizedGate { + name, parameters, .. + } => { let mut params = vec![]; for param_node in parameters { if let ASTNode::FloatLiteral(f) = param_node { @@ -176,21 +210,21 @@ impl HardwareExecutor { } Ok((name.to_lowercase(), params, false)) } - + ASTNode::Dagger { gate_expr, .. } => { let (name, params, _) = self.parse_gate_expression(gate_expr)?; Ok((name, params, true)) } - + ASTNode::Controlled { gate_expr, .. } => { let (name, params, is_dagger) = self.parse_gate_expression(gate_expr)?; Ok((format!("c{}", name), params, is_dagger)) } - - _ => Err("Invalid gate expression".to_string()) + + _ => Err("Invalid gate expression".to_string()), } } - + fn extract_qubit_indices(&self, arguments: &[ASTNode]) -> Result, String> { let mut indices = vec![]; for arg in arguments { @@ -198,7 +232,7 @@ impl HardwareExecutor { } Ok(indices) } - + fn extract_qubit_index(&self, node: &ASTNode) -> Result { match node { ASTNode::ArrayAccess { array, index, .. } => { @@ -207,54 +241,67 @@ impl HardwareExecutor { } else { return Err("Invalid qubit access".to_string()); }; - - let base_idx = self.recorder.qubit_mapping.get(var_name) + + let base_idx = self + .recorder + .qubit_mapping + .get(var_name) .ok_or("Unknown quantum register")?; - + let offset = if let ASTNode::IntLiteral(i) = &**index { *i as usize } else { return Err("Qubit index must be a literal".to_string()); }; - + Ok(base_idx + offset) } - _ => Err("Invalid qubit expression".to_string()) + _ => Err("Invalid qubit expression".to_string()), } } - + fn process_results(&self, result: &QuantumResult) { println!("\n📊 Quantum Hardware Results:"); println!(" Shots: {}", result.shots); - + let mut sorted_counts: Vec<_> = result.counts.iter().collect(); sorted_counts.sort_by(|a, b| b.1.cmp(a.1)); - + for (bitstring, count) in sorted_counts.iter().take(10) { let probability = **count as f64 / result.shots as f64; - println!(" |{}⟩: {} ({:.2}%)", bitstring, count, probability * 100.0); + println!( + " |{}⟩: {} ({:.2}%)", + bitstring, + count, + probability * 100.0 + ); } } } -/// CLI Configuration for hardware execution pub fn parse_hardware_config(args: &[String]) -> Option { let mut config = QuantumConfig::default(); let mut i = 0; - + while i < args.len() { match args[i].as_str() { "--hardware" => { if i + 1 < args.len() { - config.provider = match args[i + 1].as_str() { + let provider_str = &args[i + 1]; + config.provider = match provider_str.as_str() { "ibm" => crate::quantum_backend::QuantumProvider::IBM, "aws" => crate::quantum_backend::QuantumProvider::AWS, "ionq" => crate::quantum_backend::QuantumProvider::IonQ, "google" => crate::quantum_backend::QuantumProvider::GoogleCircuit, - _ => return None, + "native" | "simulator" => crate::quantum_backend::QuantumProvider::Native, + _ => { + eprintln!("Unknown provider: {}. Using native.", provider_str); + crate::quantum_backend::QuantumProvider::Native + } }; - i += 2; + i += 2; // Skip both --hardware and the provider name } else { + eprintln!("Error: --hardware requires a provider argument"); return None; } } @@ -263,16 +310,22 @@ pub fn parse_hardware_config(args: &[String]) -> Option { config.device_name = Some(args[i + 1].clone()); i += 2; } else { + eprintln!("Error: --device requires an argument"); return None; } } "--shots" => { if i + 1 < args.len() { - if let Ok(shots) = args[i + 1].parse::() { - config.shots = shots; + match args[i + 1].parse::() { + Ok(shots) => config.shots = shots, + Err(_) => { + eprintln!("Error: Invalid shots value: {}", args[i + 1]); + return None; + } } i += 2; } else { + eprintln!("Error: --shots requires an argument"); return None; } } @@ -281,13 +334,65 @@ pub fn parse_hardware_config(args: &[String]) -> Option { config.api_token = Some(args[i + 1].clone()); i += 2; } else { + eprintln!("Error: --api-token requires an argument"); return None; } } - _ => i += 1, + "--no-optimize" => { + config.optimize = false; + i += 1; + } + _ => { + // Skip unknown arguments (they might be filenames or other flags) + i += 1; + } } } - + Some(config) +} + +/// Extract filename from arguments (ignoring hardware flags) +pub fn extract_filename(args: &[String]) -> Option { + let mut i = 1; // Skip program name + + while i < args.len() { + let arg = &args[i]; + + // Skip known flags and their values + if arg == "--hardware" || arg == "--device" || arg == "--shots" || arg == "--api-token" { + i += 2; // Skip flag and its value + continue; + } + + // Skip standalone flags + if arg == "--no-optimize" || arg == "--list-devices" + || arg == "--ast" || arg == "--tokens" || arg == "--emit-llvm" + || arg == "--lto" || arg == "--v" { + i += 1; + continue; + } + + // Skip optimization flags + if arg.starts_with("-O") || arg.starts_with("--target=") { + i += 1; + continue; + } + + // Skip commands + if arg == "--compile" || arg == "--run" || arg == "--doc" + || arg == "--repl" || arg == "--test" || arg == "--lex" { + i += 1; + continue; + } + + // If it doesn't start with -- and isn't a known flag, it's probably the filename + if !arg.starts_with("--") && !arg.starts_with("-") { + return Some(arg.clone()); + } + + i += 1; + } + None } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 1c4ba9f..1ef3508 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -10,6 +10,7 @@ pub struct Lexer { column: usize, indent_stack: Vec, start_of_line: bool, + nesting_level: usize, } impl Lexer { @@ -21,29 +22,31 @@ impl Lexer { column: 1, indent_stack: vec![0], start_of_line: true, + nesting_level: 0, } } - + pub fn tokenize(&mut self) -> Result, String> { let mut tokens = Vec::new(); - + while !self.is_at_end() { loop { - if self.start_of_line { + if self.start_of_line && self.nesting_level == 0 { self.handle_indentation(&mut tokens)?; } - self.skip_whitespace_except_newline(); - + + self.skip_whitespace_except_newline(); + if self.current_char().ok() == Some('/') { if self.peek() == Some('/') { // Single-line comment - self.advance(); - self.skip_single_line_comment(); - continue; + self.advance(); + self.skip_single_line_comment(); + continue; } else if self.peek() == Some('*') { // Multiline comment /* ... */ - self.advance(); - self.advance(); + self.advance(); // consume '/' + self.advance(); // consume '*' self.skip_multiline_comment()?; continue; } else { @@ -51,176 +54,257 @@ impl Lexer { break; } } else { - break; + break; } } - + if self.is_at_end() { break; } - - if self.current_char().ok() == Some('\n') { + + /*if self.current_char().ok() == Some('\n') { self.advance(); tokens.push(self.make_token(Token::Newline, 1)); - continue; + continue; + }*/ + + // Check for and consume a newline *after* indentation + if self.current_char().ok() == Some('\n') { + self.advance(); // Consume the newline + if self.nesting_level == 0 { + tokens.push(self.make_token(Token::Newline, 1)); + } + continue; // Restart main loop } - + // --- 3. Process the next actual token --- let _start_col = self.column; let ch = self.current_char()?; - + // 'token_result' must be a Result let token_result = match ch { - + // Quantum notation '|' => { if self.is_quantum_ket() { self.ket_notation() } else if self.peek() == Some('>') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::PipeRight, 2)) } else if self.peek() == Some('|') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::DoublePipe, 2)) } else { - self.advance(); Ok(self.make_token(Token::Pipe, 1)) + self.advance(); + Ok(self.make_token(Token::Pipe, 1)) } } '{' => { if self.is_quantum_bra() { self.bra_notation() } else { - self.advance(); Ok(self.make_token(Token::LeftBrace, 1)) + self.advance(); + self.nesting_level += 1; + Ok(self.make_token(Token::LeftBrace, 1)) } } - - - '+' => { self.advance(); Ok(self.make_token(Token::Plus, 1)) } + + // Operators + '+' => { + self.advance(); + Ok(self.make_token(Token::Plus, 1)) + } '-' => { if self.peek() == Some('>') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::Arrow, 2)) } else { - self.advance(); Ok(self.make_token(Token::Minus, 1)) + self.advance(); + Ok(self.make_token(Token::Minus, 1)) } } '*' => { if self.peek() == Some('*') && self.peek_n(2) == Some('*') { - self.advance(); self.advance(); self.advance(); + self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::TensorProduct, 3)) } else { - self.advance(); Ok(self.make_token(Token::Star, 1)) + self.advance(); + Ok(self.make_token(Token::Star, 1)) } } - '/' => { self.advance(); Ok(self.make_token(Token::Slash, 1)) } - '%' => { self.advance(); Ok(self.make_token(Token::Percent, 1)) } - '^' => { self.advance(); Ok(self.make_token(Token::Caret, 1)) } + '/' => { + self.advance(); + Ok(self.make_token(Token::Slash, 1)) + } + '%' => { + self.advance(); + Ok(self.make_token(Token::Percent, 1)) + } + '^' => { + self.advance(); + Ok(self.make_token(Token::Caret, 1)) + } '=' => { if self.peek() == Some('=') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::EqualEqual, 2)) } else if self.peek() == Some('>') { if self.peek_n(2) == Some('>') { - self.advance(); self.advance(); self.advance(); + self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::PipeDouble, 3)) } else { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::FatArrow, 2)) } } else { - self.advance(); Ok(self.make_token(Token::Equal, 1)) + self.advance(); + Ok(self.make_token(Token::Equal, 1)) } } '!' => { if self.peek() == Some('=') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::NotEqual, 2)) } else { - self.advance(); Ok(self.make_token(Token::Bang, 1)) + self.advance(); + Ok(self.make_token(Token::Bang, 1)) } } '<' => { if self.peek() == Some('=') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::LessEqual, 2)) } else { - self.advance(); Ok(self.make_token(Token::Less, 1)) + self.advance(); + Ok(self.make_token(Token::Less, 1)) } } '>' => { if self.peek() == Some('=') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::GreaterEqual, 2)) } else { - self.advance(); Ok(self.make_token(Token::Greater, 1)) + self.advance(); + Ok(self.make_token(Token::Greater, 1)) } } '?' => { if self.peek() == Some('.') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::SafeNav, 2)) } else { - self.advance(); Ok(self.make_token(Token::Question, 1)) + self.advance(); + Ok(self.make_token(Token::Question, 1)) } } - - + + // Strings, Chars, Numbers, Identifiers '"' => self.string_literal(), '\'' => self.char_literal(), c if c.is_ascii_digit() => self.number_literal(), c if c.is_alphabetic() || c == '_' => Ok(self.identifier_or_keyword()), // Punctuation - '(' => { self.advance(); Ok(self.make_token(Token::LeftParen, 1)) } - ')' => { self.advance(); Ok(self.make_token(Token::RightParen, 1)) } - '[' => { self.advance(); Ok(self.make_token(Token::LeftBracket, 1)) } - ']' => { self.advance(); Ok(self.make_token(Token::RightBracket, 1)) } - '}' => { self.advance(); Ok(self.make_token(Token::RightBrace, 1)) } - ',' => { self.advance(); Ok(self.make_token(Token::Comma, 1)) } - ';' => { self.advance(); Ok(self.make_token(Token::Semicolon, 1)) } + '(' => { + self.advance(); + self.nesting_level += 1; + Ok(self.make_token(Token::LeftParen, 1)) + } + ')' => { + self.advance(); + if self.nesting_level > 0 { self.nesting_level -= 1; } + Ok(self.make_token(Token::RightParen, 1)) + } + '[' => { + self.advance(); + self.nesting_level += 1; + Ok(self.make_token(Token::LeftBracket, 1)) + } + ']' => { + self.advance(); + if self.nesting_level > 0 { self.nesting_level -= 1; } + Ok(self.make_token(Token::RightBracket, 1)) + } + '}' => { + self.advance(); + if self.nesting_level > 0 { self.nesting_level -= 1; } + Ok(self.make_token(Token::RightBrace, 1)) + } + ',' => { + self.advance(); + Ok(self.make_token(Token::Comma, 1)) + } + ';' => { + self.advance(); + Ok(self.make_token(Token::Semicolon, 1)) + } ':' => { if self.peek() == Some(':') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::DoubleColon, 2)) } else if self.peek() == Some('=') { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::ColonEqual, 2)) } else { - self.advance(); Ok(self.make_token(Token::Colon, 1)) + self.advance(); + Ok(self.make_token(Token::Colon, 1)) } } '.' => { if self.peek() == Some('.') { if self.peek_n(2) == Some('=') { - self.advance(); self.advance(); self.advance(); + self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::RangeInclusive, 3)) } else { - self.advance(); self.advance(); + self.advance(); + self.advance(); Ok(self.make_token(Token::Range, 2)) } } else { - self.advance(); Ok(self.make_token(Token::Dot, 1)) + self.advance(); + Ok(self.make_token(Token::Dot, 1)) } } - - _ => Err(format!("Unexpected character '{}' at line {}, column {}", ch, self.line, self.column)), + + _ => Err(format!( + "Unexpected character '{}' at line {}, column {}", + ch, self.line, self.column + )), }; tokens.push(token_result?); } - - + + // --- Handle final dedents and EOF --- while self.indent_stack.len() > 1 { + // Assumes 0 is root self.indent_stack.pop(); tokens.push(self.make_token(Token::Dedent, 0)); } - + tokens.push(self.make_token(Token::Eof, 0)); Ok(tokens) } fn skip_multiline_comment(&mut self) -> Result<(), String> { let start_line = self.line; - + loop { if self.is_at_end() { return Err(format!( @@ -228,108 +312,115 @@ impl Lexer { start_line )); } - + let ch = self.current_char()?; - + if ch == '*' && self.peek() == Some('/') { - - self.advance(); - self.advance(); + // Found closing */ + self.advance(); // consume '*' + self.advance(); // consume '/' return Ok(()); } - + self.advance(); } } - + fn handle_indentation(&mut self, tokens: &mut Vec) -> Result<(), String> { - let mut indent_level = 0; - - - while !self.is_at_end() { - let ch = self.current_char()?; - match ch { - ' ' => { - indent_level += 1; - self.advance(); - } - '\t' => { - indent_level += 4; - self.advance(); + let mut indent_level = 0; + + // 1. Consume whitespace and count indent + while !self.is_at_end() { + let ch = self.current_char()?; + match ch { + ' ' => { + indent_level += 1; + self.advance(); + } + '\t' => { + indent_level += 4; // Tab = 4 spaces + self.advance(); + } + '\r' => { + // FIX: Ignore Carriage Return, just skip it + self.advance(); + } + _ => break, // Stop at content, newline, or comment } - _ => break, } - } - - if !self.is_at_end() { - let ch = self.current_char()?; - - if ch == '\n' || (ch == '/' && self.peek() == Some('/')) { - - self.start_of_line = false; - return Ok(()); - } - } - - - self.start_of_line = false; - - - let current_indent = *self.indent_stack.last().unwrap(); - - if indent_level > current_indent { - self.indent_stack.push(indent_level); - tokens.push(self.make_token(Token::Indent, 0)); - } else if indent_level < current_indent { - while let Some(&stack_level) = self.indent_stack.last() { - if stack_level <= indent_level { - break; + // 2. Check if the line is blank (only newline or comment ahead) + if !self.is_at_end() { + let ch = self.current_char()?; + // Check for newline OR a comment + // FIX: We don't need to check for \r here because we consumed it in the loop above + if ch == '\n' || (ch == '/' && self.peek() == Some('/')) { + // This is a blank line. Do NOT emit Indent/Dedent. + // The main loop will handle the newline or comment. + self.start_of_line = false; // Clear the flag + return Ok(()); } - self.indent_stack.pop(); - tokens.push(self.make_token(Token::Dedent, 0)); } - - if self.indent_stack.last() != Some(&indent_level) { - return Err(format!("Indentation error at line {}", self.line)); + + // 3. Line is NOT blank. Clear start_of_line flag + self.start_of_line = false; + + // 4. Process Indent/Dedent logic. + let current_indent = *self.indent_stack.last().unwrap(); + + if indent_level > current_indent { + self.indent_stack.push(indent_level); + tokens.push(self.make_token(Token::Indent, 0)); + } else if indent_level < current_indent { + while let Some(&stack_level) = self.indent_stack.last() { + if stack_level <= indent_level { + break; + } + self.indent_stack.pop(); + tokens.push(self.make_token(Token::Dedent, 0)); + } + + if self.indent_stack.last() != Some(&indent_level) { + return Err(format!("Indentation error at line {}", self.line)); + } } + + Ok(()) } - - Ok(()) -} - + fn next_token(&mut self) -> Result, String> { let ch = self.current_char()?; let start_col = self.column; - + let token = match ch { - + // Comments '#' => { self.skip_comment(); return Ok(None); } - - - + // Newlines + // src/lexer/mod.rs (around line 190, the Newline case) + + // Newlines '\n' => { let token = Token::Newline; - - - self.advance(); - - - let length = 1; + + // 1. ADVANCE (CONSUME THE \n) + self.advance(); + + // 2. MAKE TOKEN (Length is 1, Column is now 2) + let length = 1; // \n is always one char long let token_with_loc = self.make_token(token, length); - - + + // 3. RESET COLUMN STATE self.line += 1; self.column = 1; - return Ok(Some(token_with_loc)); + return Ok(Some(token_with_loc)); // Return the token } - - + + // Quantum state notation |0}, |1}, |+}, |-} '|' => { if self.is_quantum_ket() { return Ok(Some(self.ket_notation()?)); @@ -346,8 +437,8 @@ impl Lexer { Token::Pipe } } - - + + // Bra notation {0|, {1| '{' => { if self.is_quantum_bra() { return Ok(Some(self.bra_notation()?)); @@ -356,36 +447,40 @@ impl Lexer { Token::LeftBrace } } - - + + // Operators '+' => { self.advance(); Token::Plus } '-' => { - + // Lookahead before advancing if self.peek() == Some('>') { - self.advance(); - self.advance(); - Token::Arrow + self.advance(); // consume '-' + self.advance(); // consume '>' + Token::Arrow // Found '->' } else { - self.advance(); - Token::Minus + self.advance(); // consume '-' + Token::Minus // Found single '-' } } '*' => { - + // Check for '***' (TensorProduct) first if self.peek() == Some('*') && self.input.get(self.position + 2) == Some(&'*') { - self.advance(); - self.advance(); - self.advance(); + self.advance(); // consume 1st * + self.advance(); // consume 2nd * + self.advance(); // consume 3rd * Token::TensorProduct - } - - + } + // Check for '**' (Optional: if you plan to use it for power/exponentiation) + /* else if self.peek() == Some('*') { + self.advance(); // consume 1st * + self.advance(); // consume 2nd * + Token::Power // Example: if you had a power token + } */ else { - - self.advance(); + // Only consume one * + self.advance(); Token::Star } } @@ -398,84 +493,84 @@ impl Lexer { Token::Percent } '=' => { - + // Lookahead before advancing for all two- or three-char tokens starting with '=' if self.peek() == Some('=') { - self.advance(); - self.advance(); - Token::EqualEqual + self.advance(); // consume first '=' + self.advance(); // consume second '=' + Token::EqualEqual // Handles '==' } else if self.peek() == Some('>') { - + // Check for '=>>' or '=>' if self.input.get(self.position + 2) == Some(&'>') { - self.advance(); - self.advance(); - self.advance(); - Token::PipeDouble + self.advance(); // consume '=' + self.advance(); // consume '>' + self.advance(); // consume '>' + Token::PipeDouble // Handles '=>>' } else { - self.advance(); - self.advance(); - Token::FatArrow + self.advance(); // consume '=' + self.advance(); // consume '>' + Token::FatArrow // Handles '=>' } } else { - self.advance(); - Token::Equal + self.advance(); // consume '=' + Token::Equal // Handles single '=' } } '!' => { - + // Lookahead for '!=' if self.peek() == Some('=') { - self.advance(); - self.advance(); + self.advance(); // consume ! + self.advance(); // consume = Token::NotEqual } else { - self.advance(); + self.advance(); // consume single ! Token::Bang } } '<' => { - + // Lookahead for '<=' if self.peek() == Some('=') { - self.advance(); - self.advance(); + self.advance(); // consume < + self.advance(); // consume = Token::LessEqual } else { - self.advance(); + self.advance(); // consume single < Token::Less } } '>' => { - + // Lookahead for '>=' if self.peek() == Some('=') { - self.advance(); - self.advance(); + self.advance(); // consume > + self.advance(); // consume = Token::GreaterEqual } else { - self.advance(); + self.advance(); // consume single > Token::Greater } } '?' => { - + // Lookahead for '?.' (SafeNav) if self.peek() == Some('.') { - self.advance(); - self.advance(); + self.advance(); // consume ? + self.advance(); // consume . Token::SafeNav } else { - self.advance(); + self.advance(); // consume single ? Token::Question } } - - + + // Strings '"' => return Ok(Some(self.string_literal()?)), '\'' => return Ok(Some(self.char_literal()?)), - - + + // Numbers c if c.is_ascii_digit() => return Ok(Some(self.number_literal()?)), - - + + // Identifiers and keywords c if c.is_alphabetic() || c == '_' => return Ok(Some(self.identifier_or_keyword())), - - + + // Punctuation '(' => { self.advance(); Token::LeftParen @@ -505,49 +600,54 @@ impl Lexer { Token::Semicolon } ':' => { - + // Lookahead for '::' or ':=' if self.peek() == Some(':') { - self.advance(); - self.advance(); + self.advance(); // consume first : + self.advance(); // consume second : Token::DoubleColon } else if self.peek() == Some('=') { - self.advance(); - self.advance(); + self.advance(); // consume : + self.advance(); // consume = Token::ColonEqual } else { - self.advance(); + self.advance(); // consume single : Token::Colon } } '.' => { - + // Check for '...' or '..=' if self.peek() == Some('.') { - + // Check for '..=' if self.input.get(self.position + 2) == Some(&'=') { - self.advance(); - self.advance(); - self.advance(); + self.advance(); // consume first . + self.advance(); // consume second . + self.advance(); // consume = Token::RangeInclusive } else { - - self.advance(); - self.advance(); + // Must be '..' (Range) + self.advance(); // consume first . + self.advance(); // consume second . Token::Range } } else { - + // Only consume single '.' self.advance(); Token::Dot } } - - _ => return Err(format!("Unexpected character '{}' at line {}, column {}", ch, self.line, self.column)), + + _ => { + return Err(format!( + "Unexpected character '{}' at line {}, column {}", + ch, self.line, self.column + )) + } }; - + let length = self.column - start_col; Ok(Some(self.make_token(token, length))) } - + fn is_quantum_ket(&self) -> bool { if self.position + 1 >= self.input.len() { return false; @@ -555,9 +655,9 @@ impl Lexer { let next = self.input[self.position + 1]; next.is_ascii_digit() || next == '+' || next == '-' || next.is_alphabetic() } - + fn is_quantum_bra(&self) -> bool { - + // Check if it's {0| pattern let mut i = self.position + 1; while i < self.input.len() { let ch = self.input[i]; @@ -571,49 +671,49 @@ impl Lexer { } false } - + fn ket_notation(&mut self) -> Result { let start_col = self.column; - self.advance(); - + self.advance(); // skip | + let mut state = String::new(); while !self.is_at_end() && self.current_char()? != '}' { state.push(self.current_char()?); self.advance(); } - + if self.is_at_end() || self.current_char()? != '}' { return Err(format!("Unclosed quantum ket at line {}", self.line)); } - - self.advance(); + + self.advance(); // skip } let length = self.column - start_col; Ok(self.make_token(Token::KetState(state), length)) } - + fn bra_notation(&mut self) -> Result { let start_col = self.column; - self.advance(); - + self.advance(); // skip { + let mut state = String::new(); while !self.is_at_end() && self.current_char()? != '|' { state.push(self.current_char()?); self.advance(); } - + if self.is_at_end() || self.current_char()? != '|' { return Err(format!("Unclosed quantum bra at line {}", self.line)); } - - self.advance(); + + self.advance(); // skip | let length = self.column - start_col; Ok(self.make_token(Token::BraState(state), length)) } - + fn identifier_or_keyword(&mut self) -> TokenWithLocation { let start = self.position; let start_col = self.column; - + while !self.is_at_end() { let ch = self.current_char().unwrap(); if ch.is_alphanumeric() || ch == '_' { @@ -622,10 +722,10 @@ impl Lexer { break; } } - + let text: String = self.input[start..self.position].iter().collect(); let length = self.column - start_col; - + let token = match text.as_str() { // Core keywords "let" => Token::Let, @@ -633,7 +733,11 @@ impl Lexer { "auto" => Token::Auto, "func" => Token::Func, "circuit" => Token::Circuit, - "class" => Token::Class, + "Class" => Token::Class, + "new" => Token::New, + "this" => Token::This, + "super" => Token::Super, + "extends" => Token::Extends, "return" => Token::Return, "import" => Token::Import, "from" => Token::From, @@ -641,9 +745,9 @@ impl Lexer { "as" => Token::As, "module" => Token::Module, "package" => Token::Package, - "print"=>Token::Print, - "echo"=>Token::Echo, - + "print" => Token::Print, + "echo" => Token::Echo, + // Control flow "if" => Token::If, "elif" => Token::Elif, @@ -655,7 +759,7 @@ impl Lexer { "while" => Token::While, "break" => Token::Break, "continue" => Token::Continue, - + // Error handling "Try" => Token::Try, "Catch" => Token::Catch, @@ -663,60 +767,59 @@ impl Lexer { "Finally" => Token::Finally, "Throw" => Token::Throw, "Safe" => Token::Safe, - + // Parallelism "parallel" => Token::Parallel, "task" => Token::Task, "await" => Token::Await, "async" => Token::Async, "yield" => Token::Yield, - + // Logic "And" => Token::And, "Or" => Token::Or, "Not" => Token::Not, "Is" => Token::Is, - + // Quantum "quantum" => Token::Quantum, "apply" => Token::Apply, "measure" => Token::Measure, - "Swap"=>Token::Swap, - "Reset"=>Token::Reset, - "CZ"=>Token::CZ, - "CS"=>Token::CS, - "CT"=>Token::CT, - "CPhase"=>Token::CPhase, - "U"=>Token::U, - "CCX"=>Token::CCX, - "Toffoli"=>Token::Toffoli, - "dagger"=>Token::Dagger, - "controlled"=>Token::Controlled, - "RX"=>Token::RX, - "RY"=>Token::RY, - "RZ"=>Token::RZ, - + "Swap" => Token::Swap, + "Reset" => Token::Reset, + "CZ" => Token::CZ, + "CS" => Token::CS, + "CT" => Token::CT, + "CPhase" => Token::CPhase, + "U" => Token::U, + "CCX" => Token::CCX, + "Toffoli" => Token::Toffoli, + "dagger" => Token::Dagger, + "controlled" => Token::Controlled, + "RX" => Token::RX, + "RY" => Token::RY, + "RZ" => Token::RZ, //quantum gates - "Hadamard"=>Token::Hadamard, - "CNOT"=>Token::Cnot, - "X"=>Token::X, - "Y"=>Token::Y, - "Z"=>Token::Z, - "S"=>Token::S, - "T"=>Token::T, - + "Hadamard" => Token::Hadamard, + "CNOT" => Token::Cnot, + "X" => Token::X, + "Y" => Token::Y, + "Z" => Token::Z, + "S" => Token::S, + "T" => Token::T, + // AI/ML "Tensor" => Token::Tensor, "Train" => Token::Train, "infer" => Token::Infer, "Load" => Token::Load, "Save" => Token::Save, - + // Data structures "struct" => Token::Struct, "enum" => Token::Enum, - + // Modifiers "Const" => Token::Const, "public" => Token::Public, @@ -724,24 +827,24 @@ impl Lexer { "static" => Token::Static, "extern" => Token::Extern, "inline" => Token::Inline, - + // Meta "pragma" => Token::Pragma, "sizeof" => Token::Sizeof, "typeof" => Token::Typeof, - + // Future "defer" => Token::Defer, "contract" => Token::Contract, "where" => Token::Where, "generic" => Token::Generic, - + // Literals "True" => Token::True, "False" => Token::False, "None" => Token::None, "Any" => Token::Any, - + // Types "Int" => Token::Int, "Int8" => Token::Int8, @@ -764,119 +867,130 @@ impl Lexer { "Bool" => Token::Bool, "Bit" => Token::Bit, "String" => Token::String, - + _ => Token::Identifier(text), }; - + self.make_token(token, length) } - + fn number_literal(&mut self) -> Result { let start = self.position; let start_col = self.column; let mut is_float = false; - + // Integer part while !self.is_at_end() && self.current_char()?.is_ascii_digit() { self.advance(); } - + // Decimal part if !self.is_at_end() && self.current_char()? == '.' { let next = self.peek(); if next.is_some() && next.unwrap().is_ascii_digit() { is_float = true; - self.advance(); - + self.advance(); // skip . + while !self.is_at_end() && self.current_char()?.is_ascii_digit() { self.advance(); } } } - + // Scientific notation if !self.is_at_end() && (self.current_char()? == 'e' || self.current_char()? == 'E') { is_float = true; self.advance(); - + if !self.is_at_end() && (self.current_char()? == '+' || self.current_char()? == '-') { self.advance(); } - + while !self.is_at_end() && self.current_char()?.is_ascii_digit() { self.advance(); } } - + let text: String = self.input[start..self.position].iter().collect(); let length = self.column - start_col; - + let token = if is_float { - Token::FloatLiteral(text.parse().map_err(|_| format!("Invalid float: {}", text))?) + Token::FloatLiteral( + text.parse() + .map_err(|_| format!("Invalid float: {}", text))?, + ) } else { - Token::IntLiteral(text.parse().map_err(|_| format!("Invalid integer: {}", text))?) + Token::IntLiteral( + text.parse() + .map_err(|_| format!("Invalid integer: {}", text))?, + ) }; - + Ok(self.make_token(token, length)) } - + fn string_literal(&mut self) -> Result { let start_col = self.column; - self.advance(); - + self.advance(); // skip opening " + let mut value = String::new(); - + while !self.is_at_end() && self.current_char()? != '"' { if self.current_char()? == '\\' { self.advance(); if self.is_at_end() { return Err("Unterminated string".to_string()); } - + match self.current_char()? { 'n' => value.push('\n'), 't' => value.push('\t'), 'r' => value.push('\r'), '\\' => value.push('\\'), '"' => value.push('"'), - _ => return Err(format!("Invalid escape sequence: \\{}", self.current_char()?)), + _ => { + return Err(format!( + "Invalid escape sequence: \\{}", + self.current_char()? + )) + } } } else { value.push(self.current_char()?); } self.advance(); } - + if self.is_at_end() { return Err("Unterminated string".to_string()); } - - self.advance(); // + + self.advance(); // skip closing " let length = self.column - start_col; Ok(self.make_token(Token::StringLiteral(value), length)) } - + fn char_literal(&mut self) -> Result { let start_col = self.column; - self.advance(); // - + self.advance(); // skip ' + if self.is_at_end() { return Err("Empty character literal".to_string()); } - + let ch = self.current_char()?; self.advance(); - + if self.is_at_end() || self.current_char()? != '\'' { return Err("Unterminated character literal".to_string()); } - - self.advance(); // + + self.advance(); // skip closing ' let length = self.column - start_col; Ok(self.make_token(Token::IntLiteral(ch as i64), length)) } - + // Helper methods fn current_char(&self) -> Result { if self.is_at_end() { Err("Unexpected end of input".to_string()) @@ -884,7 +998,7 @@ impl Lexer { Ok(self.input[self.position]) } } - + fn peek(&self) -> Option { if self.position + 1 < self.input.len() { Some(self.input[self.position + 1]) @@ -902,63 +1016,53 @@ impl Lexer { } fn skip_single_line_comment(&mut self) { - self.advance(); - + self.advance(); // Consume the second '/' + // Loop while 'current_char()' is Ok and not a newline while let Ok(ch) = self.current_char() { if ch == '\n' { - break; + break; // Stop at the newline (don't consume it) } self.advance(); } } - - - fn advance(&mut self) { - if !self.is_at_end() { - let ch = self.input[self.position]; - self.position += 1; - - if ch == '\n' { - self.line += 1; - self.column = 1; - self.start_of_line = true; - } else { - self.column += 1; - + if !self.is_at_end() { + let ch = self.input[self.position]; + self.position += 1; + + if ch == '\n' { + self.line += 1; + self.column = 1; + self.start_of_line = true; + } else { + self.column += 1; + // Don't modify start_of_line here - let handle_indentation do it + } } } -} - + fn is_at_end(&self) -> bool { self.position >= self.input.len() } - - - - -fn make_token(&self, token: Token, length: usize) -> TokenWithLocation { - - - let calculated_start_column = self.column.saturating_sub(length); - - - let start_column = if calculated_start_column == 0 { - 1 - } else { - calculated_start_column - }; - - TokenWithLocation::new( - token, - self.line, - start_column, - length - ) -} - + + // src/lexer/mod.rs (around line 345) + + fn make_token(&self, token: Token, length: usize) -> TokenWithLocation { + // Use saturating_sub to ensure the result does not underflow (result is 0 if length > column). + let calculated_start_column = self.column.saturating_sub(length); + + // Columns start at 1. If the calculated start is 0, set it to 1. + let start_column = if calculated_start_column == 0 { + 1 + } else { + calculated_start_column + }; + + TokenWithLocation::new(token, self.line, start_column, length) + } + fn skip_whitespace_except_newline(&mut self) { while !self.is_at_end() { let ch = self.input[self.position]; @@ -969,7 +1073,7 @@ fn make_token(&self, token: Token, length: usize) -> TokenWithLocation { } } } - + fn skip_comment(&mut self) { while !self.is_at_end() && self.current_char().unwrap() != '\n' { self.advance(); @@ -977,112 +1081,113 @@ fn make_token(&self, token: Token, length: usize) -> TokenWithLocation { } } +// Tests #[cfg(test)] mod tests { use super::*; - + #[test] fn test_keywords() { let input = "let mut quantum apply measure"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::Let); assert_eq!(tokens[1].token, Token::Mut); assert_eq!(tokens[2].token, Token::Quantum); assert_eq!(tokens[3].token, Token::Apply); assert_eq!(tokens[4].token, Token::Measure); } - + #[test] fn test_gate_tokens() { let input = "Hadamard X Y Z CNOT"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::Hadamard); assert_eq!(tokens[1].token, Token::X); assert_eq!(tokens[2].token, Token::Y); assert_eq!(tokens[3].token, Token::Z); assert_eq!(tokens[4].token, Token::Cnot); } - + #[test] fn test_quantum_ket() { let input = "quantum q = |0}"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::Quantum); assert_eq!(tokens[1].token, Token::Identifier("q".to_string())); assert_eq!(tokens[2].token, Token::Equal); assert_eq!(tokens[3].token, Token::KetState("0".to_string())); } - + #[test] fn test_quantum_states() { let input = "|0} |1} |+} |-} |psi}"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::KetState("0".to_string())); assert_eq!(tokens[1].token, Token::KetState("1".to_string())); assert_eq!(tokens[2].token, Token::KetState("+".to_string())); assert_eq!(tokens[3].token, Token::KetState("-".to_string())); assert_eq!(tokens[4].token, Token::KetState("psi".to_string())); } - + #[test] fn test_bra_notation() { let input = "{0| {1| {psi|"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::BraState("0".to_string())); assert_eq!(tokens[1].token, Token::BraState("1".to_string())); assert_eq!(tokens[2].token, Token::BraState("psi".to_string())); } - + #[test] fn test_operators() { let input = "a + b - c * d / e % f"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[1].token, Token::Plus); assert_eq!(tokens[3].token, Token::Minus); assert_eq!(tokens[5].token, Token::Star); assert_eq!(tokens[7].token, Token::Slash); assert_eq!(tokens[9].token, Token::Percent); } - + #[test] fn test_tensor_product() { let input = "q1 *** q2"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::Identifier("q1".to_string())); assert_eq!(tokens[1].token, Token::TensorProduct); assert_eq!(tokens[2].token, Token::Identifier("q2".to_string())); } - + #[test] fn test_star_vs_tensor_product() { let input = "a * b *** c"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[1].token, Token::Star); assert_eq!(tokens[3].token, Token::TensorProduct); } - + #[test] fn test_comparison_operators() { let input = "== != < > <= >="; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::EqualEqual); assert_eq!(tokens[1].token, Token::NotEqual); assert_eq!(tokens[2].token, Token::Less); @@ -1090,13 +1195,13 @@ mod tests { assert_eq!(tokens[4].token, Token::LessEqual); assert_eq!(tokens[5].token, Token::GreaterEqual); } - + #[test] fn test_arrows_and_special() { let input = "-> => :: .. ..= ?. |> =>> ||"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::Arrow); assert_eq!(tokens[1].token, Token::FatArrow); assert_eq!(tokens[2].token, Token::DoubleColon); @@ -1107,14 +1212,14 @@ mod tests { assert_eq!(tokens[7].token, Token::PipeDouble); assert_eq!(tokens[8].token, Token::DoublePipe); } - + #[test] #[allow(clippy::approx_constant)] fn test_numbers() { let input = "42 3.14 2.5e10 1.0e-5"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::IntLiteral(42)); assert_eq!(tokens[1].token, Token::FloatLiteral(3.14)); assert!(matches!(tokens[2].token, Token::FloatLiteral(_))); @@ -1126,9 +1231,15 @@ mod tests { let input = "let x = 10 // this is a comment\nlet y = 20"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); + + // Should have: Let, Identifier, Equal, IntLiteral, Newline, Let, Identifier, Equal, IntLiteral assert!(tokens.iter().any(|t| matches!(t.token, Token::Let))); - assert!(tokens.iter().any(|t| matches!(t.token, Token::IntLiteral(10)))); - assert!(tokens.iter().any(|t| matches!(t.token, Token::IntLiteral(20)))); + assert!(tokens + .iter() + .any(|t| matches!(t.token, Token::IntLiteral(10)))); + assert!(tokens + .iter() + .any(|t| matches!(t.token, Token::IntLiteral(20)))); } #[test] @@ -1136,9 +1247,13 @@ mod tests { let input = "let x = 10 /* this is a\nmultiline comment */ let y = 20"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - - assert!(tokens.iter().any(|t| matches!(t.token, Token::IntLiteral(10)))); - assert!(tokens.iter().any(|t| matches!(t.token, Token::IntLiteral(20)))); + + assert!(tokens + .iter() + .any(|t| matches!(t.token, Token::IntLiteral(10)))); + assert!(tokens + .iter() + .any(|t| matches!(t.token, Token::IntLiteral(20)))); } #[test] @@ -1146,7 +1261,7 @@ mod tests { let input = "2 ^ 3"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert_eq!(tokens[0].token, Token::IntLiteral(2)); assert_eq!(tokens[1].token, Token::Caret); assert_eq!(tokens[2].token, Token::IntLiteral(3)); @@ -1157,9 +1272,8 @@ mod tests { let input = "if True:\n let x = 10\n let y = 20"; let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - + assert!(tokens.iter().any(|t| matches!(t.token, Token::Indent))); assert!(tokens.iter().any(|t| matches!(t.token, Token::Dedent))); } } - diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 84fee55..27c4947 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -1,7 +1,7 @@ #[derive(Debug, Clone, PartialEq)] #[allow(dead_code)] pub enum Token { - // Keywords + // Keywords - Core Language Let, Print, Echo, @@ -17,7 +17,7 @@ pub enum Token { As, Module, Package, - + // Control Flow If, Elif, @@ -29,7 +29,7 @@ pub enum Token { While, Break, Continue, - + // Error & Safety Try, Catch, @@ -37,20 +37,20 @@ pub enum Token { Finally, Throw, Safe, - + // Parallelism Parallel, Task, Await, Async, Yield, - + // Logical Keywords And, Or, Not, Is, - + // Quantum Quantum, Apply, @@ -78,47 +78,64 @@ pub enum Token { RX, RY, RZ, - - + // AI/ML Tensor, Train, Infer, Load, Save, - + // Data Structures Struct, Enum, - + // Modifiers Const, Public, + New, + This, + Super, + Extends, Private, Static, Extern, Inline, - + // Meta Pragma, Sizeof, Typeof, - + // Future Reserved Defer, Contract, Where, Generic, - + // Types - Int, Int8, Int16, Int32, Int64, Int128, - Uint, Uint8, Uint16, Uint32, Uint64, Uint128, - Float, Float32, Float64, - Complex, Complex64, Complex128, + Int, + Int8, + Int16, + Int32, + Int64, + Int128, + Uint, + Uint8, + Uint16, + Uint32, + Uint64, + Uint128, + Float, + Float32, + Float64, + Complex, + Complex64, + Complex128, Bool, Bit, String, - + // Literals IntLiteral(i64), FloatLiteral(f64), @@ -127,28 +144,27 @@ pub enum Token { False, None, Any, - + // Identifiers Identifier(String), - - + // Operators - Plus, // + - Minus, // - - Star, // * - Slash, // / - Percent, // % - Equal, // = - ColonEqual, // := - EqualEqual, // == - NotEqual, // != - Less, // - Greater, // > - LessEqual, // <= - GreaterEqual, // >= - Question, // ? - Bang, // ! - + Plus, // + + Minus, // - + Star, // * + Slash, // / + Percent, // % + Equal, // = + ColonEqual, // := + EqualEqual, // == + NotEqual, // != + Less, // + Greater, // > + LessEqual, // <= + GreaterEqual, // >= + Question, // ? + Bang, // ! + // Punctuation LeftParen, // ( RightParen, // ) @@ -170,13 +186,13 @@ pub enum Token { DoublePipe, // || PipeRight, // |> PipeDouble, // =>> - Caret, //^ + Caret, //^ TensorProduct, // *** (quantum tensor product) - + // Quantum Notation - KetState(String), // |0}, |1}, |+}, |-} - BraState(String), // {0|, {1| - + KetState(String), // |0}, |1}, |+}, |-}, etc. + BraState(String), // {0|, {1|, etc. + // Special Newline, Indent, @@ -195,8 +211,11 @@ pub struct TokenWithLocation { impl TokenWithLocation { pub fn new(token: Token, line: usize, column: usize, length: usize) -> Self { - Self { token, line, column, length } + Self { + token, + line, + column, + length, + } } - } - diff --git a/src/lib.rs b/src/lib.rs index 1d93f5c..95cb6ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,22 @@ // src/lib.rs +// This is the core library of your language -pub mod lexer; -pub mod parser; +pub mod codegen; +pub mod doc_generator; pub mod environment; pub mod evaluator; -pub mod type_checker; -pub mod doc_generator; -pub mod codegen; -pub mod runtime; -pub mod quantum_backend; pub mod hardware_integration; +pub mod lexer; +pub mod parser; +pub mod quantum_backend; +pub mod runtime; +pub mod type_checker; pub use runtime::{ - quantica_rt_new_state, - quantica_rt_debug_state, - quantica_rt_apply_gate, - quantica_rt_measure, + quantica_rt_apply_gate, quantica_rt_debug_state, quantica_rt_measure, quantica_rt_new_state, }; - pub mod linker; +pub mod graphics; +pub mod graphics_runtime; +pub use graphics_runtime::*; +pub mod error; \ No newline at end of file diff --git a/src/linker.rs b/src/linker.rs index bd34e00..de18a0f 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,28 +1,28 @@ // src/linker.rs -use std::process::Command; use std::path::Path; +use std::process::Command; pub struct Linker; impl Linker { - + /// Links an object file with the Quantica runtime library to produce an executable pub fn link_executable( - object_file: &str, + object_file: &str, output_exe: &str, runtime_lib_dir: &str, enable_lto: bool, ) -> Result<(), String> { println!("🔗 Phase 5: Linking"); - - + + // Determine the runtime library path let runtime_lib = if cfg!(target_os = "windows") { format!("{}/quantica.lib", runtime_lib_dir) } else { format!("{}/libquantica.a", runtime_lib_dir) }; - - + + // Check if runtime library exists if !Path::new(&runtime_lib).exists() { return Err(format!( "Runtime library not found: {}\n \ @@ -30,60 +30,60 @@ impl Linker { runtime_lib )); } - - + + // Check if object file exists if !Path::new(object_file).exists() { return Err(format!("Object file not found: {}", object_file)); } - - + + // Try different linkers based on platform #[cfg(target_os = "windows")] { - Self::try_link_windows(object_file, output_exe, &runtime_lib,enable_lto) + Self::try_link_windows(object_file, output_exe, &runtime_lib, enable_lto) } - + #[cfg(not(target_os = "windows"))] { - Self::try_link_unix(object_file, output_exe, &runtime_lib,enable_lto) + Self::try_link_unix(object_file, output_exe, &runtime_lib, enable_lto) } } - + #[cfg(target_os = "windows")] fn try_link_windows( - object_file: &str, - output_exe: &str, + object_file: &str, + output_exe: &str, runtime_lib: &str, enable_lto: bool, ) -> Result<(), String> { - - let lto_args: Vec<&str> = if enable_lto { - + let lto_args: Vec<&str> = if enable_lto { + // In MSVC, LTO is controlled by passing an /LTCG flag. println!(" -> Enabling ThinLTO (LTCG mode)..."); - vec!["/LTCG"] - } else { - vec![] + vec!["/LTCG"] + } else { + vec![] }; - + // Try clang first (best for object files) println!(" -> Trying clang linker..."); let clang_result = Command::new("clang") .args(lto_args.iter().cloned()) .args(&[ object_file, runtime_lib, - "-o", output_exe, + "-o", + output_exe, "-Wl,/subsystem:console", ]) .output(); - + if let Ok(output) = clang_result { if output.status.success() { println!(" -> Linked with clang"); return Ok(()); } } - - + + // Try MSVC link.exe println!(" -> Trying MSVC link.exe..."); let link_result = Command::new("link.exe") .args(lto_args.iter().cloned()) @@ -93,17 +93,17 @@ impl Linker { &format!("/OUT:{}", output_exe), "/SUBSYSTEM:CONSOLE", "/NOLOGO", - - "ws2_32.lib", // Winsock (networking) - "advapi32.lib", // Advanced Windows API - "userenv.lib", // User environment - "ntdll.lib", // NT kernel functions - "bcrypt.lib", // Cryptography - "kernel32.lib", // Core Windows functions - "msvcrt.lib", // C runtime + // Windows system libraries required by Rust std library + "ws2_32.lib", // Winsock (networking) + "advapi32.lib", // Advanced Windows API + "userenv.lib", // User environment + "ntdll.lib", // NT kernel functions + "bcrypt.lib", // Cryptography + "kernel32.lib", // Core Windows functions + "msvcrt.lib", // C runtime ]) .output(); - + if let Ok(output) = link_result { if output.status.success() { println!(" -> Linked with MSVC link.exe"); @@ -114,8 +114,8 @@ impl Linker { println!(" -> link.exe: {}", stderr.trim()); } } - - + + // Try ld (MinGW) println!(" -> Trying ld (MinGW)..."); let ld_lto_arg = if enable_lto { "-Wl,--lto-thin" } else { "" }; let ld_result = Command::new("ld") @@ -123,18 +123,19 @@ impl Linker { ld_lto_arg, object_file, runtime_lib, - "-o", output_exe, + "-o", + output_exe, "-lmsvcrt", ]) .output(); - + if let Ok(output) = ld_result { if output.status.success() { println!(" -> Linked with ld"); return Ok(()); } } - + Err( "No suitable linker found.\n \ Tried: rustc, link.exe (MSVC), ld (MinGW)\n\n \ @@ -143,46 +144,36 @@ impl Linker { 2. Run: link output.o target\\debug\\quantica.lib /OUT:test_compile.exe /SUBSYSTEM:CONSOLE kernel32.lib msvcrt.lib".to_string() ) } - + #[cfg(not(target_os = "windows"))] fn try_link_unix( - object_file: &str, - output_exe: &str, + object_file: &str, + output_exe: &str, runtime_lib: &str, enable_lto: bool, ) -> Result<(), String> { let lto_arg = if enable_lto { "-flto=thin" } else { "" }; - + // Try clang first println!(" -> Trying clang linker..."); let clang_result = Command::new("clang") .arg(lto_arg) - .args(&[ - object_file, - runtime_lib, - "-o", output_exe, - "-lm", - ]) + .args(&[object_file, runtime_lib, "-o", output_exe, "-lm"]) .output(); - + if let Ok(output) = clang_result { if output.status.success() { println!(" -> Linked with clang"); return Ok(()); } } - - + + // Try gcc println!(" -> Trying gcc linker..."); let gcc_result = Command::new("gcc") .arg(lto_arg) - .args(&[ - object_file, - runtime_lib, - "-o", output_exe, - "-lm", - ]) + .args(&[object_file, runtime_lib, "-o", output_exe, "-lm"]) .output(); - + if let Ok(output) = gcc_result { if output.status.success() { println!(" -> Linked with gcc"); @@ -193,8 +184,7 @@ impl Linker { String::from_utf8_lossy(&output.stderr) )); } - + Err("No suitable linker found. Please install clang or gcc.".to_string()) } - } diff --git a/src/main.rs b/src/main.rs index 5c5729a..b4c4fe1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,59 +1,61 @@ // src/main.rs -mod lexer; -mod parser; +mod codegen; +mod doc_generator; mod environment; mod evaluator; -mod type_checker; -mod doc_generator; -mod codegen; +mod lexer; mod linker; +mod parser; mod runtime; +mod type_checker; +mod error; -mod quantum_backend; mod hardware_integration; - -use hardware_integration::{HardwareExecutor, parse_hardware_config}; +mod quantum_backend; +mod graphics; +mod graphics_runtime; +use hardware_integration::{parse_hardware_config, HardwareExecutor}; use quantum_backend::QuantumConfig; -use std::time::Instant; +use crate::codegen::Compiler; +use crate::doc_generator::DocGenerator; use crate::environment::Environment; +use crate::environment::RuntimeValue; use crate::evaluator::Evaluator; +use crate::lexer::token::Token; +use crate::linker::Linker; +use crate::parser::ast::ASTNode; use crate::type_checker::TypeChecker; use crate::type_checker::TypeEnvironment; +use inkwell::context::Context; +use inkwell::OptimizationLevel; use lexer::Lexer; +use parser::ast::ImportPath; +use parser::ast::ImportSpec; use parser::Parser; use std::env; use std::fs; -use std::path::Path; -use parser::ast::ImportSpec; -use parser::ast::ImportPath; use std::io::{self, Write}; -use crate::parser::ast::ASTNode; -use crate::environment::RuntimeValue; -use crate::doc_generator::DocGenerator; -use crate::lexer::token::Token; -use inkwell::context::Context; -use crate::codegen::Compiler; -use crate::linker::Linker; -use inkwell::OptimizationLevel; +use std::path::Path; +use std::time::Instant; +use crate::error::{ErrorReporter, Diagnostic}; +use crate::parser::ast::Loc; #[derive(Debug, Clone, Copy, PartialEq)] enum CompilationTarget { - HostCPU, // Default (LLVM/X86) - SPIRV, // Vulkan/OpenCL GPU - XLA, // TPU/Specialized Accelerator + HostCPU, // Default (LLVM/X86) + SPIRV, // Vulkan/OpenCL GPU + XLA, // TPU/Specialized Accelerator } - fn main() -> Result<(), Box> { println!("=== Quantica Compiler v0.1 ===\n"); - + let args: Vec = env::args().collect(); let mut show_ast = false; let mut show_tokens = false; - let mut verbose=false; - let mut filename: Option<&str> = None; + let mut verbose = false; let mut emit_llvm = false; let mut command: Option<&str> = None; let mut opt_level = OptimizationLevel::Default; @@ -62,36 +64,50 @@ fn main() -> Result<(), Box> { let mut hardware_config: Option = None; let mut list_devices = false; + // FIRST: Check for hardware mode and parse config + if args.contains(&"--hardware".to_string()) { + hardware_config = parse_hardware_config(&args); + } + // THEN: Parse other flags let mut i = 1; while i < args.len() { match args[i].as_str() { "--hardware" => { - hardware_config = parse_hardware_config(&args[i..]); - // Skip to after hardware arguments + // Skip --hardware and its value (already parsed) + i += 2; + } + "--device" | "--shots" | "--api-token" => { + // Skip hardware-specific flags and their values + i += 2; + } + "--no-optimize" => { i += 1; - while i < args.len() && (args[i] == "ibm" || args[i] == "google" || - args[i] == "aws" || args[i] == "ionq" || - args[i].starts_with("--device") || - args[i].starts_with("--shots") || - args[i].starts_with("--api-token")) { - i += 1; - if i > 0 && (args[i-1].starts_with("--device") || - args[i-1].starts_with("--shots") || - args[i-1].starts_with("--api-token")) { - i += 1; // skip the value after the flag - } - } } "--list-devices" => { list_devices = true; i += 1; } - "--ast" => { show_ast = true; i += 1; } - "--tokens" => { show_tokens = true; i += 1; } - "--v" => { verbose = true; i += 1; } - "--emit-llvm" => { emit_llvm = true; i += 1; } - "--lto" => { enable_lto = true; i += 1; } + "--ast" => { + show_ast = true; + i += 1; + } + "--tokens" => { + show_tokens = true; + i += 1; + } + "--v" => { + verbose = true; + i += 1; + } + "--emit-llvm" => { + emit_llvm = true; + i += 1; + } + "--lto" => { + enable_lto = true; + i += 1; + } _ if args[i].starts_with("--target=") => { let parts: Vec<&str> = args[i].split('=').collect(); if parts.len() == 2 { @@ -108,19 +124,26 @@ fn main() -> Result<(), Box> { opt_level = parse_opt_level(&args[i])?; i += 1; } - _ if args[i] == "--doc" || args[i] == "--repl" || args[i] == "--test" || - args[i] == "--lex" || args[i] == "--compile" || args[i] == "--run" => { + _ if args[i] == "--doc" + || args[i] == "--repl" + || args[i] == "--test" + || args[i] == "--lex" + || args[i] == "--compile" + || args[i] == "--run" => + { command = Some(&args[i]); i += 1; } - _ if !args[i].starts_with("--") && filename.is_none() => { - filename = Some(&args[i]); + _ => { + // Skip other args for now i += 1; } - _ => i += 1, } } + // FINALLY: Extract filename using the helper function + let filename = hardware_integration::extract_filename(&args); + if list_devices { if let Some(config) = hardware_config { use quantum_backend::BackendManager; @@ -140,45 +163,50 @@ fn main() -> Result<(), Box> { // Handle hardware execution mode if let Some(config) = hardware_config { if let Some(file) = filename { - return run_on_hardware(file, config); + println!("🚀 Executing on Quantum Hardware: {:?}\n", config.provider); + println!("📄 File: {}", file); + println!("🎯 Device: {}", config.device_name.as_ref().unwrap_or(&"default".to_string())); + println!("🎲 Shots: {}\n", config.shots); + return run_on_hardware(&file, config); } else { eprintln!("Error: No input file specified for hardware execution"); + eprintln!("Usage: quantica --hardware native "); std::process::exit(1); } } + // ... rest of your main() function continues as before ... if command.is_none() && filename.is_none() { println!("Starting REPL mode (type '.quit' to exit, '.clear' to reset)."); run_repl(); return Ok(()); } - - + if args.len() < 2 { println!("Starting REPL mode (type '.quit' to exit, '.clear' to reset)."); - run_repl(); // Run REPL if no file is given + run_repl(); return Ok(()); } - if args[1] == "--version" { + if args.len() >= 2 && args[1] == "--version" { println!("Quantica v0.1.0"); println!("Quantica By Quantica Foundation"); return Ok(()); } - if args[1] == "--help" || args[1] == "-h" { + if args.len() >= 2 && (args[1] == "--help" || args[1] == "-h") { print_help(); return Ok(()); } - if args[1] == "--doc" && args.len() >= 3 { - let filename = &args[2]; - let output_file = "docs/api.md"; - println!("📄 Generating documentation for: {}", filename); + if args.len() >= 2 && args[1] == "--doc" && args.len() >= 3 { + let fname = &args[2]; + let output_file = "docs/api.md"; + println!("📄 Generating documentation for: {}", fname); println!(" Outputting to: {}", output_file); - - match run_doc_generator(filename, output_file) { + + match run_doc_generator(fname, output_file) { Ok(()) => { println!("✓ Documentation generated successfully!"); } @@ -189,51 +217,55 @@ fn main() -> Result<(), Box> { } return Ok(()); } - - if args[1] == "--repl" { + + if args.len() >= 2 && args[1] == "--repl" { println!("Starting REPL mode (type '.quit' to exit, '.clear' to reset)."); run_repl(); return Ok(()); } - if args[1] == "--test" { + if args.len() >= 2 && args[1] == "--test" { run_test_suite(); return Ok(()); } - - - if args[1] == "--lex" && args.len() >= 3 { + + if args.len() >= 2 && args[1] == "--lex" && args.len() >= 3 { run_lexer_only(&args[2]); return Ok(()); } - if args[1] == "--compile" && args.len() >= 3 { - let filename = &args[2]; + if args.len() >= 2 && args[1] == "--compile" && args.len() >= 3 { + let fname = &args[2]; let object_file = "output.o"; - - // Determine output executable name from source file - let exe_name = Path::new(filename) + + let exe_name = Path::new(fname) .file_stem() .and_then(|s| s.to_str()) .unwrap_or("program"); - + let output_exe = if cfg!(target_os = "windows") { format!("{}.exe", exe_name) } else { exe_name.to_string() }; - - println!("🚀 Compiling to executable: {} -> {}", filename, output_exe); - - match compile_file_llvm(filename, object_file,emit_llvm,opt_level,enable_lto,target) { + + println!("🚀 Compiling to executable: {} -> {}", fname, output_exe); + + match compile_file_llvm( + fname, + object_file, + emit_llvm, + opt_level, + enable_lto, + target, + ) { Ok(()) => { println!("✓ LLVM compilation successful!\n"); - - + let runtime_lib_dir = "target/debug"; - match Linker::link_executable(object_file, &output_exe, runtime_lib_dir,enable_lto) { + match Linker::link_executable(object_file, &output_exe, runtime_lib_dir, enable_lto) + { Ok(()) => { - fs::remove_file(object_file) .map_err(|e| format!("Failed to clean up object file: {}", e))?; println!("\n🎉 Build successful!"); @@ -241,9 +273,6 @@ fn main() -> Result<(), Box> { } Err(e) => { eprintln!("\n✗ Linking failed: {}", e); - eprintln!("\nℹ️ You can try manual linking:"); - eprintln!(" 1. Open 'x64 Native Tools Command Prompt for VS'"); - eprintln!(" 2. Run: link {} target\\debug\\quantica.lib /OUT:{} /SUBSYSTEM:CONSOLE kernel32.lib msvcrt.lib", object_file, output_exe); fs::remove_file(object_file).unwrap_or_default(); std::process::exit(1); } @@ -257,11 +286,10 @@ fn main() -> Result<(), Box> { return Ok(()); } - - if args[1] == "--run" && args.len() >= 3 { - let filename = &args[2]; - println!("🚀 Running JIT Compiler: {}", filename); - match run_jit_file(filename,emit_llvm,opt_level,target) { + if args.len() >= 2 && args[1] == "--run" && args.len() >= 3 { + let fname = &args[2]; + println!("🚀 Running JIT Compiler: {}", fname); + match run_jit_file(fname, emit_llvm, opt_level, target) { Ok(()) => { println!("\n✓ JIT execution successful!"); } @@ -272,31 +300,21 @@ fn main() -> Result<(), Box> { } return Ok(()); } - + // Full compilation pipeline: Lex + Parse if let Some(file) = filename { - compile_file(file, show_ast, show_tokens, verbose); + compile_file(&file, show_ast, show_tokens, verbose); } else { eprintln!("Error: No input file specified"); eprintln!("Usage: quantica [options] "); - eprintln!("Options:"); - eprintln!(" -v, --verbose Show compilation phases"); - eprintln!(" --ast Show Abstract Syntax Tree"); - eprintln!(" --tokens Show token stream"); - eprintln!(" --emit-llvm Emit LLVM IR"); - eprintln!(" --compile Compile to executable"); - eprintln!(" --run JIT compile and run"); - eprintln!(" --doc Generate documentation"); - eprintln!(" --repl Start interactive REPL"); - eprintln!(" --test Run test suite"); + eprintln!("\nTry: quantica --help"); std::process::exit(1); } Ok(()) } -fn compile_file(filename: &str,show_ast: bool, show_tokens: bool,verbose:bool) { - +fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool) { if verbose { println!("📄 Compiling: {}\n", filename); } @@ -315,7 +333,7 @@ fn compile_file(filename: &str,show_ast: bool, show_tokens: bool,verbose:bool) { println!("{:-<60}\n", ""); } // Lexical Analysis - if verbose{ + if verbose { println!("🔤 Phase 1: Lexical Analysis"); } let mut lexer = Lexer::new(&source); @@ -341,57 +359,80 @@ fn compile_file(filename: &str,show_ast: bool, show_tokens: bool,verbose:bool) { } println!("==============\n"); } - + // Syntax Analysis (Parsing) - if verbose{ + if verbose { println!("🌳 Phase 2: Syntax Analysis"); } let mut parser = Parser::new(tokens); + let reporter = ErrorReporter::new(&source, filename); let ast = match parser.parse() { Ok(tree) => { if verbose { println!("✓ Parser succeeded!\n"); - } + } tree } Err(e) => { - eprintln!("✗ Parser error: {}", e); + let loc = parse_error_location(&e).unwrap_or( + // Fallback to 1:1 if parsing fails + Loc { line: 1, column: 1 } + ); + + let diag = Diagnostic::error(loc, &e) + .with_code("E001") + .with_hint("Check for missing semicolons, parentheses, or braces."); + + reporter.report(&diag); std::process::exit(1); } }; - if verbose{ + if verbose { println!("🔬 Phase 2.5: Type Checking"); } match TypeChecker::check_program(&ast) { Ok(()) => { - if verbose{ + if verbose { println!("✓ Type check succeeded!\n"); } } Err(e) => { - eprintln!("✗ Type error: {}", e); + let loc = parse_error_location(&e).unwrap_or( + crate::parser::ast::Loc { line: 1, column: 1 } + ); + + // 2. Clean the message (Remove the "Type Error at..." prefix) + let clean_msg = if let Some(idx) = e.find("]: ") { + &e[idx + 3..] // Skip past "]: " + } else { + &e + }; + + let diag = Diagnostic::error(loc, clean_msg) + .with_code("E002") + .with_hint("Check your variable declarations and types."); + + reporter.report(&diag); std::process::exit(1); } } - + // Display AST (Optional, but good for debugging) - + if show_ast { println!("=== ABSTRACT SYNTAX TREE ==="); print_ast(&ast, 0); println!("============================\n"); } - - - + //INTERPRETATION (EVALUATION) - if verbose{ + if verbose { println!("✨ Phase 3: Interpretation"); } // Wrap the root environment in Rc> let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); - if verbose{ + if verbose { println!("Program Output:"); println!("{:-<60}", ""); } @@ -399,16 +440,34 @@ fn compile_file(filename: &str,show_ast: bool, show_tokens: bool,verbose:bool) { let start_time = Instant::now(); let evaluation_result = Evaluator::evaluate_program(&ast, &env); let duration = start_time.elapsed(); - + println!("{:-<60}", ""); - println!("⏱️ Interpreter Time: {:.6} seconds", duration.as_secs_f64()); - + println!( + "⏱️ Interpreter Time: {:.6} seconds", + duration.as_secs_f64() + ); + match evaluation_result { Ok(_) => { println!("✓ Execution successful!"); } Err(e) => { - eprintln!("✗ Runtime Error: {}", e); + let loc = parse_error_location(&e).unwrap_or( + crate::parser::ast::Loc { line: 1, column: 1 } + ); + + // 2. Clean the message + let clean_msg = if let Some(idx) = e.find("]: ") { + &e[idx + 3..] + } else { + &e + }; + + let diag = Diagnostic::error(loc, clean_msg) + .with_code("E003") // Runtime/Panic Error + .with_hint("This error happened while the code was running."); + + reporter.report(&diag); std::process::exit(1); } } @@ -421,7 +480,7 @@ fn print_help() { println!(" quantica [OPTIONS] "); println!(); println!("OPTIONS:"); - println!(" --version, Show version information"); + println!(" --version Show version information"); println!(" --help, -h Show this help message"); println!(" --compile Compile to native executable"); println!(" --run JIT compile and run"); @@ -439,21 +498,67 @@ fn print_help() { println!(" --target= Compilation target (host, spirv, xla)"); println!(); println!("QUANTUM HARDWARE OPTIONS:"); - println!(" --hardware Run on quantum hardware (ibm, aws, ionq)"); + println!(" --hardware Run on quantum hardware/simulator"); + println!(" Providers:"); + println!(" - native : Native Rust simulator (default, fast, no deps)"); + println!(" - ibm : IBM Quantum (requires Python + qiskit)"); + println!(" - google : Google Cirq (requires Python + cirq)"); + println!(" - aws : AWS Braket"); + println!(" - ionq : IonQ"); println!(" --device Specify device name"); println!(" --shots Number of measurements (default: 1024)"); println!(" --api-token API authentication token"); println!(" --list-devices List available quantum devices"); + println!(" --no-optimize Disable circuit optimization"); println!(); println!("EXAMPLES:"); - println!(" quantica hello.qc # Run a Quantica program"); - println!(" quantica --compile app.qc # Compile to executable"); - println!(" quantica --repl # Start REPL"); - println!(" quantica --doc lib.qc # Generate documentation"); - println!(" quantica --hardware ibm --device ibmq_lima bell.qc # Run on IBM"); - println!(" quantica --hardware ibm --list-devices # List devices"); + println!(" # Run a Quantica program (interpreted)"); + println!(" quantica hello.qc"); + println!(); + println!(" # Compile to executable"); + println!(" quantica --compile app.qc"); + println!(); + println!(" # JIT compile and run with optimizations"); + println!(" quantica --run -O3 program.qc"); + println!(); + println!(" # Start REPL"); + println!(" quantica --repl"); + println!(); + println!(" # Generate documentation"); + println!(" quantica --doc lib.qc"); + println!(); + println!(" # === QUANTUM SIMULATION EXAMPLES ==="); + println!(); + println!(" # Use native Rust simulator (recommended - fast and no dependencies!)"); + println!(" quantica --hardware native bell.qc"); + println!(); + println!(" # Native simulator with 10,000 shots"); + println!(" quantica --hardware native --shots 10000 grover.qc"); + println!(); + println!(" # Use IBM Qiskit backend (requires Python)"); + println!(" quantica --hardware ibm --device ibmq_lima bell.qc"); + println!(); + println!(" # Use Google Cirq backend (requires Python)"); + println!(" quantica --hardware google bell.qc"); + println!(); + println!(" # List available devices for a provider"); + println!(" quantica --hardware native --list-devices"); + println!(); + println!("BACKEND COMPARISON:"); + println!(" Native: ✅ Fast, no dependencies, works offline"); + println!(" Qiskit: ✅ Real IBM hardware, ❌ requires Python"); + println!(" Cirq: ✅ Google integration, ❌ requires Python"); + println!(); + println!("For more information, visit: https://github.com/Quantica-Foundation/quantica-lang.git"); } -fn compile_file_llvm(filename: &str, output_file: &str, emit_llvm: bool, opt_level: OptimizationLevel,enable_lto: bool,target: CompilationTarget) -> Result<(), String> { +fn compile_file_llvm( + filename: &str, + output_file: &str, + emit_llvm: bool, + opt_level: OptimizationLevel, + enable_lto: bool, + target: CompilationTarget, +) -> Result<(), String> { println!("📄 Compiling: {}\n", filename); // Read source @@ -463,26 +568,24 @@ fn compile_file_llvm(filename: &str, output_file: &str, emit_llvm: bool, opt_lev // Lexical Analysis println!("🔤 Phase 1: Lexical Analysis"); let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize() + let tokens = lexer + .tokenize() .map_err(|e| format!("Lexer error: {}", e))?; - + //Syntax Analysis (Parsing) println!("🌳 Phase 2: Syntax Analysis"); let mut parser = Parser::new(tokens); - let ast = parser.parse() - .map_err(|e| format!("Parser error: {}", e))?; + let ast = parser.parse().map_err(|e| format!("Parser error: {}", e))?; //Type Checking println!("🔬 Phase 3: Type Checking"); - TypeChecker::check_program(&ast) - .map_err(|e| format!("Type error: {}", e))?; + TypeChecker::check_program(&ast).map_err(|e| format!("Type error: {}", e))?; println!("✓ Type check succeeded!\n"); - //Initialize LLVM println!("🤖 Phase 4: Code Generation (LLVM)"); let context = Context::create(); - let mut compiler = Compiler::new(&context,opt_level); + let mut compiler = Compiler::new(&context, opt_level); //Compile Program compiler.compile_program(&ast)?; @@ -495,20 +598,21 @@ fn compile_file_llvm(filename: &str, output_file: &str, emit_llvm: bool, opt_lev compiler.finalize_debug_info(); println!(" -> Finalized debug info"); - let module_name = Path::new(filename).file_stem().and_then(|s| s.to_str()).unwrap_or("quantica_program"); - + let module_name = Path::new(filename) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("quantica_program"); + // Simulate exporting the graph for the TPU let hlo_ir = compiler.export_to_hlo_ir(module_name)?; println!(" -> XLA/HLO IR Exported"); - - // --- LLVM IR Dump if emit_llvm { println!("\n--- GENERATED LLVM IR (Optimized) ---"); compiler.dump_ir(); println!("-------------------------------------\n"); - + // dump the specialized HLO IR when the flag is present println!("--- XLA/HLO IR (TPU Target) ---"); println!("{}", hlo_ir); @@ -517,10 +621,16 @@ fn compile_file_llvm(filename: &str, output_file: &str, emit_llvm: bool, opt_lev if target != CompilationTarget::HostCPU { // GPU/TPU compilation path: generate the IR and skip the linking to a .exe - println!("⚠️ Target is {:?}. Skipping AOT linking and outputting IR only.", target); - + println!( + "⚠️ Target is {:?}. Skipping AOT linking and outputting IR only.", + target + ); + // Simulating the final HLO/SPIR-V export by calling the exporter - let module_name = Path::new(filename).file_stem().and_then(|s| s.to_str()).unwrap_or("quantica_program"); + let module_name = Path::new(filename) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("quantica_program"); let hlo_ir = compiler.export_to_hlo_ir(module_name)?; if emit_llvm { @@ -530,18 +640,20 @@ fn compile_file_llvm(filename: &str, output_file: &str, emit_llvm: bool, opt_lev } return Ok(()); // } - + // Save to object file compiler.write_to_object_file(output_file)?; println!(" -> Emitted object file"); - - Ok(()) } - -fn run_jit_file(filename: &str, emit_llvm: bool, opt_level: OptimizationLevel, target: CompilationTarget) -> Result<(), String> { +fn run_jit_file( + filename: &str, + emit_llvm: bool, + opt_level: OptimizationLevel, + target: CompilationTarget, +) -> Result<(), String> { println!("🚀 JIT Compiling and Running: {}\n", filename); // Read, Lex, Parse @@ -549,30 +661,29 @@ fn run_jit_file(filename: &str, emit_llvm: bool, opt_level: OptimizationLevel, t .map_err(|e| format!("Failed to read file '{}': {}", filename, e))?; let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize() + let tokens = lexer + .tokenize() .map_err(|e| format!("Lexer error: {}", e))?; - + let mut parser = Parser::new(tokens); - let ast = parser.parse() - .map_err(|e| format!("Parser error: {}", e))?; + let ast = parser.parse().map_err(|e| format!("Parser error: {}", e))?; // Type Check - TypeChecker::check_program(&ast) - .map_err(|e| format!("Type error: {}", e))?; + TypeChecker::check_program(&ast).map_err(|e| format!("Type error: {}", e))?; if target == CompilationTarget::SPIRV { println!("🚀 Accelerating with SPIR-V/GPU Backend..."); } else if target == CompilationTarget::XLA { println!("🚀 Accelerating with XLA/TPU Backend..."); } - + println!("⚙️ Phase 3: Compiling for JIT..."); let jit_program = if let ASTNode::Program(ref nodes) = ast { - let has_main = nodes.iter().any(|node| { - matches!(node, ASTNode::FunctionDeclaration { name, .. } if name == "main") - }); - + let has_main = nodes.iter().any( + |node| matches!(node, ASTNode::FunctionDeclaration { name, .. } if name == "main"), + ); + if has_main { ast } else { @@ -593,13 +704,15 @@ fn run_jit_file(filename: &str, emit_llvm: bool, opt_level: OptimizationLevel, t let mut compiler = Compiler::new(&context, opt_level); let function_names = if let ASTNode::Program(s) = &jit_program { - s.iter().filter_map(|stmt| { - if let ASTNode::FunctionDeclaration { name, .. } = stmt { - Some(name.clone()) - } else { - None - } - }).collect() + s.iter() + .filter_map(|stmt| { + if let ASTNode::FunctionDeclaration { name, .. } = stmt { + Some(name.clone()) + } else { + None + } + }) + .collect() } else { Vec::new() }; @@ -612,29 +725,31 @@ fn run_jit_file(filename: &str, emit_llvm: bool, opt_level: OptimizationLevel, t println!("\n--- GENERATED LLVM IR (JIT) ---"); compiler.dump_ir(); println!("-------------------------------\n"); - } - + } + println!("✨ Program Output:"); println!(); - + // Flush stdout BEFORE executing JIT code use std::io::Write; let _ = std::io::stdout().flush(); - + let start_time = Instant::now(); compiler.run_jit()?; let duration = start_time.elapsed(); - + // Flush stdout AFTER JIT execution let _ = std::io::stdout().flush(); - + println!(); - println!("⏱️ JIT Execution Time: {:.6} seconds", duration.as_secs_f64()); + println!( + "⏱️ JIT Execution Time: {:.6} seconds", + duration.as_secs_f64() + ); Ok(()) } - fn run_test_suite() { println!("=== Running Quantica Test Suite ===\n"); let test_dir = "tests/"; @@ -651,14 +766,14 @@ fn run_test_suite() { continue; } }; - + let path = entry.path(); if path.is_file() { if let Some(ext) = path.extension() { if ext == "qc" { let filename = path.to_str().unwrap_or("unknown file"); print!("Running test: {} ... ", filename); - + // Run the test file and capture its result match run_test_file(filename) { Ok(()) => { @@ -695,52 +810,52 @@ fn run_test_suite() { } } - /// Runs the full pipeline on a single file, returning a Result. fn run_test_file(filename: &str) -> Result<(), String> { - //Read source let source = fs::read_to_string(filename) .map_err(|e| format!("Failed to read file '{}': {}", filename, e))?; - + // Lexical Analysis let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize() + let tokens = lexer + .tokenize() .map_err(|e| format!("Lexer error: {}", e))?; - + // Syntax Analysis (Parsing) let mut parser = Parser::new(tokens); - let ast = parser.parse() - .map_err(|e| format!("Parser error: {}", e))?; - + let ast = parser.parse().map_err(|e| format!("Parser error: {}", e))?; + // Type Checking - TypeChecker::check_program(&ast) - .map_err(|e| format!("Type error: {}", e))?; - + TypeChecker::check_program(&ast).map_err(|e| format!("Type error: {}", e))?; + //Interpretation (Evaluation) let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); - Evaluator::evaluate_program(&ast, &env) - .map_err(|e| format!("Runtime Error: {}", e))?; // assert() failure will be caught here + Evaluator::register_builtins(&env); + Evaluator::evaluate_program(&ast, &env).map_err(|e| format!("Runtime Error: {}", e))?; // assert() failure will be caught here //If all steps passed: Ok(()) } -fn print_ast(node: &ASTNode, indent: usize) { +fn print_ast(node: &ASTNode, indent: usize) { let prefix = " ".repeat(indent); - + match node { - ASTNode::Program(statements) => { + ASTNode::Program(statements) => { println!("{}Program:", prefix); for stmt in statements { print_ast(stmt, indent + 1); } } ASTNode::LetDeclaration { .. } => { - println!("{}LetDeclaration", prefix); - + println!("{}LetDeclaration", prefix); } - ASTNode::QuantumDeclaration { name, size, initial_state } => { + ASTNode::QuantumDeclaration { + name, + size, + initial_state, + } => { let size_str = if let Some(_s) = size { format!("[size]") } else { @@ -759,23 +874,47 @@ fn print_ast(node: &ASTNode, indent: usize) { }; println!("{}Import {} as {}", prefix, path_str, alias); } - - ASTNode::FunctionDeclaration { name, parameters, return_type, body , .. } => { + + ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + body, + .. + } => { let ret_str = if let Some(t) = return_type { format!(" -> {:?}", t) } else { String::new() }; - println!("{}Function {}({} params){}", prefix, name, parameters.len(), ret_str); + println!( + "{}Function {}({} params){}", + prefix, + name, + parameters.len(), + ret_str + ); print_ast(body, indent + 1); } - ASTNode::CircuitDeclaration { name, parameters, return_type, body, .. } => { + ASTNode::CircuitDeclaration { + name, + parameters, + return_type, + body, + .. + } => { let ret_str = if let Some(t) = return_type { format!(" -> {:?}", t) } else { String::new() }; - println!("{}Circuit {}({} params){}", prefix, name, parameters.len(), ret_str); + println!( + "{}Circuit {}({} params){}", + prefix, + name, + parameters.len(), + ret_str + ); print_ast(body, indent + 1); } ASTNode::Return(value) => { @@ -784,7 +923,12 @@ fn print_ast(node: &ASTNode, indent: usize) { print_ast(v, indent + 1); } } - ASTNode::If { condition, then_block, elif_blocks, else_block } => { + ASTNode::If { + condition, + then_block, + elif_blocks, + else_block, + } => { println!("{}If:", prefix); println!("{} Condition:", prefix); print_ast(condition, indent + 2); @@ -800,7 +944,11 @@ fn print_ast(node: &ASTNode, indent: usize) { print_ast(else_body, indent + 2); } } - ASTNode::For { variable, iterator, body } => { + ASTNode::For { + variable, + iterator, + body, + } => { println!("{}For {} in:", prefix, variable); print_ast(iterator, indent + 1); print_ast(body, indent + 1); @@ -810,20 +958,35 @@ fn print_ast(node: &ASTNode, indent: usize) { print_ast(condition, indent + 1); print_ast(body, indent + 1); } - ASTNode::Binary { operator, left, right, .. } => { + ASTNode::Binary { + operator, + left, + right, + .. + } => { println!("{}Binary {:?}:", prefix, operator); print_ast(left, indent + 1); print_ast(right, indent + 1); } - - ASTNode::ParameterizedGate { ref name, ref parameters, loc: _, ..} => { - println!("{}Parameterized gate: {} with params {:?}", prefix, name, parameters); - }, + + ASTNode::ParameterizedGate { + ref name, + ref parameters, + loc: _, + .. + } => { + println!( + "{}Parameterized gate: {} with params {:?}", + prefix, name, parameters + ); + } ASTNode::Unary { operator, operand } => { println!("{}Unary {:?}:", prefix, operator); print_ast(operand, indent + 1); } - ASTNode::FunctionCall { callee, arguments, .. } => { + ASTNode::FunctionCall { + callee, arguments, .. + } => { println!("{}FunctionCall({} args)", prefix, arguments.len()); println!("{} Callee:", prefix); print_ast(callee, indent + 2); @@ -832,7 +995,11 @@ fn print_ast(node: &ASTNode, indent: usize) { print_ast(arg, indent + 2); } } - ASTNode::Apply { gate_expr, arguments, .. } => { + ASTNode::Apply { + gate_expr, + arguments, + .. + } => { println!("{}Apply ({} args):", prefix, arguments.len()); println!("{} Gate:", prefix); print_ast(gate_expr, indent + 2); // Print the gate expression @@ -890,6 +1057,71 @@ fn print_ast(node: &ASTNode, indent: usize) { println!("{}MemberAccess .{}:", prefix, member); print_ast(object, indent + 1); } + ASTNode::ClassDeclaration { + name, + superclass, + fields, + methods, + constructor, + .. + } => { + let super_str = if let Some(s) = superclass { + format!(" extends {}", s) + } else { + String::new() + }; + println!("{}Class {}{}:", prefix, name, super_str); + + if !fields.is_empty() { + println!("{} Fields:", prefix); + for field in fields { + println!("{} {}: {:?}", prefix, field.name, field.field_type); + } + } + + if let Some(ctor) = constructor { + println!("{} Constructor:", prefix); + print_ast(ctor, indent + 2); + } + + if !methods.is_empty() { + println!("{} Methods:", prefix); + for method in methods { + println!("{} Method {}:", prefix, method.name); + print_ast(&method.body, indent + 3); + } + } + } + + ASTNode::NewInstance { + class_name, + arguments, + .. + } => { + println!("{}New Instance of {}:", prefix, class_name); + for arg in arguments { + print_ast(arg, indent + 1); + } + } + + ASTNode::MethodCall { + object, + method_name, + arguments, + .. + } => { + println!("{}Method Call .{}():", prefix, method_name); + println!("{} Object:", prefix); + print_ast(object, indent + 2); + println!("{} Arguments:", prefix); + for arg in arguments { + print_ast(arg, indent + 2); + } + } + + ASTNode::SelfRef { .. } => { + println!("{}Self", prefix); + } ASTNode::ArrayLiteral(elements) => { println!("{}Array[{}]:", prefix, elements.len()); for elem in elements { @@ -905,7 +1137,11 @@ fn print_ast(node: &ASTNode, indent: usize) { print_ast(value, indent + 2); } } - ASTNode::Range { start, end, inclusive } => { + ASTNode::Range { + start, + end, + inclusive, + } => { let op = if *inclusive { "..=" } else { ".." }; println!("{}Range {}:", prefix, op); print_ast(start, indent + 1); @@ -941,7 +1177,7 @@ fn print_ast(node: &ASTNode, indent: usize) { ImportPath::Module(m) => m.join("."), }; println!("{}FromImport {}:", prefix, path_str); - + match spec { ImportSpec::All => { println!("{} - * (All)", prefix); @@ -956,7 +1192,11 @@ fn print_ast(node: &ASTNode, indent: usize) { ASTNode::Continue => { println!("{}Continue", prefix); } - ASTNode::TryCatch { try_block, error_variable, catch_block } => { + ASTNode::TryCatch { + try_block, + error_variable, + catch_block, + } => { println!("{}Try:", prefix); print_ast(try_block, indent + 1); @@ -973,7 +1213,7 @@ fn print_ast(node: &ASTNode, indent: usize) { fn run_lexer_only(filename: &str) { println!("🔤 Tokenizing: {}\n", filename); - + let source = match fs::read_to_string(filename) { Ok(content) => content, Err(e) => { @@ -981,30 +1221,26 @@ fn run_lexer_only(filename: &str) { std::process::exit(1); } }; - + let mut lexer = Lexer::new(&source); - + match lexer.tokenize() { Ok(tokens) => { println!("✓ Lexer succeeded! Found {} tokens\n", tokens.len()); println!("{:-<60}", ""); - - let important_tokens: Vec<_> = tokens.iter() - .filter(|t| !matches!(t.token, - Token::Newline | - Token::Indent | - Token::Dedent)) + + let important_tokens: Vec<_> = tokens + .iter() + .filter(|t| !matches!(t.token, Token::Newline | Token::Indent | Token::Dedent)) .collect(); - + for (i, token) in important_tokens.iter().enumerate() { - println!("{:4}: Line {:3}, Col {:3} - {:?}", - i, - token.line, - token.column, - token.token + println!( + "{:4}: Line {:3}, Col {:3} - {:?}", + i, token.line, token.column, token.token ); } - + println!("{:-<60}", ""); println!("\n✓ Tokenization complete!"); } @@ -1019,9 +1255,10 @@ fn run_repl() { // Create persistent environments for the *entire session* let runtime_env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); let type_env = std::rc::Rc::new(std::cell::RefCell::new(TypeEnvironment::new())); - + // Prefill the type environment just once TypeChecker::prefill_environment(&type_env); + Evaluator::register_builtins(&runtime_env); let mut source_buffer = String::new(); let mut is_continuation = false; @@ -1060,7 +1297,7 @@ fn run_repl() { } // Try to compile the buffer - + // Lexer let mut lexer = Lexer::new(&source_buffer); let tokens = match lexer.tokenize() { @@ -1083,8 +1320,11 @@ fn run_repl() { let mut parser = Parser::new(tokens.clone()); let ast = match parser.parse() { Ok(tree) => tree, - Err(e) if e.contains("Unexpected EOF") || e.contains("Expected Indent") || e.contains("Dedent") => { - + Err(e) + if e.contains("Unexpected EOF") + || e.contains("Expected Indent") + || e.contains("Dedent") => + { is_continuation = true; continue; } @@ -1105,7 +1345,7 @@ fn run_repl() { if let Err(e) = type_check_result { println!("Type Error: {}", e); // Don't execute if type check fails - break; + break; } // Evaluate the single statement @@ -1130,28 +1370,29 @@ fn run_repl() { } } fn run_doc_generator(filename: &str, output_file: &str) -> Result<(), String> { - // Read source println!(" -> Step 1: Reading source file..."); let source = fs::read_to_string(filename) .map_err(|e| format!("Failed to read file '{}': {}", filename, e))?; - + // Lexical Analysis println!(" -> Step 2: Lexing tokens..."); let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize() + let tokens = lexer + .tokenize() .map_err(|e| format!("Lexer error: {}", e))?; - + // Syntax Analysis (Parsing) println!(" -> Step 3: Parsing AST..."); let mut parser = Parser::new(tokens); - let ast = parser.parse() // + let ast = parser + .parse() // .map_err(|e| format!("Parser error: {}", e))?; - + // 4. Generate Documentation println!(" -> Step 4: Generating docs..."); DocGenerator::run(&ast, output_file)?; - + println!(" -> Step 5: Done."); Ok(()) } @@ -1162,66 +1403,99 @@ fn parse_opt_level(arg: &str) -> Result { "-O1" => Ok(OptimizationLevel::Less), "-O2" => Ok(OptimizationLevel::Default), "-O3" => Ok(OptimizationLevel::Aggressive), - _ => Err(format!("Unknown optimization flag: {}. Use -O0, -O1, -O2, or -O3.", arg)), + _ => Err(format!( + "Unknown optimization flag: {}. Use -O0, -O1, -O2, or -O3.", + arg + )), } } - -fn run_on_hardware(filename: &str, config: QuantumConfig) -> Result<(), Box> { +fn run_on_hardware( + filename: &str, + config: QuantumConfig, +) -> Result<(), Box> { println!("🚀 Executing on Quantum Hardware: {:?}\n", config.provider); println!("📄 File: {}", filename); - println!("🎯 Device: {}", config.device_name.as_ref().unwrap_or(&"default".to_string())); + println!( + "🎯 Device: {}", + config + .device_name + .as_ref() + .unwrap_or(&"default".to_string()) + ); println!("🎲 Shots: {}\n", config.shots); // Read source - let source = fs::read_to_string(filename) - .map_err(|e| format!("Failed to read file: {}", e))?; + let source = fs::read_to_string(filename).map_err(|e| format!("Failed to read file: {}", e))?; // Lex println!("🔤 Phase 1: Lexical Analysis"); let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize() + let tokens = lexer + .tokenize() .map_err(|e| format!("Lexer error: {}", e))?; // Parse println!("🌳 Phase 2: Syntax Analysis"); let mut parser = Parser::new(tokens); - let ast = parser.parse() - .map_err(|e| format!("Parser error: {}", e))?; + let ast = parser.parse().map_err(|e| format!("Parser error: {}", e))?; // Type Check println!("🔬 Phase 3: Type Checking"); - TypeChecker::check_program(&ast) - .map_err(|e| format!("Type error: {}", e))?; + TypeChecker::check_program(&ast).map_err(|e| format!("Type error: {}", e))?; println!("✓ Type check succeeded!\n"); // Execute on hardware println!("📡 Phase 4: Hardware Execution"); let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); let mut executor = HardwareExecutor::new(config); - - let result = executor.execute_on_hardware(&ast, &env) + + let result = executor + .execute_on_hardware(&ast, &env) .map_err(|e| format!("Hardware execution error: {}", e))?; if result.success { println!("\n✅ Hardware execution successful!"); - + // Show top results if !result.counts.is_empty() { println!("\n📊 Measurement Results:"); let mut sorted: Vec<_> = result.counts.iter().collect(); sorted.sort_by(|a, b| b.1.cmp(a.1)); - + for (i, (bitstring, count)) in sorted.iter().take(10).enumerate() { let prob = **count as f64 / result.shots as f64; - println!(" {}. |{}⟩: {} ({:.2}%)", i + 1, bitstring, count, prob * 100.0); + println!( + " {}. |{}⟩: {} ({:.2}%)", + i + 1, + bitstring, + count, + prob * 100.0 + ); } } } else { - eprintln!("❌ Hardware execution failed: {}", - result.error_message.unwrap_or("Unknown error".to_string())); + eprintln!( + "❌ Hardware execution failed: {}", + result.error_message.unwrap_or("Unknown error".to_string()) + ); } Ok(()) } - +fn parse_error_location(error_msg: &str) -> Option { + // Look for "at [Line X, Col Y]" + let start_bytes = error_msg.find("[Line ")?; + let rest = &error_msg[start_bytes..]; + let end_bytes = rest.find(']')?; + let content = &rest[6..end_bytes]; // skip "[Line " + + // content is now "X, Col Y" + let parts: Vec<&str> = content.split(", Col ").collect(); + if parts.len() == 2 { + let line = parts[0].parse().ok()?; + let column = parts[1].parse().ok()?; + return Some(parser::ast::Loc { line, column }); + } + None +} diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 948caa7..66e8f6d 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,7 +1,7 @@ // src/parser/ast.rs -use std::fmt; use std::collections::HashMap; -#[derive(Debug, Clone, PartialEq,Copy)] +use std::fmt; +#[derive(Debug, Clone, PartialEq, Copy)] pub struct Loc { pub line: usize, pub column: usize, @@ -22,18 +22,16 @@ pub enum ImportPath { #[derive(Debug, Clone, PartialEq)] pub enum ImportSpec { List(Vec), // For 'import name1, name2' - All, // For 'import *' + All, // For 'import *' } - #[derive(Debug, Clone, PartialEq)] pub enum ASTNode { // Program root Program(Vec), - + // Statements LetDeclaration { - name: String, type_annotation: Option, value: Box, @@ -42,7 +40,7 @@ pub enum ASTNode { Import { path: ImportPath, // e.g., "./my_math.qc" - alias: String, // e.g., "math" + alias: String, // e.g., "math" }, FromImport { @@ -55,28 +53,26 @@ pub enum ASTNode { error_variable: Option, // For 'catch err:' catch_block: Box, }, - + QuantumDeclaration { name: String, size: Option>, // For arrays like quantum q[5] initial_state: Option>, }, FunctionDeclaration { - name: String, parameters: Vec, return_type: Option, body: Box, }, CircuitDeclaration { - name: String, parameters: Vec, return_type: Option, body: Box, }, Return(Option>), - + // Control flow If { condition: Box, @@ -99,13 +95,13 @@ pub enum ASTNode { }, Break, Continue, - + // Expressions Binary { operator: BinaryOperator, left: Box, right: Box, - loc: Loc + loc: Loc, }, Unary { operator: UnaryOperator, @@ -125,7 +121,6 @@ pub enum ASTNode { Gate { name: String, loc: Loc, - }, ParameterizedGate { @@ -142,16 +137,16 @@ pub enum ASTNode { loc: Loc, }, Measure(Box), - + // Literals IntLiteral(i64), FloatLiteral(f64), StringLiteral(String), BoolLiteral(bool), NoneLiteral, - QuantumKet(String), // |0}, |1}, |+}, etc. - QuantumBra(String), // {0|, {1|, etc. - + QuantumKet(String), // |0}, |1}, |+}, etc. + QuantumBra(String), // {0|, {1|, etc. + // Variables and access Identifier { name: String, @@ -162,11 +157,34 @@ pub enum ASTNode { index: Box, loc: Loc, }, + ClassDeclaration { + name: String, + superclass: Option, + fields: Vec, + methods: Vec, + constructor: Option>, + loc: Loc, + }, + + // Object Creation + NewInstance { + class_name: String, + arguments: Vec, + loc: Loc, + }, + MethodCall { + object: Box, + method_name: String, + arguments: Vec, + loc: Loc, + }, MemberAccess { object: Box, member: String, }, - + + SelfRef { loc: Loc }, + // Collections ArrayLiteral(Vec), DictLiteral(Vec<(ASTNode, ASTNode)>), @@ -175,10 +193,10 @@ pub enum ASTNode { end: Box, inclusive: bool, }, - + // Block Block(Vec), - + // Assignment Assignment { target: Box, @@ -188,12 +206,22 @@ pub enum ASTNode { #[derive(Debug, Clone, PartialEq)] pub enum BinaryOperator { - Add, Sub, Mul, Div, Mod, - Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual, - And, Or, - TensorProduct, // *** - Pipeline, - Power, + Add, + Sub, + Mul, + Div, + Mod, + Equal, + NotEqual, + Less, + Greater, + LessEqual, + GreaterEqual, + And, + Or, + TensorProduct, // *** + Pipeline, + Power, } #[derive(Debug, Clone, PartialEq)] @@ -211,10 +239,24 @@ pub struct Parameter { #[derive(Debug, Clone, PartialEq)] pub enum Type { - Int, Int8, Int16, Int32, Int64, Int128, - Uint, Uint8, Uint16, Uint32, Uint64, Uint128, - Float, Float32, Float64, - Complex, Complex64, Complex128, + Int, + Int8, + Int16, + Int32, + Int64, + Int128, + Uint, + Uint8, + Uint16, + Uint32, + Uint64, + Uint128, + Float, + Float32, + Float64, + Complex, + Complex64, + Complex128, Bool, Bit, String, @@ -228,6 +270,8 @@ pub enum Type { Function(Vec, Box), Custom(String), Any, + Class(String), // For class types + Instance(String), None, } @@ -236,5 +280,22 @@ pub enum Pattern { Literal(ASTNode), Identifier(String), Wildcard, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ClassField { + pub name: String, + pub field_type: Type, + pub default_value: Option>, + pub is_public: bool, +} +#[derive(Debug, Clone, PartialEq)] +pub struct ClassMethod { + pub name: String, + pub parameters: Vec, + pub return_type: Option, + pub body: Box, + pub is_public: bool, + pub is_static: bool, } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f158725..9208406 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2,8 +2,8 @@ pub mod ast; use crate::lexer::token::{Token, TokenWithLocation}; -use ast::*; use crate::parser::ast::UnaryOperator; +use ast::*; pub struct Parser { tokens: Vec, @@ -12,60 +12,61 @@ pub struct Parser { impl Parser { pub fn new(tokens: Vec) -> Self { - Self { tokens, position: 0 } + Self { + tokens, + position: 0, + } } - + pub fn parse(&mut self) -> Result { let mut statements = Vec::new(); - + while !self.is_at_end() { - + // Skip newlines at top level if self.match_token(&Token::Newline) { continue; } - + statements.push(self.parse_statement()?); } - + Ok(ASTNode::Program(statements)) } fn parse_array_literal(&mut self) -> Result { let mut elements = Vec::new(); - + // Check if the array is empty: [] if self.check(&Token::RightBracket) { - self.advance(); + self.advance(); // Consume ']' return Ok(ASTNode::ArrayLiteral(elements)); } - - + + // Parse elements loop { - + // Parse one element elements.push(self.parse_expression()?); - - + + // If the next token is ']', we are done if self.check(&Token::RightBracket) { break; } - - + + // Otherwise, expect a comma separator self.expect(&Token::Comma)?; } - - self.expect(&Token::RightBracket)?; - + + self.expect(&Token::RightBracket)?; // Consume ']' + Ok(ASTNode::ArrayLiteral(elements)) } - + fn parse_statement(&mut self) -> Result { - - - + // Skip newlines + if self.is_at_end() { return Err("Unexpected end of file".to_string()); } - - + match &self.current()?.token { Token::Let => self.parse_let_declaration(), Token::Mut => self.parse_mut_declaration(), @@ -81,6 +82,8 @@ impl Parser { Token::For => self.parse_for(), Token::Apply => self.parse_apply_statement(), Token::While => self.parse_while(), + Token::Class => self.parse_class_declaration(), + Token::New => self.parse_new_instance(), Token::Break => { self.advance(); self.skip_newlines(); @@ -92,11 +95,10 @@ impl Parser { Ok(ASTNode::Continue) } _ => { - - + // Expression statement or assignment let expr = self.parse_expression()?; - - + + // Check for assignment if self.match_token(&Token::Equal) { let value = self.parse_expression()?; self.skip_newlines(); @@ -105,38 +107,42 @@ impl Parser { value: Box::new(value), }); } - + self.skip_newlines(); - return Ok(expr) + return Ok(expr); } } } fn parse_gate_expression(&mut self) -> Result { let loc = self.get_loc(self.current()?); - - if self.match_token(&Token::Controlled) { + if self.match_token(&Token::Controlled) { + // Parse: controlled( ... ) self.expect(&Token::LeftParen)?; let gate_expr = self.parse_gate_expression()?; self.expect(&Token::RightParen)?; - Ok(ASTNode::Controlled { gate_expr: Box::new(gate_expr), loc }) - + Ok(ASTNode::Controlled { + gate_expr: Box::new(gate_expr), + loc, + }) } else if self.match_token(&Token::Dagger) { - + // Parse: dagger( ... ) self.expect(&Token::LeftParen)?; let gate_expr = self.parse_gate_expression()?; self.expect(&Token::RightParen)?; - Ok(ASTNode::Dagger { gate_expr: Box::new(gate_expr), loc }) - + Ok(ASTNode::Dagger { + gate_expr: Box::new(gate_expr), + loc, + }) } else { - + // Base case: A simple gate or parameterized gate let gate_token = self.expect_identifier()?; let gate_name = self.extract_identifier_name(&gate_token)?; - - + + // Check if it's parameterized (e.g., RX(PI)) if self.check(&Token::LeftParen) { - self.advance(); + self.advance(); // consume '(' let mut params = Vec::new(); if !self.check(&Token::RightParen) { loop { @@ -153,7 +159,7 @@ impl Parser { loc: self.get_loc(&gate_token), }) } else { - + // Simple gate (e.g., X, CNOT) Ok(ASTNode::Gate { name: gate_name, loc: self.get_loc(&gate_token), @@ -163,136 +169,176 @@ impl Parser { } fn parse_apply_statement(&mut self) -> Result { - let loc = self.get_loc(self.current()?); - self.expect(&Token::Apply)?; + let loc = self.get_loc(self.current()?); + self.expect(&Token::Apply)?; // Consume 'apply' + // Check if this is "apply dagger(...)(args)" for CIRCUITS/FUNCTIONS (not gates) + // Detect pattern: dagger(identifier...)( + if self.check(&Token::Dagger) { + let saved_pos = self.position; + self.advance(); // consume 'dagger' - if self.check(&Token::Dagger) { - let saved_pos = self.position; - self.advance(); - - if self.check(&Token::LeftParen) { - self.advance(); - - - let is_gate_expr = matches!( - self.current().ok().map(|t| &t.token), - Some(Token::X) | Some(Token::Y) | Some(Token::Z) | Some(Token::S) | Some(Token::T) | - Some(Token::Hadamard) | Some(Token::Cnot) | Some(Token::Swap) | Some(Token::Reset) | - Some(Token::CZ) | Some(Token::CS) | Some(Token::CT) | Some(Token::CCX) | Some(Token::Toffoli) | - Some(Token::RX) | Some(Token::RY) | Some(Token::RZ) | Some(Token::U) | Some(Token::CPhase) | - Some(Token::Controlled) | Some(Token::Dagger) - ); - - - self.position = saved_pos; - - if is_gate_expr { + if self.check(&Token::LeftParen) { + self.advance(); // consume '(' + + // Peek ahead - if it's a gate token or 'controlled', parse as gate + let is_gate_expr = matches!( + self.current().ok().map(|t| &t.token), + Some(Token::X) + | Some(Token::Y) + | Some(Token::Z) + | Some(Token::S) + | Some(Token::T) + | Some(Token::Hadamard) + | Some(Token::Cnot) + | Some(Token::Swap) + | Some(Token::Reset) + | Some(Token::CZ) + | Some(Token::CS) + | Some(Token::CT) + | Some(Token::CCX) + | Some(Token::Toffoli) + | Some(Token::RX) + | Some(Token::RY) + | Some(Token::RZ) + | Some(Token::U) + | Some(Token::CPhase) + | Some(Token::Controlled) + | Some(Token::Dagger) + ); + + // Reset position + self.position = saved_pos; + + if is_gate_expr { + // Parse as: apply dagger(gate_expr)(qubit_args) + // This is a gate application, not a circuit call + // Fall through to regular gate parsing below + } else { + // Parse as: apply dagger(circuit_identifier)(args) + self.advance(); // consume 'dagger' + self.expect(&Token::LeftParen)?; + let callee_expr = self.parse_expression()?; + self.expect(&Token::RightParen)?; + self.expect(&Token::LeftParen)?; + let arguments = self.parse_arguments()?; + self.skip_newlines(); + return Ok(ASTNode::FunctionCall { + callee: Box::new(callee_expr), + arguments, + loc, + is_dagger: true, + }); + } } else { - - self.advance(); - self.expect(&Token::LeftParen)?; - let callee_expr = self.parse_expression()?; - self.expect(&Token::RightParen)?; - self.expect(&Token::LeftParen)?; - let arguments = self.parse_arguments()?; - self.skip_newlines(); - - return Ok(ASTNode::FunctionCall { - callee: Box::new(callee_expr), - arguments, - loc, - is_dagger: true, - }); + // Reset if no '(' after dagger + self.position = saved_pos; } - } else { - - self.position = saved_pos; } - } + // Lookahead to detect if this is a circuit/function call (not a gate) + let saved_pos = self.position; + let mut is_function_call = false; + + if self.check(&Token::Identifier("".to_string())) { + let mut temp_pos = self.position; + temp_pos += 1; - let saved_pos = self.position; - let mut is_function_call = false; - - if self.check(&Token::Identifier("".to_string())) { - let mut temp_pos = self.position; - temp_pos += 1; - - while temp_pos < self.tokens.len() { - if std::mem::discriminant(&self.tokens[temp_pos].token) == std::mem::discriminant(&Token::Dot) { - temp_pos += 1; - if temp_pos < self.tokens.len() && - std::mem::discriminant(&self.tokens[temp_pos].token) == std::mem::discriminant(&Token::Identifier("".to_string())) { + while temp_pos < self.tokens.len() { + if std::mem::discriminant(&self.tokens[temp_pos].token) + == std::mem::discriminant(&Token::Dot) + { temp_pos += 1; + if temp_pos < self.tokens.len() + && std::mem::discriminant(&self.tokens[temp_pos].token) + == std::mem::discriminant(&Token::Identifier("".to_string())) + { + temp_pos += 1; + } else { + break; + } } else { break; } - } else { - break; + } + + if temp_pos < self.tokens.len() + && std::mem::discriminant(&self.tokens[temp_pos].token) + == std::mem::discriminant(&Token::LeftParen) + { + is_function_call = true; } } - - if temp_pos < self.tokens.len() && - std::mem::discriminant(&self.tokens[temp_pos].token) == std::mem::discriminant(&Token::LeftParen) { - is_function_call = true; - } - } - - self.position = saved_pos; - - if is_function_call { - let mut callee_expr = self.parse_primary()?; - - while self.check(&Token::Dot) { - self.advance(); - let name_loc = self.expect_identifier()?; - let member = self.extract_identifier_name(&name_loc)?; - - callee_expr = ASTNode::MemberAccess { - object: Box::new(callee_expr), - member, - }; + + self.position = saved_pos; + + if is_function_call { + let mut callee_expr = self.parse_primary()?; + + while self.check(&Token::Dot) { + self.advance(); + let name_loc = self.expect_identifier()?; + let member = self.extract_identifier_name(&name_loc)?; + + callee_expr = ASTNode::MemberAccess { + object: Box::new(callee_expr), + member, + }; + } + + self.expect(&Token::LeftParen)?; + let arguments = self.parse_arguments()?; + self.skip_newlines(); + + return Ok(ASTNode::FunctionCall { + callee: Box::new(callee_expr), + arguments, + loc, + is_dagger: false, + }); } - - self.expect(&Token::LeftParen)?; - let arguments = self.parse_arguments()?; + + // Parse as a gate application + let apply_node = self.parse_gate_application(loc)?; self.skip_newlines(); - - return Ok(ASTNode::FunctionCall { - callee: Box::new(callee_expr), - arguments, - loc, - is_dagger: false, - }); + Ok(apply_node) } - - - let apply_node = self.parse_gate_application(loc)?; - self.skip_newlines(); - Ok(apply_node) -} fn parse_gate_application(&mut self, loc: Loc) -> Result { + // Special handling for simple gate names like X, H, etc. + // These should parse as: apply X(qubit_args), not apply X(...)(qubit_args) + + // Check if this is a simple gate (not controlled or dagger) let is_simple_gate = matches!( self.current()?.token, - Token::X | Token::Y | Token::Z | Token::Hadamard | Token::S | Token::T | - Token::Cnot | Token::Swap | Token::Reset | Token::CZ | Token::CS | Token::CT | - Token::CCX | Token::Toffoli + Token::X + | Token::Y + | Token::Z + | Token::Hadamard + | Token::S + | Token::T + | Token::Cnot + | Token::Swap + | Token::Reset + | Token::CZ + | Token::CS + | Token::CT + | Token::CCX + | Token::Toffoli ); - + let is_parameterized_gate = matches!( self.current()?.token, Token::RX | Token::RY | Token::RZ | Token::U | Token::CPhase ); - + // If it's a simple gate or parameterized gate at top level, parse as: GATE(qubit_args) if is_simple_gate || is_parameterized_gate { let gate_token = self.expect_identifier()?; let gate_name = self.extract_identifier_name(&gate_token)?; - + // For parameterized gates, parse parameters first if is_parameterized_gate { self.expect(&Token::LeftParen)?; let mut params = Vec::new(); @@ -305,35 +351,35 @@ impl Parser { } } self.expect(&Token::RightParen)?; - + let gate_expr = ASTNode::ParameterizedGate { name: gate_name, parameters: params, loc, }; - + // Now parse qubit arguments self.skip_newlines(); self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; - + return Ok(ASTNode::Apply { gate_expr: Box::new(gate_expr), arguments, loc, }); } else { - + // Simple gate let gate_expr = ASTNode::Gate { name: gate_name, loc, }; - + // Now parse qubit arguments self.skip_newlines(); self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; - + return Ok(ASTNode::Apply { gate_expr: Box::new(gate_expr), arguments, @@ -341,14 +387,14 @@ impl Parser { }); } } - + // For controlled/dagger gates, use parse_gate_expression let gate_expr = self.parse_gate_expression()?; - + // Skip any newlines before the qubit arguments self.skip_newlines(); - + // Now expect '(' for the qubit arguments if !self.check(&Token::LeftParen) { let current_token = self.current()?; return Err(format!( @@ -363,23 +409,22 @@ impl Parser { current_token.line, current_token.column, current_token.token )); } - self.advance(); - let arguments = self.parse_arguments()?; - + self.advance(); // Consume '(' + let arguments = self.parse_arguments()?; // This consumes ')' + Ok(ASTNode::Apply { gate_expr: Box::new(gate_expr), arguments, loc, }) } - - - fn parse_let_declaration(&mut self,) -> Result { + + fn parse_let_declaration(&mut self) -> Result { self.expect(&Token::Let)?; - - let name_loc = self.expect_identifier()?; + + let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; - + let type_annotation = if self.match_token(&Token::Colon) { Some(self.parse_type()?) } else { @@ -391,43 +436,38 @@ impl Parser { } else { Box::new(ASTNode::NoneLiteral) }; - - + self.skip_newlines(); - + Ok(ASTNode::LetDeclaration { - name, type_annotation, value: value_box, is_mutable: false, }) } - - fn parse_mut_declaration(&mut self,) -> Result { + + fn parse_mut_declaration(&mut self) -> Result { self.expect(&Token::Mut)?; - + let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; - + let type_annotation = if self.match_token(&Token::Colon) { Some(self.parse_type()?) } else { None }; - - + let value_box = if self.match_token(&Token::Equal) { Box::new(self.parse_expression()?) } else { Box::new(ASTNode::NoneLiteral) }; - - + self.skip_newlines(); - + Ok(ASTNode::LetDeclaration { - name, type_annotation, value: value_box, @@ -466,7 +506,7 @@ impl Parser { fn parse_import_statement(&mut self) -> Result { self.expect(&Token::Import)?; - + let path; if self.check(&Token::StringLiteral("".into())) { let current_token = self.current()?.clone(); @@ -474,25 +514,22 @@ impl Parser { Token::StringLiteral(s) => { self.advance(); s.clone() - }, + } _ => unreachable!(), }; path = ImportPath::File(path_str); } else { path = ImportPath::Module(self.parse_module_path()?); } - + self.expect(&Token::As)?; - + let name_loc = self.expect_identifier()?; let alias = self.extract_identifier_name(&name_loc)?; - - self.skip_newlines(); - - Ok(ASTNode::Import { - path, - alias, - }) + + self.skip_newlines(); + + Ok(ASTNode::Import { path, alias }) } fn parse_module_path(&mut self) -> Result, String> { @@ -510,14 +547,13 @@ impl Parser { Ok(path_segments) } - - + fn parse_quantum_declaration(&mut self) -> Result { self.expect(&Token::Quantum)?; - - let name_loc = self.expect_identifier()?; + + let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; - + let size = if self.match_token(&Token::LeftBracket) { let size_expr = self.parse_expression()?; self.expect(&Token::RightBracket)?; @@ -525,24 +561,22 @@ impl Parser { } else { None }; - + let initial_state = if self.match_token(&Token::Equal) { Some(Box::new(self.parse_expression()?)) } else { None }; - - + if size.is_some() && initial_state.is_some() { return Err(format!( "Syntax Error at {}: Cannot define a register size `[N]` and an initial state `=` at the same time.", self.get_loc(&name_loc) )); } - self.skip_newlines(); - + Ok(ASTNode::QuantumDeclaration { name, size, @@ -552,24 +586,24 @@ impl Parser { fn parse_from_import(&mut self) -> Result { self.expect(&Token::From)?; - + let path; if self.check(&Token::StringLiteral("".into())) { let current_token = self.current()?.clone(); let path_str = match ¤t_token.token { Token::StringLiteral(s) => { - self.advance(); + self.advance(); s.clone() - }, + } _ => unreachable!(), }; path = ImportPath::File(path_str); } else { path = ImportPath::Module(self.parse_module_path()?); } - + self.expect(&Token::Import)?; - + let spec; if self.match_token(&Token::Star) { spec = ImportSpec::All; @@ -579,110 +613,271 @@ impl Parser { let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; names.push(name); - + if !self.match_token(&Token::Comma) { - break; + break; } } spec = ImportSpec::List(names); } - - self.skip_newlines(); - - Ok(ASTNode::FromImport { - path, - spec, - }) + + self.skip_newlines(); + + Ok(ASTNode::FromImport { path, spec }) } - - fn parse_function_declaration(&mut self,) -> Result { + + fn parse_function_declaration(&mut self) -> Result { self.expect(&Token::Func)?; - - let name_loc = self.expect_identifier()?; + + let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; - + self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; self.expect(&Token::RightParen)?; - + let return_type = if self.match_token(&Token::Arrow) { Some(self.parse_type()?) } else { None }; - - self.expect(&Token::Colon)?; + + if self.check(&Token::Colon) { + self.advance(); + } self.skip_newlines(); - + let body = self.parse_block()?; - + Ok(ASTNode::FunctionDeclaration { - name, parameters, return_type, body: Box::new(body), }) } - - fn parse_circuit_declaration(&mut self,) -> Result { + + fn parse_class_declaration(&mut self) -> Result { + let loc = self.get_loc(self.current()?); + self.expect(&Token::Class)?; + + let name_loc = self.expect_identifier()?; + let name = self.extract_identifier_name(&name_loc)?; + + // Optional inheritance + let superclass = if self.match_token(&Token::LeftParen) { + let super_loc = self.expect_identifier()?; + let super_name = self.extract_identifier_name(&super_loc)?; + self.expect(&Token::RightParen)?; + Some(super_name) + } else { + None + }; + + if self.check(&Token::Colon) { + self.advance(); + } + + self.skip_newlines(); + self.expect(&Token::Indent)?; + + let mut fields = Vec::new(); + let mut methods = Vec::new(); + let mut constructor = None; + + while !self.check(&Token::Dedent) && !self.is_at_end() { + self.skip_newlines(); + + // --- FIX START: Handle spurious Dedent caused by empty lines --- + if self.check(&Token::Dedent) { + // Peek ahead: If this Dedent is followed by Newlines and then an Indent, + // it's just a glitch caused by an empty line resetting indentation. + let mut temp_pos = self.position + 1; + while temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Newline) { + temp_pos += 1; + } + + if temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Indent) { + // It is spurious! Consume the Dedent, Newlines, and the restoring Indent. + self.advance(); // consume Dedent + self.skip_newlines(); // consume Newlines + self.advance(); // consume Indent + continue; // Continue parsing class members + } + + // If not followed by Indent, it's a real Dedent (end of class). + break; + } + // --- FIX END --- + + // Check for visibility modifier + let is_public = if self.match_token(&Token::Public) { + true + } else if self.match_token(&Token::Private) { + false + } else { + true // default to public + }; + + // Check for static modifier + let is_static = self.match_token(&Token::Static); + + if self.check(&Token::Func) { + // Parse method + self.advance(); + let method_name_loc = self.expect_identifier()?; + let method_name = self.extract_identifier_name(&method_name_loc)?; + + self.expect(&Token::LeftParen)?; + let parameters = self.parse_parameters()?; + self.expect(&Token::RightParen)?; + + let return_type = if self.match_token(&Token::Arrow) { + Some(self.parse_type()?) + } else { + None + }; + + if self.check(&Token::Colon) { + self.advance(); + } + + // FIX: Do NOT call self.skip_newlines() here. + // Let parse_block handle the layout. + let body = self.parse_block()?; + + // Check if this is a constructor + if method_name == "init" || method_name == "__init__" { + constructor = Some(Box::new(ASTNode::FunctionDeclaration { + name: method_name.clone(), + parameters, + return_type: None, + body: Box::new(body), + })); + } else { + methods.push(ClassMethod { + name: method_name, + parameters, + return_type, + body: Box::new(body), + is_public, + is_static, + }); + } + } else if self.check(&Token::Let) || self.check(&Token::Mut) || matches!(self.current()?.token, Token::Identifier(_)) { + // Parse field + let is_mutable = self.match_token(&Token::Mut); + if !is_mutable { + self.match_token(&Token::Let); + } + + let field_name_loc = self.expect_identifier()?; + let field_name = self.extract_identifier_name(&field_name_loc)?; + + self.expect(&Token::Colon)?; + let field_type = self.parse_type()?; + + let default_value = if self.match_token(&Token::Equal) { + Some(Box::new(self.parse_expression()?)) + } else { + None + }; + + self.skip_newlines(); + + fields.push(ClassField { + name: field_name, + field_type, + default_value, + is_public, + }); + } else { + return Err(format!("Expected field or method declaration in class at {}", loc)); + } + } + + self.expect(&Token::Dedent)?; + + Ok(ASTNode::ClassDeclaration { + name, + superclass, + fields, + methods, + constructor, + loc, + }) + } + + fn parse_new_instance(&mut self) -> Result { + let loc = self.get_loc(self.current()?); + self.expect(&Token::New)?; + + let class_name_loc = self.expect_identifier()?; + let class_name = self.extract_identifier_name(&class_name_loc)?; + + self.expect(&Token::LeftParen)?; + let arguments = self.parse_arguments()?; + + Ok(ASTNode::NewInstance { + class_name, + arguments, + loc, + }) + } + + fn parse_circuit_declaration(&mut self) -> Result { self.expect(&Token::Circuit)?; - - let name_loc = self.expect_identifier()?; + + let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; - + self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; self.expect(&Token::RightParen)?; - + let return_type = if self.match_token(&Token::Arrow) { Some(self.parse_type()?) } else { None }; - + self.expect(&Token::Colon)?; self.skip_newlines(); - + let body = self.parse_block()?; - + Ok(ASTNode::CircuitDeclaration { - name, parameters, return_type, body: Box::new(body), }) } - + fn parse_parameters(&mut self) -> Result, String> { let mut params = Vec::new(); - + if self.check(&Token::RightParen) { return Ok(params); } - + loop { - let name_loc = self.expect_identifier()?; + let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; self.expect(&Token::Colon)?; let param_type = self.parse_type()?; - - params.push(Parameter { - name, - param_type, - }); - + + params.push(Parameter { name, param_type }); + if !self.match_token(&Token::Comma) { break; } } - + Ok(params) } - fn parse_arguments(&mut self) -> Result, String> { let mut arguments = Vec::new(); if !self.check(&Token::RightParen) { @@ -693,33 +888,78 @@ impl Parser { } } } - self.expect(&Token::RightParen)?; + self.expect(&Token::RightParen)?; // <-- THIS MUST CONSUME THE FINAL ')' Ok(arguments) } - + fn parse_type(&mut self) -> Result { let type_token = self.current()?.token.clone(); - + let base_type = match type_token { - - Token::Int => { self.advance(); Type::Int }, - Token::Int8 => { self.advance(); Type::Int8 }, - Token::Int16 => { self.advance(); Type::Int16 }, - Token::Int32 => { self.advance(); Type::Int32 }, - Token::Int64 => { self.advance(); Type::Int64 }, - Token::Uint => { self.advance(); Type::Uint }, - Token::Float => { self.advance(); Type::Float }, - Token::Float32 => { self.advance(); Type::Float32 }, - Token::Float64 => { self.advance(); Type::Float64 }, - Token::Bool => { self.advance(); Type::Bool }, - Token::Bit => { self.advance(); Type::Bit }, - Token::String => { self.advance(); Type::String }, - Token::None => { self.advance(); Type::None }, - Token::Any => { self.advance(); Type::Any }, - - - Token::Quantum => { self.advance(); Type::QuantumRegister(None) }, - + // Built-in Type Tokens + Token::Int => { + self.advance(); + Type::Int + } + Token::Int8 => { + self.advance(); + Type::Int8 + } + Token::Int16 => { + self.advance(); + Type::Int16 + } + Token::Int32 => { + self.advance(); + Type::Int32 + } + Token::Int64 => { + self.advance(); + Type::Int64 + } + Token::Uint => { + self.advance(); + Type::Uint + } + Token::Float => { + self.advance(); + Type::Float + } + Token::Float32 => { + self.advance(); + Type::Float32 + } + Token::Float64 => { + self.advance(); + Type::Float64 + } + Token::Bool => { + self.advance(); + Type::Bool + } + Token::Bit => { + self.advance(); + Type::Bit + } + Token::String => { + self.advance(); + Type::String + } + Token::None => { + self.advance(); + Type::None + } + Token::Any => { + self.advance(); + Type::Any + } + + // --- FIXED THIS LINE --- + Token::Quantum => { + self.advance(); + Type::QuantumRegister(None) + } + Token::Identifier(name) => { self.advance(); match name.as_str() { @@ -728,55 +968,55 @@ impl Parser { _ => Type::Custom(name), } } - + _ => return Err(format!("Expected a type, but found {:?}", type_token)), }; - + if self.match_token(&Token::LeftBracket) { if self.check(&Token::RightBracket) { self.advance(); return Ok(Type::Array(Box::new(base_type))); } - + let size = self.parse_expression()?; self.expect(&Token::RightBracket)?; - + if let ASTNode::IntLiteral(n) = size { return Ok(Type::QuantumArray(Box::new(base_type), Some(n as usize))); } - + return Ok(Type::QuantumArray(Box::new(base_type), None)); } - + Ok(base_type) } - + fn parse_return(&mut self) -> Result { self.expect(&Token::Return)?; - + if self.check(&Token::Newline) || self.is_at_end() { self.skip_newlines(); return Ok(ASTNode::Return(None)); } - + let value = self.parse_expression()?; self.skip_newlines(); - + Ok(ASTNode::Return(Some(Box::new(value)))) } - + fn parse_if(&mut self) -> Result { self.expect(&Token::If)?; - + let condition = self.parse_expression()?; self.expect(&Token::Colon)?; - + let then_block = if self.check(&Token::Newline) { self.skip_newlines(); self.parse_block()? } else { let expr = self.parse_expression()?; - self.skip_newlines(); + self.skip_newlines(); expr }; @@ -784,7 +1024,7 @@ impl Parser { while self.match_token(&Token::Elif) { let elif_condition = self.parse_expression()?; self.expect(&Token::Colon)?; - + let elif_body = if self.check(&Token::Newline) { self.skip_newlines(); self.parse_block()? @@ -793,13 +1033,13 @@ impl Parser { self.skip_newlines(); expr }; - + elif_blocks.push((elif_condition, elif_body)); } let else_block = if self.match_token(&Token::Else) { self.expect(&Token::Colon)?; - + let else_body = if self.check(&Token::Newline) { self.skip_newlines(); self.parse_block()? @@ -808,12 +1048,12 @@ impl Parser { self.skip_newlines(); expr }; - + Some(Box::new(else_body)) } else { None }; - + Ok(ASTNode::If { condition: Box::new(condition), then_block: Box::new(then_block), @@ -821,55 +1061,55 @@ impl Parser { else_block, }) } - + fn parse_match(&mut self) -> Result { self.expect(&Token::Match)?; - + let value = self.parse_expression()?; self.expect(&Token::Colon)?; self.skip_newlines(); - + self.expect(&Token::Indent)?; - + let mut cases = Vec::new(); - + while !self.check(&Token::Dedent) && !self.is_at_end() { self.skip_newlines(); - + if self.check(&Token::Dedent) { break; } - + self.expect(&Token::Case)?; - + let pattern = self.parse_pattern()?; self.expect(&Token::Arrow)?; - + let body = self.parse_expression()?; self.skip_newlines(); - + cases.push((pattern, body)); } - + self.expect(&Token::Dedent)?; - + Ok(ASTNode::Match { value: Box::new(value), cases, }) } - + fn parse_pattern(&mut self) -> Result { if self.match_token(&Token::Identifier("_".to_string())) { return Ok(Pattern::Wildcard); } - + if let Token::Identifier(name) = &self.current()?.token { let id = name.clone(); self.advance(); return Ok(Pattern::Identifier(id)); } - + let expr = self.parse_primary()?; Ok(Pattern::Literal(expr)) } @@ -884,101 +1124,148 @@ impl Parser { } None } - + fn parse_for(&mut self) -> Result { self.expect(&Token::For)?; - + let variable_loc = self.expect_identifier()?; let variable = self.extract_identifier_name(&variable_loc)?; self.expect(&Token::In)?; - + let iterator = self.parse_expression()?; self.expect(&Token::Colon)?; self.skip_newlines(); - + let body = self.parse_block()?; - + Ok(ASTNode::For { variable, iterator: Box::new(iterator), body: Box::new(body), }) } - + fn parse_while(&mut self) -> Result { self.expect(&Token::While)?; - + let condition = self.parse_expression()?; self.expect(&Token::Colon)?; self.skip_newlines(); - + let body = self.parse_block()?; - + Ok(ASTNode::While { condition: Box::new(condition), body: Box::new(body), }) } - + fn debug_tokens(&self, start: usize, count: usize) { + println!("=== DEBUG: Tokens from position {} ===", start); + for i in start..std::cmp::min(start + count, self.tokens.len()) { + println!(" [{}] {:?}", i, self.tokens[i]); + } + println!("=== END DEBUG ==="); + } + fn parse_block(&mut self) -> Result { - self.expect(&Token::Indent)?; self.skip_newlines(); - + let mut statements = Vec::new(); - - - while !self.check(&Token::Dedent) && !self.is_at_end() { - - statements.push(self.parse_statement()?); - + // Expect Indent + if !self.check(&Token::Indent) { + // Empty block? But for functions, expect indent. + return Err("Expected indented block after ':'".to_string()); + } + self.advance(); // consume Indent + loop { + // Skip newlines self.skip_newlines(); + + // *** REMOVED: if self.check(&Token::Dedent) { break; } <-- This blocked the fix below + + // --- FIX START: Handle spurious Dedent caused by empty lines --- + if self.check(&Token::Dedent) { + let mut temp_pos = self.position + 1; + while temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Newline) { + temp_pos += 1; + } + if temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Indent) { + self.advance(); // consume Dedent + self.skip_newlines(); // consume Newlines + self.advance(); // consume Indent + continue; + } else { + // No, it's a real Dedent. The block is finished. + break; + } + } + + + // Parse the next statement + statements.push(self.parse_statement()?); } - + + // Consume the final Dedent of the block self.expect(&Token::Dedent)?; - + Ok(ASTNode::Block(statements)) } - + + fn parse_expression(&mut self) -> Result { self.parse_pipeline() - } - + fn parse_pipeline(&mut self) -> Result { let mut expr = self.parse_logical_or()?; while let Some(op_token) = self.match_tokens_loc(&[Token::PipeRight]) { let operator = BinaryOperator::Pipeline; let loc = self.get_loc(&op_token); let right = self.parse_logical_or()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_logical_or(&mut self) -> Result { let mut expr = self.parse_logical_and()?; while let Some(op_token) = self.match_tokens_loc(&[Token::Or]) { let operator = BinaryOperator::Or; let loc = self.get_loc(&op_token); let right = self.parse_logical_and()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_logical_and(&mut self) -> Result { let mut expr = self.parse_equality()?; while let Some(op_token) = self.match_tokens_loc(&[Token::And]) { let operator = BinaryOperator::And; let loc = self.get_loc(&op_token); let right = self.parse_equality()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_equality(&mut self) -> Result { let mut expr = self.parse_comparison()?; while let Some(op_token) = self.match_tokens_loc(&[Token::EqualEqual, Token::NotEqual]) { @@ -989,14 +1276,24 @@ impl Parser { }; let loc = self.get_loc(&op_token); let right = self.parse_comparison()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_comparison(&mut self) -> Result { let mut expr = self.parse_range()?; - while let Some(op_token) = self.match_tokens_loc(&[Token::Less, Token::Greater, Token::LessEqual, Token::GreaterEqual]) { + while let Some(op_token) = self.match_tokens_loc(&[ + Token::Less, + Token::Greater, + Token::LessEqual, + Token::GreaterEqual, + ]) { let operator = match op_token.token { Token::Less => BinaryOperator::Less, Token::Greater => BinaryOperator::Greater, @@ -1006,11 +1303,16 @@ impl Parser { }; let loc = self.get_loc(&op_token); let right = self.parse_range()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_range(&mut self) -> Result { let expr = self.parse_term()?; if let Some(op) = self.match_tokens(&[Token::Range, Token::RangeInclusive]) { @@ -1024,7 +1326,7 @@ impl Parser { } Ok(expr) } - + fn parse_term(&mut self) -> Result { let mut expr = self.parse_factor()?; while let Some(op_token) = self.match_tokens_loc(&[Token::Plus, Token::Minus]) { @@ -1035,40 +1337,57 @@ impl Parser { }; let loc = self.get_loc(&op_token); let right = self.parse_factor()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_factor(&mut self) -> Result { - let mut expr = self.parse_power()?; - while let Some(op_token) = self.match_tokens_loc(&[Token::Star, Token::Slash, Token::Percent]) { - let operator = match op_token.token { - Token::Star => BinaryOperator::Mul, - Token::Slash => BinaryOperator::Div, - Token::Percent => BinaryOperator::Mod, - _ => unreachable!(), - }; - let loc = self.get_loc(&op_token); - let right = self.parse_power()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + let mut expr = self.parse_power()?; // ← CHANGED: was parse_tensor_product + while let Some(op_token) = + self.match_tokens_loc(&[Token::Star, Token::Slash, Token::Percent]) + { + let operator = match op_token.token { + Token::Star => BinaryOperator::Mul, + Token::Slash => BinaryOperator::Div, + Token::Percent => BinaryOperator::Mod, + _ => unreachable!(), + }; + let loc = self.get_loc(&op_token); + let right = self.parse_power()?; // ← CHANGED: was parse_tensor_product + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; + } + Ok(expr) } - Ok(expr) -} - + fn parse_tensor_product(&mut self) -> Result { let mut expr = self.parse_unary()?; while let Some(op_token) = self.match_tokens_loc(&[Token::TensorProduct]) { let operator = BinaryOperator::TensorProduct; let loc = self.get_loc(&op_token); let right = self.parse_unary()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; } Ok(expr) } - + fn parse_unary(&mut self) -> Result { - if let Some(op) = self.match_tokens(&[Token::Not,Token::Bang, Token::Minus, Token::Plus]) { + if let Some(op) = self.match_tokens(&[Token::Not, Token::Bang, Token::Minus, Token::Plus]) { let operator = match op { Token::Not => UnaryOperator::Not, Token::Bang => UnaryOperator::Not, @@ -1076,95 +1395,105 @@ impl Parser { Token::Plus => UnaryOperator::Plus, _ => unreachable!(), }; - + let operand = self.parse_unary()?; return Ok(ASTNode::Unary { operator, operand: Box::new(operand), }); } - + self.parse_postfix() } fn parse_power(&mut self) -> Result { - let mut expr = self.parse_tensor_product()?; - - - if let Some(op_token) = self.match_tokens_loc(&[Token::Caret]) { - let operator = BinaryOperator::Power; - let loc = self.get_loc(&op_token); - let right = self.parse_power()?; - expr = ASTNode::Binary { operator, left: Box::new(expr), right: Box::new(right), loc }; - } - - Ok(expr) -} + let mut expr = self.parse_tensor_product()?; + // Power is RIGHT-associative: 2^3^4 = 2^(3^4) + if let Some(op_token) = self.match_tokens_loc(&[Token::Caret]) { + let operator = BinaryOperator::Power; + let loc = self.get_loc(&op_token); + let right = self.parse_power()?; // ← Recursive for right-associativity + expr = ASTNode::Binary { + operator, + left: Box::new(expr), + right: Box::new(right), + loc, + }; + } - - + Ok(expr) + } fn parse_postfix(&mut self) -> Result { let mut expr = self.parse_primary()?; loop { if self.check(&Token::LeftParen) { - let loc = self.get_loc(self.current()?); self.advance(); - - let arguments = self.parse_arguments()?; - - expr = ASTNode::FunctionCall { - callee: Box::new(expr), - arguments, + + expr = ASTNode::FunctionCall { + callee: Box::new(expr), + arguments, loc, is_dagger: false, }; - - } else if self.check(&Token::LeftBracket) { - let loc = self.get_loc(self.current()?); self.advance(); let index = self.parse_expression()?; self.expect(&Token::RightBracket)?; - + expr = ASTNode::ArrayAccess { array: Box::new(expr), index: Box::new(index), loc, }; } else if self.match_token(&Token::Dot) { - let name_loc = self.expect_identifier()?; let member = self.extract_identifier_name(&name_loc)?; - - expr = ASTNode::MemberAccess { - object: Box::new(expr), - member, - }; + + // Check if this is a method call + if self.check(&Token::LeftParen) { + let loc = self.get_loc(self.current()?); + self.advance(); + let arguments = self.parse_arguments()?; + + expr = ASTNode::MethodCall { + object: Box::new(expr), + method_name: member, + arguments, + loc, + }; + } else { + expr = ASTNode::MemberAccess { + object: Box::new(expr), + member, + }; + } } else { break; } } - + Ok(expr) } fn get_loc(&self, token_loc: &TokenWithLocation) -> Loc { - Loc { line: token_loc.line, column: token_loc.column } + Loc { + line: token_loc.line, + column: token_loc.column, + } } - - fn parse_primary(&mut self) -> Result { + fn parse_primary(&mut self) -> Result { while self.check(&Token::Newline) { self.advance(); } - + // If we hit Indent/Dedent here, something is wrong with statement parsing if self.check(&Token::Indent) || self.check(&Token::Dedent) { let current_loc = self.current()?.clone(); return Err(format!( @@ -1176,39 +1505,52 @@ impl Parser { let current_loc = self.current()?.clone(); let token = ¤t_loc.token; let loc = self.get_loc(¤t_loc); - + // This match must return a Result match token { Token::If => self.parse_if(), Token::Print => { self.advance(); - Ok(ASTNode::Identifier { name: "print".to_string(), loc }) + Ok(ASTNode::Identifier { + name: "print".to_string(), + loc, + }) } Token::Echo => { self.advance(); - Ok(ASTNode::Identifier { name: "echo".to_string(), loc }) + Ok(ASTNode::Identifier { + name: "echo".to_string(), + loc, + }) } - + // --- *** MODIFIED: Token::Identifier *** --- Token::Identifier(name) => { self.advance(); - - Ok(ASTNode::Identifier { name: name.clone(), loc }) + // This arm NO LONGER handles calls or member access. + // It just returns the identifier. + Ok(ASTNode::Identifier { + name: name.clone(), + loc, + }) } + // --- *** END MODIFIED *** --- - - + // --- *** MODIFIED: Token::Dagger *** --- Token::Dagger => { - self.advance(); + self.advance(); // Consume 'dagger' self.expect(&Token::LeftParen)?; - let callee_expr = self.parse_expression()?; + let callee_expr = self.parse_expression()?; // e.g., 'my_circuit' or 'qstd.bell' self.expect(&Token::RightParen)?; - + let call_loc = self.get_loc(self.current()?); if !self.match_token(&Token::LeftParen) { - return Err(format!("Error at {}: Expected '(' to call the daggered circuit.", call_loc)); + return Err(format!( + "Error at {}: Expected '(' to call the daggered circuit.", + call_loc + )); } - + let arguments = self.parse_arguments()?; Ok(ASTNode::FunctionCall { callee: Box::new(callee_expr), @@ -1217,7 +1559,6 @@ impl Parser { is_dagger: true, }) } - Token::Measure => { self.advance(); @@ -1227,7 +1568,7 @@ impl Parser { Ok(ASTNode::Measure(Box::new(qubit))) } - + // --- LITERALS --- Token::IntLiteral(n) => { self.advance(); Ok(ASTNode::IntLiteral(*n)) @@ -1260,8 +1601,8 @@ impl Parser { self.advance(); Ok(ASTNode::QuantumBra(state.clone())) } - + // --- GROUPING & COLLECTIONS --- Token::LeftParen => { self.advance(); let expr = self.parse_expression()?; @@ -1276,24 +1617,50 @@ impl Parser { self.advance(); self.parse_dict_literal() } + Token::New => self.parse_new_instance(), + Token::Identifier(name) if name == "self" => { + self.advance(); + Ok(ASTNode::SelfRef { loc }) + } - Token::X | Token::Y | Token::Z | Token::S | Token::T | - Token::Hadamard | Token::Cnot | Token::Swap | Token::Reset | - Token::CZ | Token::CS | Token::CT | Token::CCX | Token::Toffoli | - Token::RX | Token::RY | Token::RZ | Token::U | Token::CPhase=> { + Token::X + | Token::Y + | Token::Z + | Token::S + | Token::T + | Token::Hadamard + | Token::Cnot + | Token::Swap + | Token::Reset + | Token::CZ + | Token::CS + | Token::CT + | Token::CCX + | Token::Toffoli + | Token::RX + | Token::RY + | Token::RZ + | Token::U + | Token::CPhase => { let name = format!("{:?}", token).to_lowercase(); self.advance(); Ok(ASTNode::Identifier { name, loc }) } - _ => Err(format!("Unexpected token in expression at {}: {:?}", loc, token)), + _ => Err(format!( + "Unexpected token in expression at {}: {:?}", + loc, token + )), } } - + // --- *** DELETED 'parse_call_or_dagger' *** --- fn skip_layout_tokens(&mut self) { - while self.check(&Token::Newline) || self.check(&Token::Indent) || self.check(&Token::Dedent) { + while self.check(&Token::Newline) + || self.check(&Token::Indent) + || self.check(&Token::Dedent) + { self.advance(); } } @@ -1319,10 +1686,10 @@ impl Parser { if self.check(&Token::RightBrace) { break; } - + self.expect(&Token::Comma)?; self.skip_layout_tokens(); - + if self.check(&Token::RightBrace) { break; } @@ -1332,8 +1699,8 @@ impl Parser { Ok(ASTNode::DictLiteral(pairs)) } - + // Helper methods fn current(&self) -> Result<&TokenWithLocation, String> { if self.is_at_end() { Err("Unexpected end of file".to_string()) @@ -1341,25 +1708,28 @@ impl Parser { Ok(&self.tokens[self.position]) } } - + fn advance(&mut self) { if !self.is_at_end() { self.position += 1; } } - + fn is_at_end(&self) -> bool { - self.position >= self.tokens.len() || - matches!(self.tokens.get(self.position).map(|t| &t.token), Some(Token::Eof)) + self.position >= self.tokens.len() + || matches!( + self.tokens.get(self.position).map(|t| &t.token), + Some(Token::Eof) + ) } - + fn check(&self, token: &Token) -> bool { if self.is_at_end() { return false; } std::mem::discriminant(&self.tokens[self.position].token) == std::mem::discriminant(token) } - + fn match_token(&mut self, token: &Token) -> bool { if self.check(token) { self.advance(); @@ -1368,7 +1738,7 @@ impl Parser { false } } - + fn match_tokens(&mut self, tokens: &[Token]) -> Option { for token in tokens { if self.check(token) { @@ -1379,94 +1749,120 @@ impl Parser { } None } - + fn expect(&mut self, token: &Token) -> Result<(), String> { - let current_loc = self.current()?.clone(); - let token = ¤t_loc.token; - let loc = self.get_loc(¤t_loc); if self.check(token) { self.advance(); Ok(()) } else { - let current = if self.is_at_end() { - "EOF".to_string() + // Logic to find where we are, even if we hit EOF + let (loc, current_str) = if self.position >= self.tokens.len() { + // We are past the end, use the last token's location + let last_loc = if !self.tokens.is_empty() { + let last = self.tokens.last().unwrap(); + // Point to just after the last token + Loc { line: last.line, column: last.column + 1 } + } else { + Loc { line: 1, column: 1 } + }; + (last_loc, "EOF".to_string()) } else { - format!("{:?}", self.tokens[self.position].token) + // We are at a valid token + let curr = &self.tokens[self.position]; + ( + Loc { line: curr.line, column: curr.column }, + format!("{:?}", curr.token) + ) }; - Err(format!("Expected {:?}, found {} at {}", token, current,loc)) + + // Return a standard formatted error string + Err(format!( + "Syntax Error at {}: Expected {:?}, but found {}", + loc, token, current_str + )) } } fn extract_identifier_name(&self, token_loc: &TokenWithLocation) -> Result { - match &token_loc.token { - Token::Identifier(name) => Ok(name.clone()), - Token::Hadamard => Ok("hadamard".to_string()), - Token::Cnot => Ok("cnot".to_string()), - Token::X => Ok("x".to_string()), - Token::Y => Ok("y".to_string()), - Token::Z => Ok("z".to_string()), - Token::S => Ok("s".to_string()), - Token::T => Ok("t".to_string()), - Token::Swap => Ok("swap".to_string()), - Token::Reset => Ok("reset".to_string()), - Token::CZ => Ok("cz".to_string()), - Token::CS => Ok("cs".to_string()), - Token::CT => Ok("ct".to_string()), - Token::CPhase => Ok("cphase".to_string()), - Token::U => Ok("u".to_string()), - Token::RX => Ok("rx".to_string()), - Token::RY => Ok("ry".to_string()), - Token::RZ => Ok("rz".to_string()), - Token::CCX => Ok("ccx".to_string()), - Token::Toffoli => Ok("toffoli".to_string()), - _ => Err(format!("Expected Identifier token, found {:?}", token_loc.token)) + match &token_loc.token { + Token::Identifier(name) => Ok(name.clone()), + Token::Hadamard => Ok("hadamard".to_string()), + Token::Cnot => Ok("cnot".to_string()), + Token::X => Ok("x".to_string()), + Token::Y => Ok("y".to_string()), + Token::Z => Ok("z".to_string()), + Token::S => Ok("s".to_string()), + Token::T => Ok("t".to_string()), + Token::Swap => Ok("swap".to_string()), + Token::Reset => Ok("reset".to_string()), + Token::CZ => Ok("cz".to_string()), + Token::CS => Ok("cs".to_string()), + Token::CT => Ok("ct".to_string()), + Token::CPhase => Ok("cphase".to_string()), + Token::U => Ok("u".to_string()), // ← Add these + Token::RX => Ok("rx".to_string()), // ← Add these + Token::RY => Ok("ry".to_string()), // ← Add these + Token::RZ => Ok("rz".to_string()), // ← Add these + Token::CCX => Ok("ccx".to_string()), + Token::Toffoli => Ok("toffoli".to_string()), + _ => Err(format!( + "Expected Identifier token, found {:?}", + token_loc.token + )), + } } -} - + fn expect_identifier(&mut self) -> Result { - let current_loc = self.current()?; - - match ¤t_loc.token { - Token::Identifier(_) | + let current_loc = self.current()?; + + match ¤t_loc.token { + Token::Identifier(_) | Token::Hadamard | Token::Cnot | Token::X | Token::Y | Token::Z | Token::S | Token::T | Token::Swap | Token::Reset | Token::CZ | Token::CS | Token::CT | Token::CPhase | - Token::U | Token::RX | Token::RY | Token::RZ | + Token::U | Token::RX | Token::RY | Token::RZ | // ← Fixed: removed duplicate Token::U Token::CCX | Token::Toffoli => { - let owned_token_loc = current_loc.clone(); - self.advance(); - Ok(owned_token_loc) + let owned_token_loc = current_loc.clone(); + self.advance(); + Ok(owned_token_loc) } _ => Err(format!("Syntax Error: Expected Identifier or Gate name, found {:?}", current_loc.token)) } -} + } fn skip_newlines(&mut self) { while self.match_token(&Token::Newline) {} } + + // Tests } -// Tests #[cfg(test)] mod tests { use super::*; use crate::lexer::Lexer; - + fn parse_source(source: &str) -> Result { let mut lexer = Lexer::new(source); let tokens = lexer.tokenize()?; let mut parser = Parser::new(tokens); parser.parse() } - + #[test] fn test_let_declaration() { let source = "let x = 42"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert_eq!(statements.len(), 1); - if let ASTNode::LetDeclaration { name, value, is_mutable, .. } = &statements[0] { + if let ASTNode::LetDeclaration { + name, + value, + is_mutable, + .. + } = &statements[0] + { assert_eq!(name, "x"); assert_eq!(*is_mutable, false); assert!(matches!(**value, ASTNode::IntLiteral(42))); @@ -1475,15 +1871,20 @@ mod tests { } } } - + #[test] fn test_quantum_declaration() { let source = "quantum q = |0}"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert_eq!(statements.len(), 1); - if let ASTNode::QuantumDeclaration { name, initial_state, .. } = &statements[0] { + if let ASTNode::QuantumDeclaration { + name, + initial_state, + .. + } = &statements[0] + { assert_eq!(name, "q"); assert!(initial_state.is_some()); if let Some(state) = initial_state { @@ -1494,15 +1895,21 @@ mod tests { } } } - + #[test] fn test_tensor_product() { let source = "let result = q1 *** q2"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::LetDeclaration { value, .. } = &statements[0] { - if let ASTNode::Binary { operator, left, right, .. } = &**value { + if let ASTNode::Binary { + operator, + left, + right, + .. + } = &**value + { assert_eq!(*operator, BinaryOperator::TensorProduct); assert!(matches!(**left, ASTNode::Identifier{ref name, .. } if name == "q1")); assert!(matches!(**right, ASTNode::Identifier{ref name, .. } if name == "q2")); @@ -1517,10 +1924,16 @@ mod tests { fn test_function_declaration() { let source = "func add(x: Int, y: Int) -> Int:\n return x + y"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert_eq!(statements.len(), 1); - if let ASTNode::FunctionDeclaration { name, parameters, return_type, .. } = &statements[0] { + if let ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + .. + } = &statements[0] + { assert_eq!(name, "add"); assert_eq!(parameters.len(), 2); assert!(matches!(return_type, Some(Type::Int))); @@ -1534,7 +1947,7 @@ mod tests { fn test_if_statement() { let source = "if x > 5:\n print(\"yes\")"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert!(matches!(statements[0], ASTNode::If { .. })); } else { @@ -1546,9 +1959,14 @@ mod tests { fn test_if_elif_else() { let source = "if x > 5:\n print(\"big\")\nelif x > 0:\n print(\"small\")\nelse:\n print(\"zero or negative\")"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { - if let ASTNode::If { elif_blocks, else_block, .. } = &statements[0] { + if let ASTNode::If { + elif_blocks, + else_block, + .. + } = &statements[0] + { assert_eq!(elif_blocks.len(), 1); assert!(else_block.is_some()); } else { @@ -1556,12 +1974,12 @@ mod tests { } } } - + #[test] fn test_dict_literal() { let source = "let d = { 1: \"a\", \"b\": 2 }"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::LetDeclaration { value, .. } = &statements[0] { if let ASTNode::DictLiteral(pairs) = &**value { @@ -1579,7 +1997,7 @@ mod tests { fn test_apply_statement() { let source = "apply Hadamard(q[0])"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert!(matches!(statements[0], ASTNode::Apply { .. })); } @@ -1589,7 +2007,7 @@ mod tests { fn test_controlled_gate() { let source = "apply controlled(X)(q[0], q[1])"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::Apply { gate_expr, .. } = &statements[0] { assert!(matches!(**gate_expr, ASTNode::Controlled { .. })); @@ -1603,7 +2021,7 @@ mod tests { fn test_dagger_gate() { let source = "apply dagger(S)(q[0])"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::Apply { gate_expr, .. } = &statements[0] { assert!(matches!(**gate_expr, ASTNode::Dagger { .. })); @@ -1617,7 +2035,7 @@ mod tests { fn test_parameterized_gate() { let source = "apply RX(3.14)(q[0])"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::Apply { gate_expr, .. } = &statements[0] { assert!(matches!(**gate_expr, ASTNode::ParameterizedGate { .. })); @@ -1631,7 +2049,7 @@ mod tests { fn test_power_operator() { let source = "let result = 2 ^ 3"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::LetDeclaration { value, .. } = &statements[0] { if let ASTNode::Binary { operator, .. } = &**value { @@ -1647,7 +2065,7 @@ mod tests { fn test_import_statement() { let source = "import \"math.qc\" as math"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert!(matches!(statements[0], ASTNode::Import { .. })); } @@ -1657,7 +2075,7 @@ mod tests { fn test_from_import() { let source = "from \"math.qc\" import PI, sin"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert!(matches!(statements[0], ASTNode::FromImport { .. })); } @@ -1667,7 +2085,7 @@ mod tests { fn test_try_catch() { let source = "Try:\n let x = 10\nCatch err:\n print(err)"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { assert!(matches!(statements[0], ASTNode::TryCatch { .. })); } @@ -1677,7 +2095,7 @@ mod tests { fn test_array_literal() { let source = "let arr = [1, 2, 3]"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::LetDeclaration { value, .. } = &statements[0] { if let ASTNode::ArrayLiteral(elements) = &**value { @@ -1693,7 +2111,7 @@ mod tests { fn test_range_expression() { let source = "let r = 0..10"; let ast = parse_source(source).unwrap(); - + if let ASTNode::Program(statements) = ast { if let ASTNode::LetDeclaration { value, .. } = &statements[0] { assert!(matches!(**value, ASTNode::Range { .. })); @@ -1701,4 +2119,3 @@ mod tests { } } } - diff --git a/src/quantum_backend/cirq_local.rs b/src/quantum_backend/cirq_local.rs index 68aa6a4..7097349 100644 --- a/src/quantum_backend/cirq_local.rs +++ b/src/quantum_backend/cirq_local.rs @@ -1,6 +1,6 @@ // src/quantum_backend/cirq_local.rs -use super::{HardwareCircuit, QuantumConfig, QuantumResult, QuantumBackend}; +use super::{HardwareCircuit, QuantumBackend, QuantumConfig, QuantumResult}; use std::collections::HashMap; use std::process::Command; @@ -10,73 +10,94 @@ impl CirqLocalBackend { pub fn new() -> Self { CirqLocalBackend } - - + + // Helper to get the correct Python command for the platform fn get_python_command() -> &'static str { if cfg!(target_os = "windows") { - "python" + "python" // Windows typically uses 'python' } else { - "python3" + "python3" // Unix-like systems use 'python3' } } - + fn generate_cirq_script(&self, circuit: &HardwareCircuit, shots: u32) -> String { let mut script = String::from("import cirq\nimport json\n\n"); - - script.push_str(&format!("qubits = [cirq.LineQubit(i) for i in range({})]\n", circuit.num_qubits)); + + script.push_str(&format!( + "qubits = [cirq.LineQubit(i) for i in range({})]\n", + circuit.num_qubits + )); script.push_str("circuit = cirq.Circuit()\n\n"); - + for gate in &circuit.gates { let gate_code = match gate.name.as_str() { "hadamard" | "h" => format!("circuit.append(cirq.H(qubits[{}]))", gate.qubits[0]), "x" => format!("circuit.append(cirq.X(qubits[{}]))", gate.qubits[0]), "y" => format!("circuit.append(cirq.Y(qubits[{}]))", gate.qubits[0]), "z" => format!("circuit.append(cirq.Z(qubits[{}]))", gate.qubits[0]), - "cnot" | "cx" => format!("circuit.append(cirq.CNOT(qubits[{}], qubits[{}]))", - gate.qubits[0], gate.qubits[1]), - "rx" => format!("circuit.append(cirq.rx({}).on(qubits[{}]))", - gate.params[0], gate.qubits[0]), - "ry" => format!("circuit.append(cirq.ry({}).on(qubits[{}]))", - gate.params[0], gate.qubits[0]), - "rz" => format!("circuit.append(cirq.rz({}).on(qubits[{}]))", - gate.params[0], gate.qubits[0]), + "cnot" | "cx" => format!( + "circuit.append(cirq.CNOT(qubits[{}], qubits[{}]))", + gate.qubits[0], gate.qubits[1] + ), + "rx" => format!( + "circuit.append(cirq.rx({}).on(qubits[{}]))", + gate.params[0], gate.qubits[0] + ), + "ry" => format!( + "circuit.append(cirq.ry({}).on(qubits[{}]))", + gate.params[0], gate.qubits[0] + ), + "rz" => format!( + "circuit.append(cirq.rz({}).on(qubits[{}]))", + gate.params[0], gate.qubits[0] + ), _ => continue, }; script.push_str(&format!("{}\n", gate_code)); } - + script.push_str("\ncircuit.append(cirq.measure(*qubits, key='result'))\n"); script.push_str("simulator = cirq.Simulator()\n"); - script.push_str(&format!("result = simulator.run(circuit, repetitions={})\n", shots)); + script.push_str(&format!( + "result = simulator.run(circuit, repetitions={})\n", + shots + )); script.push_str("counts = result.histogram(key='result')\n"); - script.push_str(&format!("print(json.dumps({{format(k, '0{}b'): int(v) for k, v in counts.items()}}))\n", circuit.num_qubits)); - + script.push_str(&format!( + "print(json.dumps({{format(k, '0{}b'): int(v) for k, v in counts.items()}}))\n", + circuit.num_qubits + )); + script } } impl QuantumBackend for CirqLocalBackend { - fn execute(&self, circuit: &HardwareCircuit, config: &QuantumConfig) -> Result { + fn execute( + &self, + circuit: &HardwareCircuit, + config: &QuantumConfig, + ) -> Result { let script = self.generate_cirq_script(circuit, config.shots); - + std::fs::write("temp_cirq.py", &script) .map_err(|e| format!("Failed to write script: {}", e))?; - + let python_cmd = Self::get_python_command(); let output = Command::new(python_cmd) .arg("temp_cirq.py") .output() .map_err(|e| format!("Failed to execute Python (tried '{}'): {}", python_cmd, e))?; - + if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(format!("Python execution failed: {}", stderr)); } - + let result_json = String::from_utf8_lossy(&output.stdout); - let parsed: serde_json::Value = serde_json::from_str(&result_json) - .map_err(|e| format!("Parse error: {}", e))?; - + let parsed: serde_json::Value = + serde_json::from_str(&result_json).map_err(|e| format!("Parse error: {}", e))?; + let mut counts = HashMap::new(); if let Some(obj) = parsed.as_object() { for (k, v) in obj { @@ -85,7 +106,7 @@ impl QuantumBackend for CirqLocalBackend { } } } - + Ok(QuantumResult { counts, shots: config.shots, @@ -93,7 +114,7 @@ impl QuantumBackend for CirqLocalBackend { error_message: None, }) } - + fn is_available(&self) -> bool { let python_cmd = Self::get_python_command(); Command::new(python_cmd) @@ -103,13 +124,12 @@ impl QuantumBackend for CirqLocalBackend { .map(|o| o.status.success()) .unwrap_or(false) } - + fn available_devices(&self) -> Vec { vec!["local_simulator".to_string()] } - + fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { circuit.clone() } - } diff --git a/src/quantum_backend/ibm_qiskit.rs b/src/quantum_backend/ibm_qiskit.rs index ba51ec4..db57811 100644 --- a/src/quantum_backend/ibm_qiskit.rs +++ b/src/quantum_backend/ibm_qiskit.rs @@ -1,6 +1,6 @@ // src/quantum_backend/ibm_qiskit.rs -use super::{HardwareCircuit, QuantumConfig, QuantumResult, QuantumBackend}; +use super::{HardwareCircuit, QuantumBackend, QuantumConfig, QuantumResult}; use std::collections::HashMap; use std::process::Command; @@ -10,7 +10,7 @@ impl IBMQiskitBackend { pub fn new() -> Self { IBMQiskitBackend } - + fn get_python_command() -> &'static str { if cfg!(target_os = "windows") { "python" @@ -18,16 +18,23 @@ impl IBMQiskitBackend { "python3" } } - - fn generate_qiskit_script(&self, circuit: &HardwareCircuit, shots: u32, device: Option<&str>) -> String { + + fn generate_qiskit_script( + &self, + circuit: &HardwareCircuit, + shots: u32, + device: Option<&str>, + ) -> String { let mut script = String::from("from qiskit import QuantumCircuit, transpile\n"); script.push_str("from qiskit_aer import AerSimulator\n"); script.push_str("import json\n\n"); - - script.push_str(&format!("qc = QuantumCircuit({}, {})\n\n", - circuit.num_qubits, circuit.num_qubits)); - - // gates + + script.push_str(&format!( + "qc = QuantumCircuit({}, {})\n\n", + circuit.num_qubits, circuit.num_qubits + )); + + // Add gates for gate in &circuit.gates { let gate_code = match gate.name.as_str() { "hadamard" | "h" => format!("qc.h({})", gate.qubits[0]), @@ -46,48 +53,56 @@ impl IBMQiskitBackend { }; script.push_str(&format!("{}\n", gate_code)); } - - // measurements + + // Add measurements script.push_str("\n# Measure all qubits\n"); for i in 0..circuit.num_qubits { script.push_str(&format!("qc.measure({}, {})\n", i, i)); } - + // Execute script.push_str("\n# Execute on simulator\n"); script.push_str("simulator = AerSimulator()\n"); script.push_str("transpiled = transpile(qc, simulator)\n"); - script.push_str(&format!("job = simulator.run(transpiled, shots={})\n", shots)); + script.push_str(&format!( + "job = simulator.run(transpiled, shots={})\n", + shots + )); script.push_str("result = job.result()\n"); script.push_str("counts = result.get_counts()\n"); script.push_str("print(json.dumps(counts))\n"); - + script } } impl QuantumBackend for IBMQiskitBackend { - fn execute(&self, circuit: &HardwareCircuit, config: &QuantumConfig) -> Result { - let script = self.generate_qiskit_script(circuit, config.shots, config.device_name.as_deref()); - + fn execute( + &self, + circuit: &HardwareCircuit, + config: &QuantumConfig, + ) -> Result { + let script = + self.generate_qiskit_script(circuit, config.shots, config.device_name.as_deref()); + std::fs::write("temp_qiskit.py", &script) .map_err(|e| format!("Failed to write script: {}", e))?; - + let python_cmd = Self::get_python_command(); let output = Command::new(python_cmd) .arg("temp_qiskit.py") .output() .map_err(|e| format!("Failed to execute Python (tried '{}'): {}", python_cmd, e))?; - + if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(format!("Python execution failed: {}", stderr)); } - + let result_json = String::from_utf8_lossy(&output.stdout); let parsed: serde_json::Value = serde_json::from_str(&result_json) .map_err(|e| format!("Parse error: {}\nOutput: {}", e, result_json))?; - + let mut counts = HashMap::new(); if let Some(obj) = parsed.as_object() { for (k, v) in obj { @@ -96,7 +111,7 @@ impl QuantumBackend for IBMQiskitBackend { } } } - + Ok(QuantumResult { counts, shots: config.shots, @@ -104,7 +119,7 @@ impl QuantumBackend for IBMQiskitBackend { error_message: None, }) } - + fn is_available(&self) -> bool { let python_cmd = Self::get_python_command(); Command::new(python_cmd) @@ -114,16 +129,15 @@ impl QuantumBackend for IBMQiskitBackend { .map(|o| o.status.success()) .unwrap_or(false) } - + fn available_devices(&self) -> Vec { vec![ "aer_simulator".to_string(), "ibmq_qasm_simulator".to_string(), ] } - + fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { circuit.clone() } - } diff --git a/src/quantum_backend/mod.rs b/src/quantum_backend/mod.rs index babe465..c835111 100644 --- a/src/quantum_backend/mod.rs +++ b/src/quantum_backend/mod.rs @@ -1,15 +1,20 @@ // src/quantum_backend/mod.rs -use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +// Import existing backends mod ibm_qiskit; use ibm_qiskit::IBMQiskitBackend; mod cirq_local; use cirq_local::CirqLocalBackend; +// Import the new native backend +mod native_simulator; +use native_simulator::NativeBackend; + /// Supported quantum hardware providers -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[derive(Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] pub enum QuantumProvider { IBM, // IBM Quantum (via Qiskit) Rigetti, // Rigetti Computing (via pyQuil) @@ -17,7 +22,8 @@ pub enum QuantumProvider { GoogleCircuit, // Google Cirq AWS, // AWS Braket Azure, // Azure Quantum - Simulator, // Local simulator (default) + Native, // Native Rust Simulator (NEW!) + Simulator, // Legacy alias for Native } /// Configuration for quantum hardware access @@ -26,14 +32,14 @@ pub struct QuantumConfig { pub provider: QuantumProvider, pub api_token: Option, pub device_name: Option, - pub shots: u32, // Number of measurements + pub shots: u32, pub optimize: bool, } impl Default for QuantumConfig { fn default() -> Self { QuantumConfig { - provider: QuantumProvider::Simulator, + provider: QuantumProvider::Native, // Changed default to Native api_token: None, device_name: None, shots: 1024, @@ -42,7 +48,7 @@ impl Default for QuantumConfig { } } - +/// Represents a quantum gate in a hardware-agnostic way #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HardwareGate { pub name: String, @@ -51,7 +57,7 @@ pub struct HardwareGate { pub is_dagger: bool, } - +/// Represents a complete quantum circuit for hardware execution #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HardwareCircuit { pub num_qubits: usize, @@ -59,7 +65,7 @@ pub struct HardwareCircuit { pub measurements: Vec, } - +/// Result from quantum hardware execution #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QuantumResult { pub counts: HashMap, @@ -68,22 +74,20 @@ pub struct QuantumResult { pub error_message: Option, } - +/// Main trait for quantum backends pub trait QuantumBackend { - - fn execute(&self, circuit: &HardwareCircuit, config: &QuantumConfig) -> Result; - + fn execute( + &self, + circuit: &HardwareCircuit, + config: &QuantumConfig, + ) -> Result; fn is_available(&self) -> bool; - - fn available_devices(&self) -> Vec; - - fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit; } -/// IBM Quantum Backend +/// IBM Quantum Backend (via REST API) pub struct IBMBackend { api_url: String, } @@ -94,7 +98,6 @@ impl IBMBackend { api_url: "https://api.quantum-computing.ibm.com/api".to_string(), } } - fn to_qasm(&self, circuit: &HardwareCircuit) -> String { let mut qasm = String::new(); @@ -102,7 +105,7 @@ impl IBMBackend { qasm.push_str("include \"qelib1.inc\";\n"); qasm.push_str(&format!("qreg q[{}];\n", circuit.num_qubits)); qasm.push_str(&format!("creg c[{}];\n", circuit.measurements.len())); - + for gate in &circuit.gates { let gate_str = match gate.name.as_str() { "x" => format!("x q[{}];\n", gate.qubits[0]), @@ -117,38 +120,35 @@ impl IBMBackend { "rx" => format!("rx({}) q[{}];\n", gate.params[0], gate.qubits[0]), "ry" => format!("ry({}) q[{}];\n", gate.params[0], gate.qubits[0]), "rz" => format!("rz({}) q[{}];\n", gate.params[0], gate.qubits[0]), - "cphase" => format!("cp({}) q[{}],q[{}];\n", gate.params[0], gate.qubits[0], gate.qubits[1]), - _ => return format!("// Unsupported gate: {}\n", gate.name), + "cphase" => format!( + "cp({}) q[{}],q[{}];\n", + gate.params[0], gate.qubits[0], gate.qubits[1] + ), + _ => format!("// Unsupported gate: {}\n", gate.name), }; - + if gate.is_dagger { qasm.push_str(&format!("// Dagger of: {}", gate_str)); - } else { qasm.push_str(&gate_str); } } - for (i, &qubit) in circuit.measurements.iter().enumerate() { qasm.push_str(&format!("measure q[{}] -> c[{}];\n", qubit, i)); } - + qasm } } impl QuantumBackend for IBMBackend { - fn execute(&self, circuit: &HardwareCircuit, config: &QuantumConfig) -> Result { - - let qasm = self.to_qasm(circuit); - //not implemented due to paid model - // In a real implementation(toDO): - // Use reqwest to POST the QASM to IBM's API - // Poll for job completion - // Parse and return results - - // For now, return a placeholder + fn execute( + &self, + circuit: &HardwareCircuit, + config: &QuantumConfig, + ) -> Result { + let _qasm = self.to_qasm(circuit); Ok(QuantumResult { counts: HashMap::new(), shots: config.shots, @@ -156,12 +156,11 @@ impl QuantumBackend for IBMBackend { error_message: None, }) } - - fn is_available(&self) -> bool { + fn is_available(&self) -> bool { true } - + fn available_devices(&self) -> Vec { vec![ "ibmq_qasm_simulator".to_string(), @@ -170,9 +169,8 @@ impl QuantumBackend for IBMBackend { "ibmq_quito".to_string(), ] } - - fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { + fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { circuit.clone() } } @@ -189,28 +187,31 @@ impl AWSBraketBackend { } impl QuantumBackend for AWSBraketBackend { - fn execute(&self, circuit: &HardwareCircuit, config: &QuantumConfig) -> Result { - + fn execute( + &self, + _circuit: &HardwareCircuit, + _config: &QuantumConfig, + ) -> Result { Err("AWS Braket backend not yet implemented".to_string()) } - + fn is_available(&self) -> bool { false } - + fn available_devices(&self) -> Vec { vec![ "arn:aws:braket:::device/quantum-simulator/amazon/sv1".to_string(), "arn:aws:braket:us-east-1::device/qpu/ionq/Harmony".to_string(), ] } - + fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { circuit.clone() } } - +/// Backend Manager - selects and manages quantum backends pub struct BackendManager { backends: HashMap>, config: QuantumConfig, @@ -219,33 +220,47 @@ pub struct BackendManager { impl BackendManager { pub fn new(config: QuantumConfig) -> Self { let mut backends: HashMap> = HashMap::new(); - + // Register available backends backends.insert(QuantumProvider::IBM, Box::new(IBMQiskitBackend::new())); - backends.insert(QuantumProvider::AWS, Box::new(AWSBraketBackend::new("us-east-1".to_string()))); - backends.insert(QuantumProvider::GoogleCircuit, Box::new(CirqLocalBackend::new())); - + backends.insert( + QuantumProvider::AWS, + Box::new(AWSBraketBackend::new("us-east-1".to_string())), + ); + backends.insert( + QuantumProvider::GoogleCircuit, + Box::new(CirqLocalBackend::new()), + ); + + // Register the native backend + backends.insert(QuantumProvider::Native, Box::new(NativeBackend::new())); + backends.insert(QuantumProvider::Simulator, Box::new(NativeBackend::new())); + BackendManager { backends, config } } - + pub fn execute_circuit(&self, circuit: &HardwareCircuit) -> Result { - let backend = self.backends.get(&self.config.provider) + let backend = self + .backends + .get(&self.config.provider) .ok_or("Backend not available")?; - + if !backend.is_available() { - return Err(format!("Backend {:?} is not available", self.config.provider)); + return Err(format!( + "Backend {:?} is not available", + self.config.provider + )); } - let optimized_circuit = if self.config.optimize { backend.optimize_circuit(circuit) } else { circuit.clone() }; - + backend.execute(&optimized_circuit, &self.config) } - + pub fn list_devices(&self) -> Vec { if let Some(backend) = self.backends.get(&self.config.provider) { backend.available_devices() @@ -258,7 +273,7 @@ impl BackendManager { #[cfg(test)] mod tests { use super::*; - + #[test] fn test_qasm_generation() { let backend = IBMBackend::new(); @@ -280,11 +295,58 @@ mod tests { ], measurements: vec![0, 1], }; - + let qasm = backend.to_qasm(&circuit); assert!(qasm.contains("OPENQASM 2.0")); assert!(qasm.contains("h q[0]")); assert!(qasm.contains("cx q[0],q[1]")); } -} + #[test] + fn test_native_backend_available() { + let backend = NativeBackend::new(); + assert!(backend.is_available()); + } + + #[test] + fn test_native_backend_bell_state() { + let backend = NativeBackend::new(); + let circuit = HardwareCircuit { + num_qubits: 2, + gates: vec![ + HardwareGate { + name: "h".to_string(), + qubits: vec![0], + params: vec![], + is_dagger: false, + }, + HardwareGate { + name: "cnot".to_string(), + qubits: vec![0, 1], + params: vec![], + is_dagger: false, + }, + ], + measurements: vec![0, 1], + }; + + let config = QuantumConfig { + provider: QuantumProvider::Native, + shots: 1000, + optimize: false, + api_token: None, + device_name: None, + }; + + let result = backend.execute(&circuit, &config).unwrap(); + assert!(result.success); + + // Check we have roughly equal counts for 00 and 11 + let count_00 = result.counts.get("00").unwrap_or(&0); + let count_11 = result.counts.get("11").unwrap_or(&0); + + // Should be close to 500 each (with some statistical variance) + assert!(*count_00 > 400 && *count_00 < 600); + assert!(*count_11 > 400 && *count_11 < 600); + } +} \ No newline at end of file diff --git a/src/quantum_backend/native_simulator.rs b/src/quantum_backend/native_simulator.rs new file mode 100644 index 0000000..b93db86 --- /dev/null +++ b/src/quantum_backend/native_simulator.rs @@ -0,0 +1,443 @@ +// src/quantum_backend/native_simulator.rs + +use super::{HardwareCircuit, QuantumBackend, QuantumConfig, QuantumResult}; +use std::collections::HashMap; +use rayon::prelude::*; + +/// Complex number representation +#[derive(Clone, Copy, Debug)] +struct Complex { + real: f64, + imag: f64, +} + +impl Complex { + fn new(real: f64, imag: f64) -> Self { + Complex { real, imag } + } + + fn zero() -> Self { + Complex::new(0.0, 0.0) + } + + fn one() -> Self { + Complex::new(1.0, 0.0) + } + + fn i() -> Self { + Complex::new(0.0, 1.0) + } + + fn magnitude_squared(&self) -> f64 { + self.real * self.real + self.imag * self.imag + } + + fn magnitude(&self) -> f64 { + self.magnitude_squared().sqrt() + } + + fn conj(&self) -> Complex { + Complex::new(self.real, -self.imag) + } + + fn add(&self, other: &Complex) -> Complex { + Complex::new(self.real + other.real, self.imag + other.imag) + } + + fn mul(&self, other: &Complex) -> Complex { + Complex::new( + self.real * other.real - self.imag * other.imag, + self.real * other.imag + self.imag * other.real, + ) + } + + fn scale(&self, scalar: f64) -> Complex { + Complex::new(self.real * scalar, self.imag * scalar) + } +} + +/// Native quantum state vector simulator +pub struct NativeSimulator { + state: Vec, + num_qubits: usize, +} + +impl NativeSimulator { + pub fn new(num_qubits: usize) -> Self { + let size = 1 << num_qubits; // 2^num_qubits + let mut state = vec![Complex::zero(); size]; + state[0] = Complex::one(); // Initialize to |0...0⟩ + + NativeSimulator { state, num_qubits } + } + + /// Get the probability of measuring a specific state + fn get_probability(&self, index: usize) -> f64 { + if index < self.state.len() { + self.state[index].magnitude_squared() + } else { + 0.0 + } + } + + /// Apply a single-qubit gate + fn apply_single_qubit_gate(&mut self, qubit: usize, matrix: [[Complex; 2]; 2]) { + let size = self.state.len(); + let mask = 1 << qubit; + + // Parallel application for better performance + let mut new_state = self.state.clone(); + + (0..size) + .into_par_iter() + .filter(|&i| (i & mask) == 0) + .for_each(|i| { + let i0 = i; + let i1 = i | mask; + + let amp0 = self.state[i0]; + let amp1 = self.state[i1]; + + let new_amp0 = matrix[0][0].mul(&0).add(&matrix[0][1].mul(&1)); + let new_amp1 = matrix[1][0].mul(&0).add(&matrix[1][1].mul(&1)); + + unsafe { + let ptr = new_state.as_ptr() as *mut Complex; + *ptr.add(i0) = new_amp0; + *ptr.add(i1) = new_amp1; + } + }); + + self.state = new_state; + } + + /// Apply a two-qubit gate + fn apply_two_qubit_gate( + &mut self, + control: usize, + target: usize, + matrix: [[Complex; 4]; 4], + ) { + let size = self.state.len(); + let control_mask = 1 << control; + let target_mask = 1 << target; + + let mut new_state = self.state.clone(); + + (0..size) + .into_par_iter() + .filter(|&i| (i & control_mask) == 0 && (i & target_mask) == 0) + .for_each(|i| { + let i00 = i; + let i01 = i | target_mask; + let i10 = i | control_mask; + let i11 = i | control_mask | target_mask; + + let amps = [ + self.state[i00], + self.state[i01], + self.state[i10], + self.state[i11], + ]; + + let mut new_amps = [Complex::zero(); 4]; + + for row in 0..4 { + for col in 0..4 { + new_amps[row] = new_amps[row].add(&matrix[row][col].mul(&s[col])); + } + } + + unsafe { + let ptr = new_state.as_ptr() as *mut Complex; + *ptr.add(i00) = new_amps[0]; + *ptr.add(i01) = new_amps[1]; + *ptr.add(i10) = new_amps[2]; + *ptr.add(i11) = new_amps[3]; + } + }); + + self.state = new_state; + } + + /// Simulate measurements and return counts + fn measure(&self, shots: u32) -> HashMap { + use rand::Rng; + let mut rng = rand::thread_rng(); + let mut counts = HashMap::new(); + + // Build cumulative probability distribution + let mut cumulative_probs = Vec::with_capacity(self.state.len()); + let mut sum = 0.0; + for amp in &self.state { + sum += amp.magnitude_squared(); + cumulative_probs.push(sum); + } + + // Perform measurements + for _ in 0..shots { + let rand_val: f64 = rng.gen(); + let index = cumulative_probs + .binary_search_by(|&p| p.partial_cmp(&rand_val).unwrap()) + .unwrap_or_else(|i| i); + + let bitstring = format!("{:0width$b}", index, width = self.num_qubits); + *counts.entry(bitstring).or_insert(0) += 1; + } + + counts + } + + /// Apply a gate based on name and parameters + fn apply_gate(&mut self, gate_name: &str, qubits: &[usize], params: &[f64]) -> Result<(), String> { + match gate_name { + "hadamard" | "h" => { + if qubits.len() != 1 { + return Err("Hadamard gate requires 1 qubit".to_string()); + } + let inv_sqrt2 = 1.0 / 2.0_f64.sqrt(); + let h_matrix = [ + [Complex::new(inv_sqrt2, 0.0), Complex::new(inv_sqrt2, 0.0)], + [Complex::new(inv_sqrt2, 0.0), Complex::new(-inv_sqrt2, 0.0)], + ]; + self.apply_single_qubit_gate(qubits[0], h_matrix); + } + "x" => { + if qubits.len() != 1 { + return Err("X gate requires 1 qubit".to_string()); + } + let x_matrix = [ + [Complex::zero(), Complex::one()], + [Complex::one(), Complex::zero()], + ]; + self.apply_single_qubit_gate(qubits[0], x_matrix); + } + "y" => { + if qubits.len() != 1 { + return Err("Y gate requires 1 qubit".to_string()); + } + let y_matrix = [ + [Complex::zero(), Complex::new(0.0, -1.0)], + [Complex::new(0.0, 1.0), Complex::zero()], + ]; + self.apply_single_qubit_gate(qubits[0], y_matrix); + } + "z" => { + if qubits.len() != 1 { + return Err("Z gate requires 1 qubit".to_string()); + } + let z_matrix = [ + [Complex::one(), Complex::zero()], + [Complex::zero(), Complex::new(-1.0, 0.0)], + ]; + self.apply_single_qubit_gate(qubits[0], z_matrix); + } + "s" => { + if qubits.len() != 1 { + return Err("S gate requires 1 qubit".to_string()); + } + let s_matrix = [ + [Complex::one(), Complex::zero()], + [Complex::zero(), Complex::i()], + ]; + self.apply_single_qubit_gate(qubits[0], s_matrix); + } + "t" => { + if qubits.len() != 1 { + return Err("T gate requires 1 qubit".to_string()); + } + let phase = std::f64::consts::PI / 4.0; + let t_matrix = [ + [Complex::one(), Complex::zero()], + [Complex::zero(), Complex::new(phase.cos(), phase.sin())], + ]; + self.apply_single_qubit_gate(qubits[0], t_matrix); + } + "rx" => { + if qubits.len() != 1 || params.is_empty() { + return Err("RX gate requires 1 qubit and 1 parameter".to_string()); + } + let theta = params[0]; + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + let rx_matrix = [ + [Complex::new(cos, 0.0), Complex::new(0.0, -sin)], + [Complex::new(0.0, -sin), Complex::new(cos, 0.0)], + ]; + self.apply_single_qubit_gate(qubits[0], rx_matrix); + } + "ry" => { + if qubits.len() != 1 || params.is_empty() { + return Err("RY gate requires 1 qubit and 1 parameter".to_string()); + } + let theta = params[0]; + let cos = (theta / 2.0).cos(); + let sin = (theta / 2.0).sin(); + let ry_matrix = [ + [Complex::new(cos, 0.0), Complex::new(-sin, 0.0)], + [Complex::new(sin, 0.0), Complex::new(cos, 0.0)], + ]; + self.apply_single_qubit_gate(qubits[0], ry_matrix); + } + "rz" => { + if qubits.len() != 1 || params.is_empty() { + return Err("RZ gate requires 1 qubit and 1 parameter".to_string()); + } + let theta = params[0]; + let phase_neg = Complex::new((theta / 2.0).cos(), -(theta / 2.0).sin()); + let phase_pos = Complex::new((theta / 2.0).cos(), (theta / 2.0).sin()); + let rz_matrix = [ + [phase_neg, Complex::zero()], + [Complex::zero(), phase_pos], + ]; + self.apply_single_qubit_gate(qubits[0], rz_matrix); + } + "cnot" | "cx" => { + if qubits.len() != 2 { + return Err("CNOT gate requires 2 qubits".to_string()); + } + let cnot_matrix = [ + [Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()], + [Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()], + [Complex::zero(), Complex::zero(), Complex::zero(), Complex::one()], + [Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()], + ]; + self.apply_two_qubit_gate(qubits[0], qubits[1], cnot_matrix); + } + "cz" => { + if qubits.len() != 2 { + return Err("CZ gate requires 2 qubits".to_string()); + } + let cz_matrix = [ + [Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()], + [Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()], + [Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()], + [Complex::zero(), Complex::zero(), Complex::zero(), Complex::new(-1.0, 0.0)], + ]; + self.apply_two_qubit_gate(qubits[0], qubits[1], cz_matrix); + } + "swap" => { + if qubits.len() != 2 { + return Err("SWAP gate requires 2 qubits".to_string()); + } + let swap_matrix = [ + [Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()], + [Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()], + [Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()], + [Complex::zero(), Complex::zero(), Complex::zero(), Complex::one()], + ]; + self.apply_two_qubit_gate(qubits[0], qubits[1], swap_matrix); + } + _ => return Err(format!("Unknown gate: {}", gate_name)), + } + Ok(()) + } +} + +/// Native Backend implementation +pub struct NativeBackend; + +impl NativeBackend { + pub fn new() -> Self { + NativeBackend + } +} + +impl QuantumBackend for NativeBackend { + fn execute( + &self, + circuit: &HardwareCircuit, + config: &QuantumConfig, + ) -> Result { + // Initialize simulator + let mut simulator = NativeSimulator::new(circuit.num_qubits); + + // Apply gates + for gate in &circuit.gates { + simulator.apply_gate(&gate.name, &gate.qubits, &gate.params)?; + } + + // Measure + let counts = simulator.measure(config.shots); + + Ok(QuantumResult { + counts, + shots: config.shots, + success: true, + error_message: None, + }) + } + + fn is_available(&self) -> bool { + true // Native backend is always available + } + + fn available_devices(&self) -> Vec { + vec![ + "native_simulator".to_string(), + "native_statevector".to_string(), + ] + } + + fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { + // Basic optimization: merge consecutive single-qubit gates + let mut optimized_gates = Vec::new(); + + for gate in &circuit.gates { + // Simple pass-through for now + // You can add optimization logic here: + // - Gate fusion + // - Commutation rules + // - Cancellation of inverse gates + optimized_gates.push(gate.clone()); + } + + HardwareCircuit { + num_qubits: circuit.num_qubits, + gates: optimized_gates, + measurements: circuit.measurements.clone(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hadamard_gate() { + let mut sim = NativeSimulator::new(1); + sim.apply_gate("h", &[0], &[]).unwrap(); + + let prob0 = sim.get_probability(0); + let prob1 = sim.get_probability(1); + + assert!((prob0 - 0.5).abs() < 1e-10); + assert!((prob1 - 0.5).abs() < 1e-10); + } + + #[test] + fn test_cnot_gate() { + let mut sim = NativeSimulator::new(2); + sim.apply_gate("x", &[0], &[]).unwrap(); + sim.apply_gate("cnot", &[0, 1], &[]).unwrap(); + + // Should be in |11⟩ state + let prob3 = sim.get_probability(3); // Binary: 11 + assert!((prob3 - 1.0).abs() < 1e-10); + } + + #[test] + fn test_bell_state() { + let mut sim = NativeSimulator::new(2); + sim.apply_gate("h", &[0], &[]).unwrap(); + sim.apply_gate("cnot", &[0, 1], &[]).unwrap(); + + // Should have equal probability for |00⟩ and |11⟩ + let prob0 = sim.get_probability(0); + let prob3 = sim.get_probability(3); + + assert!((prob0 - 0.5).abs() < 1e-10); + assert!((prob3 - 0.5).abs() < 1e-10); + } +} \ No newline at end of file diff --git a/src/runtime.rs b/src/runtime.rs index cc80d51..d2a36ec 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,34 +1,29 @@ // src/runtime.rs +use crate::environment::GateDefinition; use crate::environment::{Environment, RuntimeValue}; use crate::evaluator::Evaluator; +use libc; +use std::cell::RefCell; use std::collections::HashMap; use std::ffi::{c_char, c_void, CStr}; +use std::io::Write; use std::os::raw::c_int; -use libc; use std::rc::Rc; -use std::cell::RefCell; -use crate::environment::GateDefinition; use std::slice; -use std::io::Write; - type StatePtr = *mut c_void; - #[no_mangle] pub extern "C" fn quantica_rt_new_state(num_qubits: c_int) -> StatePtr { - let mut state_map: HashMap = HashMap::new(); state_map.insert(0, (1.0, 0.0)); - let register = RuntimeValue::QuantumRegister { state: Rc::new(RefCell::new(state_map)), size: num_qubits as usize, }; - let boxed_register = Box::new(register); Box::into_raw(boxed_register) as StatePtr } @@ -44,17 +39,14 @@ pub extern "C" fn quantica_rt_measure(state_ptr: StatePtr, qubit_index: c_int) - match register { RuntimeValue::QuantumRegister { state, size } => { - let qubit_handle = RuntimeValue::Qubit { state: state.clone(), index: qubit_index as usize, size: *size, }; - let result = Evaluator::builtin_measure(vec![qubit_handle]); - match result { Ok(RuntimeValue::Int(r)) => r as c_int, Ok(_) => -2, @@ -73,13 +65,15 @@ pub extern "C" fn quantica_rt_measure(state_ptr: StatePtr, qubit_index: c_int) - #[no_mangle] pub extern "C" fn quantica_rt_device_alloc(size_bytes: usize) -> *mut c_void { - - let ptr = unsafe { libc::malloc(size_bytes) }; - + let ptr = unsafe { libc::malloc(size_bytes) }; + if ptr.is_null() { - eprintln!("(Runtime Error) Device memory allocation failed for {} bytes.", size_bytes); + eprintln!( + "(Runtime Error) Device memory allocation failed for {} bytes.", + size_bytes + ); } - + ptr } @@ -96,11 +90,10 @@ pub unsafe extern "C" fn quantica_rt_device_free(device_ptr: *mut c_void) { #[no_mangle] pub unsafe extern "C" fn quantica_rt_htod_transfer( - host_ptr: *const c_void, - device_ptr: *mut c_void, - size_bytes: usize + host_ptr: *const c_void, + device_ptr: *mut c_void, + size_bytes: usize, ) -> c_int { - unsafe { std::ptr::copy_nonoverlapping(host_ptr, device_ptr, size_bytes); } @@ -110,11 +103,10 @@ pub unsafe extern "C" fn quantica_rt_htod_transfer( #[no_mangle] pub unsafe extern "C" fn quantica_rt_dtoh_transfer( - host_ptr: *mut c_void, - device_ptr: *const c_void, - size_bytes: usize + host_ptr: *mut c_void, + device_ptr: *const c_void, + size_bytes: usize, ) -> c_int { - unsafe { std::ptr::copy_nonoverlapping(device_ptr, host_ptr, size_bytes); } @@ -122,7 +114,6 @@ pub unsafe extern "C" fn quantica_rt_dtoh_transfer( 0 } - #[no_mangle] pub extern "C" fn quantica_rt_free_state(state_ptr: StatePtr) { if state_ptr.is_null() { @@ -130,11 +121,10 @@ pub extern "C" fn quantica_rt_free_state(state_ptr: StatePtr) { } unsafe { - let _=Box::from_raw(state_ptr as *mut RuntimeValue); + let _ = Box::from_raw(state_ptr as *mut RuntimeValue); } } - #[no_mangle] pub extern "C" fn quantica_rt_debug_state(state_ptr: StatePtr) { if state_ptr.is_null() { @@ -142,15 +132,10 @@ pub extern "C" fn quantica_rt_debug_state(state_ptr: StatePtr) { return; } + let register = unsafe { &*(state_ptr as *mut RuntimeValue) }; - let register = unsafe { - - &*(state_ptr as *mut RuntimeValue) - }; - // 2. Check the type if let RuntimeValue::QuantumRegister { state, size } = register { - println!("(Quantum Runtime) Debugging state ({} qubits):", size); Evaluator::print_quantum_state(state, *size, 10); } else { @@ -168,18 +153,19 @@ pub unsafe extern "C" fn quantica_rt_apply_gate( num_qubits: c_int, num_controls: c_int, ) -> c_int { - - match unsafe { apply_gate_unsafe( - state_ptr, - gate_name_ptr, - is_dagger_int, - params_ptr, - num_params, - qubit_indices_ptr, - num_qubits, - num_controls - ) } { - Ok(_) => 0, + match unsafe { + apply_gate_unsafe( + state_ptr, + gate_name_ptr, + is_dagger_int, + params_ptr, + num_params, + qubit_indices_ptr, + num_qubits, + num_controls, + ) + } { + Ok(_) => 0, Err(e) => { eprintln!("(Quantum Runtime Error) {}", e); 1 @@ -187,7 +173,6 @@ pub unsafe extern "C" fn quantica_rt_apply_gate( } } - unsafe fn apply_gate_unsafe( state_ptr: StatePtr, gate_name_ptr: *const c_char, @@ -198,8 +183,6 @@ unsafe fn apply_gate_unsafe( num_qubits: c_int, num_controls: c_int, ) -> Result { - - if state_ptr.is_null() { return Err("State pointer is null.".to_string()); } @@ -215,22 +198,21 @@ unsafe fn apply_gate_unsafe( // 3. Unwrap Dagger Flag let is_dagger = is_dagger_int != 0; - let params: Vec = if params_ptr.is_null() { Vec::new() } else { slice::from_raw_parts(params_ptr, num_params as usize).to_vec() }; - let qubit_indices: Vec = if qubit_indices_ptr.is_null() { Vec::new() } else { slice::from_raw_parts(qubit_indices_ptr, num_qubits as usize) - .iter().map(|&x| x as usize).collect() + .iter() + .map(|&x| x as usize) + .collect() }; - if (num_controls as usize) > qubit_indices.len() { return Err("More controls specified than total qubits.".to_string()); } @@ -255,7 +237,7 @@ pub extern "C" fn quantica_rt_print_int(n: i64) { #[no_mangle] pub unsafe extern "C" fn quantica_rt_print_string(s: *const c_char) { - if s.is_null() { + if s.is_null() { println!("(null)"); } else { let c_str = unsafe { CStr::from_ptr(s) }; @@ -267,4 +249,3 @@ pub unsafe extern "C" fn quantica_rt_print_string(s: *const c_char) { } let _ = std::io::stdout().flush(); } - diff --git a/src/type_checker.rs b/src/type_checker.rs index b791e35..79adf28 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1,18 +1,18 @@ // src/type_checker.rs +use crate::parser::ast::{ASTNode, BinaryOperator, ImportSpec, Type, UnaryOperator}; +use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use std::cell::RefCell; use std::string::String; -use crate::parser::ast::{ASTNode, Type, BinaryOperator, UnaryOperator, ImportSpec}; - -use crate::parser::ast::ImportPath; +// --- ADD THESE IMPORTS --- use crate::lexer::Lexer; +use crate::parser::ast::ImportPath; +use crate::parser::ast::Loc; use crate::parser::Parser; use std::fs; -use crate::parser::ast::Loc; - +// --- END ADDED IMPORTS --- #[derive(Debug, Clone, PartialEq)] pub struct TypeInfo { @@ -28,11 +28,17 @@ pub struct TypeEnvironment { impl TypeEnvironment { pub fn new() -> Self { - TypeEnvironment { store: HashMap::new(), outer: Option::None } + TypeEnvironment { + store: HashMap::new(), + outer: Option::None, + } } pub fn new_enclosed(outer_env: Rc>) -> Self { - TypeEnvironment { store: HashMap::new(), outer: Some(outer_env) } + TypeEnvironment { + store: HashMap::new(), + outer: Some(outer_env), + } } pub fn get(&self, name: &str) -> Option { @@ -50,15 +56,16 @@ impl TypeEnvironment { } } - pub struct TypeChecker; impl TypeChecker { - pub fn prefill_environment(env: &Rc>) { let mut env_mut = env.borrow_mut(); - let immut = |t: Type| TypeInfo { var_type: t, is_mutable: false }; + let immut = |t: Type| TypeInfo { + var_type: t, + is_mutable: false, + }; let any = Type::Any; let none = Type::None; @@ -66,25 +73,59 @@ impl TypeChecker { let none_type = Box::new(none.clone()); // --- Built-ins --- - env_mut.set("print".to_string(), immut(Type::Function(vec![], Box::new(none.clone())))); - env_mut.set("debug_state".to_string(), immut(Type::Function(vec![Type::QuantumRegister(None)], none_type.clone()))); - env_mut.set("assert".to_string(), immut( - Type::Function(vec![Type::Bool, Type::String], none_type.clone()) - )); - env_mut.set("maybe".to_string(), immut( - Type::Function(vec![Type::Any, Type::Float], Box::new(Type::Any)) - )); - env_mut.set("sample".to_string(), immut( - Type::Function(vec![Type::Any], Box::new(Type::Any)) - )); - env_mut.set("echo".to_string(), immut( - Type::Function(vec![Type::Any], Box::new(Type::Any)) - )); - env_mut.set("len".to_string(), immut(Type::Function(vec![any.clone()], Box::new(Type::Int)))); - env_mut.set("type_of".to_string(), immut(Type::Function(vec![any.clone()], Box::new(Type::String)))); - env_mut.set("to_string".to_string(), immut(Type::Function(vec![any.clone()], Box::new(Type::String)))); - env_mut.set("to_int".to_string(), immut(Type::Function(vec![any.clone()], Box::new(Type::Int)))); - env_mut.set("to_float".to_string(), immut(Type::Function(vec![any.clone()], Box::new(Type::Float)))); + env_mut.set( + "print".to_string(), + immut(Type::Function(vec![], Box::new(none.clone()))), + ); + env_mut.set( + "debug_state".to_string(), + immut(Type::Function( + vec![Type::QuantumRegister(None)], + none_type.clone(), + )), + ); + env_mut.set( + "assert".to_string(), + immut(Type::Function( + vec![Type::Bool, Type::String], + none_type.clone(), + )), + ); + env_mut.set( + "maybe".to_string(), + immut(Type::Function( + vec![Type::Any, Type::Float], + Box::new(Type::Any), + )), + ); + env_mut.set( + "sample".to_string(), + immut(Type::Function(vec![Type::Any], Box::new(Type::Any))), + ); + env_mut.set( + "echo".to_string(), + immut(Type::Function(vec![Type::Any], Box::new(Type::Any))), + ); + env_mut.set( + "len".to_string(), + immut(Type::Function(vec![any.clone()], Box::new(Type::Int))), + ); + env_mut.set( + "type_of".to_string(), + immut(Type::Function(vec![any.clone()], Box::new(Type::String))), + ); + env_mut.set( + "to_string".to_string(), + immut(Type::Function(vec![any.clone()], Box::new(Type::String))), + ); + env_mut.set( + "to_int".to_string(), + immut(Type::Function(vec![any.clone()], Box::new(Type::Int))), + ); + env_mut.set( + "to_float".to_string(), + immut(Type::Function(vec![any.clone()], Box::new(Type::Float))), + ); // --- Single-Qubit Gates --- let single_qubit_gate = Type::Function(vec![qubit_type.clone()], none_type.clone()); @@ -97,7 +138,10 @@ impl TypeChecker { env_mut.set("reset".to_string(), immut(single_qubit_gate.clone())); // --- Multi-Qubit Gates --- - let two_qubit_gate = Type::Function(vec![qubit_type.clone(), qubit_type.clone()], none_type.clone()); + let two_qubit_gate = Type::Function( + vec![qubit_type.clone(), qubit_type.clone()], + none_type.clone(), + ); env_mut.set("cnot".to_string(), immut(two_qubit_gate.clone())); env_mut.set("swap".to_string(), immut(two_qubit_gate.clone())); env_mut.set("cz".to_string(), immut(two_qubit_gate.clone())); @@ -106,7 +150,7 @@ impl TypeChecker { let three_qubit_gate = Type::Function( vec![qubit_type.clone(), qubit_type.clone(), qubit_type.clone()], - none_type.clone() + none_type.clone(), ); env_mut.set("ccx".to_string(), immut(three_qubit_gate.clone())); env_mut.set("toffoli".to_string(), immut(three_qubit_gate)); @@ -114,26 +158,195 @@ impl TypeChecker { // --- Parameterized Gates --- let cphase_gate = Type::Function( vec![Type::Float, qubit_type.clone(), qubit_type.clone()], - none_type.clone() + none_type.clone(), ); env_mut.set("cphase".to_string(), immut(cphase_gate)); let u_gate = Type::Function( vec![Type::Float, Type::Float, Type::Float, qubit_type.clone()], - none_type.clone() + none_type.clone(), ); env_mut.set("u".to_string(), immut(u_gate)); - let parameterized_gate = Type::Function( - vec![Type::Float, qubit_type.clone()], - none_type.clone() - ); + let parameterized_gate = + Type::Function(vec![Type::Float, qubit_type.clone()], none_type.clone()); env_mut.set("rx".to_string(), immut(parameterized_gate.clone())); env_mut.set("ry".to_string(), immut(parameterized_gate.clone())); env_mut.set("rz".to_string(), immut(parameterized_gate.clone())); - } + //Graphics Built-ins + + env_mut.set( + "_graphics_create_canvas".to_string(), + immut(Type::Function( + vec![Type::Int, Type::Int], + Box::new(Type::Int), + )), + ); + + // _graphics_set_background(id, r, g, b, a) + env_mut.set( + "_graphics_set_background".to_string(), + immut(Type::Function( + vec![Type::Int, Type::Int, Type::Int, Type::Int, Type::Int], + Box::new(Type::None), + )), + ); + + // _graphics_clear(id) + env_mut.set( + "_graphics_clear".to_string(), + immut(Type::Function(vec![Type::Int], Box::new(Type::None))), + ); + + // _graphics_draw_line(id, x1, y1, x2, y2, r, g, b, a, width) + env_mut.set( + "_graphics_draw_line".to_string(), + immut(Type::Function( + vec![ + Type::Int, // id + Type::Float, // x1 + Type::Float, // y1 + Type::Float, // x2 + Type::Float, // y2 + Type::Int, // r + Type::Int, // g + Type::Int, // b + Type::Int, // a + Type::Float, // width + ], + Box::new(Type::None), + )), + ); + + // _graphics_draw_rect(id, x, y, w, h, r, g, b, a, filled) + env_mut.set( + "_graphics_draw_rect".to_string(), + immut(Type::Function( + vec![ + Type::Int, // id + Type::Float, // x + Type::Float, // y + Type::Float, // w + Type::Float, // h + Type::Int, // r + Type::Int, // g + Type::Int, // b + Type::Int, // a + Type::Int, // filled (0 or 1) + ], + Box::new(Type::None), + )), + ); + + // _graphics_draw_circle(id, x, y, radius, r, g, b, a, filled) + env_mut.set( + "_graphics_draw_circle".to_string(), + immut(Type::Function( + vec![ + Type::Int, // id + Type::Float, // x + Type::Float, // y + Type::Float, // radius + Type::Int, // r + Type::Int, // g + Type::Int, // b + Type::Int, // a + Type::Int, // filled + ], + Box::new(Type::None), + )), + ); + + // _graphics_draw_text(id, x, y, text, r, g, b, a, size) + env_mut.set( + "_graphics_draw_text".to_string(), + immut(Type::Function( + vec![ + Type::Int, // id + Type::Float, // x + Type::Float, // y + Type::String, // text + Type::Int, // r + Type::Int, // g + Type::Int, // b + Type::Int, // a + Type::Float, // size + ], + Box::new(Type::None), + )), + ); + + // _graphics_save_svg(id, filename) -> Int (status) + env_mut.set( + "_graphics_save_svg".to_string(), + immut(Type::Function( + vec![Type::Int, Type::String], + Box::new(Type::Int), + )), + ); + + // _graphics_save_png(id, filename) -> Int (status) + env_mut.set( + "_graphics_save_png".to_string(), + immut(Type::Function( + vec![Type::Int, Type::String], + Box::new(Type::Int), + )), + ); + + // _graphics_destroy_canvas(id) + env_mut.set( + "_graphics_destroy_canvas".to_string(), + immut(Type::Function(vec![Type::Int], Box::new(Type::None))), + ); + + // _graphics_create_plot(type) -> Int + env_mut.set( + "_graphics_create_plot".to_string(), + immut(Type::Function(vec![Type::Int], Box::new(Type::Int))), + ); + + // _graphics_plot_set_data(id, x[], y[], len) + env_mut.set( + "_graphics_plot_set_data".to_string(), + immut(Type::Function( + vec![ + Type::Int, + Type::Array(Box::new(Type::Float)), + Type::Array(Box::new(Type::Float)), + Type::Int, + ], + Box::new(Type::None), + )), + ); + + // _graphics_plot_set_title(id, title) + env_mut.set( + "_graphics_plot_set_title".to_string(), + immut(Type::Function( + vec![Type::Int, Type::String], + Box::new(Type::None), + )), + ); + + // _graphics_plot_render(plot_id, canvas_id) -> Int + env_mut.set( + "_graphics_plot_render".to_string(), + immut(Type::Function( + vec![Type::Int, Type::Int], + Box::new(Type::Int), + )), + ); + + // _graphics_destroy_plot(id) + env_mut.set( + "_graphics_destroy_plot".to_string(), + immut(Type::Function(vec![Type::Int], Box::new(Type::None))), + ); + } + pub fn check_program(node: &ASTNode) -> Result<(), String> { if let ASTNode::Program(statements) = node { let env = Rc::new(RefCell::new(TypeEnvironment::new())); @@ -148,34 +361,46 @@ impl TypeChecker { } fn immutable_info(t: Type) -> TypeInfo { - TypeInfo { var_type: t, is_mutable: false } + TypeInfo { + var_type: t, + is_mutable: false, + } } fn check_module(path: &ImportPath) -> Result, String> { let file_path = match path { - + // This is our new logic ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - + // It's a file path f.clone() } else { - + // It's a package name, e.g., "math" + // We'll look for "q_packages//init.qc" format!("q_packages/{}/init.qc", f) } } ImportPath::Module(m) => { - + // This logic is for 'import ai.neural', which becomes 'ai/neural.qc' m.join("/") + ".qc" } }; - let source = fs::read_to_string(&file_path) - .map_err(|e| format!("Type Check Error: Failed to read module '{}': {}", file_path, e))?; + let source = fs::read_to_string(&file_path).map_err(|e| { + format!( + "Type Check Error: Failed to read module '{}': {}", + file_path, e + ) + })?; let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize().map_err(|e| format!("Module Lexer Error: {}", e))?; + let tokens = lexer + .tokenize() + .map_err(|e| format!("Module Lexer Error: {}", e))?; let mut parser = Parser::new(tokens); - let ast = parser.parse().map_err(|e| format!("Module Parser Error: {}", e))?; + let ast = parser + .parse() + .map_err(|e| format!("Module Parser Error: {}", e))?; let module_env = Rc::new(RefCell::new(TypeEnvironment::new())); Self::prefill_environment(&module_env); @@ -188,20 +413,60 @@ impl TypeChecker { return Err("Module root is not a Program node".to_string()); } - let module_types = module_env.borrow().store.iter() + let module_types = module_env + .borrow() + .store + .iter() .map(|(k, v)| (k.clone(), v.var_type.clone())) .collect(); Ok(module_types) } + fn resolve_method( + env: &Rc>, + method_key: &str, + ) -> Option { + // 1. Try direct lookup (if class is defined in the same file) + if let Some(info) = env.borrow().get(method_key) { + return Some(info.var_type.clone()); + } + + // 2. Search inside imported modules (if class came from 'import') + // We traverse up the environment chain manually + let mut current_env = Some(env.clone()); + + while let Some(env_rc) = current_env { + let env_ref = env_rc.borrow(); + // Iterate over all variables in this scope + for info in env_ref.store.values() { + // Is this variable a Module? + if let Type::Module(module_map) = &info.var_type { + // Does this module contain the method we need? + if let Some(method_type) = module_map.get(method_key) { + return Some(method_type.clone()); + } + } + } + + // Move up to outer scope + current_env = env_ref.outer.clone(); + } + + None + } + + // --- *** FIXED check_gate_expression (Base Case Lookup) *** --- fn check_gate_expression( node: &ASTNode, - env: &Rc> + env: &Rc>, ) -> Result { let loc = match node { - ASTNode::Gate { loc, .. } |ASTNode::ParameterizedGate { loc, .. } | ASTNode::Controlled { loc, .. } | ASTNode::Dagger { loc, .. } => *loc, + ASTNode::Gate { loc, .. } + | ASTNode::ParameterizedGate { loc, .. } + | ASTNode::Controlled { loc, .. } + | ASTNode::Dagger { loc, .. } => *loc, _ => Loc { line: 0, column: 0 }, }; @@ -215,91 +480,118 @@ impl TypeChecker { } } - - Err(format!("Type Error at {}: Unknown quantum gate '{}'.", loc, name)) + // This line is triggered if the gate name (like 'X') is not a function. + Err(format!( + "Type Error at {}: Unknown quantum gate '{}'.", + loc, name + )) } ASTNode::Dagger { gate_expr, .. } => { - + // Daggering a gate doesn't change its signature, so we just check the inner expression. let inner_gate_type = Self::check_gate_expression(gate_expr, env)?; if let Type::Function(..) = inner_gate_type { Ok(inner_gate_type) } else { - Err(format!("Type Error at {}: 'dagger' can only be applied to a gate or circuit.", loc)) + Err(format!( + "Type Error at {}: 'dagger' can only be applied to a gate or circuit.", + loc + )) } } ASTNode::Controlled { gate_expr, loc } => { - + // 1. Get the type of the inner gate let inner_gate_type = Self::check_gate_expression(gate_expr, env)?; match inner_gate_type { Type::Function(mut params, ret_type) => { + // 2. The *first* argument of the inner gate is always the TARGET. + // We check the target type, then add a Qubit control before it. + // Ensure the innermost gate is a single-target gate if params.is_empty() { return Err(format!("Type Error at {}: 'controlled' target gate must take at least one argument.", loc)); } - + // Add the new control qubit before the target(s) params.insert(0, Type::Qubit); - + // The return type is the same Ok(Type::Function(params, ret_type)) } - _ => Err(format!("Type Error at {}: 'controlled' can only be applied to a callable gate.", loc)) + _ => Err(format!( + "Type Error at {}: 'controlled' can only be applied to a callable gate.", + loc + )), } } - ASTNode::ParameterizedGate { name, parameters, loc } => { - let gate_name_lower = name.to_lowercase(); - - - let gate_info = env.borrow().get(&gate_name_lower) - .ok_or_else(|| format!("Type Error at {}: Unknown parameterized gate '{}'.", loc, name))?; + ASTNode::ParameterizedGate { + name, + parameters, + loc, + } => { + let gate_name_lower = name.to_lowercase(); - match gate_info.var_type { - Type::Function(param_types, return_type) => { + // 1. Look up the gate function type + let gate_info = env.borrow().get(&gate_name_lower).ok_or_else(|| { + format!( + "Type Error at {}: Unknown parameterized gate '{}'.", + loc, name + ) + })?; + match gate_info.var_type { + Type::Function(param_types, return_type) => { + // 2. The first N parameters are the gate parameters (angles, etc.) + // The remaining parameters are the qubits - if parameters.len() > param_types.len() { - return Err(format!( + if parameters.len() > param_types.len() { + return Err(format!( "Type Error at {}: Gate '{}' takes at most {} parameters, but got {}", loc, name, param_types.len(), parameters.len() )); - } - - - for (i, param_expr) in parameters.iter().enumerate() { - let param_type = Self::check(param_expr, env, None)?; - let expected_type = ¶m_types[i]; + } + // 3. Type-check each parameter expression + for (i, param_expr) in parameters.iter().enumerate() { + let param_type = Self::check(param_expr, env, None)?; + let expected_type = ¶m_types[i]; - if param_type != *expected_type && - !(*expected_type == Type::Float && param_type == Type::Int) { - return Err(format!( + // Allow Int where Float is expected + if param_type != *expected_type + && !(*expected_type == Type::Float && param_type == Type::Int) + { + return Err(format!( "Type Error at {}: Parameter {} of gate '{}' has wrong type. Expected {:?}, got {:?}", loc, i + 1, name, expected_type, param_type )); + } } - } - - let remaining_params = param_types[parameters.len()..].to_vec(); - Ok(Type::Function(remaining_params, return_type)) + // 4. Return a function type with the remaining (qubit) parameters + let remaining_params = param_types[parameters.len()..].to_vec(); + Ok(Type::Function(remaining_params, return_type)) + } + _ => Err(format!( + "Type Error at {}: '{}' is not a parameterized gate.", + loc, name + )), } - _ => Err(format!("Type Error at {}: '{}' is not a parameterized gate.", loc, name)) } - } - _ => Err(format!("Type Error: This expression is not a valid gate.")) + _ => Err(format!("Type Error: This expression is not a valid gate.")), } } - - - pub fn check(node: &ASTNode, env: &Rc>, expected_return_type: Option<&Type>) -> Result { - + /// The main recursive checking function. + pub fn check( + node: &ASTNode, + env: &Rc>, + expected_return_type: Option<&Type>, + ) -> Result { match node { // --- Literals --- ASTNode::IntLiteral(_) => Ok(Type::Int), @@ -312,29 +604,50 @@ impl TypeChecker { ASTNode::QuantumBra(_) => Err("Bra notation is not yet supported.".to_string()), // --- Declarations --- - ASTNode::LetDeclaration { name, type_annotation, value, is_mutable, ..} => { + ASTNode::LetDeclaration { + name, + type_annotation, + value, + is_mutable, + .. + } => { let value_type = Self::check(value, env, Option::None)?; let final_type = match type_annotation { Some(expected_type) => { - if value_type != *expected_type && - value_type != Type::None && - *expected_type != Type::Any { - if let (Type::QuantumRegister(_), Type::QuantumRegister(None)) = (&value_type, expected_type) { - // OK - } else { + if value_type != *expected_type + && value_type != Type::None + && *expected_type != Type::Any + && value_type != Type::Any + { + // --- FIX START: Expanded Compatibility Checks --- + let is_quantum_compat = matches!( + (&value_type, expected_type), + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + + let is_class_compat = match (&value_type, expected_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat { return Err(format!( "Type Error: Variable '{}' is annotated as {:?} but is being assigned a value of type {:?}", name, expected_type, value_type )); } + // --- FIX END --- } expected_type.clone() } Option::None => value_type, }; - let info = TypeInfo { var_type: final_type, is_mutable: *is_mutable }; + let info = TypeInfo { + var_type: final_type, + is_mutable: *is_mutable, + }; env.borrow_mut().set(name.clone(), info); Ok(Type::None) } @@ -343,40 +656,47 @@ impl TypeChecker { let new_type = Self::check(value, env, Option::None)?; match target.as_ref() { - + // Simple identifier assignment ASTNode::Identifier { name, .. } => { let original_info = match env.borrow().get(name) { Some(info) => info, Option::None => return Err(format!("Type Error: Cannot assign to undefined variable '{}'", name)), }; - if !original_info.is_mutable { return Err(format!("Mutability Error: Cannot assign to immutable variable '{}'.", name)); } - if original_info.var_type != new_type && original_info.var_type != Type::Any && - new_type != Type::None { - if let (Type::QuantumRegister(_), Type::QuantumRegister(None)) = (&new_type, &original_info.var_type) { - // OK - } else { + new_type != Type::None && + new_type != Type::Any { + // --- FIX START: Expanded Compatibility Checks --- + let is_quantum_compat = matches!( + (&new_type, &original_info.var_type), + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + + let is_class_compat = match (&new_type, &original_info.var_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat { return Err(format!( "Type Error: Mismatched types in assignment. Cannot assign type {:?} to variable '{}' of type {:?}", new_type, name, original_info.var_type )); } + // --- FIX END --- } Ok(Type::None) } - - + // Dictionary/Array subscript assignment: dict["key"] = value ASTNode::ArrayAccess { array, index, loc } => { let array_type = Self::check(array, env, Option::None)?; let index_type = Self::check(index, env, Option::None)?; - match array_type { Type::Dict => { - + // Check that the index is a valid key type if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { @@ -385,18 +705,18 @@ impl TypeChecker { loc, index_type )); } - + // For Dict, we allow any value type Ok(Type::None) } Type::Array(inner_type) => { - + // Check index is Int if index_type != Type::Int { return Err(format!( "Type Error at {}: Array index must be Int, got {:?}", loc, index_type )); } - + // Check value type matches array element type if new_type != *inner_type && *inner_type != Type::Any { return Err(format!( "Type Error at {}: Cannot assign {:?} to array of {:?}", @@ -411,46 +731,529 @@ impl TypeChecker { )) } } + ASTNode::MemberAccess { object, member } => { + // Check the object (e.g., 'self' or 'my_obj') + let object_type = Self::check(object, env, Option::None)?; + + // We only allow assignment if it's an Instance or Custom type + let class_name = match object_type { + Type::Instance(name) | Type::Custom(name) => name, + _ => return Err(format!("Type Error: Cannot assign to member '{}' of non-object type {:?}", member, object_type)), + }; + + // Construct key "ClassName::Field" + let field_key = format!("{}::{}", class_name, member); + + // Look up field info + if let Some(field_info) = env.borrow().get(&field_key) { + // Check mutability + if !field_info.is_mutable { + return Err(format!("Mutability Error: Field '{}.{}' is immutable.", class_name, member)); + } + // Check type compatibility + if field_info.var_type != new_type && field_info.var_type != Type::Any { + // Basic compatibility check + let is_compat = matches!((&new_type, &field_info.var_type), + (Type::Int, Type::Float) | // Allow Int -> Float + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + + if !is_compat && new_type != field_info.var_type { + return Err(format!( + "Type Error: Cannot assign type {:?} to field '{}.{}' of type {:?}", + new_type, class_name, member, field_info.var_type + )); + } + } + Ok(Type::None) + } else { + // Special case: If inside 'init', we might be initializing a field. + // But strictly speaking, fields should be registered by ClassDeclaration. + // If this fails, it means the field wasn't declared in the class body. + Err(format!("Type Error: Class '{}' has no field named '{}'", class_name, member)) + } + } _ => Err("Type Error: Assignment target must be an identifier or subscript expression.".to_string()) } } - ASTNode::Identifier { name, loc } => { - match env.borrow().get(name) { - Some(info) => Ok(info.var_type.clone()), - Option::None => Err(format!("Type Error at {}: Undefined variable '{}'", loc, name)), + ASTNode::Identifier { name, loc } => match env.borrow().get(name) { + Some(info) => Ok(info.var_type.clone()), + Option::None => Err(format!( + "Type Error at {}: Undefined variable '{}'", + loc, name + )), + }, + ASTNode::ClassDeclaration { name, fields, methods, constructor, .. } => { + // Register class type + let class_type = Type::Class(name.clone()); + env.borrow_mut().set( + name.clone(), + TypeInfo { var_type: class_type, is_mutable: false } + ); + + // Create class environment for checking methods + let class_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); + + // Add fields to class environment + for field in fields { + class_env.borrow_mut().set( + field.name.clone(), + TypeInfo { + var_type: field.field_type.clone(), + is_mutable: true, + } + ); + let global_field_key = format!("{}::{}", name, field.name); + env.borrow_mut().set( + global_field_key, + TypeInfo { + var_type: field.field_type.clone(), + is_mutable: true, + } + ); + } + + + + // Check constructor if it exists + if let Some(constructor_node) = constructor { + if let ASTNode::FunctionDeclaration { body, parameters, .. } = &**constructor_node { + let constructor_env = Rc::new(RefCell::new( + TypeEnvironment::new_enclosed(class_env.clone()) + )); + + // FIX 1: Register constructor signature globally as "ClassName::init" + // This allows checking calls like 'Canvas(800, 600)' + let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + let ctor_type = Type::Function(param_types, Box::new(Type::None)); + env.borrow_mut().set( + format!("{}::init", name), + TypeInfo { var_type: ctor_type, is_mutable: false } + ); + + // ... (keep existing environment setup) ... + constructor_env.borrow_mut().set( + "self".to_string(), + TypeInfo { + var_type: Type::Instance(name.clone()), + is_mutable: false, + } + ); + + // Add parameters + for param in parameters { + constructor_env.borrow_mut().set( + param.name.clone(), + TypeInfo { + var_type: param.param_type.clone(), + is_mutable: false, + } + ); + } + + Self::check(body, &constructor_env, Some(&Type::None))?; + } + } + + // Check methods + for method in methods { + + let method_param_types: Vec = method.parameters.iter() + .map(|p| p.param_type.clone()) + .collect(); + let method_return_type = method.return_type.clone().unwrap_or(Type::None); + + // Create function type + let method_func_type = Type::Function(method_param_types, Box::new(method_return_type)); + + // Store as "ClassName::MethodName" + let global_method_key = format!("{}::{}", name, method.name); + env.borrow_mut().set( + global_method_key, + TypeInfo { var_type: method_func_type, is_mutable: false } + ); + let method_env = Rc::new(RefCell::new( + TypeEnvironment::new_enclosed(class_env.clone()) + )); + + method_env.borrow_mut().set( + "self".to_string(), + TypeInfo { + var_type: Type::Instance(name.clone()), + is_mutable: false, + } + ); + + // Add parameters + for param in &method.parameters { + method_env.borrow_mut().set( + param.name.clone(), + TypeInfo { + var_type: param.param_type.clone(), + is_mutable: false, + } + ); + } + + let return_type = method.return_type.as_ref().unwrap_or(&Type::None); + Self::check(&method.body, &method_env, Some(return_type))?; + } + + Ok(Type::None) + } + + ASTNode::NewInstance { class_name, arguments, loc } => { + // Check if class exists + let class_info = env.borrow().get(class_name) + .ok_or_else(|| format!("Type Error at {}: Unknown class '{}'", loc, class_name))?; + + if !matches!(class_info.var_type, Type::Class(_)) { + return Err(format!("Type Error at {}: '{}' is not a class", loc, class_name)); + } + + // Type check arguments (simplified - would need constructor signature) + for arg in arguments { + Self::check(arg, env, None)?; + } + + Ok(Type::Instance(class_name.clone())) + } + + ASTNode::MethodCall { + object, + method_name, + arguments, + loc, + } => { + let object_type = Self::check(object, env, None)?; + + match object_type { + // Case 1: Method call on a Class Instance (e.g., my_obj.method()) + Type::Instance(class_name) | Type::Custom(class_name) => { + // 1. Construct the lookup key: "ClassName::MethodName" + let method_key = format!("{}::{}", class_name, method_name); + + // 2. Look up the method signature (using the new helper) + // FIX: Use resolve_method instead of direct get() + if let Some(method_type) = Self::resolve_method(env, &method_key) { + if let Type::Function(param_types, return_type) = method_type { + // 3. Validate Argument Count + if arguments.len() != param_types.len() { + return Err(format!( + "Type Error at {}: Method '{}.{}' expected {} arguments, but got {}", + loc, class_name, method_name, param_types.len(), arguments.len() + )); + } + + // 4. Validate Argument Types + for (i, arg_node) in arguments.iter().enumerate() { + let arg_type = Self::check(arg_node, env, None)?; + let expected_type = ¶m_types[i]; + + if arg_type != *expected_type + && *expected_type != Type::Any + && arg_type != Type::Any + { + // Compatibility Checks + let is_quantum_compat = matches!( + (&arg_type, expected_type), + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + let is_class_compat = match (&arg_type, expected_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + let is_dict_compat = match (&arg_type, expected_type) { + (Type::Dict, Type::Custom(name)) if name == "Dict" => true, + (Type::Custom(name), Type::Dict) if name == "Dict" => true, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat && !is_dict_compat { + return Err(format!( + "Type Error at {}: Argument {} of '{}.{}' is wrong type. Expected {:?}, got {:?}", + loc, (i + 1), class_name, method_name, expected_type, arg_type + )); + } + } + } + + // 5. Return the REAL return type + Ok(*return_type) + } else { + Ok(Type::Any) + } + } else { + return Err(format!( + "Type Error at {}: Class '{}' has no method named '{}'", + loc, class_name, method_name + )); + } + } + + // Case 2: Function call on a Module (e.g., math.abs(x)) + // + Type::Module(module_types) => { + if let Some(member_type) = module_types.get(method_name) { + match member_type { + // Sub-case A: Calling a Function exported by the module + Type::Function(param_types, return_type) => { + // Validate argument count + if arguments.len() != param_types.len() { + return Err(format!( + "Type Error at {}: Module function '{}.{}' expected {} arguments, but got {}", + loc, "module", method_name, param_types.len(), arguments.len() + )); + } + + // Validate argument types + for (i, arg_node) in arguments.iter().enumerate() { + let arg_type = Self::check(arg_node, env, Option::None)?; + let expected_type = ¶m_types[i]; + + if arg_type != *expected_type + && *expected_type != Type::Any + && arg_type != Type::Any + { + // --- COMPATIBILITY CHECKS --- + let is_quantum_compat = matches!((&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None))); + let is_class_compat = match (&arg_type, expected_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + let is_dict_compat = match (&arg_type, expected_type) { + (Type::Dict, Type::Custom(name)) if name == "Dict" => true, + (Type::Custom(name), Type::Dict) if name == "Dict" => true, + _ => false, + }; + let is_func_compat = match (&arg_type, expected_type) { + (Type::Function(..), Type::Custom(name)) if name == "Function" => true, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat && !is_dict_compat && !is_func_compat { + return Err(format!( + "Type Error at {}: Argument {} of '{}.{}' is wrong type. Expected {:?}, got {:?}", + loc, (i + 1), "module", method_name, expected_type, arg_type + )); + } + } + } + Ok(*return_type.clone()) + } + + // Sub-case B: Instantiating a Class exported by the module (FIX for 'graphics.Canvas(...)') + Type::Class(class_name) => { + // 1. Look for constructor "ClassName::init" IN THE MODULE TYPES + let init_key = format!("{}::init", class_name); + let constructor_params = if let Some(ctor_type) = module_types.get(&init_key) { + if let Type::Function(params, _) = ctor_type { + params.clone() + } else { + vec![] + } + } else { + vec![] + }; + + // 2. Validate Args + if arguments.len() != constructor_params.len() { + return Err(format!( + "Type Error at {}: Constructor for '{}.{}' expected {} arguments, but got {}", + loc, "module", method_name, constructor_params.len(), arguments.len() + )); + } + + for (i, arg_node) in arguments.iter().enumerate() { + let arg_type = Self::check(arg_node, env, Option::None)?; + let expected_type = &constructor_params[i]; + + if arg_type != *expected_type + && *expected_type != Type::Any + && arg_type != Type::Any + { + // Compatibility checks + let is_quantum_compat = matches!((&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None))); + let is_class_compat = match (&arg_type, expected_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + let is_dict_compat = match (&arg_type, expected_type) { + (Type::Dict, Type::Custom(name)) if name == "Dict" => true, + (Type::Custom(name), Type::Dict) if name == "Dict" => true, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat && !is_dict_compat { + return Err(format!( + "Type Error at {}: Argument {} of constructor is wrong type. Expected {:?}, got {:?}", + loc, (i + 1), expected_type, arg_type + )); + } + } + } + // Return Instance of the class + Ok(Type::Instance(class_name.clone())) + } + + _ => Err(format!( + "Type Error at {}: '{}.{}' is not a function or class.", + loc, "module", method_name + )) + } + } else { + Err(format!( + "Type Error at {}: Module has no member named '{}'", + loc, method_name + )) + } + } + + _ => Err(format!( + "Type Error at {}: Cannot call method '{}' on type {:?}", + loc, method_name, object_type + )), } } + ASTNode::SelfRef { loc } => { + // Would need to track current class context + // For now, return error + Err(format!("Type Error at {}: 'self' can only be used inside class methods", loc)) + } - ASTNode::FunctionCall { callee, arguments, loc, .. } => { + ASTNode::FunctionCall { + callee, + arguments, + loc, + .. + } => { let callee_type = Self::check(callee, env, Option::None)?; let name = format!("{:?}", callee); match callee_type { + + Type::Class(class_name) => { + // 1. Look for explicit constructor "ClassName::init" + let init_key = format!("{}::init", class_name); + let constructor_params = if let Some(ctor_info) = env.borrow().get(&init_key) { + if let Type::Function(params, _) = ctor_info.var_type { + params + } else { + vec![] + } + } else { + // No constructor defined: Expect 0 arguments + vec![] + }; + + // 2. Validate Argument Count + if arguments.len() != constructor_params.len() { + return Err(format!( + "Type Error at {}: Constructor for '{}' expected {} arguments, but got {}", + loc, class_name, constructor_params.len(), arguments.len() + )); + } + + // 3. Validate Argument Types + for (i, arg_node) in arguments.iter().enumerate() { + let arg_type = Self::check(arg_node, env, Option::None)?; + let expected_type = &constructor_params[i]; + + if arg_type != *expected_type + && *expected_type != Type::Any + && arg_type != Type::Any + { + // Compatibility checks (Quantum, Class, Dict) + let is_quantum_compat = matches!( + (&arg_type, expected_type), + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + + let is_class_compat = match (&arg_type, expected_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + + // FIX: Added Dict Compatibility + let is_dict_compat = match (&arg_type, expected_type) { + (Type::Dict, Type::Custom(name)) if name == "Dict" => true, + (Type::Custom(name), Type::Dict) if name == "Dict" => true, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat && !is_dict_compat { + return Err(format!( + "Type Error at {}: Argument {} of function call is wrong type. Expected {:?}, got {:?}", + loc, (i + 1), expected_type, arg_type + )); + } + } + } + + // 4. Return the Instance Type + Ok(Type::Instance(class_name)) + } + + Type::Custom(ref name) if name == "Function" => { + // "Function" is a generic type, so we can't validate argument count or types. + // We just check that the arguments themselves are valid expressions. + for arg in arguments { + Self::check(arg, env, Option::None)?; + } + // We assume a generic Function returns Any + Ok(Type::Any) + } + Type::Function(param_types, return_type) => { if arguments.len() > 0 && param_types.len() == 0 { - + // Hack for 'print' } else if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Function '{}' expected {} arguments, but got {}", - loc, name, param_types.len(), arguments.len() + loc, + name, + param_types.len(), + arguments.len() )); } for (i, arg_node) in arguments.iter().enumerate() { - if i >= param_types.len() { break; } + if i >= param_types.len() { + break; + } let arg_type = Self::check(arg_node, env, Option::None)?; let expected_type = ¶m_types[i]; - if arg_type != *expected_type && *expected_type != Type::Any { - if let (Type::QuantumRegister(_), Type::QuantumRegister(None)) = (&arg_type, expected_type) { - // OK - } else { + if arg_type != *expected_type + && *expected_type != Type::Any + && arg_type != Type::Any { + // --- FIX START: Expanded Compatibility Checks --- + let is_quantum_compat = matches!( + (&arg_type, expected_type), + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + + let is_func_compat = match (&arg_type, expected_type) { + // Allow passing any specific Function to a parameter expecting generic "Function" + (Type::Function(..), Type::Custom(name)) if name == "Function" => true, + _ => false, + }; + + let is_class_compat = match (&arg_type, expected_type) { + (Type::Instance(got), Type::Custom(expected)) => got == expected, + _ => false, + }; + + + + if !is_quantum_compat && !is_class_compat { return Err(format!( "Type Error at {}: Argument {} of function call is wrong type. Expected {:?}, got {:?}", loc, (i + 1), expected_type, arg_type )); } + // --- FIX END --- } } Ok(*return_type) @@ -462,10 +1265,12 @@ impl TypeChecker { } } - - - - ASTNode::Binary { operator, left, right, loc } => { + ASTNode::Binary { + operator, + left, + right, + loc, + } => { let left_type = Self::check(left, env, Option::None)?; let right_type = Self::check(right, env, Option::None)?; @@ -477,8 +1282,15 @@ impl TypeChecker { (Type::QuantumRegister(_), Type::QuantumRegister(_)) => { return Ok(Type::QuantumRegister(None)); } + (Type::Array(t1), Type::Array(t2)) => { + if t1 == t2 { + return Ok(Type::Array(t1)); + } else { + return Ok(Type::Array(Box::new(Type::Any))); // Mixed types + } + } _ => return Err(format!( - "Type Error at {}: Tensor product '***' is only defined for quantum registers, got {:?} and {:?}", + "Type Error at {}: Tensor product '***' is only defined for quantum registers and arrays, got {:?} and {:?}", loc, left_type, right_type )), } @@ -492,6 +1304,8 @@ impl TypeChecker { (Type::Int, Type::Float) => Ok(Type::Float), (Type::Float, Type::Int) => Ok(Type::Float), (Type::String, Type::String) => Ok(Type::String), + // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Cannot add types {:?} and {:?}", loc, left_type, right_type)), } } @@ -501,6 +1315,8 @@ impl TypeChecker { (Type::Float, Type::Float) => Ok(Type::Float), (Type::Int, Type::Float) => Ok(Type::Float), (Type::Float, Type::Int) => Ok(Type::Float), + // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Cannot perform arithmetic on types {:?} and {:?}", loc, left_type, right_type)), } } @@ -510,6 +1326,8 @@ impl TypeChecker { (Type::Float, Type::Float) => Ok(Type::Float), (Type::Int, Type::Float) => Ok(Type::Float), (Type::Float, Type::Int) => Ok(Type::Float), + // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Power operator (^) requires numeric types, got {:?} and {:?}", loc, left_type, right_type)), } } @@ -517,12 +1335,16 @@ impl TypeChecker { BinaryOperator::Less | BinaryOperator::Greater | BinaryOperator::LessEqual | BinaryOperator::GreaterEqual => { match (&left_type, &right_type) { (Type::Int, Type::Int) | (Type::Float, Type::Float) => Ok(Type::Bool), + // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Bool), _ => Err(format!("Type Error at {}: Cannot perform ordered comparison on types {:?} and {:?}", loc, left_type, right_type)), } } BinaryOperator::And | BinaryOperator::Or => { match (&left_type, &right_type) { (Type::Bool, Type::Bool) => Ok(Type::Bool), + // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Bool), _ => Err(format!("Type Error at {}: Logical operators 'and'/'or' require two booleans, got {:?} and {:?}", loc, left_type, right_type)), } } @@ -535,27 +1357,41 @@ impl TypeChecker { match operator { UnaryOperator::Not => { if operand_type != Type::Bool { - return Err(format!("Type Error: Unary operator '!' cannot be applied to type {:?}", operand_type)); + return Err(format!( + "Type Error: Unary operator '!' cannot be applied to type {:?}", + operand_type + )); } Ok(Type::Bool) } UnaryOperator::Minus | UnaryOperator::Plus => { if operand_type != Type::Int && operand_type != Type::Float { - return Err(format!("Type Error: Unary operator '-' or '+' cannot be applied to type {:?}", operand_type)); + return Err(format!("Type Error: Unary operator '-' or '+' cannot be applied to type {:?}", operand_type)); } Ok(operand_type) } } } - ASTNode::QuantumDeclaration { name, size, initial_state } => { + ASTNode::QuantumDeclaration { + name, + size, + initial_state, + } => { let register_type: Type; if let Some(size_expr) = size { let size_type = Self::check(size_expr, env, Option::None)?; if size_type != Type::Int { - return Err(format!("Type Error: Quantum register size must be an Int, but got {:?}", size_type)); + return Err(format!( + "Type Error: Quantum register size must be an Int, but got {:?}", + size_type + )); } - let size_val = if let ASTNode::IntLiteral(n) = &**size_expr { Some(*n as usize) } else { None }; + let size_val = if let ASTNode::IntLiteral(n) = &**size_expr { + Some(*n as usize) + } else { + None + }; register_type = Type::QuantumRegister(size_val); } else if let Some(state_expr) = initial_state { let init_type = Self::check(state_expr, env, Option::None)?; @@ -567,74 +1403,158 @@ impl TypeChecker { } else { register_type = Type::QuantumRegister(Some(1)); } - env.borrow_mut().set(name.clone(), Self::immutable_info(register_type)); + env.borrow_mut() + .set(name.clone(), Self::immutable_info(register_type)); Ok(Type::None) } ASTNode::ArrayAccess { array, index, loc } => { let array_type = Self::check(array, env, Option::None)?; let index_type = Self::check(index, env, Option::None)?; - if index_type != Type::Int { - return Err(format!("Type Error at {}: Array index must be an Int, but got {:?}", loc, index_type)); - } + match array_type { - Type::QuantumRegister(size_opt) => { - if let (Some(size), ASTNode::IntLiteral(idx)) = (size_opt, &**index) { - if *idx < 0 || *idx as usize >= size { - return Err(format!("Type Error at {}: Qubit index {} is out of bounds for register of size {}.", loc, idx, size)); - } + // 1. Quantum Register (Index must be Int) + Type::QuantumRegister(size_opt) => { + if index_type != Type::Int { + return Err(format!( + "Type Error at {}: Quantum register index must be an Int, but got {:?}", + loc, index_type + )); + } + if let (Some(size), ASTNode::IntLiteral(idx)) = (size_opt, &**index) { + if *idx < 0 || *idx as usize >= size { + return Err(format!( + "Type Error at {}: Qubit index {} is out of bounds for register of size {}.", + loc, idx, size + )); } - Ok(Type::Qubit) } - Type::Array(inner_type) => Ok(*inner_type), - Type::String => Ok(Type::String), - Type::Dict => Ok(Type::Any), - _ => Err(format!("Type Error at {}: Cannot perform array access '[]' on type {:?}", loc, array_type)), + Ok(Type::Qubit) + } + + // 2. Arrays (Index must be Int) + Type::Array(inner_type) => { + if index_type != Type::Int { + return Err(format!( + "Type Error at {}: Array index must be an Int, but got {:?}", + loc, index_type + )); + } + Ok(*inner_type) + } + + // 3. Strings (Index must be Int) + Type::String => { + if index_type != Type::Int { + return Err(format!( + "Type Error at {}: String index must be an Int, but got {:?}", + loc, index_type + )); + } + Ok(Type::String) + } + + // 4a. Built-in Dictionaries + Type::Dict => { + if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { + return Err(format!( + "Type Error at {}: Dictionary key must be String, Int, or Bool, but got {:?}", + loc, index_type + )); + } + Ok(Type::Any) } + + // 4b. Custom "Dict" Types (Legacy/Parsed as custom) + Type::Custom(ref name) if name == "Dict" => { + if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { + return Err(format!( + "Type Error at {}: Dictionary key must be String, Int, or Bool, but got {:?}", + loc, index_type + )); + } + Ok(Type::Any) + } + + _ => Err(format!( + "Type Error at {}: Cannot perform array access '[]' on type {:?}", + loc, array_type + )), + } } ASTNode::MemberAccess { object, member } => { let object_type = Self::check(object, env, Option::None)?; + let class_name_opt = match &object_type { + Type::Instance(name) | Type::Custom(name) => Some(name), + _ => None, + }; + + if let Some(class_name) = class_name_opt { + // Construct the lookup key: "ClassName::MemberName" + let field_key = format!("{}::{}", class_name, member); + + // Look it up in the environment + if let Some(field_info) = env.borrow().get(&field_key) { + return Ok(field_info.var_type.clone()); + } else { + return Err(format!( + "Type Error: Class '{}' has no member named '{}'", + class_name, member + )); + } + } + if let Type::Module(module_types) = object_type { match module_types.get(member) { Some(t) => Ok(t.clone()), - None => Err(format!("Type Error: Module has no member named '{}'", member)) + None => Err(format!( + "Type Error: Module has no member named '{}'", + member + )), } - } - - else if member == "length" { + } else if member == "length" { match object_type { Type::Array(_) | Type::String | Type::Dict => Ok(Type::Int), Type::QuantumRegister(Some(_size)) => Ok(Type::Int), Type::QuantumRegister(None) => Ok(Type::Int), - _ => Err(format!("Type Error: Cannot get .length of type {:?}", object_type)), + _ => Err(format!( + "Type Error: Cannot get .length of type {:?}", + object_type + )), } } else { if let Type::Dict = object_type { Ok(Type::Any) } else { - Err(format!("Type Error: Type {:?} has no member named '{}'", object_type, member)) + Err(format!( + "Type Error: Type {:?} has no member named '{}'", + object_type, member + )) } } } - ASTNode::Dagger { .. } => { - Self::check_gate_expression(node, env) - } - - - ASTNode::Apply { gate_expr, arguments, loc } => { - + ASTNode::Dagger { .. } => Self::check_gate_expression(node, env), + + ASTNode::Apply { + gate_expr, + arguments, + loc, + } => { + // 1. Get the type of the gate expression let gate_type = Self::check_gate_expression(gate_expr, env)?; - + // 2. Check that it's a function match gate_type { Type::Function(param_types, return_type) => { - + // 3. Validate arguments if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Gate requires {} arguments, but got {}", - loc, param_types.len(), arguments.len() + loc, + param_types.len(), + arguments.len() )); } @@ -650,20 +1570,24 @@ impl TypeChecker { } } - Ok(*return_type) + Ok(*return_type) // Usually Type::None } - _ => Err(format!("Type Error at {}: This expression is not a callable gate.", loc)) + _ => Err(format!( + "Type Error at {}: This expression is not a callable gate.", + loc + )), } } - + // --- *** NEW: Gate *** --- ASTNode::Gate { .. } => { + // <-- Use '..' Self::check_gate_expression(node, env) } - + // --- *** NEW: Controlled *** --- ASTNode::Controlled { gate_expr, loc } => { - + // This node is checked via `check_gate_expression` Self::check_gate_expression(node, env) } @@ -671,7 +1595,10 @@ impl TypeChecker { let target_type = Self::check(target_expr, env, Option::None)?; match target_type { Type::Qubit | Type::Any => Ok(Type::Int), - _ => Err(format!("Type Error: 'measure' can only be used on a single Qubit, got {:?}", target_type)), + _ => Err(format!( + "Type Error: 'measure' can only be used on a single Qubit, got {:?}", + target_type + )), } } @@ -693,7 +1620,8 @@ impl TypeChecker { ImportSpec::List(names) => { for name in names { if let Some(var_type) = module_types.get(name.as_str()) { - env.borrow_mut().set(name.clone(), Self::immutable_info(var_type.clone())); + env.borrow_mut() + .set(name.clone(), Self::immutable_info(var_type.clone())); } else { return Err(format!("Type Error: Cannot import name '{}'. It does not exist in module.", name)); } @@ -703,28 +1631,50 @@ impl TypeChecker { Ok(Type::None) } - - ASTNode::FunctionDeclaration { name, parameters, return_type, body, .. } => { - let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + // ... (Other functions are unchanged) ... + ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + body, + .. + } => { + let param_types: Vec = + parameters.iter().map(|p| p.param_type.clone()).collect(); let rt = return_type.clone().unwrap_or(Type::Any); let func_type = Type::Function(param_types, Box::new(rt.clone())); - env.borrow_mut().set(name.clone(), Self::immutable_info(func_type)); + env.borrow_mut() + .set(name.clone(), Self::immutable_info(func_type)); let func_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); for param in parameters { - func_env.borrow_mut().set(param.name.clone(), Self::immutable_info(param.param_type.clone())); + func_env.borrow_mut().set( + param.name.clone(), + Self::immutable_info(param.param_type.clone()), + ); } Self::check(body, &func_env, Some(&rt))?; Ok(Type::None) } - ASTNode::CircuitDeclaration { name, parameters, return_type, body, .. } => { - let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + ASTNode::CircuitDeclaration { + name, + parameters, + return_type, + body, + .. + } => { + let param_types: Vec = + parameters.iter().map(|p| p.param_type.clone()).collect(); let rt = return_type.clone().unwrap_or(Type::Any); let func_type = Type::Function(param_types, Box::new(rt.clone())); - env.borrow_mut().set(name.clone(), Self::immutable_info(func_type)); + env.borrow_mut() + .set(name.clone(), Self::immutable_info(func_type)); let func_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); for param in parameters { - func_env.borrow_mut().set(param.name.clone(), Self::immutable_info(param.param_type.clone())); + func_env.borrow_mut().set( + param.name.clone(), + Self::immutable_info(param.param_type.clone()), + ); } Self::check(body, &func_env, Some(&rt))?; Ok(Type::None) @@ -736,14 +1686,40 @@ impl TypeChecker { } else { Type::None }; - match expected_return_type { - Option::None => Err("Type Error: 'return' statement found outside of a function.".to_string()), + match expected_return_type { + Option::None => Err( + "Type Error: 'return' statement found outside of a function.".to_string(), + ), Some(expected) => { - if value_type != *expected && *expected != Type::Any { - Err(format!("Type Error: Function expected return type {:?}, but found return with type {:?}", expected, value_type)) - } else { - Ok(Type::None) + // FIX: Added '&& value_type != Type::Any' to allow dynamic types + if value_type != *expected + && *expected != Type::Any + && value_type != Type::Any + { + // 1. Quantum Register Compatibility + let is_quantum_compat = matches!( + (&value_type, expected), + (Type::QuantumRegister(_), Type::QuantumRegister(None)) + ); + + // 2. Class Instance Compatibility (Instance vs Custom) + let is_class_compat = match (&value_type, expected) { + (Type::Instance(got), Type::Custom(expected_name)) => got == expected_name, + _ => false, + }; + + // 3. FIX: Dict Compatibility (Dict vs Custom("Dict")) + let is_dict_compat = match (&value_type, expected) { + (Type::Dict, Type::Custom(name)) if name == "Dict" => true, + (Type::Custom(name), Type::Dict) if name == "Dict" => true, + _ => false, + }; + + if !is_quantum_compat && !is_class_compat && !is_dict_compat { + return Err(format!("Type Error: Function expected return type {:?}, but found return with type {:?}", expected, value_type)); + } } + Ok(Type::None) } } } @@ -751,7 +1727,10 @@ impl TypeChecker { ASTNode::While { condition, body } => { let cond_type = Self::check(condition, env, Option::None)?; if cond_type != Type::Bool { - return Err(format!("Type Error: 'while' loop condition must be a Bool, but got {:?}", cond_type)); + return Err(format!( + "Type Error: 'while' loop condition must be a Bool, but got {:?}", + cond_type + )); } Self::check(body, env, expected_return_type)?; Ok(Type::None) @@ -777,17 +1756,27 @@ impl TypeChecker { let end_type = Self::check(end, env, Option::None)?; if start_type != Type::Int { - return Err(format!("Type Error: Range start must be an Int, got {:?}", start_type)); + return Err(format!( + "Type Error: Range start must be an Int, got {:?}", + start_type + )); } if end_type != Type::Int { - return Err(format!("Type Error: Range end must be an Int, got {:?}", end_type)); + return Err(format!( + "Type Error: Range end must be an Int, got {:?}", + end_type + )); } - + // Return the special type that your 'for' loop check already expects Ok(Type::Custom("range".to_string())) } - ASTNode::For { variable, iterator, body } => { + ASTNode::For { + variable, + iterator, + body, + } => { let iterator_type = Self::check(iterator, env, Option::None)?; let element_type = match iterator_type { Type::Array(inner_type) => *inner_type, @@ -795,24 +1784,42 @@ impl TypeChecker { Type::Dict => Type::String, Type::Custom(name) if name == "range" => Type::Int, Type::Any => Type::Any, - _ => return Err(format!("Type Error: 'for' loop cannot iterate over type {:?}", iterator_type)), + _ => { + return Err(format!( + "Type Error: 'for' loop cannot iterate over type {:?}", + iterator_type + )) + } }; let loop_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); - loop_env.borrow_mut().set(variable.clone(), Self::immutable_info(element_type)); + loop_env + .borrow_mut() + .set(variable.clone(), Self::immutable_info(element_type)); Self::check(body, &loop_env, expected_return_type)?; Ok(Type::None) } - ASTNode::If { condition, then_block, elif_blocks, else_block } => { + ASTNode::If { + condition, + then_block, + elif_blocks, + else_block, + } => { let cond_type = Self::check(condition, env, Option::None)?; if cond_type != Type::Bool { - return Err(format!("Type Error: 'if' condition must be a Bool, but got {:?}", cond_type)); + return Err(format!( + "Type Error: 'if' condition must be a Bool, but got {:?}", + cond_type + )); } let then_type = Self::check(then_block, env, expected_return_type)?; for (elif_cond, elif_body) in elif_blocks { let elif_cond_type = Self::check(elif_cond, env, Option::None)?; if elif_cond_type != Type::Bool { - return Err(format!("Type Error: 'elif' condition must be a Bool, but got {:?}", elif_cond_type)); + return Err(format!( + "Type Error: 'elif' condition must be a Bool, but got {:?}", + elif_cond_type + )); } Self::check(elif_body, env, expected_return_type)?; } @@ -829,16 +1836,20 @@ impl TypeChecker { Ok(Type::None) } - ASTNode::TryCatch { try_block, catch_block, .. } => { + ASTNode::TryCatch { + try_block, + catch_block, + .. + } => { Self::check(try_block, env, expected_return_type)?; Self::check(catch_block, env, expected_return_type)?; Ok(Type::None) } - _ => { - Err(format!("Type checking is not implemented for this node: {:?}", node)) - } + _ => Err(format!( + "Type checking is not implemented for this node: {:?}", + node + )), } } - } diff --git a/stdlib/graphics.qc b/stdlib/graphics.qc new file mode 100644 index 0000000..d33ad0f --- /dev/null +++ b/stdlib/graphics.qc @@ -0,0 +1,334 @@ +// stdlib/graphics.qc +/* + Quantica Standard Graphics Library + Provides 2D/3D graphics, plotting, and visualization +*/ + +/* ============================================ + COLOR CONSTANTS + ============================================ */ +let COLOR_BLACK = {r: 0, g: 0, b: 0, a: 255} +let COLOR_WHITE = {r: 255, g: 255, b: 255, a: 255} +let COLOR_RED = {r: 255, g: 0, b: 0, a: 255} +let COLOR_GREEN = {r: 0, g: 255, b: 0, a: 255} +let COLOR_BLUE = {r: 0, g: 0, b: 255, a: 255} +let COLOR_YELLOW = {r: 255, g: 255, b: 0, a: 255} +let COLOR_CYAN = {r: 0, g: 255, b: 255, a: 255} +let COLOR_MAGENTA = {r: 255, g: 0, b: 255, a: 255} +let COLOR_GRAY = {r: 128, g: 128, b: 128, a: 255} +let COLOR_ORANGE = {r: 255, g: 165, b: 0, a: 255} +let COLOR_PURPLE = {r: 128, g: 0, b: 128, a: 255} + +/* ============================================ + PLOT TYPES + ============================================ */ +let PLOT_LINE = 0 +let PLOT_SCATTER = 1 +let PLOT_BAR = 2 +let PLOT_HISTOGRAM = 3 +let PLOT_HEATMAP = 4 + +/* ============================================ + CANVAS CLASS (2D Drawing) + ============================================ */ + +/* Canvas represents a 2D drawing surface */ +Class Canvas: + let width: Int + let height: Int + let id: Int + + func init(w: Int, h: Int): + self.width = w + self.height = h + // Call native function to create canvas + self.id = _graphics_create_canvas(w, h) + + func set_background(color: Dict): + _graphics_set_background( + self.id, + color["r"], color["g"], color["b"], color["a"] + ) + + func clear(): + _graphics_clear(self.id) + + func draw_line(x1: Float, y1: Float, x2: Float, y2: Float,color: Dict, width: Float): + _graphics_draw_line( + self.id, x1, y1, x2, y2, + color["r"], color["g"], color["b"], color["a"], + width + ) + + func draw_rect(x: Float, y: Float, w: Float, h: Float,color: Dict, filled: Bool): + let fill_int = if filled: 1 else: 0 + _graphics_draw_rect( + self.id, x, y, w, h, + color["r"], color["g"], color["b"], color["a"], + fill_int + ) + + func draw_circle(x: Float, y: Float, radius: Float,color: Dict, filled: Bool): + let fill_int = if filled: 1 else: 0 + _graphics_draw_circle( + self.id, x, y, radius, + color["r"], color["g"], color["b"], color["a"], + fill_int + ) + + func draw_text(x: Float, y: Float, text: String,color: Dict, size: Float): + _graphics_draw_text( + self.id, x, y, text, + color["r"], color["g"], color["b"], color["a"], + size + ) + + func save_svg(filename: String) -> Bool: + return _graphics_save_svg(self.id, filename) == 0 + + func save_png(filename: String) -> Bool: + return _graphics_save_png(self.id, filename) == 0 + + func destroy(): + _graphics_destroy_canvas(self.id) + +/* ============================================ + PLOT CLASS (Data Visualization) + ============================================ */ + +Class Plot: + let id: Int + let plot_type: Int + + func init(ptype: Int): + self.plot_type = ptype + self.id = _graphics_create_plot(ptype) + + func set_data(x_data: Float[], y_data: Float[]): + _graphics_plot_set_data(self.id, x_data, y_data, len(x_data)) + + func set_title(title: String): + _graphics_plot_set_title(self.id, title) + + func render(canvas: Canvas) -> Bool: + return _graphics_plot_render(self.id, canvas.id) == 0 + + func destroy(): + _graphics_destroy_plot(self.id) + +/* ============================================ + HELPER FUNCTIONS + ============================================ */ + +/* Create a color from RGB values */ +func rgb(r: Int, g: Int, b: Int) -> Dict: + return {r: r, g: g, b: b, a: 255} + +/* Create a color from RGBA values */ +func rgba(r: Int, g: Int, b: Int, a: Int) -> Dict: + return {r: r, g: g, b: b, a: a} + +/* Create a color from hex string */ +func hex_to_color(hex: String) -> Dict: + // Simple implementation for #RRGGBB format + // TODO: Implement proper hex parsing + return COLOR_BLACK + +/* Linear interpolation between two colors */ +func lerp_color(c1: Dict, c2: Dict, t: Float) -> Dict: + let r = to_int(c1["r"] * (1.0 - t) + c2["r"] * t) + let g = to_int(c1["g"] * (1.0 - t) + c2["g"] * t) + let b = to_int(c1["b"] * (1.0 - t) + c2["b"] * t) + let a = to_int(c1["a"] * (1.0 - t) + c2["a"] * t) + return {r: r, g: g, b: b, a: a} + +/* ============================================ + DRAWING FUNCTIONS (Convenience) + ============================================ */ + +/* Draw a regular polygon */ +func draw_polygon(canvas: Canvas, x: Float, y: Float,radius: Float, sides: Int,color: Dict, filled: Bool): + import "stdlib/math.qc" as math + + let angle_step = math.TAU / to_float(sides) + mut i = 0 + + while i < sides: + let angle1 = angle_step * to_float(i) + let angle2 = angle_step * to_float(i + 1) + + let x1 = x + radius * math.cos(angle1) + let y1 = y + radius * math.sin(angle1) + let x2 = x + radius * math.cos(angle2) + let y2 = y + radius * math.sin(angle2) + + canvas.draw_line(x1, y1, x2, y2, color, 2.0) + i = i + 1 + +/* Draw a star */ +func draw_star(canvas: Canvas, x: Float, y: Float,outer_radius: Float, inner_radius: Float,points: Int, color: Dict): + import "stdlib/math.qc" as math + + let angle_step = math.TAU / to_float(points * 2) + mut i = 0 + + while i < points * 2: + let angle1 = angle_step * to_float(i) + let angle2 = angle_step * to_float(i + 1) + + let r1 = if i % 2 == 0: outer_radius else: inner_radius + let r2 = if (i + 1) % 2 == 0: outer_radius else: inner_radius + + let x1 = x + r1 * math.cos(angle1) + let y1 = y + r1 * math.sin(angle1) + let x2 = x + r2 * math.cos(angle2) + let y2 = y + r2 * math.sin(angle2) + + canvas.draw_line(x1, y1, x2, y2, color, 2.0) + i = i + 1 + +/* Draw a grid */ +func draw_grid(canvas: Canvas, spacing: Float, color: Dict): + let w = to_float(canvas.width) + let h = to_float(canvas.height) + + // Vertical lines + mut x = 0.0 + while x <= w: + canvas.draw_line(x, 0.0, x, h, color, 1.0) + x = x + spacing + + // Horizontal lines + mut y = 0.0 + while y <= h: + canvas.draw_line(0.0, y, w, y, color, 1.0) + y = y + spacing + +/* Draw axes */ +func draw_axes(canvas: Canvas, origin_x: Float, origin_y: Float,length: Float, color: Dict): + // X-axis + canvas.draw_line( + origin_x - length, origin_y, + origin_x + length, origin_y, + color, 2.0 + ) + + // Y-axis + canvas.draw_line( + origin_x, origin_y - length, + origin_x, origin_y + length, + color, 2.0 + ) + + // Arrow heads + let arrow_size = 10.0 + canvas.draw_line( + origin_x + length, origin_y, + origin_x + length - arrow_size, origin_y - arrow_size / 2.0, + color, 2.0 + ) + canvas.draw_line( + origin_x + length, origin_y, + origin_x + length - arrow_size, origin_y + arrow_size / 2.0, + color, 2.0 + ) + +/* ============================================ + PLOTTING FUNCTIONS + ============================================ */ + +/* Quick plot function for line plots */ +func quick_plot(x_data: Float[], y_data: Float[],title: String, filename: String): + let canvas = Canvas(800, 600) + canvas.set_background(COLOR_WHITE) + + let plot = Plot(PLOT_LINE) + plot.set_data(x_data, y_data) + plot.set_title(title) + plot.render(canvas) + + canvas.save_svg(filename) + canvas.destroy() + plot.destroy() + +/* Scatter plot */ +func scatter_plot(x_data: Float[], y_data: Float[],title: String, filename: String): + let canvas = Canvas(800, 600) + canvas.set_background(COLOR_WHITE) + + let plot = Plot(PLOT_SCATTER) + plot.set_data(x_data, y_data) + plot.set_title(title) + plot.render(canvas) + + canvas.save_svg(filename) + canvas.destroy() + plot.destroy() + +/* Bar chart */ +func bar_chart(x_data: Float[], y_data: Float[],title: String, filename: String): + let canvas = Canvas(800, 600) + canvas.set_background(COLOR_WHITE) + + let plot = Plot(PLOT_BAR) + plot.set_data(x_data, y_data) + plot.set_title(title) + plot.render(canvas) + + canvas.save_svg(filename) + canvas.destroy() + plot.destroy() + +/* ============================================ + VISUALIZATION EXAMPLES + ============================================ */ + +/* Visualize quantum state probabilities */ +func visualize_quantum_state(state: QuantumRegister,filename: String): + // Create canvas + let canvas = Canvas(800, 600) + canvas.set_background(COLOR_WHITE) + + // Draw title + canvas.draw_text(400.0, 30.0, "Quantum State Probabilities", + COLOR_BLACK, 24.0) + + // TODO: Extract probabilities from quantum state + // and visualize as bar chart + + canvas.save_svg(filename) + canvas.destroy() + +/* Draw Bloch sphere representation */ +func draw_bloch_sphere(canvas: Canvas, x: Float, y: Float,radius: Float): + // Draw sphere + canvas.draw_circle(x, y, radius, COLOR_BLUE, False) + + // Draw axes + canvas.draw_line(x, y - radius, x, y + radius, COLOR_BLACK, 2.0) + canvas.draw_line(x - radius, y, x + radius, y, COLOR_BLACK, 2.0) + + // Labels + canvas.draw_text(x, y - radius - 20.0, "|0⟩", COLOR_BLACK, 16.0) + canvas.draw_text(x, y + radius + 20.0, "|1⟩", COLOR_BLACK, 16.0) + canvas.draw_text(x + radius + 10.0, y, "|+⟩", COLOR_BLACK, 16.0) + canvas.draw_text(x - radius - 30.0, y, "|-⟩", COLOR_BLACK, 16.0) + +/* ============================================ + ANIMATION SUPPORT + ============================================ */ + +/* Create animation frames */ +func create_animation(num_frames: Int,frame_func: Function,output_dir: String): + mut i = 0 + while i < num_frames: + let canvas = Canvas(800, 600) + frame_func(canvas, i, num_frames) + + let filename = output_dir + "/frame_" + to_string(i) + ".svg" + canvas.save_svg(filename) + canvas.destroy() + + i = i + 1 + + print("Created " + to_string(num_frames) + " animation frames") \ No newline at end of file diff --git a/tests/classes_test.qc b/tests/classes_test.qc new file mode 100644 index 0000000..e45f5c0 --- /dev/null +++ b/tests/classes_test.qc @@ -0,0 +1,131 @@ +// tests/Classes_test.qc +print("--- Running Classes & Objects Tests ---") + +// --- Test 1: Basic Class Declaration and Instantiation --- +Class Testclass1: + let value: Int = 42 + func get_value() -> Int: + return value + +let obj1 = new Testclass1() +assert(obj1.value == 42, "Class field access") +assert(obj1.get_value() == 42, "Method call on instance") +print("✓ Test 1: Basic Class passed") + +// --- Test 2: Constructor --- +Class TestClass2: + let x: Int + let y: Int + + func init(a: Int, b: Int): + x = a + y = b + + func sum() -> Int: + return x + y + +let obj2 = new TestClass2(10, 20) +assert(obj2.x == 10, "Constructor parameter 1") +assert(obj2.y == 20, "Constructor parameter 2") +assert(obj2.sum() == 30, "Method using constructor values") +print("✓ Test 2: Constructor passed") + +// --- Test 3: Multiple Instances --- +Class TestClass3: + mut counter: Int = 0 + + func increment(): + counter = counter + 1 + + func get_counter() -> Int: + return counter + +let obj3a = new TestClass3() +let obj3b = new TestClass3() + +obj3a.increment() +obj3a.increment() +obj3b.increment() + +assert(obj3a.get_counter() == 2, "First instance counter") +assert(obj3b.get_counter() == 1, "Second instance counter") +print("✓ Test 3: Multiple instances passed") + +// --- Test 4: Method Chaining (if supported) --- +Class TestClass4: + mut value: Int = 0 + + func set_value(v: Int): + value = v + + func add(v: Int): + value = value + v + + func multiply(v: Int): + value = value * v + + func get_value() -> Int: + return value + +let obj4 = new TestClass4() +obj4.set_value(5) +obj4.add(3) +obj4.multiply(2) +assert(obj4.get_value() == 16, "Method chaining") +print("✓ Test 4: Method chaining passed") + +// --- Test 5: Private/Public Fields --- +Class TestClass5: + public let public_val: Int = 100 + private let private_val: Int = 200 + + func get_private() -> Int: + return private_val + +let obj5 = new TestClass5() +assert(obj5.public_val == 100, "Public field access") +assert(obj5.get_private() == 200, "Private field via method") +print("✓ Test 5: Access modifiers passed") + +// --- Test 6: Quantum Circuit in Class --- +Class QuantumTest: + let qreg: QuantumRegister + + func init(): + quantum q[2] + qreg = q + + func create_entanglement(): + apply Hadamard(qreg[0]) + apply CNOT(qreg[0], qreg[1]) + + func measure_first() -> Int: + return measure(qreg[0]) + +let qtest = new QuantumTest() +qtest.create_entanglement() +let measurement = qtest.measure_first() +assert(measurement == 0 Or measurement == 1, "Quantum measurement in Class") +print("✓ Test 6: Quantum operations in Class passed") + +// --- Test 7: Nested Object References --- +Class Inner: + let val: Int = 999 + + func get_val() -> Int: + return val + +Class Outer: + let inner: Inner + + func init(): + inner = new Inner() + + func get_inner_val() -> Int: + return inner.get_val() + +let outer = new Outer() +assert(outer.get_inner_val() == 999, "Nested object reference") +print("✓ Test 7: Nested objects passed") + +print("\n--- All Classes & Objects Tests Passed! ---") \ No newline at end of file diff --git a/tests/test_draw.qc b/tests/test_draw.qc new file mode 100644 index 0000000..2bb330c --- /dev/null +++ b/tests/test_draw.qc @@ -0,0 +1,43 @@ +// test_draw.qc +import "stdlib/graphics.qc" as graphics + +print("--- Starting Graphics Test ---") + +// 1. Create a canvas (800x600) +// This calls Canvas.init -> _graphics_create_canvas +let canvas = graphics.Canvas(800, 600) +print("Canvas created with ID: " + to_string(canvas.id)) + +// 2. Set background to white +// This calls Canvas.set_background -> _graphics_set_background +let white = graphics.COLOR_WHITE +canvas.set_background(white) +print("Background set to white") + +// 3. Draw some shapes +// Red Rectangle +let red = graphics.COLOR_RED +canvas.draw_rect(50.0, 50.0, 200.0, 100.0, red, True) +print("Drawn red rectangle") + +// Blue Circle (Outline) +let blue = graphics.COLOR_BLUE +canvas.draw_circle(400.0, 300.0, 50.0, blue, False) +print("Drawn blue circle outline") + +// Green Line +let green = graphics.COLOR_GREEN +canvas.draw_line(10.0, 10.0, 790.0, 590.0, green, 5.0) +print("Drawn green diagonal line") + +// 4. Save the result +// This calls Canvas.save_svg -> _graphics_save_svg +let filename = "output_test.svg" +if canvas.save_svg(filename): + print("Success! Image saved to: " + filename) +else: + print("Error: Failed to save image.") + +// 5. Cleanup +canvas.destroy() +print("Canvas destroyed") \ No newline at end of file From f9111da59f6c8b37b0c43ea042e4738890129f5e Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 6 Dec 2025 20:26:55 +0530 Subject: [PATCH 02/27] Support for Input() and split --- src/environment/mod.rs | 2 ++ src/evaluator/mod.rs | 46 ++++++++++++++++++++++++++++++++++++++++++ src/type_checker.rs | 15 ++++++++++---- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/environment/mod.rs b/src/environment/mod.rs index a525d0f..305dcd4 100644 --- a/src/environment/mod.rs +++ b/src/environment/mod.rs @@ -178,6 +178,8 @@ impl Environment { "print".to_string(), RuntimeValue::BuiltinFunction("print".to_string()), ); + env.set("input".to_string(), RuntimeValue::BuiltinFunction("input".to_string())); + env.set("split".to_string(), RuntimeValue::BuiltinFunction("split".to_string())); env.set( "echo".to_string(), RuntimeValue::BuiltinFunction("echo".to_string()), diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 272e41d..cc29bae 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -19,6 +19,7 @@ use num_complex::Complex; type C64 = Complex; use crate::graphics::{Canvas, Color, Plot, PlotType}; use std::sync::Mutex; +use std::io::Write; lazy_static::lazy_static! { static ref INTERPRETER_CANVASES: Mutex> = Mutex::new(HashMap::new()); static ref INTERPRETER_PLOTS: Mutex> = Mutex::new(HashMap::new()); @@ -1612,6 +1613,8 @@ impl Evaluator { match func_name.as_str() { "print" => Self::builtin_print(evaluated_args), + "input" => Self::builtin_input(evaluated_args), + "split" => Self::builtin_split(evaluated_args), "echo" => Self::builtin_echo(evaluated_args), "maybe" => Self::builtin_maybe(evaluated_args), "sample" => Self::builtin_sample(evaluated_args), @@ -2023,6 +2026,49 @@ impl Evaluator { println!("{}", output.join(" ")); Ok(RuntimeValue::None) } + fn builtin_split(args: Vec) -> Result { + if args.len() != 2 { + return Err("Runtime Error: 'split' expects 2 arguments (string, delimiter).".to_string()); + } + + let target_str = match &args[0] { + RuntimeValue::String(s) => s, + _ => return Err("Runtime Error: First argument to split must be a String.".to_string()) + }; + + let delim = match &args[1] { + RuntimeValue::String(s) => s, + _ => return Err("Runtime Error: Delimiter must be a String.".to_string()) + }; + + // Split and convert to RuntimeValues + let elements: Vec>> = target_str + .split(delim) + .map(|s| { + // Trim whitespace (e.g. " 20 " -> "20") + let clean_str = s.trim().to_string(); + std::rc::Rc::new(std::cell::RefCell::new(RuntimeValue::String(clean_str))) + }) + .collect(); + + Ok(RuntimeValue::Register(elements)) + } + fn builtin_input(args: Vec) -> Result { + // 1. Print the prompt if provided + if let Some(val) = args.get(0) { + print!("{}", val); + let _ = std::io::stdout().flush(); // Ensure prompt appears before typing + } + + // 2. Read the line + let mut input_buffer = String::new(); + std::io::stdin() + .read_line(&mut input_buffer) + .map_err(|e| format!("Runtime Error: Failed to read input. {}", e))?; + + // 3. Return trimmed string (remove newline) + Ok(RuntimeValue::String(input_buffer.trim().to_string())) + } fn builtin_to_int(args: Vec) -> Result { if args.len() != 1 { diff --git a/src/type_checker.rs b/src/type_checker.rs index 79adf28..71a3822 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -5,15 +5,11 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::string::String; - -// --- ADD THESE IMPORTS --- use crate::lexer::Lexer; use crate::parser::ast::ImportPath; use crate::parser::ast::Loc; use crate::parser::Parser; use std::fs; -// --- END ADDED IMPORTS --- - #[derive(Debug, Clone, PartialEq)] pub struct TypeInfo { pub var_type: Type, @@ -77,6 +73,17 @@ impl TypeChecker { "print".to_string(), immut(Type::Function(vec![], Box::new(none.clone()))), ); + env_mut.set("input".to_string(), immut( + // It takes 1 argument (the prompt string) and returns a String + Type::Function(vec![Type::Any], Box::new(Type::String)) + )); + env_mut.set("split".to_string(), immut( + // Func(String, String) -> Array + Type::Function( + vec![Type::String, Type::String], + Box::new(Type::Array(Box::new(Type::String))) + ) + )); env_mut.set( "debug_state".to_string(), immut(Type::Function( From 46435f2ef6d1a69a4eb072976313b43cde44db58 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sun, 7 Dec 2025 14:24:10 +0530 Subject: [PATCH 03/27] added nodes for continue and break --- src/type_checker.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/type_checker.rs b/src/type_checker.rs index 71a3822..574327a 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -5,11 +5,15 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::string::String; + +// --- ADD THESE IMPORTS --- use crate::lexer::Lexer; use crate::parser::ast::ImportPath; use crate::parser::ast::Loc; use crate::parser::Parser; use std::fs; +// --- END ADDED IMPORTS --- + #[derive(Debug, Clone, PartialEq)] pub struct TypeInfo { pub var_type: Type, @@ -1852,6 +1856,8 @@ impl TypeChecker { Self::check(catch_block, env, expected_return_type)?; Ok(Type::None) } + ASTNode::Break => Ok(Type::None), + ASTNode::Continue => Ok(Type::None), _ => Err(format!( "Type checking is not implemented for this node: {:?}", From 145a80d46d037d8890dfabc2bffa0343c12f8112 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sun, 7 Dec 2025 16:48:16 +0530 Subject: [PATCH 04/27] fixed the bug that wants explicit type annotation inside classes for DICT --- src/parser/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9208406..4b85d3b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -774,8 +774,11 @@ impl Parser { let field_name_loc = self.expect_identifier()?; let field_name = self.extract_identifier_name(&field_name_loc)?; - self.expect(&Token::Colon)?; - let field_type = self.parse_type()?; + let field_type = if self.match_token(&Token::Colon) { + self.parse_type()? + } else { + Type::Any // Infer type as Any if not specified + }; let default_value = if self.match_token(&Token::Equal) { Some(Box::new(self.parse_expression()?)) From c12a976f2d47a9db57f0cbbf61ec9af47a62d091 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sun, 7 Dec 2025 17:13:24 +0530 Subject: [PATCH 05/27] error handling system --- Cargo.toml | 1 + src/error.rs | 334 +++++++++++++++++++++++++++++++++++++------- src/main.rs | 130 ++++++++++++----- src/type_checker.rs | 45 ++++-- 4 files changed, 414 insertions(+), 96 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9c2585..2f21c10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ reqwest = { version = "0.11", features = ["json", "blocking"] } serde_json = "1.0" lazy_static = "1.4" rayon = "1.7" +colored = "2.1" [profile.dev] diff --git a/src/error.rs b/src/error.rs index a293261..df9e8d9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,31 +1,67 @@ // src/error.rs use crate::parser::ast::Loc; use std::fmt; +use colored::Colorize; #[derive(Debug, Clone, Copy, PartialEq)] pub enum ErrorLevel { Error, Warning, Note, + Help, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ErrorCategory { + Syntax, + Type, + Runtime, + Semantic, + Import, + Quantum, + Memory, + Performance, } #[derive(Debug, Clone)] pub struct Diagnostic { pub level: ErrorLevel, + pub category: ErrorCategory, pub message: String, - pub code: Option, // e.g. "E001" + pub code: Option, pub loc: Loc, pub hint: Option, + pub suggestion: Option, + pub related_info: Vec<(Loc, String)>, + pub documentation_url: Option, } impl Diagnostic { - pub fn error(loc: Loc, msg: &str) -> Self { + pub fn error(loc: Loc, category: ErrorCategory, msg: &str) -> Self { Self { level: ErrorLevel::Error, + category, + message: msg.to_string(), + code: None, + loc, + hint: None, + suggestion: None, + related_info: Vec::new(), + documentation_url: None, + } + } + + pub fn warning(loc: Loc, category: ErrorCategory, msg: &str) -> Self { + Self { + level: ErrorLevel::Warning, + category, message: msg.to_string(), code: None, loc, hint: None, + suggestion: None, + related_info: Vec::new(), + documentation_url: None, } } @@ -38,11 +74,27 @@ impl Diagnostic { self.hint = Some(hint.to_string()); self } + + pub fn with_suggestion(mut self, suggestion: &str) -> Self { + self.suggestion = Some(suggestion.to_string()); + self + } + + pub fn with_related(mut self, loc: Loc, msg: &str) -> Self { + self.related_info.push((loc, msg.to_string())); + self + } + + pub fn with_docs(mut self, url: &str) -> Self { + self.documentation_url = Some(url.to_string()); + self + } } pub struct ErrorReporter<'a> { source_code: &'a str, filename: &'a str, + show_colors: bool, } impl<'a> ErrorReporter<'a> { @@ -50,83 +102,259 @@ impl<'a> ErrorReporter<'a> { Self { source_code, filename, + show_colors: true, } } pub fn report(&self, diagnostic: &Diagnostic) { - let color_code = match diagnostic.level { - ErrorLevel::Error => "\x1b[31m", // Red - ErrorLevel::Warning => "\x1b[33m", // Yellow - ErrorLevel::Note => "\x1b[34m", // Blue - }; - let reset = "\x1b[0m"; - let bold = "\x1b[1m"; - - // Header: "error[E001]: Message" - let level_str = match diagnostic.level { - ErrorLevel::Error => "error", - ErrorLevel::Warning => "warning", - ErrorLevel::Note => "note", + self.print_header(diagnostic); + self.print_location(diagnostic); + self.print_source_snippet(diagnostic); + self.print_hint(diagnostic); + self.print_suggestion(diagnostic); + self.print_related_info(diagnostic); + self.print_documentation(diagnostic); + println!(); + } + + fn print_header(&self, diagnostic: &Diagnostic) { + let (level_str, color) = match diagnostic.level { + ErrorLevel::Error => ("error", "red"), + ErrorLevel::Warning => ("warning", "yellow"), + ErrorLevel::Note => ("note", "blue"), + ErrorLevel::Help => ("help", "green"), }; + let category_str = format!("{:?}", diagnostic.category).to_lowercase(); + let code_display = if let Some(c) = &diagnostic.code { format!("[{}]", c) } else { String::new() }; - println!( - "{}{}{}{}: {}{}", - color_code, bold, level_str, code_display, reset, diagnostic.message - ); + if self.show_colors { + println!( + "{}{}{}: {}", + level_str.color(color).bold(), + code_display.color(color), + format!("({})", category_str).dimmed(), + diagnostic.message.bold() + ); + } else { + println!("{}{}: {}", level_str, code_display, diagnostic.message); + } + } - // Location: " --> src/main.qc:10:5" - println!( - " {}--> {}:{}:{}{}", - "\x1b[34m", // Blue arrow - self.filename, - diagnostic.loc.line, - diagnostic.loc.column, - reset - ); + fn print_location(&self, diagnostic: &Diagnostic) { + if self.show_colors { + println!( + " {} {}:{}:{}", + "-->".blue().bold(), + self.filename, + diagnostic.loc.line, + diagnostic.loc.column + ); + } else { + println!( + " --> {}:{}:{}", + self.filename, + diagnostic.loc.line, + diagnostic.loc.column + ); + } + } + + fn print_source_snippet(&self, diagnostic: &Diagnostic) { + let line_num = diagnostic.loc.line; + let col = diagnostic.loc.column; + + // Get surrounding lines for context + let lines: Vec<&str> = self.source_code.lines().collect(); + let start_line = if line_num > 2 { line_num - 2 } else { 1 }; + let end_line = std::cmp::min(line_num + 2, lines.len()); - // Code Snippet - if let Some(line_content) = self.get_line_content(diagnostic.loc.line) { - let line_num_padding = " "; - let line_num = diagnostic.loc.line.to_string(); + let line_num_width = end_line.to_string().len(); - // Empty pipe line - println!("{} |", line_num_padding); + // Print separator + if self.show_colors { + println!(" {}", "|".blue()); + } else { + println!(" |"); + } - // The actual code - println!(" {:<4} | {}", line_num, line_content); + // Print context lines + for i in start_line..=end_line { + if i > lines.len() { + break; + } - // The pointer line (^^^^^) - print!("{} |", line_num_padding); + let line_content = lines[i - 1]; + let is_error_line = i == line_num; - // Calculate spaces to the column - // (Note: column is 1-based, we treat it carefully) - let spaces = if diagnostic.loc.column > 0 { diagnostic.loc.column - 1 } else { 0 }; - let pointer_str = " ".repeat(spaces); + if self.show_colors { + if is_error_line { + println!( + " {:width$} {} {}", + i.to_string().blue().bold(), + "|".blue(), + line_content, + width = line_num_width + ); + } else { + println!( + " {:width$} {} {}", + i.to_string().blue(), + "|".blue(), + line_content.dimmed(), + width = line_num_width + ); + } + } else { + println!(" {:width$} | {}", i, line_content, width = line_num_width); + } - println!(" {}{}^", pointer_str, color_code); + // Print error pointer for the error line + if is_error_line { + let pointer_padding = " ".repeat(line_num_width + 1); + let spaces = " ".repeat(if col > 0 { col - 1 } else { 0 }); - // Print hint if it exists - if let Some(hint) = &diagnostic.hint { + if self.show_colors { + println!( + "{} {} {}{}", + pointer_padding, + "|".blue(), + spaces, + "^".repeat(std::cmp::min( + line_content.len().saturating_sub(col.saturating_sub(1)), + 1 + )).red().bold() + ); + } else { + println!("{} | {}^", pointer_padding, spaces); + } + } + } + + // Print separator + if self.show_colors { + println!(" {}", "|".blue()); + } else { + println!(" |"); + } + } + + fn print_hint(&self, diagnostic: &Diagnostic) { + if let Some(hint) = &diagnostic.hint { + if self.show_colors { println!( - "{} = hint: {}{}", - " ".repeat(line_num_padding.len() + 3 + spaces), - hint, - reset + " {} {}", + "=".blue().bold(), + format!("hint: {}", hint).cyan() ); + } else { + println!(" = hint: {}", hint); + } + } + } + + fn print_suggestion(&self, diagnostic: &Diagnostic) { + if let Some(suggestion) = &diagnostic.suggestion { + if self.show_colors { + println!( + " {} {}", + "=".blue().bold(), + format!("suggestion: {}", suggestion).green().bold() + ); + } else { + println!(" = suggestion: {}", suggestion); + } + } + } + + fn print_related_info(&self, diagnostic: &Diagnostic) { + if !diagnostic.related_info.is_empty() { + if self.show_colors { + println!("\n {} Related information:", "=".blue().bold()); + } else { + println!("\n = Related information:"); } - // Reset color - println!("{}", reset); + for (loc, msg) in &diagnostic.related_info { + if self.show_colors { + println!( + " {} {}:{}:{}: {}", + "-->".blue(), + self.filename, + loc.line, + loc.column, + msg.dimmed() + ); + } else { + println!( + " --> {}:{}:{}: {}", + self.filename, + loc.line, + loc.column, + msg + ); + } + } } } - fn get_line_content(&self, line_number: usize) -> Option<&str> { - self.source_code.lines().nth(line_number - 1) + fn print_documentation(&self, diagnostic: &Diagnostic) { + if let Some(url) = &diagnostic.documentation_url { + if self.show_colors { + println!( + "\n {} For more information, see: {}", + "=".blue().bold(), + url.cyan().underline() + ); + } else { + println!("\n = For more information, see: {}", url); + } + } + } +} + +// Fuzzy string matching for suggestions +pub fn levenshtein_distance(s1: &str, s2: &str) -> usize { + let len1 = s1.len(); + let len2 = s2.len(); + let mut matrix = vec![vec![0; len2 + 1]; len1 + 1]; + + for i in 0..=len1 { + matrix[i][0] = i; } + for j in 0..=len2 { + matrix[0][j] = j; + } + + for i in 1..=len1 { + for j in 1..=len2 { + let cost = if s1.chars().nth(i - 1) == s2.chars().nth(j - 1) { + 0 + } else { + 1 + }; + matrix[i][j] = std::cmp::min( + std::cmp::min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1), + matrix[i - 1][j - 1] + cost, + ); + } + } + + matrix[len1][len2] +} + +pub fn find_similar_names(target: &str, candidates: &[String]) -> Vec { + let mut scored: Vec<(String, usize)> = candidates + .iter() + .map(|c| (c.clone(), levenshtein_distance(target, c))) + .filter(|(_, dist)| *dist <= 3) // Only suggest if within 3 edits + .collect(); + + scored.sort_by_key(|(_, dist)| *dist); + scored.into_iter().take(3).map(|(name, _)| name).collect() } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b4c4fe1..f8f6960 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,7 @@ use std::fs; use std::io::{self, Write}; use std::path::Path; use std::time::Instant; -use crate::error::{ErrorReporter, Diagnostic}; +use crate::error::{ErrorReporter, Diagnostic,ErrorCategory}; use crate::parser::ast::Loc; #[derive(Debug, Clone, Copy, PartialEq)] @@ -318,6 +318,7 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool if verbose { println!("📄 Compiling: {}\n", filename); } + // Read source let source = match fs::read_to_string(filename) { Ok(content) => content, @@ -326,12 +327,17 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool std::process::exit(1); } }; + if verbose { println!("Source code:"); println!("{:-<60}", ""); println!("{}", source); println!("{:-<60}\n", ""); } + + // Create error reporter + let reporter = ErrorReporter::new(&source, filename); + // Lexical Analysis if verbose { println!("🔤 Phase 1: Lexical Analysis"); @@ -345,7 +351,14 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool t } Err(e) => { - eprintln!("✗ Lexer error: {}", e); + let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); + + let diag = Diagnostic::error(loc, ErrorCategory::Syntax, &e) + .with_code("E001") + .with_hint("Check for unclosed strings, invalid characters, or unterminated comments.") + .with_docs("https://quantica.dev/docs/errors/E001"); + + reporter.report(&diag); std::process::exit(1); } }; @@ -365,7 +378,6 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool println!("🌳 Phase 2: Syntax Analysis"); } let mut parser = Parser::new(tokens); - let reporter = ErrorReporter::new(&source, filename); let ast = match parser.parse() { Ok(tree) => { if verbose { @@ -374,22 +386,39 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool tree } Err(e) => { - let loc = parse_error_location(&e).unwrap_or( - // Fallback to 1:1 if parsing fails - Loc { line: 1, column: 1 } - ); + let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - let diag = Diagnostic::error(loc, &e) - .with_code("E001") - .with_hint("Check for missing semicolons, parentheses, or braces."); + // Extract the actual error message + let clean_msg = if let Some(idx) = e.find("]: ") { + &e[idx + 3..] + } else { + &e + }; + + let mut diag = Diagnostic::error(loc, ErrorCategory::Syntax, clean_msg) + .with_code("E002") + .with_hint("Check for missing semicolons, parentheses, braces, or colons."); + + // Add specific suggestions based on error type + if clean_msg.contains("Expected Colon") { + diag = diag.with_suggestion("Add a colon ':' for type annotation or after control flow statements."); + } else if clean_msg.contains("Expected LeftParen") { + diag = diag.with_suggestion("Function calls require parentheses '()' even if there are no arguments."); + } else if clean_msg.contains("Unexpected EOF") { + diag = diag.with_suggestion("You may have an unclosed block or missing 'return' statement."); + } + + diag = diag.with_docs("https://quantica.dev/docs/errors/E002"); reporter.report(&diag); std::process::exit(1); } }; + if verbose { println!("🔬 Phase 2.5: Type Checking"); } + match TypeChecker::check_program(&ast) { Ok(()) => { if verbose { @@ -397,75 +426,98 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } } Err(e) => { - let loc = parse_error_location(&e).unwrap_or( - crate::parser::ast::Loc { line: 1, column: 1 } - ); + let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - // 2. Clean the message (Remove the "Type Error at..." prefix) let clean_msg = if let Some(idx) = e.find("]: ") { - &e[idx + 3..] // Skip past "]: " + &e[idx + 3..] } else { &e }; - let diag = Diagnostic::error(loc, clean_msg) - .with_code("E002") - .with_hint("Check your variable declarations and types."); + let mut diag = Diagnostic::error(loc, ErrorCategory::Type, clean_msg) + .with_code("E003"); + + // Intelligent suggestions based on error content + if clean_msg.contains("Undefined variable") { + let var_name = extract_variable_name(clean_msg); + diag = diag + .with_hint("This variable was not declared in the current scope.") + .with_suggestion(&format!( + "Did you mean to declare it? Try: 'let {} = ...' or 'mut {} = ...'", + var_name, var_name + )); + } else if clean_msg.contains("Mismatched types") { + diag = diag + .with_hint("The types don't match. You may need to convert one type to another.") + .with_suggestion("Use type conversion functions: to_int(), to_float(), to_string()"); + } else if clean_msg.contains("Cannot call method") { + diag = diag + .with_hint("This type doesn't have the method you're trying to call.") + .with_suggestion("Check the type's available methods in the documentation."); + } + + diag = diag.with_docs("https://quantica.dev/docs/errors/E003"); reporter.report(&diag); std::process::exit(1); } } - // Display AST (Optional, but good for debugging) - + // Display AST (Optional) if show_ast { println!("=== ABSTRACT SYNTAX TREE ==="); print_ast(&ast, 0); println!("============================\n"); } - //INTERPRETATION (EVALUATION) + // INTERPRETATION (EVALUATION) if verbose { println!("✨ Phase 3: Interpretation"); } - // Wrap the root environment in Rc> + let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); if verbose { println!("Program Output:"); println!("{:-<60}", ""); } - // Pass a reference to the Rc + let start_time = Instant::now(); let evaluation_result = Evaluator::evaluate_program(&ast, &env); let duration = start_time.elapsed(); println!("{:-<60}", ""); - println!( - "⏱️ Interpreter Time: {:.6} seconds", - duration.as_secs_f64() - ); + println!("⏱️ Interpreter Time: {:.6} seconds", duration.as_secs_f64()); match evaluation_result { Ok(_) => { println!("✓ Execution successful!"); } Err(e) => { - let loc = parse_error_location(&e).unwrap_or( - crate::parser::ast::Loc { line: 1, column: 1 } - ); + let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - // 2. Clean the message let clean_msg = if let Some(idx) = e.find("]: ") { &e[idx + 3..] } else { &e }; - let diag = Diagnostic::error(loc, clean_msg) - .with_code("E003") // Runtime/Panic Error - .with_hint("This error happened while the code was running."); + let mut diag = Diagnostic::error(loc, ErrorCategory::Runtime, clean_msg) + .with_code("E004") + .with_hint("This error occurred while your program was running."); + + // Runtime-specific suggestions + if clean_msg.contains("division by zero") { + diag = diag.with_suggestion("Add a check: 'if denominator != 0: ...'"); + } else if clean_msg.contains("index out of bounds") { + diag = diag.with_suggestion("Check array length before accessing: 'if index < len(array): ...'"); + } else if clean_msg.contains("Assertion Failed") { + diag = diag + .with_hint("An assertion in your code failed.") + .with_suggestion("Review the assertion condition and the actual values being tested."); + } + + diag = diag.with_docs("https://quantica.dev/docs/errors/E004"); reporter.report(&diag); std::process::exit(1); @@ -473,6 +525,16 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } } +// Helper function to extract variable name from error message +fn extract_variable_name(msg: &str) -> String { + if let Some(start) = msg.find('\'') { + if let Some(end) = msg[start + 1..].find('\'') { + return msg[start + 1..start + 1 + end].to_string(); + } + } + "variable".to_string() +} + fn print_help() { println!("Quantica Compiler v0.1.0"); println!(); diff --git a/src/type_checker.rs b/src/type_checker.rs index 574327a..c73a973 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -6,13 +6,13 @@ use std::collections::HashMap; use std::rc::Rc; use std::string::String; -// --- ADD THESE IMPORTS --- + use crate::lexer::Lexer; use crate::parser::ast::ImportPath; use crate::parser::ast::Loc; use crate::parser::Parser; use std::fs; -// --- END ADDED IMPORTS --- +use crate::error::{find_similar_names, levenshtein_distance}; #[derive(Debug, Clone, PartialEq)] pub struct TypeInfo { @@ -788,13 +788,40 @@ impl TypeChecker { _ => Err("Type Error: Assignment target must be an identifier or subscript expression.".to_string()) } } - ASTNode::Identifier { name, loc } => match env.borrow().get(name) { - Some(info) => Ok(info.var_type.clone()), - Option::None => Err(format!( - "Type Error at {}: Undefined variable '{}'", - loc, name - )), - }, + ASTNode::Identifier { name, loc } => { + match env.borrow().get(name) { + Some(info) => Ok(info.var_type.clone()), + None => { + // Collect all available variable names for suggestions + let mut all_names = Vec::new(); + let mut current_env = Some(env.clone()); + + while let Some(env_rc) = current_env { + let env_ref = env_rc.borrow(); + for var_name in env_ref.store.keys() { + all_names.push(var_name.clone()); + } + current_env = env_ref.outer.clone(); + } + + // Find similar names + let suggestions = find_similar_names(name, &all_names); + + let mut error_msg = format!("Undefined variable '{}'", name); + if !suggestions.is_empty() { + error_msg.push_str(&format!( + ". Did you mean: {}?", + suggestions.join(", ") + )); + } + + Err(format!( + "Type Error at {}: {}", + loc, error_msg + )) + } + } + } ASTNode::ClassDeclaration { name, fields, methods, constructor, .. } => { // Register class type let class_type = Type::Class(name.clone()); From a1a1482fae17f1de5bb48e07e9ab75477bdef877 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:35:16 +0530 Subject: [PATCH 06/27] Added better Error handling v2 still needs bug fixes --- src/graphics/error_bridge.rs | 349 +++++++++++++++++++++++++++ src/graphics/error_codes.rs | 452 +++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/main.rs | 124 +--------- 4 files changed, 817 insertions(+), 112 deletions(-) create mode 100644 src/graphics/error_bridge.rs create mode 100644 src/graphics/error_codes.rs diff --git a/src/graphics/error_bridge.rs b/src/graphics/error_bridge.rs new file mode 100644 index 0000000..ef21fa9 --- /dev/null +++ b/src/graphics/error_bridge.rs @@ -0,0 +1,349 @@ +// src/error_bridge.rs + +use crate::error::{Diagnostic, ErrorReporter, ErrorCategory}; +use crate::error_codes::ErrorCode; +use crate::parser::ast::Loc; + + +pub fn parse_error_location(error_msg: &str) -> Loc { + if let Some(start) = error_msg.find("[Line ") { + let rest = &error_msg[start..]; + if let Some(end) = rest.find(']') { + let content = &rest[6..end]; + let parts: Vec<&str> = content.split(", Col ").collect(); + if parts.len() == 2 { + if let (Ok(line), Ok(column)) = (parts[0].parse(), parts[1].parse()) { + return Loc { line, column }; + } + } + } + } + + // Also try parsing "at line X, column Y" format + if let Some(line_pos) = error_msg.find("line ") { + let rest = &error_msg[line_pos + 5..]; + if let Some(comma_pos) = rest.find(',') { + let line_str = &rest[..comma_pos].trim(); + if let Ok(line) = line_str.parse::() { + if let Some(col_pos) = rest.find("column ") { + let col_rest = &rest[col_pos + 7..]; + // Find the end of the number + let col_end = col_rest.find(|c: char| !c.is_numeric()).unwrap_or(col_rest.len()); + if let Ok(column) = col_rest[..col_end].parse::() { + return Loc { line, column }; + } + } + } + } + } + + Loc { line: 1, column: 1 } +} + +pub fn detect_error_code(error_msg: &str) -> ErrorCode { + let msg_lower = error_msg.to_lowercase(); + + // Syntax errors - check these FIRST before lexical + if msg_lower.contains("unexpected token") { + return ErrorCode::E100; + } + + // Check for specific token expectation errors + if msg_lower.contains("expected") && (msg_lower.contains("found") || msg_lower.contains("but")) { + // Check for specific delimiters + if msg_lower.contains("colon") || msg_lower.contains("':'") { + return ErrorCode::E103; + } + if msg_lower.contains("leftparen") || msg_lower.contains("'('") || msg_lower.contains("(") { + return ErrorCode::E104; + } + if msg_lower.contains("rightparen") || msg_lower.contains("')'") || msg_lower.contains(")") { + return ErrorCode::E104; + } + if msg_lower.contains("leftbracket") || msg_lower.contains("'['") || msg_lower.contains("[") { + return ErrorCode::E105; + } + if msg_lower.contains("rightbracket") || msg_lower.contains("']'") || msg_lower.contains("]") { + return ErrorCode::E105; + } + if msg_lower.contains("leftbrace") || msg_lower.contains("'{'") || msg_lower.contains("{") { + return ErrorCode::E106; + } + if msg_lower.contains("rightbrace") || msg_lower.contains("'}'") || msg_lower.contains("}") { + return ErrorCode::E106; + } + return ErrorCode::E101; + } + + // Check for "unexpected" followed by token names + if msg_lower.contains("unexpected") { + if msg_lower.contains("rightparen") || (msg_lower.contains(")") && !msg_lower.contains("expected")) { + return ErrorCode::E104; + } + if msg_lower.contains("rightbracket") || (msg_lower.contains("]") && !msg_lower.contains("expected")) { + return ErrorCode::E105; + } + if msg_lower.contains("rightbrace") || (msg_lower.contains("}") && !msg_lower.contains("expected")) { + return ErrorCode::E106; + } + } + + if msg_lower.contains("unexpected end") || msg_lower.contains("unexpected eof") { + return ErrorCode::E107; + } + + // Unmatched delimiters + if msg_lower.contains("unmatched") { + if msg_lower.contains("parenthes") || msg_lower.contains("(") || msg_lower.contains(")") { + return ErrorCode::E104; + } + if msg_lower.contains("bracket") || msg_lower.contains("[") || msg_lower.contains("]") { + return ErrorCode::E105; + } + if msg_lower.contains("brace") || msg_lower.contains("{") || msg_lower.contains("}") { + return ErrorCode::E106; + } + } + + // Lexical errors + if msg_lower.contains("unterminated string") { + return ErrorCode::E001; + } + if msg_lower.contains("unterminated character") { + return ErrorCode::E002; + } + if msg_lower.contains("unterminated comment") { + return ErrorCode::E003; + } + if msg_lower.contains("unexpected character") || msg_lower.contains("invalid character") { + return ErrorCode::E004; + } + if msg_lower.contains("invalid escape") { + return ErrorCode::E005; + } + if msg_lower.contains("invalid number") || msg_lower.contains("invalid integer") || msg_lower.contains("invalid float") { + return ErrorCode::E006; + } + if msg_lower.contains("indentation error") { + return ErrorCode::E010; + } + + // Type errors + if msg_lower.contains("type mismatch") || msg_lower.contains("mismatched types") { + return ErrorCode::E200; + } + if msg_lower.contains("undefined variable") { + return ErrorCode::E201; + } + if msg_lower.contains("undefined function") { + return ErrorCode::E202; + } + if msg_lower.contains("undefined class") { + return ErrorCode::E203; + } + if msg_lower.contains("undefined member") { + return ErrorCode::E204; + } + if msg_lower.contains("wrong number of arguments") || msg_lower.contains("expected") && msg_lower.contains("arguments") { + return ErrorCode::E205; + } + if msg_lower.contains("cannot assign to immutable") || msg_lower.contains("immutable variable") { + return ErrorCode::E207; + } + if msg_lower.contains("method not found") { + return ErrorCode::E214; + } + if msg_lower.contains("field not found") { + return ErrorCode::E215; + } + + // Runtime errors + if msg_lower.contains("division by zero") { + return ErrorCode::E301; + } + if msg_lower.contains("out of bounds") || msg_lower.contains("index") { + return ErrorCode::E302; + } + if msg_lower.contains("assertion failed") { + return ErrorCode::E305; + } + if msg_lower.contains("measurement error") { + return ErrorCode::E312; + } + if msg_lower.contains("gate application") { + return ErrorCode::E313; + } + + // Semantic errors + if msg_lower.contains("break outside loop") || msg_lower.contains("break") && msg_lower.contains("loop") { + return ErrorCode::E402; + } + if msg_lower.contains("continue outside loop") { + return ErrorCode::E403; + } + if msg_lower.contains("return outside function") { + return ErrorCode::E404; + } + + // Import errors + if msg_lower.contains("module not found") || msg_lower.contains("failed to read") { + return ErrorCode::E500; + } + if msg_lower.contains("circular import") { + return ErrorCode::E501; + } + + // Quantum errors + if msg_lower.contains("invalid quantum gate") || msg_lower.contains("unknown gate") { + return ErrorCode::E600; + } + if msg_lower.contains("gate parameter") { + return ErrorCode::E601; + } + if msg_lower.contains("register size") && msg_lower.contains("too large") { + return ErrorCode::E604; + } + if msg_lower.contains("qubit index") { + return ErrorCode::E315; + } + + // Hardware errors + if msg_lower.contains("backend not available") || msg_lower.contains("backend") { + return ErrorCode::E800; + } + if msg_lower.contains("device not found") { + return ErrorCode::E801; + } + + // Default - check for general error types + if msg_lower.contains("runtime error") { + ErrorCode::E308 + } else if msg_lower.contains("type error") { + ErrorCode::E200 + } else if msg_lower.contains("syntax error") { + ErrorCode::E100 + } else { + // If we can't determine the error type, default to E100 (unexpected token) + // since most parsing errors fall into this category + ErrorCode::E100 + } +} + +/// Convert ErrorCode to ErrorCategory +fn error_code_to_category(code: ErrorCode) -> ErrorCategory { + match code as u32 { + 1..=10 => ErrorCategory::Syntax, // Lexical errors (E001-E010) + 100..=199 => ErrorCategory::Syntax, // Syntax errors + 200..=299 => ErrorCategory::Type, + 300..=399 => ErrorCategory::Runtime, + 400..=499 => ErrorCategory::Semantic, + 500..=599 => ErrorCategory::Import, + 600..=699 => ErrorCategory::Quantum, + 700..=799 => ErrorCategory::Syntax, // Compilation + 800..=899 => ErrorCategory::Runtime, // Hardware + _ => ErrorCategory::Runtime, + } +} + +pub fn string_to_diagnostic(error: String) -> Diagnostic { + let loc = parse_error_location(&error); + let code = detect_error_code(&error); + let category = error_code_to_category(code); + + // Extract just the message part (remove "Error at [Line X, Col Y]: " prefix) + let message = if let Some(pos) = error.find("]: ") { + &error[pos + 3..] + } else if let Some(pos) = error.find(": ") { + &error[pos + 2..] + } else { + &error + }; + + let mut diagnostic = Diagnostic::error(loc, category, message); + + // Add error code + diagnostic.code = Some(format!("{}", code)); + + // Add hint if available + if let Some(hint) = code.hint() { + diagnostic.hint = Some(hint.to_string()); + } + + // Add suggestion if available + if let Some(suggestion) = code.suggestion() { + diagnostic.suggestion = Some(suggestion.to_string()); + } + + // Add documentation URL + diagnostic.documentation_url = Some(code.docs_url()); + + diagnostic +} + +/// Print a String error with nice formatting +pub fn report_string_error(error: String, source_code: &str, filename: &str) { + let diagnostic = string_to_diagnostic(error); + let reporter = ErrorReporter::new(source_code, filename); + reporter.report(&diagnostic); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_detection() { + // Test various error patterns + assert_eq!( + detect_error_code("Unterminated string literal at line 5"), + ErrorCode::E001 + ); + + assert_eq!( + detect_error_code("Type Error: Undefined variable 'x'"), + ErrorCode::E201 + ); + + assert_eq!( + detect_error_code("Runtime Error: Division by zero"), + ErrorCode::E301 + ); + + assert_eq!( + detect_error_code("Array index 5 out of bounds"), + ErrorCode::E302 + ); + + assert_eq!( + detect_error_code("Expected '(', but found RightParen"), + ErrorCode::E104 + ); + + assert_eq!( + detect_error_code("Unexpected token: RightParen at line 3"), + ErrorCode::E100 + ); + } + + #[test] + fn test_location_parsing() { + let loc = parse_error_location("Error at [Line 10, Col 25]: Some message"); + assert_eq!(loc.line, 10); + assert_eq!(loc.column, 25); + + let loc2 = parse_error_location("Syntax Error at line 5, column 12: message"); + assert_eq!(loc2.line, 5); + assert_eq!(loc2.column, 12); + } + + #[test] + fn test_string_to_diagnostic() { + let error = "Type Error at [Line 5, Col 10]: Undefined variable 'foo'".to_string(); + let diag = string_to_diagnostic(error); + + assert_eq!(diag.code, Some("E201".to_string())); + assert_eq!(diag.loc.line, 5); + assert_eq!(diag.loc.column, 10); + assert!(diag.message.contains("Undefined variable 'foo'")); + } +} \ No newline at end of file diff --git a/src/graphics/error_codes.rs b/src/graphics/error_codes.rs new file mode 100644 index 0000000..f2516fc --- /dev/null +++ b/src/graphics/error_codes.rs @@ -0,0 +1,452 @@ +// src/error_codes.rs +//! Quantica Error Code System +//! +//! Error codes are organized by category: +//! - E001-E099: Lexical Analysis Errors +//! - E100-E199: Syntax/Parser Errors +//! - E200-E299: Type Checking Errors +//! - E300-E399: Runtime Errors +//! - E400-E499: Semantic Errors +//! - E500-E599: Import/Module Errors +//! - E600-E699: Quantum-Specific Errors +//! - E700-E799: Compilation/Codegen Errors +//! - E800-E899: Hardware Integration Errors + +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum ErrorCode { + // === LEXICAL ERRORS (E001-E099) === + E001 = 1, // Unterminated string + E002 = 2, // Unterminated character literal + E003 = 3, // Unterminated comment + E004 = 4, // Invalid character + E005 = 5, // Invalid escape sequence + E006 = 6, // Invalid number literal + E007 = 7, // Invalid unicode escape + E008 = 8, // Unexpected end of file in lexer + E009 = 9, // Invalid quantum ket/bra notation + E010 = 10, // Indentation error + + // === SYNTAX ERRORS (E100-E199) === + E100 = 100, // Unexpected token + E101 = 101, // Expected token not found + E102 = 102, // Missing semicolon + E103 = 103, // Missing colon + E104 = 104, // Unmatched parenthesis + E105 = 105, // Unmatched bracket + E106 = 106, // Unmatched brace + E107 = 107, // Unexpected end of file + E108 = 108, // Invalid expression + E109 = 109, // Invalid statement + E110 = 110, // Invalid function declaration + E111 = 111, // Invalid class declaration + E112 = 112, // Invalid circuit declaration + E113 = 113, // Invalid parameter list + E114 = 114, // Invalid argument list + E115 = 115, // Invalid block structure + E116 = 116, // Invalid quantum declaration + E117 = 117, // Invalid gate expression + E118 = 118, // Invalid apply statement + E119 = 119, // Malformed control flow + E120 = 120, // Invalid assignment target + + // === TYPE ERRORS (E200-E299) === + E200 = 200, // Type mismatch + E201 = 201, // Undefined variable + E202 = 202, // Undefined function + E203 = 203, // Undefined class + E204 = 204, // Undefined member + E205 = 205, // Wrong number of arguments + E206 = 206, // Incompatible types for operator + E207 = 207, // Cannot assign to immutable variable + E208 = 208, // Return type mismatch + E209 = 209, // Array type mismatch + E210 = 210, // Invalid array index type + E211 = 211, // Invalid quantum register size + E212 = 212, // Quantum type mismatch + E213 = 213, // Cannot call non-function + E214 = 214, // Method not found + E215 = 215, // Field not found + E216 = 216, // Invalid member access + E217 = 217, // Type annotation mismatch + E218 = 218, // Cannot infer type + E219 = 219, // Circular type dependency + E220 = 220, // Invalid type for operation + + // === RUNTIME ERRORS (E300-E399) === + E300 = 300, // Null pointer dereference + E301 = 301, // Division by zero + E302 = 302, // Array index out of bounds + E303 = 303, // Stack overflow + E304 = 304, // Out of memory + E305 = 305, // Assertion failed + E306 = 306, // Invalid cast + E307 = 307, // Uninitialized variable + E308 = 308, // Invalid operation + E309 = 309, // Resource not available + E310 = 310, // Timeout error + E311 = 311, // Invalid state + E312 = 312, // Measurement error + E313 = 313, // Gate application error + E314 = 314, // Register size mismatch + E315 = 315, // Qubit index out of bounds + + // === SEMANTIC ERRORS (E400-E499) === + E400 = 400, // Duplicate declaration + E401 = 401, // Invalid scope + E402 = 402, // Break outside loop + E403 = 403, // Continue outside loop + E404 = 404, // Return outside function + E405 = 405, // Invalid use of 'self' + E406 = 406, // Invalid superclass + E407 = 407, // Cyclic inheritance + E408 = 408, // Abstract method not implemented + E409 = 409, // Invalid override + E410 = 410, // Dead code detected + E411 = 411, // Unreachable code + E412 = 412, // Unused variable + E413 = 413, // Unused function + E414 = 414, // Invalid constant expression + E415 = 415, // Mutability violation + + // === IMPORT/MODULE ERRORS (E500-E599) === + E500 = 500, // Module not found + E501 = 501, // Circular import + E502 = 502, // Invalid import path + E503 = 503, // Symbol not found in module + E504 = 504, // Conflicting imports + E505 = 505, // Module parse error + E506 = 506, // Module type error + E507 = 507, // Invalid module structure + E508 = 508, // Package not found + E509 = 509, // Version conflict + + // === QUANTUM ERRORS (E600-E699) === + E600 = 600, // Invalid quantum gate + E601 = 601, // Gate parameter error + E602 = 602, // Incompatible qubit operation + E603 = 603, // Invalid control configuration + E604 = 604, // Quantum register too large + E605 = 605, // Invalid initial state + E606 = 606, // Measurement on uninitialized qubit + E607 = 607, // Invalid dagger operation + E608 = 608, // Gate arity mismatch + E609 = 609, // Invalid tensor product + E610 = 610, // Quantum state normalization error + E611 = 611, // Invalid quantum circuit + E612 = 612, // Superposition error + E613 = 613, // Entanglement error + E614 = 614, // Decoherence detected + + // === COMPILATION ERRORS (E700-E799) === + E700 = 700, // LLVM error + E701 = 701, // Code generation failed + E702 = 702, // Optimization failed + E703 = 703, // Linking error + E704 = 704, // Invalid target + E705 = 705, // Unsupported feature + E706 = 706, // Assembly error + E707 = 707, // Object file error + E708 = 708, // Debug info error + E709 = 709, // ABI mismatch + E710 = 710, // Platform not supported + + // === HARDWARE INTEGRATION ERRORS (E800-E899) === + E800 = 800, // Backend not available + E801 = 801, // Device not found + E802 = 802, // Connection error + E803 = 803, // Authentication failed + E804 = 804, // Job submission failed + E805 = 805, // Job execution error + E806 = 806, // Result retrieval failed + E807 = 807, // Unsupported gate on hardware + E808 = 808, // Hardware timeout + E809 = 809, // Calibration error + E810 = 810, // Noise model error +} + +impl ErrorCode { + /// Get the category name for this error code + pub fn category(&self) -> &'static str { + match *self as u32 { + 1..=99 => "Lexical", + 100..=199 => "Syntax", + 200..=299 => "Type", + 300..=399 => "Runtime", + 400..=499 => "Semantic", + 500..=599 => "Import", + 600..=699 => "Quantum", + 700..=799 => "Compilation", + 800..=899 => "Hardware", + _ => "Unknown", + } + } + + /// Get a short description of this error + pub fn description(&self) -> &'static str { + match self { + // Lexical + ErrorCode::E001 => "Unterminated string literal", + ErrorCode::E002 => "Unterminated character literal", + ErrorCode::E003 => "Unterminated comment", + ErrorCode::E004 => "Invalid or unexpected character", + ErrorCode::E005 => "Invalid escape sequence", + ErrorCode::E006 => "Invalid number literal", + ErrorCode::E007 => "Invalid unicode escape", + ErrorCode::E008 => "Unexpected end of input", + ErrorCode::E009 => "Invalid quantum notation", + ErrorCode::E010 => "Indentation error", + + // Syntax + ErrorCode::E100 => "Unexpected token", + ErrorCode::E101 => "Expected token not found", + ErrorCode::E102 => "Missing semicolon", + ErrorCode::E103 => "Missing colon", + ErrorCode::E104 => "Unmatched parenthesis", + ErrorCode::E105 => "Unmatched bracket", + ErrorCode::E106 => "Unmatched brace", + ErrorCode::E107 => "Unexpected end of file", + ErrorCode::E108 => "Invalid expression syntax", + ErrorCode::E109 => "Invalid statement syntax", + ErrorCode::E110 => "Invalid function declaration", + ErrorCode::E111 => "Invalid class declaration", + ErrorCode::E112 => "Invalid circuit declaration", + ErrorCode::E113 => "Invalid parameter list", + ErrorCode::E114 => "Invalid argument list", + ErrorCode::E115 => "Invalid block structure", + ErrorCode::E116 => "Invalid quantum declaration", + ErrorCode::E117 => "Invalid gate expression", + ErrorCode::E118 => "Invalid apply statement", + ErrorCode::E119 => "Malformed control flow statement", + ErrorCode::E120 => "Invalid assignment target", + + // Type + ErrorCode::E200 => "Type mismatch", + ErrorCode::E201 => "Undefined variable", + ErrorCode::E202 => "Undefined function", + ErrorCode::E203 => "Undefined class", + ErrorCode::E204 => "Undefined member", + ErrorCode::E205 => "Wrong number of arguments", + ErrorCode::E206 => "Incompatible types for operator", + ErrorCode::E207 => "Cannot assign to immutable variable", + ErrorCode::E208 => "Return type mismatch", + ErrorCode::E209 => "Array element type mismatch", + ErrorCode::E210 => "Invalid array index type", + ErrorCode::E211 => "Invalid quantum register size", + ErrorCode::E212 => "Quantum type mismatch", + ErrorCode::E213 => "Cannot call non-function type", + ErrorCode::E214 => "Method not found on type", + ErrorCode::E215 => "Field not found on type", + ErrorCode::E216 => "Invalid member access", + ErrorCode::E217 => "Type annotation does not match inferred type", + ErrorCode::E218 => "Cannot infer type", + ErrorCode::E219 => "Circular type dependency", + ErrorCode::E220 => "Invalid type for this operation", + + // Runtime + ErrorCode::E300 => "Null pointer dereference", + ErrorCode::E301 => "Division by zero", + ErrorCode::E302 => "Array index out of bounds", + ErrorCode::E303 => "Stack overflow", + ErrorCode::E304 => "Out of memory", + ErrorCode::E305 => "Assertion failed", + ErrorCode::E306 => "Invalid type cast", + ErrorCode::E307 => "Uninitialized variable access", + ErrorCode::E308 => "Invalid operation at runtime", + ErrorCode::E309 => "Required resource not available", + ErrorCode::E310 => "Operation timeout", + ErrorCode::E311 => "Invalid program state", + ErrorCode::E312 => "Quantum measurement error", + ErrorCode::E313 => "Gate application error", + ErrorCode::E314 => "Quantum register size mismatch", + ErrorCode::E315 => "Qubit index out of bounds", + + // Semantic + ErrorCode::E400 => "Duplicate declaration", + ErrorCode::E401 => "Invalid scope access", + ErrorCode::E402 => "Break statement outside loop", + ErrorCode::E403 => "Continue statement outside loop", + ErrorCode::E404 => "Return statement outside function", + ErrorCode::E405 => "Invalid use of 'self' keyword", + ErrorCode::E406 => "Invalid superclass", + ErrorCode::E407 => "Cyclic inheritance detected", + ErrorCode::E408 => "Abstract method not implemented", + ErrorCode::E409 => "Invalid method override", + ErrorCode::E410 => "Dead code detected", + ErrorCode::E411 => "Unreachable code", + ErrorCode::E412 => "Unused variable", + ErrorCode::E413 => "Unused function", + ErrorCode::E414 => "Invalid constant expression", + ErrorCode::E415 => "Mutability violation", + + // Import + ErrorCode::E500 => "Module or package not found", + ErrorCode::E501 => "Circular import detected", + ErrorCode::E502 => "Invalid import path", + ErrorCode::E503 => "Symbol not found in module", + ErrorCode::E504 => "Conflicting import declarations", + ErrorCode::E505 => "Error parsing imported module", + ErrorCode::E506 => "Type error in imported module", + ErrorCode::E507 => "Invalid module structure", + ErrorCode::E508 => "Package not found", + ErrorCode::E509 => "Package version conflict", + + // Quantum + ErrorCode::E600 => "Unknown or invalid quantum gate", + ErrorCode::E601 => "Invalid gate parameters", + ErrorCode::E602 => "Incompatible qubit operation", + ErrorCode::E603 => "Invalid control qubit configuration", + ErrorCode::E604 => "Quantum register size exceeds limit", + ErrorCode::E605 => "Invalid initial quantum state", + ErrorCode::E606 => "Measurement on uninitialized qubit", + ErrorCode::E607 => "Invalid dagger/adjoint operation", + ErrorCode::E608 => "Gate arity mismatch", + ErrorCode::E609 => "Invalid tensor product operation", + ErrorCode::E610 => "Quantum state normalization error", + ErrorCode::E611 => "Invalid quantum circuit structure", + ErrorCode::E612 => "Superposition calculation error", + ErrorCode::E613 => "Entanglement operation error", + ErrorCode::E614 => "Quantum decoherence detected", + + // Compilation + ErrorCode::E700 => "LLVM internal error", + ErrorCode::E701 => "Code generation failed", + ErrorCode::E702 => "Optimization pass failed", + ErrorCode::E703 => "Linking error", + ErrorCode::E704 => "Invalid compilation target", + ErrorCode::E705 => "Feature not supported on this target", + ErrorCode::E706 => "Assembly generation error", + ErrorCode::E707 => "Object file generation error", + ErrorCode::E708 => "Debug information error", + ErrorCode::E709 => "ABI compatibility error", + ErrorCode::E710 => "Platform not supported", + + // Hardware + ErrorCode::E800 => "Quantum backend not available", + ErrorCode::E801 => "Quantum device not found", + ErrorCode::E802 => "Connection to quantum hardware failed", + ErrorCode::E803 => "Authentication failed", + ErrorCode::E804 => "Job submission to hardware failed", + ErrorCode::E805 => "Job execution error on hardware", + ErrorCode::E806 => "Failed to retrieve results", + ErrorCode::E807 => "Gate not supported on this hardware", + ErrorCode::E808 => "Hardware operation timeout", + ErrorCode::E809 => "Calibration error", + ErrorCode::E810 => "Noise model error", + } + } + + /// Get a hint for resolving this error + pub fn hint(&self) -> Option<&'static str> { + match self { + ErrorCode::E001 => Some("Make sure all string literals are properly closed with matching quotes"), + ErrorCode::E003 => Some("Multi-line comments must be closed with */"), + ErrorCode::E010 => Some("Check that all lines in a block have consistent indentation"), + ErrorCode::E101 => Some("Check for missing colons after function/class declarations, or missing parentheses"), + ErrorCode::E104 => Some("Check that all opening parentheses '(' have matching closing parentheses ')'"), + ErrorCode::E107 => Some("You may have an unclosed block, missing return statement, or incomplete expression"), + ErrorCode::E201 => Some("Make sure the variable is declared before use with 'let' or 'mut'"), + ErrorCode::E205 => Some("Check the function signature to see how many parameters it expects"), + ErrorCode::E207 => Some("Variables declared with 'let' are immutable. Use 'mut' to make them mutable"), + ErrorCode::E302 => Some("Add a length check before accessing array elements: 'if index < len(array)'"), + ErrorCode::E301 => Some("Add a check for zero before division: 'if denominator != 0'"), + ErrorCode::E305 => Some("Review the assertion condition and the values being tested"), + ErrorCode::E402 => Some("Break statements can only be used inside for/while loops"), + ErrorCode::E500 => Some("Check the file path and make sure the module exists"), + ErrorCode::E604 => Some("Try using a smaller number of qubits or optimize your circuit"), + ErrorCode::E800 => Some("Make sure the required quantum backend (qiskit, cirq) is installed"), + _ => None, + } + } + + /// Get a suggestion for fixing this error + pub fn suggestion(&self) -> Option<&'static str> { + match self { + ErrorCode::E001 => Some("Add a closing quote: \"your string\""), + ErrorCode::E101 => Some("Add the required colon ':' after the declaration"), + ErrorCode::E104 => Some("Add the missing ')' or remove the extra ')'"), + ErrorCode::E201 => Some("Declare the variable first: 'let variable_name = value'"), + ErrorCode::E207 => Some("Change 'let' to 'mut': 'mut variable_name = value'"), + ErrorCode::E200 => Some("Use type conversion functions: to_int(), to_float(), to_string()"), + ErrorCode::E205 => Some("Adjust the number of arguments to match the function signature"), + ErrorCode::E302 => Some("Check array bounds: 'if index < len(array): value = array[index]'"), + ErrorCode::E301 => Some("Add a conditional: 'if denominator != 0: result = numerator / denominator'"), + ErrorCode::E402 => Some("Move the break statement inside a loop, or remove it"), + ErrorCode::E500 => Some("Check the import path or install the required package"), + ErrorCode::E604 => Some("Reduce the number of qubits or use a hardware backend"), + _ => None, + } + } + + /// Get documentation URL for this error + pub fn docs_url(&self) -> String { + format!("https://quantica.dev/docs/errors/{}", self) + } +} + +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "E{:03}", *self as u32) + } +} + +/// Helper function to create common error patterns +pub mod patterns { + use super::ErrorCode; + + pub fn undefined_variable(name: &str, similar: &[String]) -> (ErrorCode, String, Option) { + let mut msg = format!("Undefined variable '{}'", name); + let suggestion = if !similar.is_empty() { + let sug = format!("Did you mean: {}?", similar.join(", ")); + msg.push_str(&format!(". {}", sug)); + Some(format!("Check the spelling or declare it with: let {} = ...", name)) + } else { + Some(format!("Declare the variable first: let {} = value", name)) + }; + + (ErrorCode::E201, msg, suggestion) + } + + pub fn type_mismatch(expected: &str, got: &str, context: &str) -> (ErrorCode, String) { + ( + ErrorCode::E200, + format!("Type mismatch in {}: expected {}, but got {}", context, expected, got) + ) + } + + pub fn wrong_arg_count(func: &str, expected: usize, got: usize) -> (ErrorCode, String) { + ( + ErrorCode::E205, + format!("Function '{}' expects {} arguments, but got {}", func, expected, got) + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_code_display() { + assert_eq!(format!("{}", ErrorCode::E001), "E001"); + assert_eq!(format!("{}", ErrorCode::E201), "E201"); + assert_eq!(format!("{}", ErrorCode::E600), "E600"); + } + + #[test] + fn test_error_code_category() { + assert_eq!(ErrorCode::E001.category(), "Lexical"); + assert_eq!(ErrorCode::E201.category(), "Type"); + assert_eq!(ErrorCode::E600.category(), "Quantum"); + } + + #[test] + fn test_error_code_docs() { + assert_eq!( + ErrorCode::E001.docs_url(), + "https://quantica.dev/docs/errors/E001" + ); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 95cb6ec..d3b90fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,4 +19,6 @@ pub mod graphics; pub mod graphics_runtime; pub use graphics_runtime::*; -pub mod error; \ No newline at end of file +pub mod error; +pub mod error_codes; +pub mod error_bridge; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f8f6960..7e5e4f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,8 @@ mod parser; mod runtime; mod type_checker; mod error; +mod error_bridge; +mod error_codes; mod hardware_integration; mod quantum_backend; @@ -40,6 +42,7 @@ use std::path::Path; use std::time::Instant; use crate::error::{ErrorReporter, Diagnostic,ErrorCategory}; use crate::parser::ast::Loc; +use crate::error_bridge::report_string_error; #[derive(Debug, Clone, Copy, PartialEq)] enum CompilationTarget { @@ -336,7 +339,7 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } // Create error reporter - let reporter = ErrorReporter::new(&source, filename); + let _reporter = ErrorReporter::new(&source, filename); // Lexical Analysis if verbose { @@ -344,21 +347,9 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } let mut lexer = Lexer::new(&source); let tokens = match lexer.tokenize() { - Ok(t) => { - if verbose { - println!("✓ Lexer succeeded! {} tokens\n", t.len()); - } - t - } + Ok(t) => t, Err(e) => { - let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - - let diag = Diagnostic::error(loc, ErrorCategory::Syntax, &e) - .with_code("E001") - .with_hint("Check for unclosed strings, invalid characters, or unterminated comments.") - .with_docs("https://quantica.dev/docs/errors/E001"); - - reporter.report(&diag); + report_string_error(e, &source, filename); std::process::exit(1); } }; @@ -379,38 +370,9 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } let mut parser = Parser::new(tokens); let ast = match parser.parse() { - Ok(tree) => { - if verbose { - println!("✓ Parser succeeded!\n"); - } - tree - } + Ok(tree) => tree, Err(e) => { - let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - - // Extract the actual error message - let clean_msg = if let Some(idx) = e.find("]: ") { - &e[idx + 3..] - } else { - &e - }; - - let mut diag = Diagnostic::error(loc, ErrorCategory::Syntax, clean_msg) - .with_code("E002") - .with_hint("Check for missing semicolons, parentheses, braces, or colons."); - - // Add specific suggestions based on error type - if clean_msg.contains("Expected Colon") { - diag = diag.with_suggestion("Add a colon ':' for type annotation or after control flow statements."); - } else if clean_msg.contains("Expected LeftParen") { - diag = diag.with_suggestion("Function calls require parentheses '()' even if there are no arguments."); - } else if clean_msg.contains("Unexpected EOF") { - diag = diag.with_suggestion("You may have an unclosed block or missing 'return' statement."); - } - - diag = diag.with_docs("https://quantica.dev/docs/errors/E002"); - - reporter.report(&diag); + report_string_error(e, &source, filename); std::process::exit(1); } }; @@ -420,45 +382,9 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } match TypeChecker::check_program(&ast) { - Ok(()) => { - if verbose { - println!("✓ Type check succeeded!\n"); - } - } + Ok(()) => {}, Err(e) => { - let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - - let clean_msg = if let Some(idx) = e.find("]: ") { - &e[idx + 3..] - } else { - &e - }; - - let mut diag = Diagnostic::error(loc, ErrorCategory::Type, clean_msg) - .with_code("E003"); - - // Intelligent suggestions based on error content - if clean_msg.contains("Undefined variable") { - let var_name = extract_variable_name(clean_msg); - diag = diag - .with_hint("This variable was not declared in the current scope.") - .with_suggestion(&format!( - "Did you mean to declare it? Try: 'let {} = ...' or 'mut {} = ...'", - var_name, var_name - )); - } else if clean_msg.contains("Mismatched types") { - diag = diag - .with_hint("The types don't match. You may need to convert one type to another.") - .with_suggestion("Use type conversion functions: to_int(), to_float(), to_string()"); - } else if clean_msg.contains("Cannot call method") { - diag = diag - .with_hint("This type doesn't have the method you're trying to call.") - .with_suggestion("Check the type's available methods in the documentation."); - } - - diag = diag.with_docs("https://quantica.dev/docs/errors/E003"); - - reporter.report(&diag); + report_string_error(e, &source, filename); std::process::exit(1); } } @@ -487,39 +413,15 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool let duration = start_time.elapsed(); println!("{:-<60}", ""); - println!("⏱️ Interpreter Time: {:.6} seconds", duration.as_secs_f64()); + println!("Interpreter Time: {:.6} seconds", duration.as_secs_f64()); match evaluation_result { Ok(_) => { println!("✓ Execution successful!"); } + Ok(_) => println!("✓ Execution successful!"), Err(e) => { - let loc = parse_error_location(&e).unwrap_or(Loc { line: 1, column: 1 }); - - let clean_msg = if let Some(idx) = e.find("]: ") { - &e[idx + 3..] - } else { - &e - }; - - let mut diag = Diagnostic::error(loc, ErrorCategory::Runtime, clean_msg) - .with_code("E004") - .with_hint("This error occurred while your program was running."); - - // Runtime-specific suggestions - if clean_msg.contains("division by zero") { - diag = diag.with_suggestion("Add a check: 'if denominator != 0: ...'"); - } else if clean_msg.contains("index out of bounds") { - diag = diag.with_suggestion("Check array length before accessing: 'if index < len(array): ...'"); - } else if clean_msg.contains("Assertion Failed") { - diag = diag - .with_hint("An assertion in your code failed.") - .with_suggestion("Review the assertion condition and the actual values being tested."); - } - - diag = diag.with_docs("https://quantica.dev/docs/errors/E004"); - - reporter.report(&diag); + report_string_error(e, &source, filename); // <- Changed this line std::process::exit(1); } } From 6c8c1e5b36139cc2f46fa841cd0c5fd5510d7d22 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:39:15 +0530 Subject: [PATCH 07/27] Add files via upload --- src/error_codes.rs | 452 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 src/error_codes.rs diff --git a/src/error_codes.rs b/src/error_codes.rs new file mode 100644 index 0000000..f2516fc --- /dev/null +++ b/src/error_codes.rs @@ -0,0 +1,452 @@ +// src/error_codes.rs +//! Quantica Error Code System +//! +//! Error codes are organized by category: +//! - E001-E099: Lexical Analysis Errors +//! - E100-E199: Syntax/Parser Errors +//! - E200-E299: Type Checking Errors +//! - E300-E399: Runtime Errors +//! - E400-E499: Semantic Errors +//! - E500-E599: Import/Module Errors +//! - E600-E699: Quantum-Specific Errors +//! - E700-E799: Compilation/Codegen Errors +//! - E800-E899: Hardware Integration Errors + +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum ErrorCode { + // === LEXICAL ERRORS (E001-E099) === + E001 = 1, // Unterminated string + E002 = 2, // Unterminated character literal + E003 = 3, // Unterminated comment + E004 = 4, // Invalid character + E005 = 5, // Invalid escape sequence + E006 = 6, // Invalid number literal + E007 = 7, // Invalid unicode escape + E008 = 8, // Unexpected end of file in lexer + E009 = 9, // Invalid quantum ket/bra notation + E010 = 10, // Indentation error + + // === SYNTAX ERRORS (E100-E199) === + E100 = 100, // Unexpected token + E101 = 101, // Expected token not found + E102 = 102, // Missing semicolon + E103 = 103, // Missing colon + E104 = 104, // Unmatched parenthesis + E105 = 105, // Unmatched bracket + E106 = 106, // Unmatched brace + E107 = 107, // Unexpected end of file + E108 = 108, // Invalid expression + E109 = 109, // Invalid statement + E110 = 110, // Invalid function declaration + E111 = 111, // Invalid class declaration + E112 = 112, // Invalid circuit declaration + E113 = 113, // Invalid parameter list + E114 = 114, // Invalid argument list + E115 = 115, // Invalid block structure + E116 = 116, // Invalid quantum declaration + E117 = 117, // Invalid gate expression + E118 = 118, // Invalid apply statement + E119 = 119, // Malformed control flow + E120 = 120, // Invalid assignment target + + // === TYPE ERRORS (E200-E299) === + E200 = 200, // Type mismatch + E201 = 201, // Undefined variable + E202 = 202, // Undefined function + E203 = 203, // Undefined class + E204 = 204, // Undefined member + E205 = 205, // Wrong number of arguments + E206 = 206, // Incompatible types for operator + E207 = 207, // Cannot assign to immutable variable + E208 = 208, // Return type mismatch + E209 = 209, // Array type mismatch + E210 = 210, // Invalid array index type + E211 = 211, // Invalid quantum register size + E212 = 212, // Quantum type mismatch + E213 = 213, // Cannot call non-function + E214 = 214, // Method not found + E215 = 215, // Field not found + E216 = 216, // Invalid member access + E217 = 217, // Type annotation mismatch + E218 = 218, // Cannot infer type + E219 = 219, // Circular type dependency + E220 = 220, // Invalid type for operation + + // === RUNTIME ERRORS (E300-E399) === + E300 = 300, // Null pointer dereference + E301 = 301, // Division by zero + E302 = 302, // Array index out of bounds + E303 = 303, // Stack overflow + E304 = 304, // Out of memory + E305 = 305, // Assertion failed + E306 = 306, // Invalid cast + E307 = 307, // Uninitialized variable + E308 = 308, // Invalid operation + E309 = 309, // Resource not available + E310 = 310, // Timeout error + E311 = 311, // Invalid state + E312 = 312, // Measurement error + E313 = 313, // Gate application error + E314 = 314, // Register size mismatch + E315 = 315, // Qubit index out of bounds + + // === SEMANTIC ERRORS (E400-E499) === + E400 = 400, // Duplicate declaration + E401 = 401, // Invalid scope + E402 = 402, // Break outside loop + E403 = 403, // Continue outside loop + E404 = 404, // Return outside function + E405 = 405, // Invalid use of 'self' + E406 = 406, // Invalid superclass + E407 = 407, // Cyclic inheritance + E408 = 408, // Abstract method not implemented + E409 = 409, // Invalid override + E410 = 410, // Dead code detected + E411 = 411, // Unreachable code + E412 = 412, // Unused variable + E413 = 413, // Unused function + E414 = 414, // Invalid constant expression + E415 = 415, // Mutability violation + + // === IMPORT/MODULE ERRORS (E500-E599) === + E500 = 500, // Module not found + E501 = 501, // Circular import + E502 = 502, // Invalid import path + E503 = 503, // Symbol not found in module + E504 = 504, // Conflicting imports + E505 = 505, // Module parse error + E506 = 506, // Module type error + E507 = 507, // Invalid module structure + E508 = 508, // Package not found + E509 = 509, // Version conflict + + // === QUANTUM ERRORS (E600-E699) === + E600 = 600, // Invalid quantum gate + E601 = 601, // Gate parameter error + E602 = 602, // Incompatible qubit operation + E603 = 603, // Invalid control configuration + E604 = 604, // Quantum register too large + E605 = 605, // Invalid initial state + E606 = 606, // Measurement on uninitialized qubit + E607 = 607, // Invalid dagger operation + E608 = 608, // Gate arity mismatch + E609 = 609, // Invalid tensor product + E610 = 610, // Quantum state normalization error + E611 = 611, // Invalid quantum circuit + E612 = 612, // Superposition error + E613 = 613, // Entanglement error + E614 = 614, // Decoherence detected + + // === COMPILATION ERRORS (E700-E799) === + E700 = 700, // LLVM error + E701 = 701, // Code generation failed + E702 = 702, // Optimization failed + E703 = 703, // Linking error + E704 = 704, // Invalid target + E705 = 705, // Unsupported feature + E706 = 706, // Assembly error + E707 = 707, // Object file error + E708 = 708, // Debug info error + E709 = 709, // ABI mismatch + E710 = 710, // Platform not supported + + // === HARDWARE INTEGRATION ERRORS (E800-E899) === + E800 = 800, // Backend not available + E801 = 801, // Device not found + E802 = 802, // Connection error + E803 = 803, // Authentication failed + E804 = 804, // Job submission failed + E805 = 805, // Job execution error + E806 = 806, // Result retrieval failed + E807 = 807, // Unsupported gate on hardware + E808 = 808, // Hardware timeout + E809 = 809, // Calibration error + E810 = 810, // Noise model error +} + +impl ErrorCode { + /// Get the category name for this error code + pub fn category(&self) -> &'static str { + match *self as u32 { + 1..=99 => "Lexical", + 100..=199 => "Syntax", + 200..=299 => "Type", + 300..=399 => "Runtime", + 400..=499 => "Semantic", + 500..=599 => "Import", + 600..=699 => "Quantum", + 700..=799 => "Compilation", + 800..=899 => "Hardware", + _ => "Unknown", + } + } + + /// Get a short description of this error + pub fn description(&self) -> &'static str { + match self { + // Lexical + ErrorCode::E001 => "Unterminated string literal", + ErrorCode::E002 => "Unterminated character literal", + ErrorCode::E003 => "Unterminated comment", + ErrorCode::E004 => "Invalid or unexpected character", + ErrorCode::E005 => "Invalid escape sequence", + ErrorCode::E006 => "Invalid number literal", + ErrorCode::E007 => "Invalid unicode escape", + ErrorCode::E008 => "Unexpected end of input", + ErrorCode::E009 => "Invalid quantum notation", + ErrorCode::E010 => "Indentation error", + + // Syntax + ErrorCode::E100 => "Unexpected token", + ErrorCode::E101 => "Expected token not found", + ErrorCode::E102 => "Missing semicolon", + ErrorCode::E103 => "Missing colon", + ErrorCode::E104 => "Unmatched parenthesis", + ErrorCode::E105 => "Unmatched bracket", + ErrorCode::E106 => "Unmatched brace", + ErrorCode::E107 => "Unexpected end of file", + ErrorCode::E108 => "Invalid expression syntax", + ErrorCode::E109 => "Invalid statement syntax", + ErrorCode::E110 => "Invalid function declaration", + ErrorCode::E111 => "Invalid class declaration", + ErrorCode::E112 => "Invalid circuit declaration", + ErrorCode::E113 => "Invalid parameter list", + ErrorCode::E114 => "Invalid argument list", + ErrorCode::E115 => "Invalid block structure", + ErrorCode::E116 => "Invalid quantum declaration", + ErrorCode::E117 => "Invalid gate expression", + ErrorCode::E118 => "Invalid apply statement", + ErrorCode::E119 => "Malformed control flow statement", + ErrorCode::E120 => "Invalid assignment target", + + // Type + ErrorCode::E200 => "Type mismatch", + ErrorCode::E201 => "Undefined variable", + ErrorCode::E202 => "Undefined function", + ErrorCode::E203 => "Undefined class", + ErrorCode::E204 => "Undefined member", + ErrorCode::E205 => "Wrong number of arguments", + ErrorCode::E206 => "Incompatible types for operator", + ErrorCode::E207 => "Cannot assign to immutable variable", + ErrorCode::E208 => "Return type mismatch", + ErrorCode::E209 => "Array element type mismatch", + ErrorCode::E210 => "Invalid array index type", + ErrorCode::E211 => "Invalid quantum register size", + ErrorCode::E212 => "Quantum type mismatch", + ErrorCode::E213 => "Cannot call non-function type", + ErrorCode::E214 => "Method not found on type", + ErrorCode::E215 => "Field not found on type", + ErrorCode::E216 => "Invalid member access", + ErrorCode::E217 => "Type annotation does not match inferred type", + ErrorCode::E218 => "Cannot infer type", + ErrorCode::E219 => "Circular type dependency", + ErrorCode::E220 => "Invalid type for this operation", + + // Runtime + ErrorCode::E300 => "Null pointer dereference", + ErrorCode::E301 => "Division by zero", + ErrorCode::E302 => "Array index out of bounds", + ErrorCode::E303 => "Stack overflow", + ErrorCode::E304 => "Out of memory", + ErrorCode::E305 => "Assertion failed", + ErrorCode::E306 => "Invalid type cast", + ErrorCode::E307 => "Uninitialized variable access", + ErrorCode::E308 => "Invalid operation at runtime", + ErrorCode::E309 => "Required resource not available", + ErrorCode::E310 => "Operation timeout", + ErrorCode::E311 => "Invalid program state", + ErrorCode::E312 => "Quantum measurement error", + ErrorCode::E313 => "Gate application error", + ErrorCode::E314 => "Quantum register size mismatch", + ErrorCode::E315 => "Qubit index out of bounds", + + // Semantic + ErrorCode::E400 => "Duplicate declaration", + ErrorCode::E401 => "Invalid scope access", + ErrorCode::E402 => "Break statement outside loop", + ErrorCode::E403 => "Continue statement outside loop", + ErrorCode::E404 => "Return statement outside function", + ErrorCode::E405 => "Invalid use of 'self' keyword", + ErrorCode::E406 => "Invalid superclass", + ErrorCode::E407 => "Cyclic inheritance detected", + ErrorCode::E408 => "Abstract method not implemented", + ErrorCode::E409 => "Invalid method override", + ErrorCode::E410 => "Dead code detected", + ErrorCode::E411 => "Unreachable code", + ErrorCode::E412 => "Unused variable", + ErrorCode::E413 => "Unused function", + ErrorCode::E414 => "Invalid constant expression", + ErrorCode::E415 => "Mutability violation", + + // Import + ErrorCode::E500 => "Module or package not found", + ErrorCode::E501 => "Circular import detected", + ErrorCode::E502 => "Invalid import path", + ErrorCode::E503 => "Symbol not found in module", + ErrorCode::E504 => "Conflicting import declarations", + ErrorCode::E505 => "Error parsing imported module", + ErrorCode::E506 => "Type error in imported module", + ErrorCode::E507 => "Invalid module structure", + ErrorCode::E508 => "Package not found", + ErrorCode::E509 => "Package version conflict", + + // Quantum + ErrorCode::E600 => "Unknown or invalid quantum gate", + ErrorCode::E601 => "Invalid gate parameters", + ErrorCode::E602 => "Incompatible qubit operation", + ErrorCode::E603 => "Invalid control qubit configuration", + ErrorCode::E604 => "Quantum register size exceeds limit", + ErrorCode::E605 => "Invalid initial quantum state", + ErrorCode::E606 => "Measurement on uninitialized qubit", + ErrorCode::E607 => "Invalid dagger/adjoint operation", + ErrorCode::E608 => "Gate arity mismatch", + ErrorCode::E609 => "Invalid tensor product operation", + ErrorCode::E610 => "Quantum state normalization error", + ErrorCode::E611 => "Invalid quantum circuit structure", + ErrorCode::E612 => "Superposition calculation error", + ErrorCode::E613 => "Entanglement operation error", + ErrorCode::E614 => "Quantum decoherence detected", + + // Compilation + ErrorCode::E700 => "LLVM internal error", + ErrorCode::E701 => "Code generation failed", + ErrorCode::E702 => "Optimization pass failed", + ErrorCode::E703 => "Linking error", + ErrorCode::E704 => "Invalid compilation target", + ErrorCode::E705 => "Feature not supported on this target", + ErrorCode::E706 => "Assembly generation error", + ErrorCode::E707 => "Object file generation error", + ErrorCode::E708 => "Debug information error", + ErrorCode::E709 => "ABI compatibility error", + ErrorCode::E710 => "Platform not supported", + + // Hardware + ErrorCode::E800 => "Quantum backend not available", + ErrorCode::E801 => "Quantum device not found", + ErrorCode::E802 => "Connection to quantum hardware failed", + ErrorCode::E803 => "Authentication failed", + ErrorCode::E804 => "Job submission to hardware failed", + ErrorCode::E805 => "Job execution error on hardware", + ErrorCode::E806 => "Failed to retrieve results", + ErrorCode::E807 => "Gate not supported on this hardware", + ErrorCode::E808 => "Hardware operation timeout", + ErrorCode::E809 => "Calibration error", + ErrorCode::E810 => "Noise model error", + } + } + + /// Get a hint for resolving this error + pub fn hint(&self) -> Option<&'static str> { + match self { + ErrorCode::E001 => Some("Make sure all string literals are properly closed with matching quotes"), + ErrorCode::E003 => Some("Multi-line comments must be closed with */"), + ErrorCode::E010 => Some("Check that all lines in a block have consistent indentation"), + ErrorCode::E101 => Some("Check for missing colons after function/class declarations, or missing parentheses"), + ErrorCode::E104 => Some("Check that all opening parentheses '(' have matching closing parentheses ')'"), + ErrorCode::E107 => Some("You may have an unclosed block, missing return statement, or incomplete expression"), + ErrorCode::E201 => Some("Make sure the variable is declared before use with 'let' or 'mut'"), + ErrorCode::E205 => Some("Check the function signature to see how many parameters it expects"), + ErrorCode::E207 => Some("Variables declared with 'let' are immutable. Use 'mut' to make them mutable"), + ErrorCode::E302 => Some("Add a length check before accessing array elements: 'if index < len(array)'"), + ErrorCode::E301 => Some("Add a check for zero before division: 'if denominator != 0'"), + ErrorCode::E305 => Some("Review the assertion condition and the values being tested"), + ErrorCode::E402 => Some("Break statements can only be used inside for/while loops"), + ErrorCode::E500 => Some("Check the file path and make sure the module exists"), + ErrorCode::E604 => Some("Try using a smaller number of qubits or optimize your circuit"), + ErrorCode::E800 => Some("Make sure the required quantum backend (qiskit, cirq) is installed"), + _ => None, + } + } + + /// Get a suggestion for fixing this error + pub fn suggestion(&self) -> Option<&'static str> { + match self { + ErrorCode::E001 => Some("Add a closing quote: \"your string\""), + ErrorCode::E101 => Some("Add the required colon ':' after the declaration"), + ErrorCode::E104 => Some("Add the missing ')' or remove the extra ')'"), + ErrorCode::E201 => Some("Declare the variable first: 'let variable_name = value'"), + ErrorCode::E207 => Some("Change 'let' to 'mut': 'mut variable_name = value'"), + ErrorCode::E200 => Some("Use type conversion functions: to_int(), to_float(), to_string()"), + ErrorCode::E205 => Some("Adjust the number of arguments to match the function signature"), + ErrorCode::E302 => Some("Check array bounds: 'if index < len(array): value = array[index]'"), + ErrorCode::E301 => Some("Add a conditional: 'if denominator != 0: result = numerator / denominator'"), + ErrorCode::E402 => Some("Move the break statement inside a loop, or remove it"), + ErrorCode::E500 => Some("Check the import path or install the required package"), + ErrorCode::E604 => Some("Reduce the number of qubits or use a hardware backend"), + _ => None, + } + } + + /// Get documentation URL for this error + pub fn docs_url(&self) -> String { + format!("https://quantica.dev/docs/errors/{}", self) + } +} + +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "E{:03}", *self as u32) + } +} + +/// Helper function to create common error patterns +pub mod patterns { + use super::ErrorCode; + + pub fn undefined_variable(name: &str, similar: &[String]) -> (ErrorCode, String, Option) { + let mut msg = format!("Undefined variable '{}'", name); + let suggestion = if !similar.is_empty() { + let sug = format!("Did you mean: {}?", similar.join(", ")); + msg.push_str(&format!(". {}", sug)); + Some(format!("Check the spelling or declare it with: let {} = ...", name)) + } else { + Some(format!("Declare the variable first: let {} = value", name)) + }; + + (ErrorCode::E201, msg, suggestion) + } + + pub fn type_mismatch(expected: &str, got: &str, context: &str) -> (ErrorCode, String) { + ( + ErrorCode::E200, + format!("Type mismatch in {}: expected {}, but got {}", context, expected, got) + ) + } + + pub fn wrong_arg_count(func: &str, expected: usize, got: usize) -> (ErrorCode, String) { + ( + ErrorCode::E205, + format!("Function '{}' expects {} arguments, but got {}", func, expected, got) + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_code_display() { + assert_eq!(format!("{}", ErrorCode::E001), "E001"); + assert_eq!(format!("{}", ErrorCode::E201), "E201"); + assert_eq!(format!("{}", ErrorCode::E600), "E600"); + } + + #[test] + fn test_error_code_category() { + assert_eq!(ErrorCode::E001.category(), "Lexical"); + assert_eq!(ErrorCode::E201.category(), "Type"); + assert_eq!(ErrorCode::E600.category(), "Quantum"); + } + + #[test] + fn test_error_code_docs() { + assert_eq!( + ErrorCode::E001.docs_url(), + "https://quantica.dev/docs/errors/E001" + ); + } +} \ No newline at end of file From 9f4f08513ae1f2969458b31be84ec01ce6734af0 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:39:33 +0530 Subject: [PATCH 08/27] Add files via upload --- src/error_bridge.rs | 349 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 src/error_bridge.rs diff --git a/src/error_bridge.rs b/src/error_bridge.rs new file mode 100644 index 0000000..ef21fa9 --- /dev/null +++ b/src/error_bridge.rs @@ -0,0 +1,349 @@ +// src/error_bridge.rs + +use crate::error::{Diagnostic, ErrorReporter, ErrorCategory}; +use crate::error_codes::ErrorCode; +use crate::parser::ast::Loc; + + +pub fn parse_error_location(error_msg: &str) -> Loc { + if let Some(start) = error_msg.find("[Line ") { + let rest = &error_msg[start..]; + if let Some(end) = rest.find(']') { + let content = &rest[6..end]; + let parts: Vec<&str> = content.split(", Col ").collect(); + if parts.len() == 2 { + if let (Ok(line), Ok(column)) = (parts[0].parse(), parts[1].parse()) { + return Loc { line, column }; + } + } + } + } + + // Also try parsing "at line X, column Y" format + if let Some(line_pos) = error_msg.find("line ") { + let rest = &error_msg[line_pos + 5..]; + if let Some(comma_pos) = rest.find(',') { + let line_str = &rest[..comma_pos].trim(); + if let Ok(line) = line_str.parse::() { + if let Some(col_pos) = rest.find("column ") { + let col_rest = &rest[col_pos + 7..]; + // Find the end of the number + let col_end = col_rest.find(|c: char| !c.is_numeric()).unwrap_or(col_rest.len()); + if let Ok(column) = col_rest[..col_end].parse::() { + return Loc { line, column }; + } + } + } + } + } + + Loc { line: 1, column: 1 } +} + +pub fn detect_error_code(error_msg: &str) -> ErrorCode { + let msg_lower = error_msg.to_lowercase(); + + // Syntax errors - check these FIRST before lexical + if msg_lower.contains("unexpected token") { + return ErrorCode::E100; + } + + // Check for specific token expectation errors + if msg_lower.contains("expected") && (msg_lower.contains("found") || msg_lower.contains("but")) { + // Check for specific delimiters + if msg_lower.contains("colon") || msg_lower.contains("':'") { + return ErrorCode::E103; + } + if msg_lower.contains("leftparen") || msg_lower.contains("'('") || msg_lower.contains("(") { + return ErrorCode::E104; + } + if msg_lower.contains("rightparen") || msg_lower.contains("')'") || msg_lower.contains(")") { + return ErrorCode::E104; + } + if msg_lower.contains("leftbracket") || msg_lower.contains("'['") || msg_lower.contains("[") { + return ErrorCode::E105; + } + if msg_lower.contains("rightbracket") || msg_lower.contains("']'") || msg_lower.contains("]") { + return ErrorCode::E105; + } + if msg_lower.contains("leftbrace") || msg_lower.contains("'{'") || msg_lower.contains("{") { + return ErrorCode::E106; + } + if msg_lower.contains("rightbrace") || msg_lower.contains("'}'") || msg_lower.contains("}") { + return ErrorCode::E106; + } + return ErrorCode::E101; + } + + // Check for "unexpected" followed by token names + if msg_lower.contains("unexpected") { + if msg_lower.contains("rightparen") || (msg_lower.contains(")") && !msg_lower.contains("expected")) { + return ErrorCode::E104; + } + if msg_lower.contains("rightbracket") || (msg_lower.contains("]") && !msg_lower.contains("expected")) { + return ErrorCode::E105; + } + if msg_lower.contains("rightbrace") || (msg_lower.contains("}") && !msg_lower.contains("expected")) { + return ErrorCode::E106; + } + } + + if msg_lower.contains("unexpected end") || msg_lower.contains("unexpected eof") { + return ErrorCode::E107; + } + + // Unmatched delimiters + if msg_lower.contains("unmatched") { + if msg_lower.contains("parenthes") || msg_lower.contains("(") || msg_lower.contains(")") { + return ErrorCode::E104; + } + if msg_lower.contains("bracket") || msg_lower.contains("[") || msg_lower.contains("]") { + return ErrorCode::E105; + } + if msg_lower.contains("brace") || msg_lower.contains("{") || msg_lower.contains("}") { + return ErrorCode::E106; + } + } + + // Lexical errors + if msg_lower.contains("unterminated string") { + return ErrorCode::E001; + } + if msg_lower.contains("unterminated character") { + return ErrorCode::E002; + } + if msg_lower.contains("unterminated comment") { + return ErrorCode::E003; + } + if msg_lower.contains("unexpected character") || msg_lower.contains("invalid character") { + return ErrorCode::E004; + } + if msg_lower.contains("invalid escape") { + return ErrorCode::E005; + } + if msg_lower.contains("invalid number") || msg_lower.contains("invalid integer") || msg_lower.contains("invalid float") { + return ErrorCode::E006; + } + if msg_lower.contains("indentation error") { + return ErrorCode::E010; + } + + // Type errors + if msg_lower.contains("type mismatch") || msg_lower.contains("mismatched types") { + return ErrorCode::E200; + } + if msg_lower.contains("undefined variable") { + return ErrorCode::E201; + } + if msg_lower.contains("undefined function") { + return ErrorCode::E202; + } + if msg_lower.contains("undefined class") { + return ErrorCode::E203; + } + if msg_lower.contains("undefined member") { + return ErrorCode::E204; + } + if msg_lower.contains("wrong number of arguments") || msg_lower.contains("expected") && msg_lower.contains("arguments") { + return ErrorCode::E205; + } + if msg_lower.contains("cannot assign to immutable") || msg_lower.contains("immutable variable") { + return ErrorCode::E207; + } + if msg_lower.contains("method not found") { + return ErrorCode::E214; + } + if msg_lower.contains("field not found") { + return ErrorCode::E215; + } + + // Runtime errors + if msg_lower.contains("division by zero") { + return ErrorCode::E301; + } + if msg_lower.contains("out of bounds") || msg_lower.contains("index") { + return ErrorCode::E302; + } + if msg_lower.contains("assertion failed") { + return ErrorCode::E305; + } + if msg_lower.contains("measurement error") { + return ErrorCode::E312; + } + if msg_lower.contains("gate application") { + return ErrorCode::E313; + } + + // Semantic errors + if msg_lower.contains("break outside loop") || msg_lower.contains("break") && msg_lower.contains("loop") { + return ErrorCode::E402; + } + if msg_lower.contains("continue outside loop") { + return ErrorCode::E403; + } + if msg_lower.contains("return outside function") { + return ErrorCode::E404; + } + + // Import errors + if msg_lower.contains("module not found") || msg_lower.contains("failed to read") { + return ErrorCode::E500; + } + if msg_lower.contains("circular import") { + return ErrorCode::E501; + } + + // Quantum errors + if msg_lower.contains("invalid quantum gate") || msg_lower.contains("unknown gate") { + return ErrorCode::E600; + } + if msg_lower.contains("gate parameter") { + return ErrorCode::E601; + } + if msg_lower.contains("register size") && msg_lower.contains("too large") { + return ErrorCode::E604; + } + if msg_lower.contains("qubit index") { + return ErrorCode::E315; + } + + // Hardware errors + if msg_lower.contains("backend not available") || msg_lower.contains("backend") { + return ErrorCode::E800; + } + if msg_lower.contains("device not found") { + return ErrorCode::E801; + } + + // Default - check for general error types + if msg_lower.contains("runtime error") { + ErrorCode::E308 + } else if msg_lower.contains("type error") { + ErrorCode::E200 + } else if msg_lower.contains("syntax error") { + ErrorCode::E100 + } else { + // If we can't determine the error type, default to E100 (unexpected token) + // since most parsing errors fall into this category + ErrorCode::E100 + } +} + +/// Convert ErrorCode to ErrorCategory +fn error_code_to_category(code: ErrorCode) -> ErrorCategory { + match code as u32 { + 1..=10 => ErrorCategory::Syntax, // Lexical errors (E001-E010) + 100..=199 => ErrorCategory::Syntax, // Syntax errors + 200..=299 => ErrorCategory::Type, + 300..=399 => ErrorCategory::Runtime, + 400..=499 => ErrorCategory::Semantic, + 500..=599 => ErrorCategory::Import, + 600..=699 => ErrorCategory::Quantum, + 700..=799 => ErrorCategory::Syntax, // Compilation + 800..=899 => ErrorCategory::Runtime, // Hardware + _ => ErrorCategory::Runtime, + } +} + +pub fn string_to_diagnostic(error: String) -> Diagnostic { + let loc = parse_error_location(&error); + let code = detect_error_code(&error); + let category = error_code_to_category(code); + + // Extract just the message part (remove "Error at [Line X, Col Y]: " prefix) + let message = if let Some(pos) = error.find("]: ") { + &error[pos + 3..] + } else if let Some(pos) = error.find(": ") { + &error[pos + 2..] + } else { + &error + }; + + let mut diagnostic = Diagnostic::error(loc, category, message); + + // Add error code + diagnostic.code = Some(format!("{}", code)); + + // Add hint if available + if let Some(hint) = code.hint() { + diagnostic.hint = Some(hint.to_string()); + } + + // Add suggestion if available + if let Some(suggestion) = code.suggestion() { + diagnostic.suggestion = Some(suggestion.to_string()); + } + + // Add documentation URL + diagnostic.documentation_url = Some(code.docs_url()); + + diagnostic +} + +/// Print a String error with nice formatting +pub fn report_string_error(error: String, source_code: &str, filename: &str) { + let diagnostic = string_to_diagnostic(error); + let reporter = ErrorReporter::new(source_code, filename); + reporter.report(&diagnostic); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_detection() { + // Test various error patterns + assert_eq!( + detect_error_code("Unterminated string literal at line 5"), + ErrorCode::E001 + ); + + assert_eq!( + detect_error_code("Type Error: Undefined variable 'x'"), + ErrorCode::E201 + ); + + assert_eq!( + detect_error_code("Runtime Error: Division by zero"), + ErrorCode::E301 + ); + + assert_eq!( + detect_error_code("Array index 5 out of bounds"), + ErrorCode::E302 + ); + + assert_eq!( + detect_error_code("Expected '(', but found RightParen"), + ErrorCode::E104 + ); + + assert_eq!( + detect_error_code("Unexpected token: RightParen at line 3"), + ErrorCode::E100 + ); + } + + #[test] + fn test_location_parsing() { + let loc = parse_error_location("Error at [Line 10, Col 25]: Some message"); + assert_eq!(loc.line, 10); + assert_eq!(loc.column, 25); + + let loc2 = parse_error_location("Syntax Error at line 5, column 12: message"); + assert_eq!(loc2.line, 5); + assert_eq!(loc2.column, 12); + } + + #[test] + fn test_string_to_diagnostic() { + let error = "Type Error at [Line 5, Col 10]: Undefined variable 'foo'".to_string(); + let diag = string_to_diagnostic(error); + + assert_eq!(diag.code, Some("E201".to_string())); + assert_eq!(diag.loc.line, 5); + assert_eq!(diag.loc.column, 10); + assert!(diag.message.contains("Undefined variable 'foo'")); + } +} \ No newline at end of file From c71e0295a59ff788b27b4a4e9dd337a3a8d0dae2 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Mon, 8 Dec 2025 18:26:44 +0530 Subject: [PATCH 09/27] added error_codes website --- src/error_codes.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/error_codes.rs b/src/error_codes.rs index f2516fc..5d323d7 100644 --- a/src/error_codes.rs +++ b/src/error_codes.rs @@ -382,7 +382,7 @@ impl ErrorCode { /// Get documentation URL for this error pub fn docs_url(&self) -> String { - format!("https://quantica.dev/docs/errors/{}", self) + format!("https://quantica-foundation.github.io/quantica-lang/errorcodes.html") } } @@ -441,12 +441,4 @@ mod tests { assert_eq!(ErrorCode::E201.category(), "Type"); assert_eq!(ErrorCode::E600.category(), "Quantum"); } - - #[test] - fn test_error_code_docs() { - assert_eq!( - ErrorCode::E001.docs_url(), - "https://quantica.dev/docs/errors/E001" - ); - } } \ No newline at end of file From 97709eccdb32ecbcaea8fba307e05d811253fffc Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:04:37 +0530 Subject: [PATCH 10/27] comments updtion --- src/error_codes.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/error_codes.rs b/src/error_codes.rs index 5d323d7..a9a3e9f 100644 --- a/src/error_codes.rs +++ b/src/error_codes.rs @@ -168,7 +168,6 @@ pub enum ErrorCode { } impl ErrorCode { - /// Get the category name for this error code pub fn category(&self) -> &'static str { match *self as u32 { 1..=99 => "Lexical", @@ -184,7 +183,6 @@ impl ErrorCode { } } - /// Get a short description of this error pub fn description(&self) -> &'static str { match self { // Lexical @@ -338,8 +336,8 @@ impl ErrorCode { } } - /// Get a hint for resolving this error - pub fn hint(&self) -> Option<&'static str> { + ///hint + pub fn hint(&sel for resolving this errorf) -> Option<&'static str> { match self { ErrorCode::E001 => Some("Make sure all string literals are properly closed with matching quotes"), ErrorCode::E003 => Some("Multi-line comments must be closed with */"), @@ -361,7 +359,7 @@ impl ErrorCode { } } - /// Get a suggestion for fixing this error + ///suggestion pub fn suggestion(&self) -> Option<&'static str> { match self { ErrorCode::E001 => Some("Add a closing quote: \"your string\""), @@ -380,7 +378,7 @@ impl ErrorCode { } } - /// Get documentation URL for this error + ///documentation URL pub fn docs_url(&self) -> String { format!("https://quantica-foundation.github.io/quantica-lang/errorcodes.html") } @@ -392,7 +390,7 @@ impl fmt::Display for ErrorCode { } } -/// Helper function to create common error patterns +///common error patterns pub mod patterns { use super::ErrorCode; From d8834099928766c2925144974539635fd866321f Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:06:40 +0530 Subject: [PATCH 11/27] comments updation --- src/error_bridge.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/error_bridge.rs b/src/error_bridge.rs index ef21fa9..e07269c 100644 --- a/src/error_bridge.rs +++ b/src/error_bridge.rs @@ -18,8 +18,6 @@ pub fn parse_error_location(error_msg: &str) -> Loc { } } } - - // Also try parsing "at line X, column Y" format if let Some(line_pos) = error_msg.find("line ") { let rest = &error_msg[line_pos + 5..]; if let Some(comma_pos) = rest.find(',') { @@ -43,12 +41,12 @@ pub fn parse_error_location(error_msg: &str) -> Loc { pub fn detect_error_code(error_msg: &str) -> ErrorCode { let msg_lower = error_msg.to_lowercase(); - // Syntax errors - check these FIRST before lexical + // Syntax errors if msg_lower.contains("unexpected token") { return ErrorCode::E100; } - // Check for specific token expectation errors + //token expectation errors if msg_lower.contains("expected") && (msg_lower.contains("found") || msg_lower.contains("but")) { // Check for specific delimiters if msg_lower.contains("colon") || msg_lower.contains("':'") { @@ -74,8 +72,6 @@ pub fn detect_error_code(error_msg: &str) -> ErrorCode { } return ErrorCode::E101; } - - // Check for "unexpected" followed by token names if msg_lower.contains("unexpected") { if msg_lower.contains("rightparen") || (msg_lower.contains(")") && !msg_lower.contains("expected")) { return ErrorCode::E104; @@ -215,7 +211,7 @@ pub fn detect_error_code(error_msg: &str) -> ErrorCode { return ErrorCode::E801; } - // Default - check for general error types + //general error types if msg_lower.contains("runtime error") { ErrorCode::E308 } else if msg_lower.contains("type error") { @@ -229,7 +225,6 @@ pub fn detect_error_code(error_msg: &str) -> ErrorCode { } } -/// Convert ErrorCode to ErrorCategory fn error_code_to_category(code: ErrorCode) -> ErrorCategory { match code as u32 { 1..=10 => ErrorCategory::Syntax, // Lexical errors (E001-E010) @@ -249,8 +244,6 @@ pub fn string_to_diagnostic(error: String) -> Diagnostic { let loc = parse_error_location(&error); let code = detect_error_code(&error); let category = error_code_to_category(code); - - // Extract just the message part (remove "Error at [Line X, Col Y]: " prefix) let message = if let Some(pos) = error.find("]: ") { &error[pos + 3..] } else if let Some(pos) = error.find(": ") { @@ -261,26 +254,25 @@ pub fn string_to_diagnostic(error: String) -> Diagnostic { let mut diagnostic = Diagnostic::error(loc, category, message); - // Add error code + diagnostic.code = Some(format!("{}", code)); - // Add hint if available + if let Some(hint) = code.hint() { diagnostic.hint = Some(hint.to_string()); } - // Add suggestion if available if let Some(suggestion) = code.suggestion() { diagnostic.suggestion = Some(suggestion.to_string()); } - // Add documentation URL + diagnostic.documentation_url = Some(code.docs_url()); diagnostic } -/// Print a String error with nice formatting + pub fn report_string_error(error: String, source_code: &str, filename: &str) { let diagnostic = string_to_diagnostic(error); let reporter = ErrorReporter::new(source_code, filename); @@ -293,7 +285,7 @@ mod tests { #[test] fn test_error_detection() { - // Test various error patterns + assert_eq!( detect_error_code("Unterminated string literal at line 5"), ErrorCode::E001 From 8fe3d28d83e9e4c90052c257075434f9cdc48754 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:09:17 +0530 Subject: [PATCH 12/27] comments updation bug fixed --- src/error_codes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error_codes.rs b/src/error_codes.rs index a9a3e9f..51aa823 100644 --- a/src/error_codes.rs +++ b/src/error_codes.rs @@ -337,7 +337,7 @@ impl ErrorCode { } ///hint - pub fn hint(&sel for resolving this errorf) -> Option<&'static str> { + pub fn hint(&self) -> Option<&'static str> { match self { ErrorCode::E001 => Some("Make sure all string literals are properly closed with matching quotes"), ErrorCode::E003 => Some("Multi-line comments must be closed with */"), From 8304983e54be0c76f4b18094fd78f320d4b0a546 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:59:01 +0530 Subject: [PATCH 13/27] Qubit Lifecycle Management system v1 --- src/evaluator/mod.rs | 450 +++++++++++++++++++++++++------ src/lib.rs | 3 +- src/main.rs | 77 +++--- src/type_checker.rs | 624 +++++++++++++++++++++++++++++-------------- 4 files changed, 834 insertions(+), 320 deletions(-) diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index cc29bae..75a1d6b 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -1,17 +1,17 @@ // src/evaluator/mod.rs use crate::environment::{Environment, GateDefinition, RuntimeValue}; -use crate::lexer::Lexer; // Restored +use crate::lexer::Lexer; use crate::parser::ast::ASTNode; use crate::parser::ast::BinaryOperator; -use crate::parser::ast::ImportPath; // Restored -use crate::parser::ast::ImportSpec; // Restored +use crate::parser::ast::ImportPath; +use crate::parser::ast::ImportSpec; use crate::parser::ast::Loc; -use crate::parser::Parser; // Restored +use crate::parser::Parser; use rand::Rng; use std::cell::RefCell; -use std::collections::HashMap; // Restored -use std::fs; // Restored -use std::path::PathBuf; // Restored +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; use std::rc::Rc; use crate::parser::ast::ClassField; use crate::parser::ast::ClassMethod; @@ -25,6 +25,7 @@ lazy_static::lazy_static! { static ref INTERPRETER_PLOTS: Mutex> = Mutex::new(HashMap::new()); static ref NEXT_ID: Mutex = Mutex::new(0); } +use crate::qubit_lifecycle::{QubitLifecycleManager, QubitId, QubitOperation, QubitState}; pub struct Evaluator; @@ -44,7 +45,6 @@ impl Evaluator { } } - // --- Core 'evaluate' function --- pub fn evaluate( node: &ASTNode, env: &Rc>, @@ -316,9 +316,9 @@ impl Evaluator { // Check against the initial value we recorded if let Some(initial_val) = initial_values.get(field_name) { - // FIX: Use the helper instead of '!=' + if !Self::are_values_equal(&local_val, initial_val) { - // Local var changed (e.g., 'x = 10'), so update the field + *field_rc.borrow_mut() = local_val; } } @@ -336,8 +336,7 @@ impl Evaluator { (RuntimeValue::Float(x), RuntimeValue::Float(y)) => (x - y).abs() < f64::EPSILON, (RuntimeValue::Bool(x), RuntimeValue::Bool(y)) => x == y, (RuntimeValue::String(x), RuntimeValue::String(y)) => x == y, - // For complex types, we assume they are different to ensure we sync them if needed. - // But crucially, None == None returns true, which fixes the bug. + _ => false, } } @@ -409,8 +408,7 @@ impl Evaluator { // Execute method body let result = Self::evaluate(&method.body, &method_env)?; - // 2. FIX START: COPY-OUT strategy - // Sync changes from method_env back to the original instance fields + for (field_name, field_rc) in &fields { // Check if the field exists in the method environment (it should) if let Some(env_val_rc) = method_env.borrow().get(field_name) { @@ -421,9 +419,6 @@ impl Evaluator { *field_rc.borrow_mut() = new_val; } } - // FIX END - - // Handle return value match result { RuntimeValue::ReturnValue(val) => Ok(*val), other => Ok(other), @@ -439,12 +434,12 @@ impl Evaluator { ) })?; - // We must clone the value to avoid borrowing conflicts with the environment + let member_val = member_val.borrow().clone(); let evaluated_args = Self::eval_arguments(arguments, env)?; match member_val { - // Sub-case A: Calling a standard Function exported by the module + RuntimeValue::Function { parameters, body, @@ -476,11 +471,11 @@ impl Evaluator { } } - // Sub-case B: Instantiating a CLASS exported by the module (The FIX for graphics.Canvas) + RuntimeValue::Class { name, fields, methods, constructor, .. } => { - // 1. Initialize instance fields & Capture Initial Values + let mut instance_fields = HashMap::new(); - let mut initial_values = HashMap::new(); // <--- Capture Initials + let mut initial_values = HashMap::new(); for field in &fields { let value = if let Some(default_expr) = &field.default_value { @@ -575,9 +570,6 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - // In methods, 'self' refers to the instance - // This is a simplified implementation - // In a more complete implementation, you'd store a reference to the current instance Err(format!( "Runtime Error at {}: 'self' can only be used inside class methods", loc @@ -601,8 +593,7 @@ impl Evaluator { )) } }; - if size > 20 { - // Max size is now more about memory, but still good to limit + if size > 25 { return Err(format!( "Runtime Error: Register size {} is too large to simulate.", size @@ -714,8 +705,6 @@ impl Evaluator { fn complex_norm_sq((a, b): (f64, f64)) -> f64 { a * a + b * b } - - // --- *** NEW: eval_apply_statement *** --- fn eval_apply_statement( gate_expr_node: &ASTNode, arg_nodes: &[ASTNode], @@ -749,7 +738,6 @@ impl Evaluator { let mut controls = Vec::new(); let mut targets = Vec::new(); - // --- THIS TYPE HAS CHANGED --- let mut state_rc: Option>>> = None; let mut reg_size: Option = None; @@ -761,7 +749,6 @@ impl Evaluator { for (i, qubit_val) in qubit_args.iter().enumerate() { let (q_state_rc, q_index, q_size) = match qubit_val { - // This now matches on the sparse state RuntimeValue::Qubit { state, index, size } => (state.clone(), *index, *size), _ => { return Err(format!( @@ -833,7 +820,7 @@ impl Evaluator { } => { Ok(RuntimeValue::Gate { base_name, - is_dagger: !is_dagger, // Flip the flag + is_dagger: !is_dagger, num_controls, }) } @@ -851,7 +838,7 @@ impl Evaluator { Ok(RuntimeValue::Gate { base_name, is_dagger, - num_controls: num_controls + 1, // Add one control + num_controls: num_controls + 1, // }) } _ => { @@ -920,8 +907,6 @@ impl Evaluator { RuntimeValue::Float(f) => f, RuntimeValue::Int(i) => i as f64, _ => { - // Only Qubit arguments should remain after the numerical ones - // We stop processing if it's not a number. break; } }; @@ -934,12 +919,10 @@ impl Evaluator { } } - // --- *** NEW: apply_multi_controlled_gate *** --- pub fn apply_multi_controlled_gate( gate: GateDefinition, is_dagger: bool, ) -> Result { - // Check for native 2-qubit gates let is_native_2qubit = matches!( gate.name.as_str(), "cphase" | "cnot" | "cz" | "cx" | "cy" | "swap" @@ -986,7 +969,6 @@ impl Evaluator { let old_state_map = &*state_map_guard; let mut new_state_map = HashMap::new(); - // We need to keep track of states we've already processed let mut processed = std::collections::HashSet::new(); // Iterate over the non-zero amplitudes @@ -1040,7 +1022,7 @@ impl Evaluator { processed.insert(idx1); } else { // Control bits are not active, so this state is unchanged - // We just need to copy it + // new_state_map.insert(basis_state, amp_tuple); processed.insert(basis_state); } @@ -1140,10 +1122,10 @@ impl Evaluator { Ok(RuntimeValue::None) } - // --- *** NEW: get_gate_matrix *** --- + fn get_gate_matrix( name: &str, - params: &[f64], // This now receives the parameters + params: &[f64], is_dagger: bool, ) -> Result<[[C64; 2]; 2], String> { let i = C64::new(0.0, 1.0); @@ -1181,8 +1163,6 @@ impl Evaluator { [C64::new(0.0, 0.0), C64::from_polar(1.0, angle)], ]) } - - // --- *** PARAMETERIZED GATE LOGIC (NEW) *** --- "rx" => { let theta = params.get(0).cloned().unwrap_or(0.0) * eff_dagger; let t_2 = theta / 2.0; @@ -1211,9 +1191,6 @@ impl Evaluator { } "cphase" => { let phi = params.get(0).cloned().unwrap_or(0.0) * eff_dagger; - // CPhase logic is applied outside the 2x2 matrix for controlled gates, - // but since we are using apply_multi_controlled, the base gate is an I gate - // with a phase applied to the |1> component. Ok([ [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], [C64::new(0.0, 0.0), C64::from_polar(1.0, phi)], @@ -1237,7 +1214,7 @@ impl Evaluator { Ok([[c00, c01], [c10, c11]]) } - // --- *** END PARAMETERIZED LOGIC *** --- + _ => Err(format!( "Runtime Error: Unknown gate name '{}' for matrix generation.", name @@ -1251,7 +1228,7 @@ impl Evaluator { ) -> Result { let target_val = Self::evaluate(target_expr, env)?; let (state_rc, target_index, reg_size) = match target_val { - // This now gets the sparse state + RuntimeValue::Qubit { state, index, size } => (state, index, size), _ => { return Err(format!( @@ -1265,7 +1242,7 @@ impl Evaluator { } fn perform_measurement( - state_rc: &Rc>>, // <-- TYPE CHANGED + state_rc: &Rc>>, // target_index: usize, total_size: usize, ) -> Result { @@ -1410,9 +1387,8 @@ impl Evaluator { ) -> Result { let object = Self::evaluate(object_expr, env)?; match object { - // *** NEW: Handle Module member access *** + RuntimeValue::Module(module_env) => { - // Look up the member in the module's environment match module_env.borrow().get(member) { Some(value_rc) => Ok(value_rc.borrow().clone()), None => Err(format!( @@ -1461,7 +1437,6 @@ impl Evaluator { } fn insert_if_nonzero(state_map: &mut HashMap, index: usize, amp: C64) { - // Use a small epsilon to avoid floating point issues if amp.norm_sqr() > 1e-12 { state_map.insert(index, (amp.re, amp.im)); } @@ -1687,10 +1662,9 @@ impl Evaluator { } RuntimeValue::Class { name, fields, methods, constructor, .. } => { - // 1. Initialize instance fields & Capture Initial Values - let mut instance_fields = HashMap::new(); - let mut initial_values = HashMap::new(); // <--- Capture Initials + let mut instance_fields = HashMap::new(); + let mut initial_values = HashMap::new(); for field in &fields { let value = if let Some(default_expr) = &field.default_value { Self::evaluate(default_expr, env)? @@ -1853,7 +1827,7 @@ impl Evaluator { loc: *loc, } } - // *** NEW: Parameterized gate: RX(theta) -> dagger(RX(theta)) *** + ASTNode::ParameterizedGate { name, parameters, @@ -1904,7 +1878,6 @@ impl Evaluator { Ok(last_result) } - // --- (Rest of builtins) --- pub fn print_quantum_state( state: &Rc>>, @@ -1942,8 +1915,8 @@ impl Evaluator { } match &args[0] { RuntimeValue::QuantumRegister { size, state } => { - // Call the new, shared print function - Self::print_quantum_state(state, *size, 10); // 10 is max_entries + + Self::print_quantum_state(state, *size, 10); Ok(RuntimeValue::None) } _ => Err(format!( @@ -1970,7 +1943,7 @@ impl Evaluator { } }; - // This calls the core logic inside the Evaluator + Self::perform_measurement(&state_rc, target_index, reg_size) } @@ -2002,10 +1975,10 @@ impl Evaluator { }; if condition { - // Assertion passed, do nothing. + Ok(RuntimeValue::None) } else { - // Assertion failed. Return an error, which will stop execution. + Err(format!("Assertion Failed: {}", message)) } } @@ -2014,12 +1987,12 @@ impl Evaluator { .into_iter() .map(|val| { match val { - RuntimeValue::String(s) => s, // Just the string content + RuntimeValue::String(s) => s, RuntimeValue::Int(n) => n.to_string(), RuntimeValue::Float(f) => f.to_string(), RuntimeValue::Bool(b) => b.to_string(), RuntimeValue::None => "None".to_string(), - _ => format!("{}", val), // Uses Display trait + _ => format!("{}", val), } }) .collect(); @@ -2045,7 +2018,6 @@ impl Evaluator { let elements: Vec>> = target_str .split(delim) .map(|s| { - // Trim whitespace (e.g. " 20 " -> "20") let clean_str = s.trim().to_string(); std::rc::Rc::new(std::cell::RefCell::new(RuntimeValue::String(clean_str))) }) @@ -2057,7 +2029,7 @@ impl Evaluator { // 1. Print the prompt if provided if let Some(val) = args.get(0) { print!("{}", val); - let _ = std::io::stdout().flush(); // Ensure prompt appears before typing + let _ = std::io::stdout().flush(); } // 2. Read the line @@ -2066,7 +2038,7 @@ impl Evaluator { .read_line(&mut input_buffer) .map_err(|e| format!("Runtime Error: Failed to read input. {}", e))?; - // 3. Return trimmed string (remove newline) + Ok(RuntimeValue::String(input_buffer.trim().to_string())) } @@ -2184,7 +2156,7 @@ impl Evaluator { } } - // (Restored eval_let_declaration, etc.) + fn eval_let_declaration( name: &str, value_expr: &ASTNode, @@ -2216,7 +2188,7 @@ impl Evaluator { } } - // Subscript assignment: dict["key"] = value or array[0] = value + ASTNode::ArrayAccess { array, index, loc } => { let collection_val = Self::evaluate(array, env)?; let index_val = Self::evaluate(index, env)?; @@ -2254,7 +2226,7 @@ impl Evaluator { } ASTNode::MemberAccess { object, member } => { - // Evaluate the object (e.g., 'self' or 'canvas') + let object_val = Self::evaluate(object, env)?; match object_val { @@ -2302,21 +2274,19 @@ impl Evaluator { alias: &str, env: &Rc>, ) -> Result { - // --- THIS IS THE NEW LOGIC --- + let file_path = match path { ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - // It's a file path + // file path f.clone() } else { - // It's a package name + // package name format!("q_packages/{}/init.qc", f) } } ImportPath::Module(m) => m.join("/") + ".qc", }; - - // The rest of the function is the same, just using 'file_path' let source = fs::read_to_string(&file_path).map_err(|e| { format!( "Runtime Error: Failed to read module '{}': {}", @@ -2334,15 +2304,15 @@ impl Evaluator { .parse() .map_err(|e| format!("Module Parser Error: {}", e))?; - // Each module gets its own new, clean environment + let module_env = Rc::new(RefCell::new(Environment::new())); Self::register_builtins(&module_env); Self::evaluate_program(&ast, &module_env)?; - // Create a Module runtime value + let module_val = RuntimeValue::Module(module_env); - // Set the alias in the *current* environment + env.borrow_mut().set(alias.to_string(), module_val); Ok(RuntimeValue::None) @@ -2512,8 +2482,6 @@ impl Evaluator { _env: &Rc>, ) -> Result { use crate::parser::ast::BinaryOperator::*; - - // --- SPARSE TENSOR PRODUCT --- if matches!(operator, TensorProduct) { match (left_val.clone(), right_val.clone()) { ( @@ -2532,14 +2500,14 @@ impl Evaluator { let mut new_state = HashMap::new(); - // Iterate over non-zero amplitudes of A + + for (&i, &_a) in state_a.iter() { - // Iterate over non-zero amplitudes of B for (&j, &_b) in state_b.iter() { let new_index = (i << size_b) | j; let new_amp_tuple = Self::complex_mul(amp_a, amp_b); - // Check for zero is implicit, as amp_a and amp_b are non-zero + new_state.insert(new_index, new_amp_tuple); } } @@ -2560,8 +2528,6 @@ impl Evaluator { } } } - // --- END SPARSE TENSOR PRODUCT --- - match operator { Equal => { let result = match (&left_val, &right_val) { @@ -2631,7 +2597,6 @@ impl Evaluator { )), }, _ => { - // (This block is for Add, Sub, Mul, Div, Mod, etc.) let (op, l, r) = match (operator, left_val, right_val) { (Div, RuntimeValue::Int(l), RuntimeValue::Int(r)) => ( operator, @@ -2873,7 +2838,7 @@ impl Evaluator { Ok(RuntimeValue::None) } - // --- PLOT FUNCTIONS --- + //PLOT FUNCTIONS fn builtin_graphics_create_plot(args: Vec) -> Result { let ptype_int = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => 0 }; @@ -2902,7 +2867,6 @@ impl Evaluator { match val { Some(RuntimeValue::Register(arr_rc)) => { let mut res = Vec::new(); - // Fix: Borrow the Register elements for item in arr_rc.iter() { let inner = item.borrow(); if let RuntimeValue::Float(f) = *inner { res.push(f); } @@ -2957,4 +2921,314 @@ impl Evaluator { INTERPRETER_PLOTS.lock().unwrap().remove(&id); Ok(RuntimeValue::None) } + pub fn evaluate_with_lifecycle( + node: &ASTNode, + env: &Rc>, + lifecycle: &mut QubitLifecycleManager, + ) -> Result { + match node { + ASTNode::Measure(target_expr) => { + let target_val = Self::evaluate(target_expr, env)?; + + // Extract qubit ID + if let RuntimeValue::Qubit { state, index, .. } = target_val { + // Check lifecycle + let qubit_id = QubitId::new("runtime".to_string(), index); + lifecycle.record_operation( + &qubit_id, + QubitOperation::Measure, + Loc { line: 0, column: 0 } + ).map_err(|e| e.to_string())?; + } + + // Perform measurement + Self::eval_measure(target_expr, env) + } + _ => Self::evaluate(node, env) + } + } + pub fn evaluate_program_with_lifecycle( + program: &ASTNode, + env: &Rc>, + lifecycle: &mut QubitLifecycleManager, + ) -> Result { + if let ASTNode::Program(statements) = program { + let mut last_result = RuntimeValue::None; + for stmt in statements { + last_result = Self::evaluate_with_lifecycle_recursive(stmt, env, lifecycle)?; + } + Ok(last_result) + } else { + Err("Expected ASTNode::Program at root.".to_string()) + } + } + + fn evaluate_with_lifecycle_recursive( + node: &ASTNode, + env: &Rc>, + lifecycle: &mut QubitLifecycleManager, + ) -> Result { + match node { + // 1. Quantum Declarations (Intercepted) + ASTNode::QuantumDeclaration { name, size, initial_state } => { + let register_size = if let Some(size_expr) = size { + let size_val = Self::evaluate(size_expr, env)?; + match size_val { + RuntimeValue::Int(n) if n > 0 => n as usize, + _ => 1, + } + } else { + 1 + }; + + lifecycle.register_qubits( + name, + register_size, + QubitState::Classical(false) + ); + Self::eval_quantum_declaration(name, size, initial_state, env) + } + + + ASTNode::Apply { gate_expr, arguments, loc } => { + let qubit_ids = Self::extract_qubit_ids_runtime(arguments, env)?; + let gate_name = Self::extract_gate_name_runtime(gate_expr)?; + let is_controlled = Self::is_controlled_gate(gate_expr); + + if is_controlled { + let num_controls = Self::count_controls(gate_expr); + let (controls, targets) = qubit_ids.split_at(num_controls); + lifecycle.record_controlled_gate(controls, targets, &gate_name, *loc) + .map_err(|e| e.to_string())?; + } else { + for qubit_id in &qubit_ids { + lifecycle.record_operation(qubit_id, QubitOperation::ApplyGate(gate_name.clone()), *loc) + .map_err(|e| e.to_string())?; + } + } + Self::eval_apply_statement(gate_expr, arguments, loc, env) + } + + ASTNode::Measure(qubit_expr) => { + let qubit_ids = Self::extract_qubit_ids_runtime(&[*qubit_expr.clone()], env)?; + let loc = Self::get_node_location(qubit_expr); + + for qubit_id in &qubit_ids { + lifecycle.record_operation(qubit_id, QubitOperation::Measure, loc) + .map_err(|e| e.to_string())?; + } + Self::eval_measure(qubit_expr, env) + } + + + ASTNode::LetDeclaration { name, value, .. } => { + let val = Self::evaluate_with_lifecycle_recursive(value, env, lifecycle)?; + env.borrow_mut().set(name.clone(), val); + Ok(RuntimeValue::None) + } + + // 5. Assignment (Recurse into value) + ASTNode::Assignment { target, value } => { + let new_value = Self::evaluate_with_lifecycle_recursive(value, env, lifecycle)?; + + match &**target { + ASTNode::Identifier { name, .. } => { + if let Some(var_rc) = env.borrow().get(name) { + *var_rc.borrow_mut() = new_value; + Ok(RuntimeValue::None) + } else { + Err(format!("Runtime Error: Undefined variable '{}'.", name)) + } + } + + _ => { + Self::eval_assignment(target, value, env) + } + } + } + + // 6. Block (Recurse) + ASTNode::Block(statements) => { + let mut last_result = RuntimeValue::None; + for stmt in statements { + last_result = Self::evaluate_with_lifecycle_recursive(stmt, env, lifecycle)?; + match last_result { + RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => { + return Ok(last_result); + } + _ => {} + } + } + Ok(last_result) + } + + // 7. If Statement (Recurse) + ASTNode::If { condition, then_block, elif_blocks, else_block } => { + + let cond_val = Self::evaluate_with_lifecycle_recursive(condition, env, lifecycle)?; + + if Self::is_truthy(&cond_val) { + return Self::evaluate_with_lifecycle_recursive(then_block, env, lifecycle); + } + + for (elif_cond, elif_body) in elif_blocks { + let elif_val = Self::evaluate_with_lifecycle_recursive(elif_cond, env, lifecycle)?; + if Self::is_truthy(&elif_val) { + return Self::evaluate_with_lifecycle_recursive(elif_body, env, lifecycle); + } + } + + if let Some(else_body) = else_block { + return Self::evaluate_with_lifecycle_recursive(else_body, env, lifecycle); + } + Ok(RuntimeValue::None) + } + + // 8. Loops (Recurse) + ASTNode::While { condition, body } => { + loop { + let cond_val = Self::evaluate_with_lifecycle_recursive(condition, env, lifecycle)?; + if !Self::is_truthy(&cond_val) { break; } + + let res = Self::evaluate_with_lifecycle_recursive(body, env, lifecycle)?; + match res { + RuntimeValue::Break => break, + RuntimeValue::Continue => continue, + RuntimeValue::ReturnValue(v) => return Ok(RuntimeValue::ReturnValue(v)), + _ => {} + } + } + Ok(RuntimeValue::None) + } + + ASTNode::For { variable, iterator, body } => { + let iter_val = Self::evaluate(iterator, env)?; + let iterable: Vec = match iter_val { + RuntimeValue::Range(r) => r.into_iter().map(RuntimeValue::Int).collect(), + RuntimeValue::Register(r) => r.iter().map(|v| v.borrow().clone()).collect(), + _ => return Err("Invalid iterator".to_string()), + }; + + for item in iterable { + env.borrow_mut().set(variable.clone(), item); + let res = Self::evaluate_with_lifecycle_recursive(body, env, lifecycle)?; + match res { + RuntimeValue::Break => break, + RuntimeValue::Continue => continue, + RuntimeValue::ReturnValue(v) => return Ok(RuntimeValue::ReturnValue(v)), + _ => {} + } + } + Ok(RuntimeValue::None) + } + ASTNode::FunctionCall { callee, arguments, loc, is_dagger } => { + Self::eval_function_call_with_lifecycle(callee, arguments, loc, env, *is_dagger, lifecycle) + } + + + _ => Self::evaluate(node, env) + } + } + fn eval_function_call_with_lifecycle( + callee_expr: &ASTNode, + arguments: &[ASTNode], + loc: &Loc, + env: &Rc>, + is_dagger: bool, + lifecycle: &mut QubitLifecycleManager, + ) -> Result { + let evaluated_args = Self::eval_arguments(arguments, env)?; + let function = Self::evaluate(callee_expr, env)?; + + match function { + RuntimeValue::Function { parameters, body, env: func_env } => { + let mut function_scope = Environment::new_enclosed(func_env); + if parameters.len() != evaluated_args.len() { + return Err(format!("Runtime Error: Arg count mismatch at {}", loc)); + } + for (param, arg_val) in parameters.iter().zip(evaluated_args) { + function_scope.set(param.name.clone(), arg_val); + } + let function_scope_rc = Rc::new(RefCell::new(function_scope)); + + + let result = Self::evaluate_with_lifecycle_recursive(&body, &function_scope_rc, lifecycle)?; + + if let RuntimeValue::ReturnValue(val) = result { + Ok(*val) + } else { + Ok(result) + } + } + _ => Self::eval_function_call(callee_expr, arguments, loc, env, is_dagger) + } + } + + + fn extract_qubit_ids_runtime( + arguments: &[ASTNode], + env: &Rc>, + ) -> Result, String> { + let mut ids = Vec::new(); + + for arg in arguments { + let val = Self::evaluate(arg, env)?; + + if let RuntimeValue::Qubit { index, .. } = val { + + let reg_name = match arg { + ASTNode::ArrayAccess { array, .. } => { + if let ASTNode::Identifier { name, .. } = &**array { + name.clone() + } else { + "unknown".to_string() + } + } + ASTNode::Identifier { name, .. } => name.clone(), + _ => "unknown".to_string() + }; + + ids.push(QubitId::new(reg_name, index)); + } + } + + Ok(ids) + } + + fn extract_gate_name_runtime(gate_expr: &ASTNode) -> Result { + match gate_expr { + ASTNode::Gate { name, .. } => Ok(name.to_lowercase()), + ASTNode::ParameterizedGate { name, .. } => Ok(name.to_lowercase()), + ASTNode::Controlled { gate_expr, .. } => Self::extract_gate_name_runtime(gate_expr), + ASTNode::Dagger { gate_expr, .. } => Self::extract_gate_name_runtime(gate_expr), + _ => Err("Invalid gate expression".to_string()) + } + } + fn is_controlled_gate(gate_expr: &ASTNode) -> bool { + match gate_expr { + ASTNode::Controlled { .. } => true, + ASTNode::Dagger { gate_expr: inner, .. } => Self::is_controlled_gate(inner), + _ => false + } + } + + + fn count_controls(gate_expr: &ASTNode) -> usize { + match gate_expr { + ASTNode::Controlled { gate_expr, .. } => 1 + Self::count_controls(gate_expr), + ASTNode::Dagger { gate_expr, .. } => Self::count_controls(gate_expr), + _ => 0 + } + } + + + fn get_node_location(node: &ASTNode) -> Loc { + match node { + ASTNode::ArrayAccess { loc, .. } => *loc, + ASTNode::Identifier { loc, .. } => *loc, + ASTNode::Apply { loc, .. } => *loc, + ASTNode::Measure(inner) => Self::get_node_location(inner), + _ => Loc { line: 0, column: 0 } + } + } } diff --git a/src/lib.rs b/src/lib.rs index d3b90fa..8171f13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,4 +21,5 @@ pub mod graphics_runtime; pub use graphics_runtime::*; pub mod error; pub mod error_codes; -pub mod error_bridge; \ No newline at end of file +pub mod error_bridge; +pub mod qubit_lifecycle; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7e5e4f5..a0ebdf1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,9 @@ mod parser; mod runtime; mod type_checker; mod error; -mod error_bridge; mod error_codes; +mod error_bridge; +mod qubit_lifecycle; mod hardware_integration; mod quantum_backend; @@ -18,6 +19,7 @@ mod graphics; mod graphics_runtime; use hardware_integration::{parse_hardware_config, HardwareExecutor}; use quantum_backend::QuantumConfig; +use crate::qubit_lifecycle::QubitLifecycleManager; use crate::codegen::Compiler; use crate::doc_generator::DocGenerator; @@ -40,9 +42,9 @@ use std::fs; use std::io::{self, Write}; use std::path::Path; use std::time::Instant; +use crate::error_bridge::report_string_error; use crate::error::{ErrorReporter, Diagnostic,ErrorCategory}; use crate::parser::ast::Loc; -use crate::error_bridge::report_string_error; #[derive(Debug, Clone, Copy, PartialEq)] enum CompilationTarget { @@ -66,22 +68,33 @@ fn main() -> Result<(), Box> { let mut target = CompilationTarget::HostCPU; let mut hardware_config: Option = None; let mut list_devices = false; + let mut enable_lifecycle_checking = true; - // FIRST: Check for hardware mode and parse config if args.contains(&"--hardware".to_string()) { hardware_config = parse_hardware_config(&args); } + for arg in &args { + if arg == "--no-lifecycle" { + enable_lifecycle_checking = false; + } + } + + if enable_lifecycle_checking { + println!("✓ Qubit lifecycle checking enabled"); + } else { + println!("⚠️ Qubit lifecycle checking disabled"); + } + - // THEN: Parse other flags let mut i = 1; while i < args.len() { match args[i].as_str() { "--hardware" => { - // Skip --hardware and its value (already parsed) + i += 2; } "--device" | "--shots" | "--api-token" => { - // Skip hardware-specific flags and their values + i += 2; } "--no-optimize" => { @@ -138,13 +151,12 @@ fn main() -> Result<(), Box> { i += 1; } _ => { - // Skip other args for now + i += 1; } } } - // FINALLY: Extract filename using the helper function let filename = hardware_integration::extract_filename(&args); if list_devices { @@ -178,8 +190,6 @@ fn main() -> Result<(), Box> { } } - // ... rest of your main() function continues as before ... - if command.is_none() && filename.is_none() { println!("Starting REPL mode (type '.quit' to exit, '.clear' to reset)."); run_repl(); @@ -338,7 +348,7 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool println!("{:-<60}\n", ""); } - // Create error reporter + let _reporter = ErrorReporter::new(&source, filename); // Lexical Analysis @@ -396,20 +406,25 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool println!("============================\n"); } - // INTERPRETATION (EVALUATION) if verbose { println!("✨ Phase 3: Interpretation"); } let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); + + let mut lifecycle = QubitLifecycleManager::new(true); // strict mode enabled + if verbose { println!("Program Output:"); println!("{:-<60}", ""); } let start_time = Instant::now(); - let evaluation_result = Evaluator::evaluate_program(&ast, &env); + + + let evaluation_result = Evaluator::evaluate_program_with_lifecycle(&ast, &env, &mut lifecycle); + let duration = start_time.elapsed(); println!("{:-<60}", ""); @@ -418,16 +433,19 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool match evaluation_result { Ok(_) => { println!("✓ Execution successful!"); + + if verbose { + println!("\n=== Qubit Lifecycle Summary ==="); + lifecycle.print_summary(); + } } - Ok(_) => println!("✓ Execution successful!"), Err(e) => { - report_string_error(e, &source, filename); // <- Changed this line + report_string_error(e, &source, filename); std::process::exit(1); } } } -// Helper function to extract variable name from error message fn extract_variable_name(msg: &str) -> String { if let Some(start) = msg.find('\'') { if let Some(end) = msg[start + 1..].find('\'') { @@ -571,7 +589,7 @@ fn compile_file_llvm( let hlo_ir = compiler.export_to_hlo_ir(module_name)?; println!(" -> XLA/HLO IR Exported"); - // --- LLVM IR Dump + // LLVM IR Dump if emit_llvm { println!("\n--- GENERATED LLVM IR (Optimized) ---"); compiler.dump_ir(); @@ -694,7 +712,6 @@ fn run_jit_file( println!("✨ Program Output:"); println!(); - // Flush stdout BEFORE executing JIT code use std::io::Write; let _ = std::io::stdout().flush(); @@ -770,11 +787,10 @@ fn run_test_suite() { println!("{:-<60}", ""); if failed_count > 0 { - std::process::exit(1); // Exit with error if any test failed + std::process::exit(1); } } -/// Runs the full pipeline on a single file, returning a Result. fn run_test_file(filename: &str) -> Result<(), String> { //Read source let source = fs::read_to_string(filename) @@ -796,9 +812,8 @@ fn run_test_file(filename: &str) -> Result<(), String> { //Interpretation (Evaluation) let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); Evaluator::register_builtins(&env); - Evaluator::evaluate_program(&ast, &env).map_err(|e| format!("Runtime Error: {}", e))?; // assert() failure will be caught here + Evaluator::evaluate_program(&ast, &env).map_err(|e| format!("Runtime Error: {}", e))?; - //If all steps passed: Ok(()) } @@ -966,7 +981,7 @@ fn print_ast(node: &ASTNode, indent: usize) { } => { println!("{}Apply ({} args):", prefix, arguments.len()); println!("{} Gate:", prefix); - print_ast(gate_expr, indent + 2); // Print the gate expression + print_ast(gate_expr, indent + 2); println!("{} Args:", prefix); for arg in arguments { print_ast(arg, indent + 2); @@ -1216,11 +1231,8 @@ fn run_lexer_only(filename: &str) { } fn run_repl() { - // Create persistent environments for the *entire session* let runtime_env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); let type_env = std::rc::Rc::new(std::cell::RefCell::new(TypeEnvironment::new())); - - // Prefill the type environment just once TypeChecker::prefill_environment(&type_env); Evaluator::register_builtins(&runtime_env); @@ -1253,14 +1265,11 @@ fn run_repl() { continue; } } - - // Append to buffer source_buffer.push_str(&line); if source_buffer.trim().is_empty() { continue; } - // Try to compile the buffer // Lexer let mut lexer = Lexer::new(&source_buffer); @@ -1272,7 +1281,6 @@ fn run_repl() { continue; } Err(e) => { - // Real lexer error, report and reset println!("Lexer Error: {}", e); source_buffer.clear(); is_continuation = false; @@ -1301,34 +1309,29 @@ fn run_repl() { } }; - //complete AST. Process it statement by statement. + if let ASTNode::Program(statements) = ast { for stmt in statements { //Type Check the single statement let type_check_result = TypeChecker::check(&stmt, &type_env, None); if let Err(e) = type_check_result { println!("Type Error: {}", e); - // Don't execute if type check fails break; } - // Evaluate the single statement let eval_result = Evaluator::evaluate(&stmt, &runtime_env); match eval_result { Ok(RuntimeValue::None) => { /* Don't print None */ } Ok(value) => { - println!("{}", value); // Print result + println!("{}", value); } Err(e) => { println!("Runtime Error: {}", e); - // Stop processing this block break; } } } } - - // Clear buffer and reset prompt for next input source_buffer.clear(); is_continuation = false; } diff --git a/src/type_checker.rs b/src/type_checker.rs index c73a973..95e72dd 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1,18 +1,18 @@ -// src/type_checker.rs +//src/typechecker.rs use crate::parser::ast::{ASTNode, BinaryOperator, ImportSpec, Type, UnaryOperator}; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::string::String; - - +use crate::error::{find_similar_names, levenshtein_distance}; use crate::lexer::Lexer; use crate::parser::ast::ImportPath; use crate::parser::ast::Loc; use crate::parser::Parser; +use crate::qubit_lifecycle::{QubitLifecycleManager, QubitId, QubitOperation, QubitState}; use std::fs; -use crate::error::{find_similar_names, levenshtein_distance}; + #[derive(Debug, Clone, PartialEq)] pub struct TypeInfo { @@ -27,6 +27,7 @@ pub struct TypeEnvironment { } impl TypeEnvironment { + pub fn new() -> Self { TypeEnvironment { store: HashMap::new(), @@ -56,9 +57,10 @@ impl TypeEnvironment { } } -pub struct TypeChecker; +pub struct TypeChecker{lifecycle_manager: QubitLifecycleManager,} impl TypeChecker { + pub fn prefill_environment(env: &Rc>) { let mut env_mut = env.borrow_mut(); @@ -72,17 +74,17 @@ impl TypeChecker { let qubit_type = Type::Qubit; let none_type = Box::new(none.clone()); - // --- Built-ins --- + env_mut.set( "print".to_string(), immut(Type::Function(vec![], Box::new(none.clone()))), ); env_mut.set("input".to_string(), immut( - // It takes 1 argument (the prompt string) and returns a String + Type::Function(vec![Type::Any], Box::new(Type::String)) )); env_mut.set("split".to_string(), immut( - // Func(String, String) -> Array + Type::Function( vec![Type::String, Type::String], Box::new(Type::Array(Box::new(Type::String))) @@ -138,7 +140,7 @@ impl TypeChecker { immut(Type::Function(vec![any.clone()], Box::new(Type::Float))), ); - // --- Single-Qubit Gates --- + let single_qubit_gate = Type::Function(vec![qubit_type.clone()], none_type.clone()); env_mut.set("hadamard".to_string(), immut(single_qubit_gate.clone())); env_mut.set("x".to_string(), immut(single_qubit_gate.clone())); @@ -148,7 +150,7 @@ impl TypeChecker { env_mut.set("t".to_string(), immut(single_qubit_gate.clone())); env_mut.set("reset".to_string(), immut(single_qubit_gate.clone())); - // --- Multi-Qubit Gates --- + let two_qubit_gate = Type::Function( vec![qubit_type.clone(), qubit_type.clone()], none_type.clone(), @@ -166,7 +168,7 @@ impl TypeChecker { env_mut.set("ccx".to_string(), immut(three_qubit_gate.clone())); env_mut.set("toffoli".to_string(), immut(three_qubit_gate)); - // --- Parameterized Gates --- + let cphase_gate = Type::Function( vec![Type::Float, qubit_type.clone(), qubit_type.clone()], none_type.clone(), @@ -186,7 +188,7 @@ impl TypeChecker { env_mut.set("rz".to_string(), immut(parameterized_gate.clone())); - //Graphics Built-ins + env_mut.set( "_graphics_create_canvas".to_string(), @@ -196,7 +198,7 @@ impl TypeChecker { )), ); - // _graphics_set_background(id, r, g, b, a) + env_mut.set( "_graphics_set_background".to_string(), immut(Type::Function( @@ -205,91 +207,91 @@ impl TypeChecker { )), ); - // _graphics_clear(id) + env_mut.set( "_graphics_clear".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), ); - // _graphics_draw_line(id, x1, y1, x2, y2, r, g, b, a, width) + env_mut.set( "_graphics_draw_line".to_string(), immut(Type::Function( vec![ - Type::Int, // id - Type::Float, // x1 - Type::Float, // y1 - Type::Float, // x2 - Type::Float, // y2 - Type::Int, // r - Type::Int, // g - Type::Int, // b - Type::Int, // a - Type::Float, // width + Type::Int, + Type::Float, + Type::Float, + Type::Float, + Type::Float, + Type::Int, + Type::Int, + Type::Int, + Type::Int, + Type::Float, ], Box::new(Type::None), )), ); - // _graphics_draw_rect(id, x, y, w, h, r, g, b, a, filled) + env_mut.set( "_graphics_draw_rect".to_string(), immut(Type::Function( vec![ - Type::Int, // id - Type::Float, // x - Type::Float, // y - Type::Float, // w - Type::Float, // h - Type::Int, // r - Type::Int, // g - Type::Int, // b - Type::Int, // a - Type::Int, // filled (0 or 1) + Type::Int, + Type::Float, + Type::Float, + Type::Float, + Type::Float, + Type::Int, + Type::Int, + Type::Int, + Type::Int, + Type::Int, ], Box::new(Type::None), )), ); - // _graphics_draw_circle(id, x, y, radius, r, g, b, a, filled) + env_mut.set( "_graphics_draw_circle".to_string(), immut(Type::Function( vec![ - Type::Int, // id - Type::Float, // x - Type::Float, // y - Type::Float, // radius - Type::Int, // r - Type::Int, // g - Type::Int, // b - Type::Int, // a - Type::Int, // filled + Type::Int, + Type::Float, + Type::Float, + Type::Float, + Type::Int, + Type::Int, + Type::Int, + Type::Int, + Type::Int, ], Box::new(Type::None), )), ); - // _graphics_draw_text(id, x, y, text, r, g, b, a, size) + env_mut.set( "_graphics_draw_text".to_string(), immut(Type::Function( vec![ - Type::Int, // id - Type::Float, // x - Type::Float, // y - Type::String, // text - Type::Int, // r - Type::Int, // g - Type::Int, // b - Type::Int, // a - Type::Float, // size + Type::Int, + Type::Float, + Type::Float, + Type::String, + Type::Int, + Type::Int, + Type::Int, + Type::Int, + Type::Float, ], Box::new(Type::None), )), ); - // _graphics_save_svg(id, filename) -> Int (status) + env_mut.set( "_graphics_save_svg".to_string(), immut(Type::Function( @@ -298,7 +300,7 @@ impl TypeChecker { )), ); - // _graphics_save_png(id, filename) -> Int (status) + env_mut.set( "_graphics_save_png".to_string(), immut(Type::Function( @@ -307,19 +309,19 @@ impl TypeChecker { )), ); - // _graphics_destroy_canvas(id) + env_mut.set( "_graphics_destroy_canvas".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), ); - // _graphics_create_plot(type) -> Int + env_mut.set( "_graphics_create_plot".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::Int))), ); - // _graphics_plot_set_data(id, x[], y[], len) + env_mut.set( "_graphics_plot_set_data".to_string(), immut(Type::Function( @@ -333,7 +335,7 @@ impl TypeChecker { )), ); - // _graphics_plot_set_title(id, title) + env_mut.set( "_graphics_plot_set_title".to_string(), immut(Type::Function( @@ -342,7 +344,7 @@ impl TypeChecker { )), ); - // _graphics_plot_render(plot_id, canvas_id) -> Int + env_mut.set( "_graphics_plot_render".to_string(), immut(Type::Function( @@ -351,7 +353,7 @@ impl TypeChecker { )), ); - // _graphics_destroy_plot(id) + env_mut.set( "_graphics_destroy_plot".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), @@ -361,15 +363,249 @@ impl TypeChecker { pub fn check_program(node: &ASTNode) -> Result<(), String> { if let ASTNode::Program(statements) = node { let env = Rc::new(RefCell::new(TypeEnvironment::new())); + let mut lifecycle = QubitLifecycleManager::new(true); + Self::prefill_environment(&env); + for stmt in statements { - Self::check(stmt, &env, Option::None)?; + Self::check_with_lifecycle(stmt, &env, None, &mut lifecycle)?; } + Ok(()) } else { Err("Expected Program node".to_string()) } } + fn check_with_lifecycle( + node: &ASTNode, + env: &Rc>, + expected_return_type: Option<&Type>, + lifecycle: &mut QubitLifecycleManager, + ) -> Result { + match node { + + ASTNode::QuantumDeclaration { name, size, initial_state } => { + let register_size = if let Some(size_expr) = size { + if let ASTNode::IntLiteral(n) = &**size_expr { + *n as usize + } else { + 1 + } + } else { + 1 + }; + + lifecycle.register_qubits( + name, + register_size, + QubitState::Classical(false) + ); + + + Self::check(node, env, expected_return_type) + } + + + ASTNode::Apply { gate_expr, arguments, loc } => { + + let qubit_ids = Self::extract_qubit_ids(arguments)?; + let gate_name = Self::extract_gate_name(gate_expr)?; + let is_controlled = Self::is_controlled_gate(gate_expr); + + if is_controlled { + let num_controls = Self::count_controls(gate_expr); + let (controls, targets) = qubit_ids.split_at(num_controls); + lifecycle.record_controlled_gate(controls, targets, &gate_name, *loc) + .map_err(|e| e.to_string())?; + } else { + for qubit_id in &qubit_ids { + lifecycle.record_operation( + qubit_id, + QubitOperation::ApplyGate(gate_name.clone()), + *loc + ).map_err(|e| e.to_string())?; + } + } + + + Self::check(node, env, expected_return_type) + } + + + ASTNode::Measure(qubit_expr) => { + let qubit_ids = Self::extract_qubit_ids(&[*qubit_expr.clone()])?; + let loc = Self::get_node_location(qubit_expr); + + for qubit_id in &qubit_ids { + lifecycle.record_operation( + qubit_id, + QubitOperation::Measure, + loc + ).map_err(|e| e.to_string())?; + } + + Self::check(node, env, expected_return_type) + } + + + ASTNode::LetDeclaration { name, type_annotation, value, is_mutable, .. } => { + + let value_type = Self::check_with_lifecycle(value, env, None, lifecycle)?; + + + let final_type = match type_annotation { + Some(expected_type) => { + if value_type != *expected_type && value_type != Type::None && *expected_type != Type::Any && value_type != Type::Any { + return Err(format!("Type Error: Variable '{}' annotated as {:?} but got {:?}", name, expected_type, value_type)); + } + expected_type.clone() + } + None => value_type, + }; + + env.borrow_mut().set(name.clone(), TypeInfo { var_type: final_type, is_mutable: *is_mutable }); + Ok(Type::None) + } + + + ASTNode::Block(statements) => { + for stmt in statements { + Self::check_with_lifecycle(stmt, env, expected_return_type, lifecycle)?; + } + Ok(Type::None) + } + + + ASTNode::If { condition, then_block, elif_blocks, else_block } => { + + let cond_type = Self::check(condition, env, None)?; + if cond_type != Type::Bool { + return Err(format!("Type Error: 'if' condition must be Bool, got {:?}", cond_type)); + } + + + let then_type = Self::check_with_lifecycle(then_block, env, expected_return_type, lifecycle)?; + + for (elif_cond, elif_body) in elif_blocks { + let elif_cond_type = Self::check(elif_cond, env, None)?; + if elif_cond_type != Type::Bool { + return Err(format!("Type Error: 'elif' condition must be Bool, got {:?}", elif_cond_type)); + } + Self::check_with_lifecycle(elif_body, env, expected_return_type, lifecycle)?; + } + + if let Some(else_body) = else_block { + Self::check_with_lifecycle(else_body, env, expected_return_type, lifecycle)?; + } + Ok(then_type) + } + + + ASTNode::While { condition, body } => { + let cond_type = Self::check(condition, env, None)?; + if cond_type != Type::Bool { + return Err(format!("Type Error: 'while' condition must be Bool, got {:?}", cond_type)); + } + Self::check_with_lifecycle(body, env, expected_return_type, lifecycle)?; + Ok(Type::None) + } + + + ASTNode::For { variable, iterator, body } => { + + let iterator_type = Self::check(iterator, env, None)?; + let element_type = match iterator_type { + Type::Array(inner) => *inner, + Type::String => Type::String, + Type::Dict => Type::String, + Type::Custom(name) if name == "range" => Type::Int, + Type::Any => Type::Any, + _ => return Err(format!("Type Error: Cannot iterate over {:?}", iterator_type)), + }; + + + let loop_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); + loop_env.borrow_mut().set(variable.clone(), TypeInfo { var_type: element_type, is_mutable: false }); + + + Self::check_with_lifecycle(body, &loop_env, expected_return_type, lifecycle)?; + Ok(Type::None) + } + + + ASTNode::Return(value_expr) => { + if let Some(expr) = value_expr { + + let _ = Self::check_with_lifecycle(expr, env, None, lifecycle)?; + } + + Self::check(node, env, expected_return_type) + } + + + _ => Self::check(node, env, expected_return_type) + } + } + + + fn extract_qubit_ids(arguments: &[ASTNode]) -> Result, String> { + let mut ids = Vec::new(); + + for arg in arguments { + match arg { + ASTNode::ArrayAccess { array, index, .. } => { + if let ASTNode::Identifier { name, .. } = &**array { + if let ASTNode::IntLiteral(idx) = &**index { + ids.push(QubitId::new(name.clone(), *idx as usize)); + } + } + } + ASTNode::Identifier { name, .. } => { + + ids.push(QubitId::new(name.clone(), 0)); + } + _ => {} + } + } + + Ok(ids) + } + + + fn extract_gate_name(gate_expr: &ASTNode) -> Result { + match gate_expr { + ASTNode::Gate { name, .. } => Ok(name.clone()), + ASTNode::ParameterizedGate { name, .. } => Ok(name.clone()), + ASTNode::Controlled { gate_expr, .. } => Self::extract_gate_name(gate_expr), + ASTNode::Dagger { gate_expr, .. } => Self::extract_gate_name(gate_expr), + _ => Err("Invalid gate expression".to_string()) + } + } + + + fn is_controlled_gate(gate_expr: &ASTNode) -> bool { + matches!(gate_expr, ASTNode::Controlled { .. }) || + matches!(gate_expr, ASTNode::Dagger { gate_expr: inner, .. } + if Self::is_controlled_gate(inner)) + } + + + fn count_controls(gate_expr: &ASTNode) -> usize { + match gate_expr { + ASTNode::Controlled { gate_expr, .. } => 1 + Self::count_controls(gate_expr), + ASTNode::Dagger { gate_expr, .. } => Self::count_controls(gate_expr), + _ => 0 + } + } + + + fn get_node_location(node: &ASTNode) -> Loc { + match node { + ASTNode::ArrayAccess { loc, .. } => *loc, + ASTNode::Identifier { loc, .. } => *loc, + _ => Loc { line: 0, column: 0 } + } + } fn immutable_info(t: Type) -> TypeInfo { TypeInfo { @@ -380,19 +616,19 @@ impl TypeChecker { fn check_module(path: &ImportPath) -> Result, String> { let file_path = match path { - // This is our new logic + ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - // It's a file path + f.clone() } else { - // It's a package name, e.g., "math" - // We'll look for "q_packages//init.qc" + + format!("q_packages/{}/init.qc", f) } } ImportPath::Module(m) => { - // This logic is for 'import ai.neural', which becomes 'ai/neural.qc' + m.join("/") + ".qc" } }; @@ -438,37 +674,37 @@ impl TypeChecker { env: &Rc>, method_key: &str, ) -> Option { - // 1. Try direct lookup (if class is defined in the same file) + if let Some(info) = env.borrow().get(method_key) { return Some(info.var_type.clone()); } - // 2. Search inside imported modules (if class came from 'import') - // We traverse up the environment chain manually + + let mut current_env = Some(env.clone()); while let Some(env_rc) = current_env { let env_ref = env_rc.borrow(); - // Iterate over all variables in this scope + for info in env_ref.store.values() { - // Is this variable a Module? + if let Type::Module(module_map) = &info.var_type { - // Does this module contain the method we need? + if let Some(method_type) = module_map.get(method_key) { return Some(method_type.clone()); } } } - // Move up to outer scope + current_env = env_ref.outer.clone(); } None } - // --- *** FIXED check_gate_expression (Base Case Lookup) *** --- + fn check_gate_expression( node: &ASTNode, env: &Rc>, @@ -491,7 +727,7 @@ impl TypeChecker { } } - // This line is triggered if the gate name (like 'X') is not a function. + Err(format!( "Type Error at {}: Unknown quantum gate '{}'.", loc, name @@ -499,7 +735,7 @@ impl TypeChecker { } ASTNode::Dagger { gate_expr, .. } => { - // Daggering a gate doesn't change its signature, so we just check the inner expression. + let inner_gate_type = Self::check_gate_expression(gate_expr, env)?; if let Type::Function(..) = inner_gate_type { @@ -513,23 +749,23 @@ impl TypeChecker { } ASTNode::Controlled { gate_expr, loc } => { - // 1. Get the type of the inner gate + let inner_gate_type = Self::check_gate_expression(gate_expr, env)?; match inner_gate_type { Type::Function(mut params, ret_type) => { - // 2. The *first* argument of the inner gate is always the TARGET. - // We check the target type, then add a Qubit control before it. - // Ensure the innermost gate is a single-target gate + + + if params.is_empty() { return Err(format!("Type Error at {}: 'controlled' target gate must take at least one argument.", loc)); } - // Add the new control qubit before the target(s) + params.insert(0, Type::Qubit); - // The return type is the same + Ok(Type::Function(params, ret_type)) } _ => Err(format!( @@ -546,7 +782,7 @@ impl TypeChecker { } => { let gate_name_lower = name.to_lowercase(); - // 1. Look up the gate function type + let gate_info = env.borrow().get(&gate_name_lower).ok_or_else(|| { format!( "Type Error at {}: Unknown parameterized gate '{}'.", @@ -556,8 +792,8 @@ impl TypeChecker { match gate_info.var_type { Type::Function(param_types, return_type) => { - // 2. The first N parameters are the gate parameters (angles, etc.) - // The remaining parameters are the qubits + + if parameters.len() > param_types.len() { return Err(format!( @@ -566,12 +802,12 @@ impl TypeChecker { )); } - // 3. Type-check each parameter expression + for (i, param_expr) in parameters.iter().enumerate() { let param_type = Self::check(param_expr, env, None)?; let expected_type = ¶m_types[i]; - // Allow Int where Float is expected + if param_type != *expected_type && !(*expected_type == Type::Float && param_type == Type::Int) { @@ -582,7 +818,7 @@ impl TypeChecker { } } - // 4. Return a function type with the remaining (qubit) parameters + let remaining_params = param_types[parameters.len()..].to_vec(); Ok(Type::Function(remaining_params, return_type)) } @@ -597,14 +833,14 @@ impl TypeChecker { } } - /// The main recursive checking function. + pub fn check( node: &ASTNode, env: &Rc>, expected_return_type: Option<&Type>, ) -> Result { match node { - // --- Literals --- + ASTNode::IntLiteral(_) => Ok(Type::Int), ASTNode::FloatLiteral(_) => Ok(Type::Float), ASTNode::StringLiteral(_) => Ok(Type::String), @@ -614,7 +850,7 @@ impl TypeChecker { ASTNode::QuantumKet(_) => Ok(Type::QuantumRegister(Some(1))), ASTNode::QuantumBra(_) => Err("Bra notation is not yet supported.".to_string()), - // --- Declarations --- + ASTNode::LetDeclaration { name, type_annotation, @@ -631,7 +867,7 @@ impl TypeChecker { && *expected_type != Type::Any && value_type != Type::Any { - // --- FIX START: Expanded Compatibility Checks --- + let is_quantum_compat = matches!( (&value_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) @@ -648,7 +884,7 @@ impl TypeChecker { name, expected_type, value_type )); } - // --- FIX END --- + } expected_type.clone() } @@ -667,7 +903,7 @@ impl TypeChecker { let new_type = Self::check(value, env, Option::None)?; match target.as_ref() { - // Simple identifier assignment + ASTNode::Identifier { name, .. } => { let original_info = match env.borrow().get(name) { Some(info) => info, @@ -680,7 +916,7 @@ impl TypeChecker { original_info.var_type != Type::Any && new_type != Type::None && new_type != Type::Any { - // --- FIX START: Expanded Compatibility Checks --- + let is_quantum_compat = matches!( (&new_type, &original_info.var_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) @@ -697,17 +933,17 @@ impl TypeChecker { new_type, name, original_info.var_type )); } - // --- FIX END --- + } Ok(Type::None) } - // Dictionary/Array subscript assignment: dict["key"] = value + ASTNode::ArrayAccess { array, index, loc } => { let array_type = Self::check(array, env, Option::None)?; let index_type = Self::check(index, env, Option::None)?; match array_type { Type::Dict => { - // Check that the index is a valid key type + if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { @@ -716,18 +952,18 @@ impl TypeChecker { loc, index_type )); } - // For Dict, we allow any value type + Ok(Type::None) } Type::Array(inner_type) => { - // Check index is Int + if index_type != Type::Int { return Err(format!( "Type Error at {}: Array index must be Int, got {:?}", loc, index_type )); } - // Check value type matches array element type + if new_type != *inner_type && *inner_type != Type::Any { return Err(format!( "Type Error at {}: Cannot assign {:?} to array of {:?}", @@ -743,30 +979,30 @@ impl TypeChecker { } } ASTNode::MemberAccess { object, member } => { - // Check the object (e.g., 'self' or 'my_obj') + let object_type = Self::check(object, env, Option::None)?; - // We only allow assignment if it's an Instance or Custom type + let class_name = match object_type { Type::Instance(name) | Type::Custom(name) => name, _ => return Err(format!("Type Error: Cannot assign to member '{}' of non-object type {:?}", member, object_type)), }; - // Construct key "ClassName::Field" + let field_key = format!("{}::{}", class_name, member); - // Look up field info + if let Some(field_info) = env.borrow().get(&field_key) { - // Check mutability + if !field_info.is_mutable { return Err(format!("Mutability Error: Field '{}.{}' is immutable.", class_name, member)); } - // Check type compatibility + if field_info.var_type != new_type && field_info.var_type != Type::Any { - // Basic compatibility check + let is_compat = matches!((&new_type, &field_info.var_type), - (Type::Int, Type::Float) | // Allow Int -> Float + (Type::Int, Type::Float) | (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); @@ -779,9 +1015,9 @@ impl TypeChecker { } Ok(Type::None) } else { - // Special case: If inside 'init', we might be initializing a field. - // But strictly speaking, fields should be registered by ClassDeclaration. - // If this fails, it means the field wasn't declared in the class body. + + + Err(format!("Type Error: Class '{}' has no field named '{}'", class_name, member)) } } @@ -792,7 +1028,7 @@ impl TypeChecker { match env.borrow().get(name) { Some(info) => Ok(info.var_type.clone()), None => { - // Collect all available variable names for suggestions + let mut all_names = Vec::new(); let mut current_env = Some(env.clone()); @@ -804,7 +1040,7 @@ impl TypeChecker { current_env = env_ref.outer.clone(); } - // Find similar names + let suggestions = find_similar_names(name, &all_names); let mut error_msg = format!("Undefined variable '{}'", name); @@ -823,17 +1059,17 @@ impl TypeChecker { } } ASTNode::ClassDeclaration { name, fields, methods, constructor, .. } => { - // Register class type + let class_type = Type::Class(name.clone()); env.borrow_mut().set( name.clone(), TypeInfo { var_type: class_type, is_mutable: false } ); - // Create class environment for checking methods + let class_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); - // Add fields to class environment + for field in fields { class_env.borrow_mut().set( field.name.clone(), @@ -854,15 +1090,15 @@ impl TypeChecker { - // Check constructor if it exists + if let Some(constructor_node) = constructor { if let ASTNode::FunctionDeclaration { body, parameters, .. } = &**constructor_node { let constructor_env = Rc::new(RefCell::new( TypeEnvironment::new_enclosed(class_env.clone()) )); - // FIX 1: Register constructor signature globally as "ClassName::init" - // This allows checking calls like 'Canvas(800, 600)' + + let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); let ctor_type = Type::Function(param_types, Box::new(Type::None)); env.borrow_mut().set( @@ -870,7 +1106,7 @@ impl TypeChecker { TypeInfo { var_type: ctor_type, is_mutable: false } ); - // ... (keep existing environment setup) ... + constructor_env.borrow_mut().set( "self".to_string(), TypeInfo { @@ -879,7 +1115,7 @@ impl TypeChecker { } ); - // Add parameters + for param in parameters { constructor_env.borrow_mut().set( param.name.clone(), @@ -894,7 +1130,7 @@ impl TypeChecker { } } - // Check methods + for method in methods { let method_param_types: Vec = method.parameters.iter() @@ -902,10 +1138,10 @@ impl TypeChecker { .collect(); let method_return_type = method.return_type.clone().unwrap_or(Type::None); - // Create function type + let method_func_type = Type::Function(method_param_types, Box::new(method_return_type)); - // Store as "ClassName::MethodName" + let global_method_key = format!("{}::{}", name, method.name); env.borrow_mut().set( global_method_key, @@ -923,7 +1159,7 @@ impl TypeChecker { } ); - // Add parameters + for param in &method.parameters { method_env.borrow_mut().set( param.name.clone(), @@ -942,7 +1178,7 @@ impl TypeChecker { } ASTNode::NewInstance { class_name, arguments, loc } => { - // Check if class exists + let class_info = env.borrow().get(class_name) .ok_or_else(|| format!("Type Error at {}: Unknown class '{}'", loc, class_name))?; @@ -950,7 +1186,7 @@ impl TypeChecker { return Err(format!("Type Error at {}: '{}' is not a class", loc, class_name)); } - // Type check arguments (simplified - would need constructor signature) + for arg in arguments { Self::check(arg, env, None)?; } @@ -967,16 +1203,16 @@ impl TypeChecker { let object_type = Self::check(object, env, None)?; match object_type { - // Case 1: Method call on a Class Instance (e.g., my_obj.method()) + Type::Instance(class_name) | Type::Custom(class_name) => { - // 1. Construct the lookup key: "ClassName::MethodName" + let method_key = format!("{}::{}", class_name, method_name); - // 2. Look up the method signature (using the new helper) - // FIX: Use resolve_method instead of direct get() + + if let Some(method_type) = Self::resolve_method(env, &method_key) { if let Type::Function(param_types, return_type) = method_type { - // 3. Validate Argument Count + if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Method '{}.{}' expected {} arguments, but got {}", @@ -984,7 +1220,7 @@ impl TypeChecker { )); } - // 4. Validate Argument Types + for (i, arg_node) in arguments.iter().enumerate() { let arg_type = Self::check(arg_node, env, None)?; let expected_type = ¶m_types[i]; @@ -993,7 +1229,7 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - // Compatibility Checks + let is_quantum_compat = matches!( (&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) @@ -1017,7 +1253,7 @@ impl TypeChecker { } } - // 5. Return the REAL return type + Ok(*return_type) } else { Ok(Type::Any) @@ -1030,14 +1266,14 @@ impl TypeChecker { } } - // Case 2: Function call on a Module (e.g., math.abs(x)) - // + + Type::Module(module_types) => { if let Some(member_type) = module_types.get(method_name) { match member_type { - // Sub-case A: Calling a Function exported by the module + Type::Function(param_types, return_type) => { - // Validate argument count + if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Module function '{}.{}' expected {} arguments, but got {}", @@ -1045,7 +1281,7 @@ impl TypeChecker { )); } - // Validate argument types + for (i, arg_node) in arguments.iter().enumerate() { let arg_type = Self::check(arg_node, env, Option::None)?; let expected_type = ¶m_types[i]; @@ -1054,7 +1290,7 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - // --- COMPATIBILITY CHECKS --- + let is_quantum_compat = matches!((&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None))); let is_class_compat = match (&arg_type, expected_type) { (Type::Instance(got), Type::Custom(expected)) => got == expected, @@ -1081,9 +1317,9 @@ impl TypeChecker { Ok(*return_type.clone()) } - // Sub-case B: Instantiating a Class exported by the module (FIX for 'graphics.Canvas(...)') + Type::Class(class_name) => { - // 1. Look for constructor "ClassName::init" IN THE MODULE TYPES + let init_key = format!("{}::init", class_name); let constructor_params = if let Some(ctor_type) = module_types.get(&init_key) { if let Type::Function(params, _) = ctor_type { @@ -1095,7 +1331,7 @@ impl TypeChecker { vec![] }; - // 2. Validate Args + if arguments.len() != constructor_params.len() { return Err(format!( "Type Error at {}: Constructor for '{}.{}' expected {} arguments, but got {}", @@ -1111,7 +1347,7 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - // Compatibility checks + let is_quantum_compat = matches!((&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None))); let is_class_compat = match (&arg_type, expected_type) { (Type::Instance(got), Type::Custom(expected)) => got == expected, @@ -1131,7 +1367,7 @@ impl TypeChecker { } } } - // Return Instance of the class + Ok(Type::Instance(class_name.clone())) } @@ -1156,8 +1392,8 @@ impl TypeChecker { } ASTNode::SelfRef { loc } => { - // Would need to track current class context - // For now, return error + + Err(format!("Type Error at {}: 'self' can only be used inside class methods", loc)) } @@ -1173,7 +1409,7 @@ impl TypeChecker { match callee_type { Type::Class(class_name) => { - // 1. Look for explicit constructor "ClassName::init" + let init_key = format!("{}::init", class_name); let constructor_params = if let Some(ctor_info) = env.borrow().get(&init_key) { if let Type::Function(params, _) = ctor_info.var_type { @@ -1182,11 +1418,11 @@ impl TypeChecker { vec![] } } else { - // No constructor defined: Expect 0 arguments + vec![] }; - // 2. Validate Argument Count + if arguments.len() != constructor_params.len() { return Err(format!( "Type Error at {}: Constructor for '{}' expected {} arguments, but got {}", @@ -1194,7 +1430,7 @@ impl TypeChecker { )); } - // 3. Validate Argument Types + for (i, arg_node) in arguments.iter().enumerate() { let arg_type = Self::check(arg_node, env, Option::None)?; let expected_type = &constructor_params[i]; @@ -1203,7 +1439,7 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - // Compatibility checks (Quantum, Class, Dict) + let is_quantum_compat = matches!( (&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) @@ -1214,7 +1450,7 @@ impl TypeChecker { _ => false, }; - // FIX: Added Dict Compatibility + let is_dict_compat = match (&arg_type, expected_type) { (Type::Dict, Type::Custom(name)) if name == "Dict" => true, (Type::Custom(name), Type::Dict) if name == "Dict" => true, @@ -1230,23 +1466,23 @@ impl TypeChecker { } } - // 4. Return the Instance Type + Ok(Type::Instance(class_name)) } Type::Custom(ref name) if name == "Function" => { - // "Function" is a generic type, so we can't validate argument count or types. - // We just check that the arguments themselves are valid expressions. + + for arg in arguments { Self::check(arg, env, Option::None)?; } - // We assume a generic Function returns Any + Ok(Type::Any) } Type::Function(param_types, return_type) => { if arguments.len() > 0 && param_types.len() == 0 { - // Hack for 'print' + } else if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Function '{}' expected {} arguments, but got {}", @@ -1266,14 +1502,14 @@ impl TypeChecker { if arg_type != *expected_type && *expected_type != Type::Any && arg_type != Type::Any { - // --- FIX START: Expanded Compatibility Checks --- + let is_quantum_compat = matches!( (&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); let is_func_compat = match (&arg_type, expected_type) { - // Allow passing any specific Function to a parameter expecting generic "Function" + (Type::Function(..), Type::Custom(name)) if name == "Function" => true, _ => false, }; @@ -1291,7 +1527,7 @@ impl TypeChecker { loc, (i + 1), expected_type, arg_type )); } - // --- FIX END --- + } } Ok(*return_type) @@ -1324,7 +1560,7 @@ impl TypeChecker { if t1 == t2 { return Ok(Type::Array(t1)); } else { - return Ok(Type::Array(Box::new(Type::Any))); // Mixed types + return Ok(Type::Array(Box::new(Type::Any))); } } _ => return Err(format!( @@ -1342,7 +1578,7 @@ impl TypeChecker { (Type::Int, Type::Float) => Ok(Type::Float), (Type::Float, Type::Int) => Ok(Type::Float), (Type::String, Type::String) => Ok(Type::String), - // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Cannot add types {:?} and {:?}", loc, left_type, right_type)), } @@ -1353,7 +1589,7 @@ impl TypeChecker { (Type::Float, Type::Float) => Ok(Type::Float), (Type::Int, Type::Float) => Ok(Type::Float), (Type::Float, Type::Int) => Ok(Type::Float), - // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Cannot perform arithmetic on types {:?} and {:?}", loc, left_type, right_type)), } @@ -1364,7 +1600,7 @@ impl TypeChecker { (Type::Float, Type::Float) => Ok(Type::Float), (Type::Int, Type::Float) => Ok(Type::Float), (Type::Float, Type::Int) => Ok(Type::Float), - // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Power operator (^) requires numeric types, got {:?} and {:?}", loc, left_type, right_type)), } @@ -1373,7 +1609,7 @@ impl TypeChecker { BinaryOperator::Less | BinaryOperator::Greater | BinaryOperator::LessEqual | BinaryOperator::GreaterEqual => { match (&left_type, &right_type) { (Type::Int, Type::Int) | (Type::Float, Type::Float) => Ok(Type::Bool), - // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Bool), _ => Err(format!("Type Error at {}: Cannot perform ordered comparison on types {:?} and {:?}", loc, left_type, right_type)), } @@ -1381,7 +1617,7 @@ impl TypeChecker { BinaryOperator::And | BinaryOperator::Or => { match (&left_type, &right_type) { (Type::Bool, Type::Bool) => Ok(Type::Bool), - // FIX: Allow Any + (Type::Any, _) | (_, Type::Any) => Ok(Type::Bool), _ => Err(format!("Type Error at {}: Logical operators 'and'/'or' require two booleans, got {:?} and {:?}", loc, left_type, right_type)), } @@ -1451,7 +1687,7 @@ impl TypeChecker { let index_type = Self::check(index, env, Option::None)?; match array_type { - // 1. Quantum Register (Index must be Int) + Type::QuantumRegister(size_opt) => { if index_type != Type::Int { return Err(format!( @@ -1470,7 +1706,7 @@ impl TypeChecker { Ok(Type::Qubit) } - // 2. Arrays (Index must be Int) + Type::Array(inner_type) => { if index_type != Type::Int { return Err(format!( @@ -1481,7 +1717,7 @@ impl TypeChecker { Ok(*inner_type) } - // 3. Strings (Index must be Int) + Type::String => { if index_type != Type::Int { return Err(format!( @@ -1492,7 +1728,7 @@ impl TypeChecker { Ok(Type::String) } - // 4a. Built-in Dictionaries + Type::Dict => { if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { return Err(format!( @@ -1503,7 +1739,7 @@ impl TypeChecker { Ok(Type::Any) } - // 4b. Custom "Dict" Types (Legacy/Parsed as custom) + Type::Custom(ref name) if name == "Dict" => { if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { return Err(format!( @@ -1530,10 +1766,10 @@ impl TypeChecker { }; if let Some(class_name) = class_name_opt { - // Construct the lookup key: "ClassName::MemberName" + let field_key = format!("{}::{}", class_name, member); - // Look it up in the environment + if let Some(field_info) = env.borrow().get(&field_key) { return Ok(field_info.var_type.clone()); } else { @@ -1580,13 +1816,13 @@ impl TypeChecker { arguments, loc, } => { - // 1. Get the type of the gate expression + let gate_type = Self::check_gate_expression(gate_expr, env)?; - // 2. Check that it's a function + match gate_type { Type::Function(param_types, return_type) => { - // 3. Validate arguments + if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Gate requires {} arguments, but got {}", @@ -1608,7 +1844,7 @@ impl TypeChecker { } } - Ok(*return_type) // Usually Type::None + Ok(*return_type) } _ => Err(format!( "Type Error at {}: This expression is not a callable gate.", @@ -1617,15 +1853,15 @@ impl TypeChecker { } } - // --- *** NEW: Gate *** --- + ASTNode::Gate { .. } => { - // <-- Use '..' + Self::check_gate_expression(node, env) } - // --- *** NEW: Controlled *** --- + ASTNode::Controlled { gate_expr, loc } => { - // This node is checked via `check_gate_expression` + Self::check_gate_expression(node, env) } @@ -1669,7 +1905,7 @@ impl TypeChecker { Ok(Type::None) } - // ... (Other functions are unchanged) ... + ASTNode::FunctionDeclaration { name, parameters, @@ -1729,24 +1965,24 @@ impl TypeChecker { "Type Error: 'return' statement found outside of a function.".to_string(), ), Some(expected) => { - // FIX: Added '&& value_type != Type::Any' to allow dynamic types + if value_type != *expected && *expected != Type::Any && value_type != Type::Any { - // 1. Quantum Register Compatibility + let is_quantum_compat = matches!( (&value_type, expected), (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); - // 2. Class Instance Compatibility (Instance vs Custom) + let is_class_compat = match (&value_type, expected) { (Type::Instance(got), Type::Custom(expected_name)) => got == expected_name, _ => false, }; - // 3. FIX: Dict Compatibility (Dict vs Custom("Dict")) + let is_dict_compat = match (&value_type, expected) { (Type::Dict, Type::Custom(name)) if name == "Dict" => true, (Type::Custom(name), Type::Dict) if name == "Dict" => true, @@ -1806,7 +2042,7 @@ impl TypeChecker { )); } - // Return the special type that your 'for' loop check already expects + Ok(Type::Custom("range".to_string())) } From a4f0594b14939fea3b0e2da0e188075974a5397c Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:59:43 +0530 Subject: [PATCH 14/27] Add files via upload --- src/qubit_lifecycle.rs | 471 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 src/qubit_lifecycle.rs diff --git a/src/qubit_lifecycle.rs b/src/qubit_lifecycle.rs new file mode 100644 index 0000000..98bd9e1 --- /dev/null +++ b/src/qubit_lifecycle.rs @@ -0,0 +1,471 @@ +//src/qubit_lifecycle.rs +use std::collections::{HashMap, HashSet}; +use crate::parser::ast::Loc; + + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QubitState { + + Classical(bool), + + + Superposition, + + + Measured(bool), + + + Entangled(HashSet), + + + Invalid, + + + Reset, +} + + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct QubitId { + pub register_name: String, + pub index: usize, +} + +impl QubitId { + pub fn new(register_name: String, index: usize) -> Self { + QubitId { register_name, index } + } +} + + +#[derive(Debug, Clone, PartialEq)] +pub enum QubitOperation { + Initialize, + ApplyGate(String), + ApplyControlledGate(String), + Measure, + Reset, + Entangle(Vec), +} + + +#[derive(Debug, Clone)] +pub enum LifecycleError { + + UsedAfterMeasurement { + qubit: QubitId, + loc: Loc, + measured_at: Loc, + }, + + + DoubleMeasurement { + qubit: QubitId, + loc: Loc, + first_measurement: Loc, + }, + + + UninitializedQubit { + qubit: QubitId, + loc: Loc, + }, + + + InvalidEntangledOperation { + qubit: QubitId, + entangled_with: Vec, + operation: String, + loc: Loc, + }, + + + InvalidState { + qubit: QubitId, + state: String, + operation: String, + loc: Loc, + }, + + + ClassicalQuantumMixing { + qubit: QubitId, + loc: Loc, + explanation: String, + }, +} + +impl LifecycleError { + pub fn to_string(&self) -> String { + match self { + LifecycleError::UsedAfterMeasurement { qubit, loc, measured_at } => { + format!( + "Lifecycle Error at {}: Qubit '{}[{}]' was used after being measured (measured at {}). \ + Once a qubit is measured, it collapses to a classical state and cannot be used in quantum operations.", + loc, qubit.register_name, qubit.index, measured_at + ) + } + LifecycleError::DoubleMeasurement { qubit, loc, first_measurement } => { + format!( + "Lifecycle Error at {}: Qubit '{}[{}]' was measured twice (first measurement at {}). \ + A qubit can only be measured once per computation path.", + loc, qubit.register_name, qubit.index, first_measurement + ) + } + LifecycleError::UninitializedQubit { qubit, loc } => { + format!( + "Lifecycle Error at {}: Qubit '{}[{}]' is used before initialization. \ + All qubits must be initialized before use.", + loc, qubit.register_name, qubit.index + ) + } + LifecycleError::InvalidEntangledOperation { qubit, entangled_with, operation, loc } => { + let entangled_list: Vec = entangled_with + .iter() + .map(|q| format!("{}[{}]", q.register_name, q.index)) + .collect(); + format!( + "Lifecycle Error at {}: Cannot perform '{}' on qubit '{}[{}]' because it is entangled with {}. \ + Operations on entangled qubits must consider the entire entangled system.", + loc, operation, qubit.register_name, qubit.index, entangled_list.join(", ") + ) + } + LifecycleError::InvalidState { qubit, state, operation, loc } => { + format!( + "Lifecycle Error at {}: Cannot perform '{}' on qubit '{}[{}]' in state '{}'. \ + The qubit is in an invalid state for this operation.", + loc, operation, qubit.register_name, qubit.index, state + ) + } + LifecycleError::ClassicalQuantumMixing { qubit, loc, explanation } => { + format!( + "Lifecycle Error at {}: Incorrect mixing of classical and quantum operations on qubit '{}[{}]'. {}", + loc, qubit.register_name, qubit.index, explanation + ) + } + } + } +} + + +pub struct QubitLifecycleManager { + + qubit_states: HashMap, + + + measurement_locations: HashMap, + + + operation_history: HashMap>, + + + entanglement_groups: Vec>, + + + strict_mode: bool, +} + +impl QubitLifecycleManager { + pub fn new(strict_mode: bool) -> Self { + QubitLifecycleManager { + qubit_states: HashMap::new(), + measurement_locations: HashMap::new(), + operation_history: HashMap::new(), + entanglement_groups: Vec::new(), + strict_mode, + } + } + + + pub fn register_qubits( + &mut self, + register_name: &str, + size: usize, + initial_state: QubitState, + ) { + for i in 0..size { + let qubit_id = QubitId::new(register_name.to_string(), i); + self.qubit_states.insert(qubit_id.clone(), initial_state.clone()); + self.operation_history.insert(qubit_id, Vec::new()); + } + } + + + pub fn check_operation( + &self, + qubit: &QubitId, + operation: &QubitOperation, + loc: Loc, + ) -> Result<(), LifecycleError> { + let state = self.qubit_states.get(qubit).ok_or_else(|| { + LifecycleError::UninitializedQubit { + qubit: qubit.clone(), + loc, + } + })?; + + match (state, operation) { + + (QubitState::Measured(_), QubitOperation::ApplyGate(gate_name)) => { + if let Some(measured_loc) = self.measurement_locations.get(qubit) { + return Err(LifecycleError::UsedAfterMeasurement { + qubit: qubit.clone(), + loc, + measured_at: *measured_loc, + }); + } + Err(LifecycleError::InvalidState { + qubit: qubit.clone(), + state: "measured".to_string(), + operation: gate_name.clone(), + loc, + }) + } + + + (QubitState::Measured(_), QubitOperation::Measure) => { + if let Some(first_loc) = self.measurement_locations.get(qubit) { + return Err(LifecycleError::DoubleMeasurement { + qubit: qubit.clone(), + loc, + first_measurement: *first_loc, + }); + } + Ok(()) + } + + + (QubitState::Invalid, _) => Err(LifecycleError::InvalidState { + qubit: qubit.clone(), + state: "invalid".to_string(), + operation: format!("{:?}", operation), + loc, + }), + + + (QubitState::Entangled(entangled_set), QubitOperation::ApplyGate(gate_name)) + if self.strict_mode => + { + Err(LifecycleError::InvalidEntangledOperation { + qubit: qubit.clone(), + entangled_with: entangled_set.iter().cloned().collect(), + operation: gate_name.clone(), + loc, + }) + } + + + _ => Ok(()), + } + } + + + pub fn record_operation( + &mut self, + qubit: &QubitId, + operation: QubitOperation, + loc: Loc, + ) -> Result<(), LifecycleError> { + + self.check_operation(qubit, &operation, loc)?; + + + match &operation { + QubitOperation::ApplyGate(gate_name) => { + + if gate_name.to_lowercase() == "x" || gate_name.to_lowercase() == "not" { + + if let Some(QubitState::Classical(bit)) = self.qubit_states.get(qubit) { + self.qubit_states.insert(qubit.clone(), QubitState::Classical(!bit)); + } else { + self.qubit_states.insert(qubit.clone(), QubitState::Superposition); + } + } else { + self.qubit_states.insert(qubit.clone(), QubitState::Superposition); + } + } + + QubitOperation::Measure => { + + self.qubit_states.insert(qubit.clone(), QubitState::Measured(false)); + self.measurement_locations.insert(qubit.clone(), loc); + } + + QubitOperation::Reset => { + + self.qubit_states.insert(qubit.clone(), QubitState::Reset); + self.measurement_locations.remove(qubit); + } + + QubitOperation::Entangle(qubits) => { + + let mut group = HashSet::new(); + for q in qubits { + group.insert(q.clone()); + self.qubit_states.insert(q.clone(), QubitState::Entangled(group.clone())); + } + self.entanglement_groups.push(group); + } + + _ => {} + } + + + if let Some(history) = self.operation_history.get_mut(qubit) { + history.push((operation, loc)); + } + + Ok(()) + } + + + pub fn record_controlled_gate( + &mut self, + control_qubits: &[QubitId], + target_qubits: &[QubitId], + gate_name: &str, + loc: Loc, + ) -> Result<(), LifecycleError> { + + for qubit in control_qubits.iter().chain(target_qubits.iter()) { + self.check_operation( + qubit, + &QubitOperation::ApplyControlledGate(gate_name.to_string()), + loc, + )?; + } + + + let mut all_qubits: Vec = control_qubits.to_vec(); + all_qubits.extend(target_qubits.iter().cloned()); + + let entangle_op = QubitOperation::Entangle(all_qubits.clone()); + for qubit in &all_qubits { + self.record_operation(qubit, entangle_op.clone(), loc)?; + } + + Ok(()) + } + + + pub fn get_state(&self, qubit: &QubitId) -> Option<&QubitState> { + self.qubit_states.get(qubit) + } + + + pub fn get_history(&self, qubit: &QubitId) -> Option<&Vec<(QubitOperation, Loc)>> { + self.operation_history.get(qubit) + } + + + pub fn is_measured(&self, qubit: &QubitId) -> bool { + matches!(self.qubit_states.get(qubit), Some(QubitState::Measured(_))) + } + + + pub fn is_entangled(&self, qubit: &QubitId) -> bool { + matches!(self.qubit_states.get(qubit), Some(QubitState::Entangled(_))) + } + + + pub fn get_entangled_qubits(&self, qubit: &QubitId) -> Vec { + if let Some(QubitState::Entangled(group)) = self.qubit_states.get(qubit) { + group.iter().cloned().collect() + } else { + Vec::new() + } + } + + + pub fn print_summary(&self) { + println!("=== Qubit Lifecycle Summary ==="); + + let mut qubits: Vec<_> = self.qubit_states.keys().collect(); + qubits.sort_by(|a, b| { + a.register_name.cmp(&b.register_name) + .then(a.index.cmp(&b.index)) + }); + + for qubit in qubits { + if let Some(state) = self.qubit_states.get(qubit) { + println!("{}[{}]: {:?}", qubit.register_name, qubit.index, state); + + if let Some(history) = self.operation_history.get(qubit) { + if !history.is_empty() { + println!(" History: {} operations", history.len()); + } + } + } + } + + if !self.entanglement_groups.is_empty() { + println!("\nEntanglement Groups:"); + for (i, group) in self.entanglement_groups.iter().enumerate() { + let qubits: Vec = group + .iter() + .map(|q| format!("{}[{}]", q.register_name, q.index)) + .collect(); + println!(" Group {}: {}", i + 1, qubits.join(", ")); + } + } + + println!("==============================="); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic_lifecycle() { + let mut manager = QubitLifecycleManager::new(true); + + + manager.register_qubits("q", 1, QubitState::Classical(false)); + + let q0 = QubitId::new("q".to_string(), 0); + let loc = Loc { line: 1, column: 1 }; + + + assert!(manager.record_operation(&q0, QubitOperation::ApplyGate("H".to_string()), loc).is_ok()); + + + assert!(manager.record_operation(&q0, QubitOperation::Measure, loc).is_ok()); + + + assert!(manager.record_operation(&q0, QubitOperation::ApplyGate("X".to_string()), loc).is_err()); + } + + #[test] + fn test_double_measurement() { + let mut manager = QubitLifecycleManager::new(true); + manager.register_qubits("q", 1, QubitState::Classical(false)); + + let q0 = QubitId::new("q".to_string(), 0); + let loc = Loc { line: 1, column: 1 }; + + + assert!(manager.record_operation(&q0, QubitOperation::Measure, loc).is_ok()); + + + assert!(manager.record_operation(&q0, QubitOperation::Measure, loc).is_err()); + } + + #[test] + fn test_entanglement() { + let mut manager = QubitLifecycleManager::new(true); + manager.register_qubits("q", 2, QubitState::Classical(false)); + + let q0 = QubitId::new("q".to_string(), 0); + let q1 = QubitId::new("q".to_string(), 1); + let loc = Loc { line: 1, column: 1 }; + + + assert!(manager.record_controlled_gate(&[q0.clone()], &[q1.clone()], "CNOT", loc).is_ok()); + + + assert!(manager.is_entangled(&q0)); + assert!(manager.is_entangled(&q1)); + } +} \ No newline at end of file From a4b0efd5db612884d35c87f24cee27a2a4259821 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Thu, 11 Dec 2025 12:44:58 +0530 Subject: [PATCH 15/27] Native backend as Default for All the quantum programs --- src/evaluator/mod.rs | 294 +++++---- src/main.rs | 47 +- src/quantum_backend/native_simulator.rs | 759 +++++++++++++----------- 3 files changed, 647 insertions(+), 453 deletions(-) diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 75a1d6b..d5e7256 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -20,12 +20,14 @@ type C64 = Complex; use crate::graphics::{Canvas, Color, Plot, PlotType}; use std::sync::Mutex; use std::io::Write; +type QubitMap = HashMap; lazy_static::lazy_static! { static ref INTERPRETER_CANVASES: Mutex> = Mutex::new(HashMap::new()); static ref INTERPRETER_PLOTS: Mutex> = Mutex::new(HashMap::new()); static ref NEXT_ID: Mutex = Mutex::new(0); } use crate::qubit_lifecycle::{QubitLifecycleManager, QubitId, QubitOperation, QubitState}; +use crate::quantum_backend::native_simulator::NativeSimulator; pub struct Evaluator; @@ -851,6 +853,27 @@ impl Evaluator { } } + fn extract_gate_info_runtime(gate_expr: &ASTNode) -> Result<(String, bool), String> { + match gate_expr { + ASTNode::Gate { name, .. } => Ok((name.to_lowercase(), false)), + ASTNode::ParameterizedGate { name, .. } => Ok((name.to_lowercase(), false)), + + // Toggle dagger flag if we see 'dagger' + ASTNode::Dagger { gate_expr, .. } => { + let (name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; + Ok((name, !is_dagger)) + }, + + // Prepend 'c' for controlled gates (e.g. "x" -> "cx") + ASTNode::Controlled { gate_expr, .. } => { + let (name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; + Ok((format!("c{}", name), is_dagger)) + }, + + _ => Err("Invalid gate expression".to_string()) + } + } + fn extract_gate_params( node: &ASTNode, params: &mut Vec, @@ -2921,84 +2944,88 @@ impl Evaluator { INTERPRETER_PLOTS.lock().unwrap().remove(&id); Ok(RuntimeValue::None) } - pub fn evaluate_with_lifecycle( - node: &ASTNode, - env: &Rc>, - lifecycle: &mut QubitLifecycleManager, - ) -> Result { - match node { - ASTNode::Measure(target_expr) => { - let target_val = Self::evaluate(target_expr, env)?; - - // Extract qubit ID - if let RuntimeValue::Qubit { state, index, .. } = target_val { - // Check lifecycle - let qubit_id = QubitId::new("runtime".to_string(), index); - lifecycle.record_operation( - &qubit_id, - QubitOperation::Measure, - Loc { line: 0, column: 0 } - ).map_err(|e| e.to_string())?; - } - // Perform measurement - Self::eval_measure(target_expr, env) - } - _ => Self::evaluate(node, env) - } - } pub fn evaluate_program_with_lifecycle( - program: &ASTNode, + node: &ASTNode, env: &Rc>, lifecycle: &mut QubitLifecycleManager, + simulator: &mut NativeSimulator, ) -> Result { - if let ASTNode::Program(statements) = program { - let mut last_result = RuntimeValue::None; - for stmt in statements { - last_result = Self::evaluate_with_lifecycle_recursive(stmt, env, lifecycle)?; + let mut qubit_map: QubitMap = HashMap::new(); + let mut next_alloc = 0; + + Self::eval_recursive(node, env, lifecycle, simulator, &mut qubit_map, &mut next_alloc) + } + fn resolve_qubits(args: &[ASTNode], map: &QubitMap) -> Result, String> { + let mut indices = Vec::new(); + for arg in args { + if let ASTNode::ArrayAccess { array, index, .. } = arg { + if let ASTNode::Identifier { name, .. } = &**array { + if let ASTNode::IntLiteral(idx) = &**index { + if let Some(start) = map.get(name) { + indices.push(start + (*idx as usize)); + } + } + } } - Ok(last_result) - } else { - Err("Expected ASTNode::Program at root.".to_string()) } + Ok(indices) } - fn evaluate_with_lifecycle_recursive( + fn eval_recursive( node: &ASTNode, env: &Rc>, lifecycle: &mut QubitLifecycleManager, + simulator: &mut NativeSimulator, + qubit_map: &mut QubitMap, + next_alloc: &mut usize, ) -> Result { match node { - // 1. Quantum Declarations (Intercepted) - ASTNode::QuantumDeclaration { name, size, initial_state } => { - let register_size = if let Some(size_expr) = size { - let size_val = Self::evaluate(size_expr, env)?; - match size_val { - RuntimeValue::Int(n) if n > 0 => n as usize, + + ASTNode::Program(statements) => { + let mut last = RuntimeValue::None; + for stmt in statements { + last = Self::eval_recursive(stmt, env, lifecycle, simulator, qubit_map, next_alloc)?; + if let RuntimeValue::ReturnValue(_) = last { + return Ok(last); + } + } + Ok(last) + } + ASTNode::QuantumDeclaration { name, size, .. } => { + let count = if let Some(size_expr) = size { + match Self::evaluate(size_expr, env)? { + RuntimeValue::Int(n) => n as usize, _ => 1, } } else { 1 }; - lifecycle.register_qubits( - name, - register_size, - QubitState::Classical(false) - ); - Self::eval_quantum_declaration(name, size, initial_state, env) - } + qubit_map.insert(name.clone(), *next_alloc); + *next_alloc += count; + + lifecycle.register_qubits(name, count, QubitState::Classical(false)); + let dummy_state = Self::default_state_vector(count)?; + let register = RuntimeValue::QuantumRegister { + size: count, + state: Rc::new(RefCell::new(dummy_state)), + }; + env.borrow_mut().set(name.clone(), register); + + Ok(RuntimeValue::None) + } ASTNode::Apply { gate_expr, arguments, loc } => { let qubit_ids = Self::extract_qubit_ids_runtime(arguments, env)?; - let gate_name = Self::extract_gate_name_runtime(gate_expr)?; - let is_controlled = Self::is_controlled_gate(gate_expr); - if is_controlled { - let num_controls = Self::count_controls(gate_expr); - let (controls, targets) = qubit_ids.split_at(num_controls); - lifecycle.record_controlled_gate(controls, targets, &gate_name, *loc) + let (gate_name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; + + if Self::is_controlled_gate(gate_expr) { + let num_controls = Self::count_controls(gate_expr); + let (controls, targets) = qubit_ids.split_at(num_controls); + lifecycle.record_controlled_gate(controls, targets, &gate_name, *loc) .map_err(|e| e.to_string())?; } else { for qubit_id in &qubit_ids { @@ -3006,91 +3033,70 @@ impl Evaluator { .map_err(|e| e.to_string())?; } } - Self::eval_apply_statement(gate_expr, arguments, loc, env) + + let target_indices = Self::resolve_qubits(arguments, qubit_map)?; + + let mut params = Vec::new(); + Self::extract_gate_params(gate_expr, &mut params, env)?; + + simulator.apply_gate(&gate_name, &target_indices, ¶ms, is_dagger) + .map_err(|e| format!("Simulator Error: {}", e))?; + + Ok(RuntimeValue::None) } ASTNode::Measure(qubit_expr) => { + // 1. Lifecycle Check let qubit_ids = Self::extract_qubit_ids_runtime(&[*qubit_expr.clone()], env)?; let loc = Self::get_node_location(qubit_expr); - for qubit_id in &qubit_ids { lifecycle.record_operation(qubit_id, QubitOperation::Measure, loc) .map_err(|e| e.to_string())?; } - Self::eval_measure(qubit_expr, env) - } + let indices = Self::resolve_qubits(&[*qubit_expr.clone()], qubit_map)?; + if indices.is_empty() { + return Err("Runtime Error: Could not resolve qubit for measurement.".to_string()); + } + let target = indices[0]; - ASTNode::LetDeclaration { name, value, .. } => { - let val = Self::evaluate_with_lifecycle_recursive(value, env, lifecycle)?; - env.borrow_mut().set(name.clone(), val); - Ok(RuntimeValue::None) - } - - // 5. Assignment (Recurse into value) - ASTNode::Assignment { target, value } => { - let new_value = Self::evaluate_with_lifecycle_recursive(value, env, lifecycle)?; + let result = simulator.measure_single(target); - match &**target { - ASTNode::Identifier { name, .. } => { - if let Some(var_rc) = env.borrow().get(name) { - *var_rc.borrow_mut() = new_value; - Ok(RuntimeValue::None) - } else { - Err(format!("Runtime Error: Undefined variable '{}'.", name)) - } - } - - _ => { - Self::eval_assignment(target, value, env) - } - } + Ok(RuntimeValue::Int(result as i64)) } - // 6. Block (Recurse) ASTNode::Block(statements) => { - let mut last_result = RuntimeValue::None; + let mut last = RuntimeValue::None; for stmt in statements { - last_result = Self::evaluate_with_lifecycle_recursive(stmt, env, lifecycle)?; - match last_result { - RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => { - return Ok(last_result); - } + last = Self::eval_recursive(stmt, env, lifecycle, simulator, qubit_map, next_alloc)?; + match last { + RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => return Ok(last), _ => {} } } - Ok(last_result) + Ok(last) } - // 7. If Statement (Recurse) ASTNode::If { condition, then_block, elif_blocks, else_block } => { - - let cond_val = Self::evaluate_with_lifecycle_recursive(condition, env, lifecycle)?; - + let cond_val = Self::evaluate(condition, env)?; if Self::is_truthy(&cond_val) { - return Self::evaluate_with_lifecycle_recursive(then_block, env, lifecycle); + return Self::eval_recursive(then_block, env, lifecycle, simulator, qubit_map, next_alloc); } - - for (elif_cond, elif_body) in elif_blocks { - let elif_val = Self::evaluate_with_lifecycle_recursive(elif_cond, env, lifecycle)?; - if Self::is_truthy(&elif_val) { - return Self::evaluate_with_lifecycle_recursive(elif_body, env, lifecycle); + for (cond, body) in elif_blocks { + if Self::is_truthy(&Self::evaluate(cond, env)?) { + return Self::eval_recursive(body, env, lifecycle, simulator, qubit_map, next_alloc); } } - if let Some(else_body) = else_block { - return Self::evaluate_with_lifecycle_recursive(else_body, env, lifecycle); + return Self::eval_recursive(else_body, env, lifecycle, simulator, qubit_map, next_alloc); } Ok(RuntimeValue::None) } - // 8. Loops (Recurse) ASTNode::While { condition, body } => { loop { - let cond_val = Self::evaluate_with_lifecycle_recursive(condition, env, lifecycle)?; - if !Self::is_truthy(&cond_val) { break; } - - let res = Self::evaluate_with_lifecycle_recursive(body, env, lifecycle)?; + if !Self::is_truthy(&Self::evaluate(condition, env)?) { break; } + let res = Self::eval_recursive(body, env, lifecycle, simulator, qubit_map, next_alloc)?; match res { RuntimeValue::Break => break, RuntimeValue::Continue => continue, @@ -3108,10 +3114,9 @@ impl Evaluator { RuntimeValue::Register(r) => r.iter().map(|v| v.borrow().clone()).collect(), _ => return Err("Invalid iterator".to_string()), }; - for item in iterable { env.borrow_mut().set(variable.clone(), item); - let res = Self::evaluate_with_lifecycle_recursive(body, env, lifecycle)?; + let res = Self::eval_recursive(body, env, lifecycle, simulator, qubit_map, next_alloc)?; match res { RuntimeValue::Break => break, RuntimeValue::Continue => continue, @@ -3121,22 +3126,88 @@ impl Evaluator { } Ok(RuntimeValue::None) } - ASTNode::FunctionCall { callee, arguments, loc, is_dagger } => { - Self::eval_function_call_with_lifecycle(callee, arguments, loc, env, *is_dagger, lifecycle) + + ASTNode::LetDeclaration { name, value, .. } => { + let val = Self::eval_recursive(value, env, lifecycle, simulator, qubit_map, next_alloc)?; + env.borrow_mut().set(name.clone(), val); + Ok(RuntimeValue::None) } + ASTNode::Assignment { target, value } => { + let new_value = Self::eval_recursive(value, env, lifecycle, simulator, qubit_map, next_alloc)?; + match &**target { + ASTNode::Identifier { name, .. } => { + if let Some(var_rc) = env.borrow().get(name) { + *var_rc.borrow_mut() = new_value; + Ok(RuntimeValue::None) + } else { + Err(format!("Runtime Error: Undefined variable '{}'.", name)) + } + } + _ => Self::eval_assignment(target, value, env) + } + } + + ASTNode::FunctionCall { callee, arguments, loc, is_dagger } => { + Self::eval_function_call_with_lifecycle_recursive( + callee, arguments, loc, env, *is_dagger, lifecycle, simulator, qubit_map, next_alloc + ) + } _ => Self::evaluate(node, env) } } - fn eval_function_call_with_lifecycle( + + fn eval_function_call_with_lifecycle_recursive( callee_expr: &ASTNode, arguments: &[ASTNode], loc: &Loc, env: &Rc>, is_dagger: bool, lifecycle: &mut QubitLifecycleManager, + simulator: &mut NativeSimulator, + qubit_map: &mut QubitMap, + next_alloc: &mut usize, ) -> Result { + + if let ASTNode::Identifier { name, .. } = callee_expr { + if name == "debug_state" { + if let Some(arg) = arguments.get(0) { + let mut indices = Vec::new(); + + match arg { + ASTNode::Identifier { name: var_name, .. } => { + if let Some(&start) = qubit_map.get(var_name) { + // Get size from environment + if let Some(val) = env.borrow().get(var_name) { + if let RuntimeValue::QuantumRegister { size, .. } = &*val.borrow() { + for i in 0..*size { + indices.push(start + i); + } + } + } + } + }, + ASTNode::ArrayAccess { array, index, .. } => { + if let ASTNode::Identifier { name: var_name, .. } = &**array { + if let ASTNode::IntLiteral(idx) = &**index { + if let Some(&start) = qubit_map.get(var_name) { + indices.push(start + (*idx as usize)); + } + } + } + }, + _ => {} + } + + if !indices.is_empty() { + simulator.dump_state(&indices); + return Ok(RuntimeValue::None); + } + } + } + } + let evaluated_args = Self::eval_arguments(arguments, env)?; let function = Self::evaluate(callee_expr, env)?; @@ -3151,8 +3222,7 @@ impl Evaluator { } let function_scope_rc = Rc::new(RefCell::new(function_scope)); - - let result = Self::evaluate_with_lifecycle_recursive(&body, &function_scope_rc, lifecycle)?; + let result = Self::eval_recursive(&body, &function_scope_rc, lifecycle, simulator, qubit_map, next_alloc)?; if let RuntimeValue::ReturnValue(val) = result { Ok(*val) diff --git a/src/main.rs b/src/main.rs index a0ebdf1..81c447b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ mod graphics_runtime; use hardware_integration::{parse_hardware_config, HardwareExecutor}; use quantum_backend::QuantumConfig; use crate::qubit_lifecycle::QubitLifecycleManager; +use crate::quantum_backend::native_simulator::NativeSimulator; use crate::codegen::Compiler; use crate::doc_generator::DocGenerator; @@ -411,9 +412,14 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); + let mut lifecycle = QubitLifecycleManager::new(true); // strict mode enabled + // 1. Scan for qubits + let total_qubits = count_total_qubits(&ast); + if verbose { println!(" -> Allocated Quantum Memory: {} qubits", total_qubits); } - let mut lifecycle = QubitLifecycleManager::new(true); // strict mode enabled + // 2. Initialize Native Backend + let mut simulator = NativeSimulator::new(total_qubits); if verbose { println!("Program Output:"); @@ -422,15 +428,20 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool let start_time = Instant::now(); - - let evaluation_result = Evaluator::evaluate_program_with_lifecycle(&ast, &env, &mut lifecycle); + //Evaluator (Hybrid Mode) + let result = Evaluator::evaluate_program_with_lifecycle( + &ast, + &env, + &mut lifecycle, + &mut simulator, + ); let duration = start_time.elapsed(); println!("{:-<60}", ""); println!("Interpreter Time: {:.6} seconds", duration.as_secs_f64()); - match evaluation_result { + match result { Ok(_) => { println!("✓ Execution successful!"); @@ -1466,3 +1477,31 @@ fn parse_error_location(error_msg: &str) -> Option { } None } +fn count_total_qubits(node: &ASTNode) -> usize { + match node { + ASTNode::Program(stmts) | ASTNode::Block(stmts) => { + stmts.iter().map(count_total_qubits).sum() + } + ASTNode::QuantumDeclaration { size, .. } => { + // If size is a literal integer, use it. Default to 1. + if let Some(size_expr) = size { + if let ASTNode::IntLiteral(n) = &**size_expr { + *n as usize + } else { + 1 // Dynamic sizes might need more complex handling + } + } else { + 1 + } + } + ASTNode::If { then_block, elif_blocks, else_block, .. } => { + let mut sum = count_total_qubits(then_block); + for (_, block) in elif_blocks { sum += count_total_qubits(block); } + if let Some(blk) = else_block { sum += count_total_qubits(blk); } + sum + } + ASTNode::For { body, .. } | ASTNode::While { body, .. } => count_total_qubits(body), + // ... Recurse into other structures as needed ... + _ => 0, + } +} diff --git a/src/quantum_backend/native_simulator.rs b/src/quantum_backend/native_simulator.rs index b93db86..a5549c0 100644 --- a/src/quantum_backend/native_simulator.rs +++ b/src/quantum_backend/native_simulator.rs @@ -4,400 +4,505 @@ use super::{HardwareCircuit, QuantumBackend, QuantumConfig, QuantumResult}; use std::collections::HashMap; use rayon::prelude::*; -/// Complex number representation -#[derive(Clone, Copy, Debug)] -struct Complex { - real: f64, - imag: f64, + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Complex { + pub real: f64, + pub imag: f64, } impl Complex { - fn new(real: f64, imag: f64) -> Self { - Complex { real, imag } - } - - fn zero() -> Self { - Complex::new(0.0, 0.0) - } - - fn one() -> Self { - Complex::new(1.0, 0.0) - } + pub fn new(real: f64, imag: f64) -> Self { Complex { real, imag } } + pub fn zero() -> Self { Complex::new(0.0, 0.0) } + pub fn one() -> Self { Complex::new(1.0, 0.0) } + pub fn i() -> Self { Complex::new(0.0, 1.0) } - fn i() -> Self { - Complex::new(0.0, 1.0) + pub fn from_polar(r: f64, theta: f64) -> Self { + Complex::new(r * theta.cos(), r * theta.sin()) } - fn magnitude_squared(&self) -> f64 { + pub fn magnitude_squared(&self) -> f64 { self.real * self.real + self.imag * self.imag } - fn magnitude(&self) -> f64 { - self.magnitude_squared().sqrt() - } - - fn conj(&self) -> Complex { - Complex::new(self.real, -self.imag) - } - - fn add(&self, other: &Complex) -> Complex { - Complex::new(self.real + other.real, self.imag + other.imag) + pub fn scale(&self, scalar: f64) -> Complex { + Complex::new(self.real * scalar, self.imag * scalar) } +} - fn mul(&self, other: &Complex) -> Complex { +impl std::ops::Add for Complex { + type Output = Self; + fn add(self, other: Self) -> Self { Complex::new(self.real + other.real, self.imag + other.imag) } +} +impl std::ops::Mul for Complex { + type Output = Self; + fn mul(self, other: Self) -> Self { Complex::new( self.real * other.real - self.imag * other.imag, self.real * other.imag + self.imag * other.real, ) } - - fn scale(&self, scalar: f64) -> Complex { - Complex::new(self.real * scalar, self.imag * scalar) - } } -/// Native quantum state vector simulator -pub struct NativeSimulator { + +#[derive(Clone)] +struct StateGroup { + qubits: Vec, state: Vec, - num_qubits: usize, } -impl NativeSimulator { - pub fn new(num_qubits: usize) -> Self { - let size = 1 << num_qubits; // 2^num_qubits - let mut state = vec![Complex::zero(); size]; - state[0] = Complex::one(); // Initialize to |0...0⟩ +impl StateGroup { + fn new(qubit_id: usize) -> Self { + StateGroup { + qubits: vec![qubit_id], + state: vec![Complex::one(), Complex::zero()], + } + } - NativeSimulator { state, num_qubits } + fn get_shift(&self, global_id: usize) -> usize { + let idx = self.qubits.iter().position(|&id| id == global_id).unwrap(); + self.qubits.len() - 1 - idx } - /// Get the probability of measuring a specific state - fn get_probability(&self, index: usize) -> f64 { - if index < self.state.len() { - self.state[index].magnitude_squared() - } else { - 0.0 + fn merge(&mut self, other: StateGroup) { + let mut new_state = Vec::with_capacity(self.state.len() * other.state.len()); + for amp_a in &self.state { + for amp_b in &other.state { + new_state.push(*amp_a * *amp_b); + } } + self.qubits.extend(other.qubits); + self.state = new_state; } +} - /// Apply a single-qubit gate - fn apply_single_qubit_gate(&mut self, qubit: usize, matrix: [[Complex; 2]; 2]) { - let size = self.state.len(); - let mask = 1 << qubit; - // Parallel application for better performance - let mut new_state = self.state.clone(); +pub struct NativeSimulator { + groups: Vec, + qubit_map: HashMap, +} - (0..size) - .into_par_iter() - .filter(|&i| (i & mask) == 0) - .for_each(|i| { - let i0 = i; - let i1 = i | mask; +impl NativeSimulator { + pub fn new(_total_qubits: usize) -> Self { + NativeSimulator { + groups: Vec::new(), + qubit_map: HashMap::new(), + } + } - let amp0 = self.state[i0]; - let amp1 = self.state[i1]; + fn ensure_qubit(&mut self, q: usize) { + if !self.qubit_map.contains_key(&q) { + let idx = self.groups.len(); + self.groups.push(StateGroup::new(q)); + self.qubit_map.insert(q, idx); + } + } - let new_amp0 = matrix[0][0].mul(&0).add(&matrix[0][1].mul(&1)); - let new_amp1 = matrix[1][0].mul(&0).add(&matrix[1][1].mul(&1)); + fn ensure_entangled(&mut self, q1: usize, q2: usize) { + self.ensure_qubit(q1); + self.ensure_qubit(q2); - unsafe { - let ptr = new_state.as_ptr() as *mut Complex; - *ptr.add(i0) = new_amp0; - *ptr.add(i1) = new_amp1; - } - }); + let g1 = self.qubit_map[&q1]; + let g2 = self.qubit_map[&q2]; - self.state = new_state; - } + if g1 != g2 { + let (dest_idx, src_idx) = if g1 < g2 { (g1, g2) } else { (g2, g1) }; + let src_group = self.groups.remove(src_idx); - /// Apply a two-qubit gate - fn apply_two_qubit_gate( - &mut self, - control: usize, - target: usize, - matrix: [[Complex; 4]; 4], - ) { - let size = self.state.len(); - let control_mask = 1 << control; - let target_mask = 1 << target; - - let mut new_state = self.state.clone(); - - (0..size) - .into_par_iter() - .filter(|&i| (i & control_mask) == 0 && (i & target_mask) == 0) - .for_each(|i| { - let i00 = i; - let i01 = i | target_mask; - let i10 = i | control_mask; - let i11 = i | control_mask | target_mask; + for (_, grp_idx) in self.qubit_map.iter_mut() { + if *grp_idx > src_idx { *grp_idx -= 1; } + } - let amps = [ - self.state[i00], - self.state[i01], - self.state[i10], - self.state[i11], - ]; + for q in &src_group.qubits { + self.qubit_map.insert(*q, dest_idx); + } - let mut new_amps = [Complex::zero(); 4]; + self.groups[dest_idx].merge(src_group); + } + } - for row in 0..4 { - for col in 0..4 { - new_amps[row] = new_amps[row].add(&matrix[row][col].mul(&s[col])); - } - } - unsafe { - let ptr = new_state.as_ptr() as *mut Complex; - *ptr.add(i00) = new_amps[0]; - *ptr.add(i01) = new_amps[1]; - *ptr.add(i10) = new_amps[2]; - *ptr.add(i11) = new_amps[3]; - } - }); + pub fn apply_gate(&mut self, name: &str, qubits: &[usize], params: &[f64], is_dagger: bool) -> Result<(), String> { + match qubits.len() { + 1 => self.ensure_qubit(qubits[0]), + 2 => self.ensure_entangled(qubits[0], qubits[1]), + 3 => { + self.ensure_entangled(qubits[0], qubits[1]); + self.ensure_entangled(qubits[0], qubits[2]); + } + _ => return Err("Gates with >3 qubits not supported natively".to_string()), + } - self.state = new_state; + let mat = Self::get_matrix(name, params, is_dagger)?; + + let group_idx = self.qubit_map[&qubits[0]]; + let group = &mut self.groups[group_idx]; + + // 3. Apply + if qubits.len() == 1 { + let shift = group.get_shift(qubits[0]); + Self::apply_1q(group, shift, mat) + } else if qubits.len() == 2 { + let s1 = group.get_shift(qubits[0]); + let s2 = group.get_shift(qubits[1]); + Self::apply_2q(group, s1, s2, mat) + } else if qubits.len() == 3 { + let s0 = group.get_shift(qubits[0]); + let s1 = group.get_shift(qubits[1]); + let s2 = group.get_shift(qubits[2]); + Self::apply_3q_gate(group, s0, s1, s2, name, params, is_dagger) + } else { + Err("Gate logic error".to_string()) + } } - /// Simulate measurements and return counts - fn measure(&self, shots: u32) -> HashMap { - use rand::Rng; - let mut rng = rand::thread_rng(); - let mut counts = HashMap::new(); + pub fn measure_single(&mut self, qubit: usize) -> u32 { + self.ensure_qubit(qubit); + let group_idx = self.qubit_map[&qubit]; + let group = &mut self.groups[group_idx]; - // Build cumulative probability distribution - let mut cumulative_probs = Vec::with_capacity(self.state.len()); - let mut sum = 0.0; - for amp in &self.state { - sum += amp.magnitude_squared(); - cumulative_probs.push(sum); + let shift = group.get_shift(qubit); + let mask = 1 << shift; + + let mut prob0 = 0.0; + for (i, amp) in group.state.iter().enumerate() { + if (i & mask) == 0 { + prob0 += amp.magnitude_squared(); + } } - // Perform measurements - for _ in 0..shots { - let rand_val: f64 = rng.gen(); - let index = cumulative_probs - .binary_search_by(|&p| p.partial_cmp(&rand_val).unwrap()) - .unwrap_or_else(|i| i); + let mut rng = rand::thread_rng(); + use rand::Rng; + let result = if rng.gen::() < prob0 { 0 } else { 1 }; - let bitstring = format!("{:0width$b}", index, width = self.num_qubits); - *counts.entry(bitstring).or_insert(0) += 1; + let norm = if result == 0 { + if prob0 > 0.0 { 1.0 / prob0.sqrt() } else { 0.0 } + } else { + if prob0 < 1.0 { 1.0 / (1.0 - prob0).sqrt() } else { 0.0 } + }; + + for (i, amp) in group.state.iter_mut().enumerate() { + let bit = (i & mask) != 0; + if (bit as u32) == result { + *amp = amp.scale(norm); + } else { + *amp = Complex::zero(); + } } - counts + result } - /// Apply a gate based on name and parameters - fn apply_gate(&mut self, gate_name: &str, qubits: &[usize], params: &[f64]) -> Result<(), String> { - match gate_name { - "hadamard" | "h" => { - if qubits.len() != 1 { - return Err("Hadamard gate requires 1 qubit".to_string()); + pub fn get_probability(&self, global_index: usize) -> f64 { + let mut total_prob = 1.0; + for group in &self.groups { + let mut local_idx = 0; + for (vec_idx, &q_id) in group.qubits.iter().enumerate() { + if (global_index >> q_id) & 1 == 1 { + let shift = group.qubits.len() - 1 - vec_idx; + local_idx |= 1 << shift; } - let inv_sqrt2 = 1.0 / 2.0_f64.sqrt(); - let h_matrix = [ - [Complex::new(inv_sqrt2, 0.0), Complex::new(inv_sqrt2, 0.0)], - [Complex::new(inv_sqrt2, 0.0), Complex::new(-inv_sqrt2, 0.0)], - ]; - self.apply_single_qubit_gate(qubits[0], h_matrix); } - "x" => { - if qubits.len() != 1 { - return Err("X gate requires 1 qubit".to_string()); - } - let x_matrix = [ - [Complex::zero(), Complex::one()], - [Complex::one(), Complex::zero()], - ]; - self.apply_single_qubit_gate(qubits[0], x_matrix); + if local_idx < group.state.len() { + total_prob *= group.state[local_idx].magnitude_squared(); + } else { + return 0.0; } - "y" => { - if qubits.len() != 1 { - return Err("Y gate requires 1 qubit".to_string()); - } - let y_matrix = [ - [Complex::zero(), Complex::new(0.0, -1.0)], - [Complex::new(0.0, 1.0), Complex::zero()], - ]; - self.apply_single_qubit_gate(qubits[0], y_matrix); + } + total_prob + } + + + fn apply_1q(group: &mut StateGroup, shift: usize, mat: Vec) -> Result<(), String> { + let size = group.state.len(); + let mask = 1 << shift; + let old_state = group.state.clone(); + let mut new_state = vec![Complex::zero(); size]; + + let m00 = mat[0]; let m01 = mat[1]; + let m10 = mat[2]; let m11 = mat[3]; + + for i in 0..size { + if (i & mask) == 0 { + let i0 = i; + let i1 = i | mask; + + let a0 = old_state[i0]; + let a1 = old_state[i1]; + + new_state[i0] = m00 * a0 + m01 * a1; + new_state[i1] = m10 * a0 + m11 * a1; } - "z" => { - if qubits.len() != 1 { - return Err("Z gate requires 1 qubit".to_string()); + } + group.state = new_state; + Ok(()) + } + + fn apply_2q(group: &mut StateGroup, s1: usize, s2: usize, mat: Vec) -> Result<(), String> { + let size = group.state.len(); + let m1 = 1 << s1; + let m2 = 1 << s2; + let old_state = group.state.clone(); + let mut new_state = vec![Complex::zero(); size]; + + for i in 0..size { + if (i & m1) == 0 && (i & m2) == 0 { + let i00 = i; + let i01 = i | m2; + let i10 = i | m1; + let i11 = i | m1 | m2; + + let a00 = old_state[i00]; + let a01 = old_state[i01]; + let a10 = old_state[i10]; + let a11 = old_state[i11]; + + let amps = [a00, a01, a10, a11]; + + // 4x4 Matrix + for row in 0..4 { + let mut sum = Complex::zero(); + for col in 0..4 { + sum = sum + mat[row * 4 + col] * amps[col]; + } + + match row { + 0 => new_state[i00] = sum, + 1 => new_state[i01] = sum, + 2 => new_state[i10] = sum, + 3 => new_state[i11] = sum, + _ => {} + } } - let z_matrix = [ - [Complex::one(), Complex::zero()], - [Complex::zero(), Complex::new(-1.0, 0.0)], - ]; - self.apply_single_qubit_gate(qubits[0], z_matrix); } - "s" => { - if qubits.len() != 1 { - return Err("S gate requires 1 qubit".to_string()); + } + group.state = new_state; + Ok(()) + } + + fn apply_3q_gate(group: &mut StateGroup, c1: usize, c2: usize, t: usize, name: &str, params: &[f64], is_dagger: bool) -> Result<(), String> { + let m1 = 1 << c1; + let m2 = 1 << c2; + let mt = 1 << t; + + let target_gate_name = match name { + "ccx" | "toffoli" => "x", + "cch" | "cchadamard" => "h", + "ccrx" => "rx", + _ => return Err(format!("3-qubit gate {} not supported", name)), + }; + + let mat = Self::get_matrix(target_gate_name, params, is_dagger)?; + let m00 = mat[0]; let m01 = mat[1]; + let m10 = mat[2]; let m11 = mat[3]; + + let old_state = group.state.clone(); + let mut new_state = old_state.clone(); + + for i in 0..group.state.len() { + // Apply only if controls are 1 + if (i & m1) != 0 && (i & m2) != 0 { + if (i & mt) == 0 { + let i0 = i; + let i1 = i | mt; + + let a0 = old_state[i0]; + let a1 = old_state[i1]; + + new_state[i0] = m00 * a0 + m01 * a1; + new_state[i1] = m10 * a0 + m11 * a1; } - let s_matrix = [ - [Complex::one(), Complex::zero()], - [Complex::zero(), Complex::i()], - ]; - self.apply_single_qubit_gate(qubits[0], s_matrix); } + } + group.state = new_state; + Ok(()) + } + + fn get_matrix(name: &str, params: &[f64], is_dagger: bool) -> Result, String> { + let z = Complex::zero(); + let o = Complex::one(); + let i = Complex::i(); + let inv_sqrt2 = 1.0 / 2.0f64.sqrt(); + let h = Complex::new(inv_sqrt2, 0.0); + let d_mul = if is_dagger { -1.0 } else { 1.0 }; + + match name { + // Single Qubit + "x" | "not" => Ok(vec![z, o, o, z]), + "y" => Ok(vec![z, i.scale(-1.0), i, z]), + "z" => Ok(vec![o, z, z, Complex::new(-1.0, 0.0)]), + "h" | "hadamard" => Ok(vec![h, h, h, h.scale(-1.0)]), + "s" => { + let phase = if is_dagger { i.scale(-1.0) } else { i }; + Ok(vec![o, z, z, phase]) + }, "t" => { - if qubits.len() != 1 { - return Err("T gate requires 1 qubit".to_string()); - } - let phase = std::f64::consts::PI / 4.0; - let t_matrix = [ - [Complex::one(), Complex::zero()], - [Complex::zero(), Complex::new(phase.cos(), phase.sin())], - ]; - self.apply_single_qubit_gate(qubits[0], t_matrix); - } + let angle = std::f64::consts::FRAC_PI_4 * d_mul; + Ok(vec![o, z, z, Complex::from_polar(1.0, angle)]) + }, + + // Two Qubit + "cnot" | "cx" | "controlled_x" => Ok(vec![o, z, z, z, z, o, z, z, z, z, z, o, z, z, o, z]), + "swap" => Ok(vec![o, z, z, z, z, z, o, z, z, o, z, z, z, z, z, o]), + "cz" | "controlled_z" => Ok(vec![o, z, z, z, z, o, z, z, z, z, o, z, z, z, z, Complex::new(-1.0, 0.0)]), + + // Controlled Phase Gates (4x4) + "ch" | "chadamard" | "controlled_hadamard" => Ok(vec![ + o, z, z, z, z, o, z, z, z, z, h, h, z, z, h, h.scale(-1.0) + ]), + "cs" | "controlled_s" => { + let phase = if is_dagger { i.scale(-1.0) } else { i }; + Ok(vec![o, z, z, z, z, o, z, z, z, z, o, z, z, z, z, phase]) + }, + "ct" | "controlled_t" => { + let angle = std::f64::consts::FRAC_PI_4 * d_mul; + let phase = Complex::from_polar(1.0, angle); + Ok(vec![o, z, z, z, z, o, z, z, z, z, o, z, z, z, z, phase]) + }, + + // Parameterized "rx" => { - if qubits.len() != 1 || params.is_empty() { - return Err("RX gate requires 1 qubit and 1 parameter".to_string()); - } - let theta = params[0]; - let cos = (theta / 2.0).cos(); - let sin = (theta / 2.0).sin(); - let rx_matrix = [ - [Complex::new(cos, 0.0), Complex::new(0.0, -sin)], - [Complex::new(0.0, -sin), Complex::new(cos, 0.0)], - ]; - self.apply_single_qubit_gate(qubits[0], rx_matrix); - } + let theta = (params.get(0).unwrap_or(&0.0) / 2.0) * d_mul; + let c = Complex::new(theta.cos(), 0.0); + let s = Complex::new(0.0, -theta.sin()); + Ok(vec![c, s, s, c]) + }, "ry" => { - if qubits.len() != 1 || params.is_empty() { - return Err("RY gate requires 1 qubit and 1 parameter".to_string()); - } - let theta = params[0]; - let cos = (theta / 2.0).cos(); - let sin = (theta / 2.0).sin(); - let ry_matrix = [ - [Complex::new(cos, 0.0), Complex::new(-sin, 0.0)], - [Complex::new(sin, 0.0), Complex::new(cos, 0.0)], - ]; - self.apply_single_qubit_gate(qubits[0], ry_matrix); - } + let theta = (params.get(0).unwrap_or(&0.0) / 2.0) * d_mul; + let c = Complex::new(theta.cos(), 0.0); + let s = Complex::new(theta.sin(), 0.0); + Ok(vec![c, s.scale(-1.0), s, c]) + }, "rz" => { - if qubits.len() != 1 || params.is_empty() { - return Err("RZ gate requires 1 qubit and 1 parameter".to_string()); - } + let theta = (params.get(0).unwrap_or(&0.0) / 2.0) * d_mul; + let a = Complex::from_polar(1.0, -theta); + let b = Complex::from_polar(1.0, theta); + Ok(vec![a, z, z, b]) + }, + "cphase" => { + let phi = params.get(0).unwrap_or(&0.0) * d_mul; + Ok(vec![o, z, z, z, z, o, z, z, z, z, o, z, z, z, z, Complex::from_polar(1.0, phi)]) + }, + "u" => { + if params.len() < 3 { return Err("U gate requires 3 parameters".to_string()); } let theta = params[0]; - let phase_neg = Complex::new((theta / 2.0).cos(), -(theta / 2.0).sin()); - let phase_pos = Complex::new((theta / 2.0).cos(), (theta / 2.0).sin()); - let rz_matrix = [ - [phase_neg, Complex::zero()], - [Complex::zero(), phase_pos], - ]; - self.apply_single_qubit_gate(qubits[0], rz_matrix); - } - "cnot" | "cx" => { - if qubits.len() != 2 { - return Err("CNOT gate requires 2 qubits".to_string()); - } - let cnot_matrix = [ - [Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()], - [Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()], - [Complex::zero(), Complex::zero(), Complex::zero(), Complex::one()], - [Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()], - ]; - self.apply_two_qubit_gate(qubits[0], qubits[1], cnot_matrix); - } - "cz" => { - if qubits.len() != 2 { - return Err("CZ gate requires 2 qubits".to_string()); + let phi = params[1]; + let lambda = params[2]; + let (t, p, l) = if is_dagger { (-theta, -lambda, -phi) } else { (theta, phi, lambda) }; + + let half_t = t / 2.0; + let cos_t = half_t.cos(); + let sin_t = half_t.sin(); + let a00 = Complex::new(cos_t, 0.0); + let a01 = Complex::from_polar(-sin_t, l); + let a10 = Complex::from_polar(sin_t, p); + let a11 = Complex::from_polar(cos_t, p + l); + Ok(vec![a00, a01, a10, a11]) + }, + + "crx" | "controlled_rx" => { + let theta = (params.get(0).unwrap_or(&0.0) / 2.0) * d_mul; + let c = Complex::new(theta.cos(), 0.0); + let s = Complex::new(0.0, -theta.sin()); + Ok(vec![o, z, z, z, z, o, z, z, z, z, c, s, z, z, s, c]) + }, + "cry" | "controlled_ry" => { + let theta = (params.get(0).unwrap_or(&0.0) / 2.0) * d_mul; + let c = Complex::new(theta.cos(), 0.0); + let s = Complex::new(theta.sin(), 0.0); + Ok(vec![o, z, z, z, z, o, z, z, z, z, c, s.scale(-1.0), z, z, s, c]) + }, + "crz" | "controlled_rz" => { + let theta = (params.get(0).unwrap_or(&0.0) / 2.0) * d_mul; + let a = Complex::from_polar(1.0, -theta); + let b = Complex::from_polar(1.0, theta); + Ok(vec![o, z, z, z, z, o, z, z, z, z, a, z, z, z, z, b]) + }, + + + "ccx" | "toffoli" | "cch" | "cchadamard" | "ccrx" => Ok(vec![]), + + _ => Err(format!("Matrix for {} not implemented", name)) + } + } + pub fn dump_state(&self, qubits: &[usize]) { + // Collect unique groups involved + let mut seen_groups = Vec::new(); + for &q in qubits { + if let Some(&g_idx) = self.qubit_map.get(&q) { + if !seen_groups.contains(&g_idx) { + seen_groups.push(g_idx); } - let cz_matrix = [ - [Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()], - [Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()], - [Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()], - [Complex::zero(), Complex::zero(), Complex::zero(), Complex::new(-1.0, 0.0)], - ]; - self.apply_two_qubit_gate(qubits[0], qubits[1], cz_matrix); } - "swap" => { - if qubits.len() != 2 { - return Err("SWAP gate requires 2 qubits".to_string()); + } + + if seen_groups.is_empty() { + println!("(No active quantum state found for these qubits)"); + return; + } + + for &g_idx in &seen_groups { + let group = &self.groups[g_idx]; + println!("--- Real Simulator State (Qubits {:?}) ---", group.qubits); + + for (i, amp) in group.state.iter().enumerate() { + if amp.magnitude_squared() > 1e-6 { + // Print binary representation + let width = group.qubits.len(); + println!(" |{:0width$b}> : {:.6} + {:.6}i", i, amp.real, amp.imag, width=width); } - let swap_matrix = [ - [Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()], - [Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()], - [Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()], - [Complex::zero(), Complex::zero(), Complex::zero(), Complex::one()], - ]; - self.apply_two_qubit_gate(qubits[0], qubits[1], swap_matrix); } - _ => return Err(format!("Unknown gate: {}", gate_name)), + println!("------------------------------------------"); } - Ok(()) } } -/// Native Backend implementation +// Wrapper for the Backend Trait pub struct NativeBackend; - -impl NativeBackend { - pub fn new() -> Self { - NativeBackend - } -} +impl NativeBackend { pub fn new() -> Self { NativeBackend } } impl QuantumBackend for NativeBackend { - fn execute( - &self, - circuit: &HardwareCircuit, - config: &QuantumConfig, - ) -> Result { - // Initialize simulator - let mut simulator = NativeSimulator::new(circuit.num_qubits); - - // Apply gates - for gate in &circuit.gates { - simulator.apply_gate(&gate.name, &gate.qubits, &gate.params)?; - } + fn execute(&self, circuit: &HardwareCircuit, config: &QuantumConfig) -> Result { + let mut counts = HashMap::new(); + let shots = if config.shots > 0 { config.shots } else { 1 }; - // Measure - let counts = simulator.measure(config.shots); + // Run the simulation 'shots' times + for _ in 0..shots { - Ok(QuantumResult { - counts, - shots: config.shots, - success: true, - error_message: None, - }) - } + let mut sim = NativeSimulator::new(circuit.num_qubits); - fn is_available(&self) -> bool { - true // Native backend is always available - } - fn available_devices(&self) -> Vec { - vec![ - "native_simulator".to_string(), - "native_statevector".to_string(), - ] - } + for gate in &circuit.gates { + sim.apply_gate(&gate.name, &gate.qubits, &gate.params, gate.is_dagger)?; + } - fn optimize_circuit(&self, circuit: &HardwareCircuit) -> HardwareCircuit { - // Basic optimization: merge consecutive single-qubit gates - let mut optimized_gates = Vec::new(); - - for gate in &circuit.gates { - // Simple pass-through for now - // You can add optimization logic here: - // - Gate fusion - // - Commutation rules - // - Cancellation of inverse gates - optimized_gates.push(gate.clone()); - } - HardwareCircuit { - num_qubits: circuit.num_qubits, - gates: optimized_gates, - measurements: circuit.measurements.clone(), + let mut bitstring = String::new(); + if circuit.measurements.is_empty() { + } else { + for &q_idx in &circuit.measurements { + let bit = sim.measure_single(q_idx); + bitstring.push_str(&bit.to_string()); + } + } + + // 4. Record the result + if !bitstring.is_empty() { + *counts.entry(bitstring).or_insert(0) += 1; + } } + + Ok(QuantumResult { + success: true, + counts, + shots, + error_message: None, + }) } + + fn is_available(&self) -> bool { true } + fn available_devices(&self) -> Vec { vec!["native_simulator".to_string()] } + fn optimize_circuit(&self, c: &HardwareCircuit) -> HardwareCircuit { c.clone() } } #[cfg(test)] @@ -407,37 +512,17 @@ mod tests { #[test] fn test_hadamard_gate() { let mut sim = NativeSimulator::new(1); - sim.apply_gate("h", &[0], &[]).unwrap(); - + sim.apply_gate("h", &[0], &[], false).unwrap(); let prob0 = sim.get_probability(0); - let prob1 = sim.get_probability(1); - assert!((prob0 - 0.5).abs() < 1e-10); - assert!((prob1 - 0.5).abs() < 1e-10); } #[test] fn test_cnot_gate() { let mut sim = NativeSimulator::new(2); - sim.apply_gate("x", &[0], &[]).unwrap(); - sim.apply_gate("cnot", &[0, 1], &[]).unwrap(); - - // Should be in |11⟩ state - let prob3 = sim.get_probability(3); // Binary: 11 - assert!((prob3 - 1.0).abs() < 1e-10); - } - - #[test] - fn test_bell_state() { - let mut sim = NativeSimulator::new(2); - sim.apply_gate("h", &[0], &[]).unwrap(); - sim.apply_gate("cnot", &[0, 1], &[]).unwrap(); - - // Should have equal probability for |00⟩ and |11⟩ - let prob0 = sim.get_probability(0); + sim.apply_gate("x", &[0], &[], false).unwrap(); + sim.apply_gate("cnot", &[0, 1], &[], false).unwrap(); let prob3 = sim.get_probability(3); - - assert!((prob0 - 0.5).abs() < 1e-10); - assert!((prob3 - 0.5).abs() < 1e-10); + assert!((prob3 - 1.0).abs() < 1e-10); } } \ No newline at end of file From f3cfc03459fe129a4488f9da4c789f7746cc89de Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Thu, 11 Dec 2025 12:48:31 +0530 Subject: [PATCH 16/27] Update mod.rs --- src/quantum_backend/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/quantum_backend/mod.rs b/src/quantum_backend/mod.rs index c835111..3219a9d 100644 --- a/src/quantum_backend/mod.rs +++ b/src/quantum_backend/mod.rs @@ -10,7 +10,7 @@ mod cirq_local; use cirq_local::CirqLocalBackend; // Import the new native backend -mod native_simulator; +pub mod native_simulator; use native_simulator::NativeBackend; /// Supported quantum hardware providers @@ -349,4 +349,5 @@ mod tests { assert!(*count_00 > 400 && *count_00 < 600); assert!(*count_11 > 400 && *count_11 < 600); } -} \ No newline at end of file + +} From da45806806824c9840d2b983f98b2247a00acc05 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:02:47 +0530 Subject: [PATCH 17/27] fixed lifecycle related bugs --- src/environment/mod.rs | 54 +++--- src/evaluator/mod.rs | 408 +++++++++++++++++++---------------------- src/main.rs | 22 ++- src/qubit_lifecycle.rs | 4 +- src/runtime.rs | 39 ++-- 5 files changed, 259 insertions(+), 268 deletions(-) diff --git a/src/environment/mod.rs b/src/environment/mod.rs index 305dcd4..3cd9d23 100644 --- a/src/environment/mod.rs +++ b/src/environment/mod.rs @@ -1,4 +1,4 @@ -// src/environment/mod.rs +/* src/environment/mod.rs */ use crate::parser::ast::Parameter; use std::cell::RefCell; use std::collections::HashMap; @@ -9,17 +9,17 @@ use crate::parser::ast::ASTNode; #[derive(Debug, Clone)] pub struct GateDefinition { - // The base gate name, e.g., "x", "u", "cnot" + pub name: String, - // The parameters, e.g., [theta, phi, lambda] for U gate + pub params: Vec, - // The list of control qubit indices + pub controls: Vec, - // The list of target qubit indices + pub targets: Vec, - // The total number of qubits in the register + pub register_size: usize, - // A shared pointer to the state vector + pub state_rc: Rc>>, } @@ -31,27 +31,31 @@ pub enum RuntimeValue { Bool(bool), None, - // A handle to a specific qubit within a register - // It holds a shared pointer to the register's state vector + + Qubit { - // A shared pointer to the register's SPARSE state + state: Rc>>, - index: usize, // This qubit's index - size: usize, // The total size of its register + index: usize, + size: usize, + register_name: String, + global_index: Option, }, - // --- THIS IS THE UPDATED REGISTER DEFINITION --- + QuantumRegister { size: usize, - // The state is now a HashMap from basis state (usize) to amplitude + state: Rc>>, + register_name: String, + global_start_index: Option, }, Gate { - // "s", "x", "u", etc. + base_name: String, is_dagger: bool, - // Number of controls this expression has added + num_controls: usize, }, Class { @@ -62,26 +66,26 @@ pub enum RuntimeValue { constructor: Option>, }, - // Object instance + Instance { class_name: String, fields: HashMap>>, methods: HashMap, }, - Register(Vec>>), // An array + Register(Vec>>), Dict(HashMap>>), Range(Vec), KetState(String), - // A user-defined function + Function { parameters: Vec, body: Box, env: Rc>, }, - // A built-in Rust function + BuiltinFunction(String), Module(Rc>), @@ -132,12 +136,12 @@ impl std::fmt::Display for RuntimeValue { RuntimeValue::Bool(b) => write!(f, "{}", b), RuntimeValue::None => write!(f, "None"), - RuntimeValue::Qubit { index, size, .. } => { - write!(f, "", index, size) + RuntimeValue::Qubit { register_name, index, .. } => { + write!(f, "", register_name, index) } - RuntimeValue::QuantumRegister { size, .. } => { - write!(f, "", size) + RuntimeValue::QuantumRegister { register_name, size, .. } => { + write!(f, "", register_name, size) } RuntimeValue::Register(elements) => { let parts: Vec = @@ -156,7 +160,7 @@ impl std::fmt::Display for RuntimeValue { RuntimeValue::Instance { class_name, fields, .. } => { write!(f, "<{} instance at {:p}>", class_name, fields) } - _ => write!(f, "{:?}", self), // Fallback + _ => write!(f, "{:?}", self), } } } diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index d5e7256..6828bda 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -1,4 +1,4 @@ -// src/evaluator/mod.rs +/* src/evaluator/mod.rs */ use crate::environment::{Environment, GateDefinition, RuntimeValue}; use crate::lexer::Lexer; use crate::parser::ast::ASTNode; @@ -109,7 +109,9 @@ impl Evaluator { let state_vec = Self::ket_to_state_vector(s, size)?; Ok(RuntimeValue::QuantumRegister { size, - state: Rc::new(RefCell::new(state_vec)) + state: Rc::new(RefCell::new(state_vec)), + register_name: "ket".to_string(), + global_start_index: None, }) }, ASTNode::Apply { gate_expr, arguments, loc } => { @@ -166,7 +168,7 @@ impl Evaluator { pub fn register_builtins(env: &Rc>) { let mut e = env.borrow_mut(); - // --- Standard Built-ins --- + e.set("print".to_string(), RuntimeValue::BuiltinFunction("print".to_string())); e.set("len".to_string(), RuntimeValue::BuiltinFunction("len".to_string())); e.set("to_string".to_string(), RuntimeValue::BuiltinFunction("to_string".to_string())); @@ -180,7 +182,7 @@ impl Evaluator { e.set("debug_state".to_string(), RuntimeValue::BuiltinFunction("debug_state".to_string())); e.set("measure".to_string(), RuntimeValue::BuiltinFunction("measure".to_string())); - // --- Graphics Built-ins --- + e.set("_graphics_create_canvas".to_string(), RuntimeValue::BuiltinFunction("_graphics_create_canvas".to_string())); e.set("_graphics_set_background".to_string(), RuntimeValue::BuiltinFunction("_graphics_set_background".to_string())); e.set("_graphics_draw_rect".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_rect".to_string())); @@ -190,7 +192,7 @@ impl Evaluator { e.set("_graphics_save_svg".to_string(), RuntimeValue::BuiltinFunction("_graphics_save_svg".to_string())); e.set("_graphics_destroy_canvas".to_string(), RuntimeValue::BuiltinFunction("_graphics_destroy_canvas".to_string())); - // --- Plot Functions --- + e.set("_graphics_create_plot".to_string(), RuntimeValue::BuiltinFunction("_graphics_create_plot".to_string())); e.set("_graphics_plot_set_data".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_data".to_string())); e.set("_graphics_plot_set_title".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_title".to_string())); @@ -220,7 +222,7 @@ impl Evaluator { constructor: &Option>, env: &Rc>, ) -> Result { - // Convert methods vector to HashMap + let mut method_map = HashMap::new(); for method in methods { method_map.insert(method.name.clone(), method.clone()); @@ -244,7 +246,7 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - // 1. Get Class Definition + let class_value = env.borrow().get(class_name) .ok_or_else(|| format!("Runtime Error at {}: Unknown class '{}'", loc, class_name))?; @@ -257,9 +259,9 @@ impl Evaluator { }; drop(class_value_borrowed); - // 2. Initialize instance fields & Capture Initial Values + let mut instance_fields = HashMap::new(); - let mut initial_values = HashMap::new(); // Store initials to detect changes + let mut initial_values = HashMap::new(); for field in &fields_def { let value = if let Some(default_expr) = &field.default_value { @@ -267,23 +269,23 @@ impl Evaluator { } else { RuntimeValue::None }; - // Save initial value for change detection + initial_values.insert(field.name.clone(), value.clone()); - // Create shared storage + instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); } - // 3. Create Instance + let instance = RuntimeValue::Instance { class_name: class_name.to_string(), fields: instance_fields.clone(), methods: methods.clone(), }; - // 4. Run Constructor + if let Some(constructor_node) = constructor { if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { - // Evaluate Args + let mut evaluated_args = Vec::new(); for arg in arguments { evaluated_args.push(Self::evaluate(arg, env)?); @@ -295,28 +297,28 @@ impl Evaluator { let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); - // A. Inject 'self' + constructor_env.borrow_mut().set("self".to_string(), instance.clone()); - // B. Inject Implicit Fields (as local vars) + for (name, val) in &initial_values { constructor_env.borrow_mut().set(name.clone(), val.clone()); } - // C. Inject Parameters (params override fields if names collide) + for (param, arg_value) in parameters.iter().zip(evaluated_args) { constructor_env.borrow_mut().set(param.name.clone(), arg_value); } - // D. Execute Body + Self::evaluate(&body, &constructor_env)?; - // E. SMART SYNC: Only update fields if the local variable CHANGED + for (field_name, field_rc) in &instance_fields { if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { let local_val = local_val_rc.borrow().clone(); - // Check against the initial value we recorded + if let Some(initial_val) = initial_values.get(field_name) { if !Self::are_values_equal(&local_val, initial_val) { @@ -350,17 +352,17 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - // Evaluate object + let object = Self::evaluate(object_expr, env)?; match object { - // Case 1: Method call on a Class Instance + RuntimeValue::Instance { class_name, fields, methods, } => { - // Find method + let method = methods.get(method_name).ok_or_else(|| { format!( "Runtime Error at {}: Class '{}' has no method '{}'", @@ -368,13 +370,13 @@ impl Evaluator { ) })?; - // Evaluate arguments + let mut evaluated_args = Vec::new(); for arg in arguments { evaluated_args.push(Self::evaluate(arg, env)?); } - // Check parameter count + if method.parameters.len() != evaluated_args.len() { return Err(format!( "Runtime Error at {}: Method '{}' expected {} arguments, got {}", @@ -385,39 +387,39 @@ impl Evaluator { )); } - // Create method environment + let method_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); let self_instance = RuntimeValue::Instance { class_name: class_name.clone(), - fields: fields.clone(), // Share the same Rc fields + fields: fields.clone(), methods: methods.clone(), }; method_env.borrow_mut().set("self".to_string(), self_instance); - // 1. COPY-IN: Add instance fields to method environment + for (field_name, field_value) in &fields { method_env .borrow_mut() .set(field_name.clone(), field_value.borrow().clone()); } - // Add parameters + for (param, arg_value) in method.parameters.iter().zip(evaluated_args) { method_env.borrow_mut().set(param.name.clone(), arg_value); } - // Execute method body + let result = Self::evaluate(&method.body, &method_env)?; for (field_name, field_rc) in &fields { - // Check if the field exists in the method environment (it should) + if let Some(env_val_rc) = method_env.borrow().get(field_name) { - // Get the potentially modified value from the environment + let new_val = env_val_rc.borrow().clone(); - // Write it back to the Instance's shared storage + *field_rc.borrow_mut() = new_val; } } @@ -427,7 +429,7 @@ impl Evaluator { } } - // Case 2: Function call on a Module (Remains unchanged) + RuntimeValue::Module(module_env) => { let member_val = module_env.borrow().get(method_name).ok_or_else(|| { format!( @@ -489,14 +491,14 @@ impl Evaluator { instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); } - // 2. Create the instance + let mut instance = RuntimeValue::Instance { class_name: name.clone(), fields: instance_fields.clone(), methods: methods.clone(), }; - // 3. Call constructor 'init' + if let Some(constructor_node) = constructor { if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { if parameters.len() != evaluated_args.len() { @@ -505,29 +507,29 @@ impl Evaluator { let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(module_env.clone()))); - // A. Inject 'self' + constructor_env.borrow_mut().set("self".to_string(), instance.clone()); - // B. Inject Implicit Fields (as local vars) + for (name, val) in &initial_values { constructor_env.borrow_mut().set(name.clone(), val.clone()); } - // C. Inject Parameters + for (param, arg_value) in parameters.iter().zip(evaluated_args) { constructor_env.borrow_mut().set(param.name.clone(), arg_value); } - // D. Execute Body + Self::evaluate(&body, &constructor_env)?; - // E. SMART SYNC: Only update fields if local var CHANGED + for (field_name, field_rc) in &instance_fields { if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { let local_val = local_val_rc.borrow().clone(); if let Some(initial_val) = initial_values.get(field_name) { - // Only overwrite if value actually changed (e.g. None -> 0) + if !Self::are_values_equal(&local_val, initial_val) { *field_rc.borrow_mut() = local_val; } @@ -535,7 +537,7 @@ impl Evaluator { } } - // Return updated instance + instance = RuntimeValue::Instance { class_name: name.clone(), fields: instance_fields, @@ -584,47 +586,28 @@ impl Evaluator { initial_state_expr: &Option>, env: &Rc>, ) -> Result { - let register = if let Some(expr) = size_expr { + let size = if let Some(expr) = size_expr { let size_val = Self::evaluate(expr, env)?; - let size = match size_val { + match size_val { RuntimeValue::Int(n) if n > 0 => n as usize, - _ => { - return Err(format!( - "Runtime Error: Quantum register size must be a positive integer, got {:?}", - size_val - )) - } - }; - if size > 25 { - return Err(format!( - "Runtime Error: Register size {} is too large to simulate.", - size - )); - } - // Use the sparse state vector - let state_map = Self::default_state_vector(size)?; - RuntimeValue::QuantumRegister { - size, - state: Rc::new(RefCell::new(state_map)), - } - } else if let Some(expr) = initial_state_expr { - let state_val = Self::evaluate(expr, env)?; - match state_val { - // Clone the sparse state - RuntimeValue::QuantumRegister { size, state } => { - RuntimeValue::QuantumRegister { size, state } - } - _ => return Err(format!("Runtime Error: Initial state for a quantum register must be a register, got {:?}", state_val.type_name())), + _ => return Err(format!("Runtime Error: Size must be > 0 int.")), } } else { - // Default to a single qubit - let size = 1; - let state_map = Self::default_state_vector(size)?; - RuntimeValue::QuantumRegister { - size, - state: Rc::new(RefCell::new(state_map)), - } + 1 }; + + if size > 25 { + return Err(format!("Runtime Error: Size {} too large.", size)); + } + + let state_map = Self::default_state_vector(size)?; + let register = RuntimeValue::QuantumRegister { + size, + state: Rc::new(RefCell::new(state_map)), + register_name: name.to_string(), + global_start_index: None, + }; + env.borrow_mut().set(name.to_string(), register.clone()); Ok(register) } @@ -661,9 +644,9 @@ impl Evaluator { if size == 0 { return Err("Runtime Error: Quantum register size must be > 0".to_string()); } - // The |0...0> state is just one entry in the map + let mut state = HashMap::new(); - state.insert(0, (1.0, 0.0)); // |0> state has index 0 + state.insert(0, (1.0, 0.0)); Ok(state) } @@ -678,8 +661,8 @@ impl Evaluator { } let mut state = HashMap::new(); match ket_str { - "0" => state.insert(0, (1.0, 0.0)), // |0> - "1" => state.insert(1, (1.0, 0.0)), // |1> + "0" => state.insert(0, (1.0, 0.0)), + "1" => state.insert(1, (1.0, 0.0)), _ => { return Err(format!( "Runtime Error: Invalid single-qubit ket state '{}'.", @@ -690,7 +673,7 @@ impl Evaluator { Ok(state) } - // --- Math helpers --- + fn complex_mul((a, b): (f64, f64), (c, d): (f64, f64)) -> (f64, f64) { (a * c - b * d, a * d + b * c) } @@ -713,13 +696,13 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - // 1. Evaluate all qubit arguments + let mut qubit_args = Vec::new(); for arg_node in arg_nodes { qubit_args.push(Self::evaluate(arg_node, env)?); } - // 2. Evaluate the gate expression + let gate_val = Self::eval_gate_expression(gate_expr_node, env)?; let (base_name, is_dagger, num_controls) = match gate_val { @@ -736,7 +719,7 @@ impl Evaluator { } }; - // 3. Extract qubit info (state, indices, size) + let mut controls = Vec::new(); let mut targets = Vec::new(); @@ -751,7 +734,7 @@ impl Evaluator { for (i, qubit_val) in qubit_args.iter().enumerate() { let (q_state_rc, q_index, q_size) = match qubit_val { - RuntimeValue::Qubit { state, index, size } => (state.clone(), *index, *size), + RuntimeValue::Qubit { state, index, size, .. } => (state.clone(), *index, *size), _ => { return Err(format!( "Runtime Error at {}: Gate arguments must be Qubits, but argument {} was {}.", @@ -778,11 +761,11 @@ impl Evaluator { } } - // 4. Get the parameters + let mut params = Vec::new(); Self::extract_gate_params(gate_expr_node, &mut params, env)?; - // 5. Build the GateDefinition + let gate_def = GateDefinition { name: base_name, params, @@ -792,7 +775,7 @@ impl Evaluator { state_rc: state_rc.unwrap(), }; - // 6. Apply the gate + Self::apply_multi_controlled_gate(gate_def, is_dagger) } @@ -840,7 +823,7 @@ impl Evaluator { Ok(RuntimeValue::Gate { base_name, is_dagger, - num_controls: num_controls + 1, // + num_controls: num_controls + 1, }) } _ => { @@ -858,13 +841,13 @@ impl Evaluator { ASTNode::Gate { name, .. } => Ok((name.to_lowercase(), false)), ASTNode::ParameterizedGate { name, .. } => Ok((name.to_lowercase(), false)), - // Toggle dagger flag if we see 'dagger' + ASTNode::Dagger { gate_expr, .. } => { let (name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; Ok((name, !is_dagger)) }, - // Prepend 'c' for controlled gates (e.g. "x" -> "cx") + ASTNode::Controlled { gate_expr, .. } => { let (name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; Ok((format!("c{}", name), is_dagger)) @@ -917,13 +900,13 @@ impl Evaluator { loc, .. } => { - // The callee must be a base Gate name (like 'rx' or 'cphase') + let gate_name = match &**callee { ASTNode::Identifier { name, .. } => name.to_lowercase(), _ => return Err(format!("Runtime Error at {}: Gate expression must be a simple identifier inside the call.", loc)), }; - // --- Extract the numerical parameters only --- + for arg_node in arguments.iter() { let arg_val = Self::evaluate(arg_node, env)?; let float_param = match arg_val { @@ -994,18 +977,18 @@ impl Evaluator { let mut processed = std::collections::HashSet::new(); - // Iterate over the non-zero amplitudes + for (&basis_state, &_tuple) in old_state_map.iter() { if processed.contains(&basis_state) { continue; } - // Check if control bits are active + if (basis_state & control_mask) == control_mask { - // Find the partner state by flipping the target bit + let partner_state = basis_state ^ target_mask; - // Get amplitudes for both self and partner + let amp_self_tuple = amp_tuple; let amp_partner_tuple = old_state_map .get(&partner_state) @@ -1017,35 +1000,35 @@ impl Evaluator { let (amp0, amp1, idx0, idx1); - // Determine if self is |0> or |1> + if (basis_state & target_mask) == 0 { - // Self is |0> + amp0 = amp_self; amp1 = amp_partner; idx0 = basis_state; idx1 = partner_state; } else { - // Self is |1> + amp0 = amp_partner; amp1 = amp_self; idx0 = partner_state; idx1 = basis_state; } - // Apply the 2x2 matrix + let new_amp0 = matrix[0][0] * amp0 + matrix[0][1] * amp1; let new_amp1 = matrix[1][0] * amp0 + matrix[1][1] * amp1; - // Insert into the new map only if non-zero + Self::insert_if_nonzero(&mut new_state_map, idx0, new_amp0); Self::insert_if_nonzero(&mut new_state_map, idx1, new_amp1); - // Mark both states as processed + processed.insert(idx0); processed.insert(idx1); } else { - // Control bits are not active, so this state is unchanged - // + + new_state_map.insert(basis_state, amp_tuple); processed.insert(basis_state); } @@ -1084,9 +1067,9 @@ impl Evaluator { let angle = if is_dagger { -phi } else { phi }; let phase_factor = C64::from_polar(1.0, angle); - // This is an in-place modification + for (&basis_state, amp_tuple) in state_map_guard.iter_mut() { - // Only apply phase when both control and target are |1> + if (basis_state & control_mask) != 0 && (basis_state & target_mask) != 0 { let amp = C64::new(amp_tuple.0, amp_tuple.1); let new_amp = amp * phase_factor; @@ -1101,11 +1084,11 @@ impl Evaluator { let bit_b = (basis_state & target_mask) != 0; if bit_a != bit_b { - // If bits differ, swap them + let swapped_idx = basis_state ^ control_mask ^ target_mask; new_state_map.insert(swapped_idx, amp_tuple); } else { - // Bits are the same, state is unchanged + new_state_map.insert(basis_state, amp_tuple); } } @@ -1114,21 +1097,21 @@ impl Evaluator { "cnot" | "cx" => { let mut new_state_map = HashMap::new(); for (&basis_state, &_tuple) in state_map_guard.iter() { - // If control is |1> + if (basis_state & control_mask) != 0 { - let swapped_idx = basis_state ^ target_mask; // Flip target bit + let swapped_idx = basis_state ^ target_mask; new_state_map.insert(swapped_idx, amp_tuple); } else { - // Control is |0>, state is unchanged + new_state_map.insert(basis_state, amp_tuple); } } *state_map_guard = new_state_map; } "cz" => { - // In-place modification + for (&basis_state, amp_tuple) in state_map_guard.iter_mut() { - // If both are |1>, apply -1 phase + if (basis_state & control_mask) != 0 && (basis_state & target_mask) != 0 { *amp_tuple = (-amp_tuple.0, -amp_tuple.1); } @@ -1152,10 +1135,10 @@ impl Evaluator { is_dagger: bool, ) -> Result<[[C64; 2]; 2], String> { let i = C64::new(0.0, 1.0); - let eff_dagger = if is_dagger { -1.0 } else { 1.0 }; // Used for RZ, S, T, CPhase + let eff_dagger = if is_dagger { -1.0 } else { 1.0 }; match name { - // --- Standard Non-Parameterized Gates (Unchanged) --- + "x" | "not" | "cnot" | "ccx" | "toffoli" => Ok([ [C64::new(0.0, 0.0), C64::new(1.0, 0.0)], [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], @@ -1252,7 +1235,7 @@ impl Evaluator { let target_val = Self::evaluate(target_expr, env)?; let (state_rc, target_index, reg_size) = match target_val { - RuntimeValue::Qubit { state, index, size } => (state, index, size), + RuntimeValue::Qubit { state, index, size, .. } => (state, index, size), _ => { return Err(format!( "Runtime Error: 'measure' can only be used on a single Qubit, got {}.", @@ -1265,7 +1248,7 @@ impl Evaluator { } fn perform_measurement( - state_rc: &Rc>>, // + state_rc: &Rc>>, target_index: usize, total_size: usize, ) -> Result { @@ -1282,10 +1265,10 @@ impl Evaluator { let target_mask = 1 << target_index; let mut prob0 = 0.0; - // Iterate over the sparse map to calculate probability + for (&basis_state, &_tuple) in old_state_map.iter() { if (basis_state & target_mask) == 0 { - // If target bit is 0 + prob0 += amp_tuple.0.powi(2) + amp_tuple.1.powi(2); } } @@ -1311,28 +1294,28 @@ impl Evaluator { let mut new_state_map = HashMap::new(); - // Iterate and build the new sparse state + for (&basis_state, &_tuple) in old_state_map.iter() { let bit_at_index = (basis_state >> target_index) & 1; - // If this state matches the measurement, keep it + if bit_at_index as i64 == measured_result { let (real, imag) = amp_tuple; let normalized_amp = Self::complex_scalar_mul((real, imag), norm_factor); new_state_map.insert(basis_state, normalized_amp); } - // If it doesn't match, we drop it (by not inserting it) + } - // Replace the old state with the new collapsed state + *state_map_guard = new_state_map; Ok(RuntimeValue::Int(measured_result)) } - // --- - // --- Other Builtins / Helpers (Restored) - // --- + + + fn eval_array_access( collection_expr: &Box, @@ -1364,30 +1347,28 @@ impl Evaluator { elements.len() )) } - // --- UPDATED LOGIC --- - RuntimeValue::QuantumRegister { size, state } => { + + RuntimeValue::QuantumRegister { size, state, register_name, global_start_index } => { let index = match index_val { RuntimeValue::Int(i) => i, - _ => { - return Err(format!( - "Runtime Error at {}: Qubit index must be an integer.", - loc - )) - } + _ => return Err(format!("Runtime Error at {}: Index must be int.", loc)), }; if index < 0 || index as usize >= size { - return Err(format!( - "Runtime Error at {}: Qubit index {} out of bounds for register of size {}.", - loc, index, size - )); + return Err(format!("Runtime Error at {}: Index out of bounds.", loc)); } + + + let global_index = global_start_index.map(|start| start + (index as usize)); + Ok(RuntimeValue::Qubit { - state: state.clone(), // Pass the sparse state Rc + state: state.clone(), index: index as usize, - size, // Pass the total register size + size, + register_name: register_name.clone(), + global_index, }) } - // --- END UPDATE --- + RuntimeValue::Dict(map) => { let key = Self::value_to_string_key(index_val)?; map.get(&key).map(|v| v.borrow().clone()).ok_or(format!( @@ -1698,14 +1679,14 @@ impl Evaluator { instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); } - // 2. Create the instance + let mut instance = RuntimeValue::Instance { class_name: name.clone(), fields: instance_fields.clone(), methods: methods.clone(), }; - // 3. Call constructor 'init' + if let Some(constructor_node) = constructor { if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { if parameters.len() != evaluated_args.len() { @@ -1714,29 +1695,29 @@ impl Evaluator { let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); - // A. Inject 'self' + constructor_env.borrow_mut().set("self".to_string(), instance.clone()); - // B. Inject Implicit Fields (as local vars) + for (name, val) in &initial_values { constructor_env.borrow_mut().set(name.clone(), val.clone()); } - // C. Inject Parameters + for (param, arg_value) in parameters.iter().zip(evaluated_args) { constructor_env.borrow_mut().set(param.name.clone(), arg_value); } - // D. Execute Body + Self::evaluate(&body, &constructor_env)?; - // E. SMART SYNC: Only update fields if local var CHANGED + for (field_name, field_rc) in &instance_fields { if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { let local_val = local_val_rc.borrow().clone(); if let Some(initial_val) = initial_values.get(field_name) { - // Only overwrite if value actually changed (e.g. None -> 0) + if !Self::are_values_equal(&local_val, initial_val) { *field_rc.borrow_mut() = local_val; } @@ -1744,7 +1725,7 @@ impl Evaluator { } } - // Return updated instance + instance = RuntimeValue::Instance { class_name: name.clone(), fields: instance_fields, @@ -1811,7 +1792,7 @@ impl Evaluator { loc, } => { let daggered_gate_expr = match &**gate_expr { - // Simple gate: X -> dagger(X) + ASTNode::Gate { name, loc, .. } => ASTNode::Dagger { gate_expr: Box::new(ASTNode::Gate { name: name.clone(), @@ -1819,9 +1800,9 @@ impl Evaluator { }), loc: *loc, }, - // Already daggered: dagger(X) -> X + ASTNode::Dagger { gate_expr, .. } => *gate_expr.clone(), - // Controlled gate: controlled(X) -> controlled(dagger(X)) + ASTNode::Controlled { gate_expr, loc } => { let inner_gate = match &**gate_expr { ASTNode::Gate { name, loc, .. } => { @@ -1937,7 +1918,7 @@ impl Evaluator { ); } match &args[0] { - RuntimeValue::QuantumRegister { size, state } => { + RuntimeValue::QuantumRegister { size, state ,..} => { Self::print_quantum_state(state, *size, 10); Ok(RuntimeValue::None) @@ -1951,23 +1932,14 @@ impl Evaluator { pub fn builtin_measure(args: Vec) -> Result { if args.len() != 1 { - return Err("Runtime Error: 'measure' expects exactly one qubit argument.".to_string()); + return Err("Runtime Error: 'measure' expects exactly one qubit.".to_string()); } - let target_val = args.into_iter().next().unwrap(); - - let (state_rc, target_index, reg_size) = match target_val { - RuntimeValue::Qubit { state, index, size } => (state, index, size), - _ => { - return Err(format!( - "Runtime Error: 'measure' can only be used on a single Qubit, got {}.", - target_val.type_name() - )) - } + let (state_rc, target_index, reg_size) = match &args[0] { + RuntimeValue::Qubit { state, index, size, .. } => (state, *index, *size), + _ => return Err(format!("Runtime Error: Not a qubit.")) }; - - - Self::perform_measurement(&state_rc, target_index, reg_size) + Self::perform_measurement(state_rc, target_index, reg_size) } fn builtin_assert(args: Vec) -> Result { @@ -2037,7 +2009,7 @@ impl Evaluator { _ => return Err("Runtime Error: Delimiter must be a String.".to_string()) }; - // Split and convert to RuntimeValues + let elements: Vec>> = target_str .split(delim) .map(|s| { @@ -2049,13 +2021,13 @@ impl Evaluator { Ok(RuntimeValue::Register(elements)) } fn builtin_input(args: Vec) -> Result { - // 1. Print the prompt if provided + if let Some(val) = args.get(0) { print!("{}", val); let _ = std::io::stdout().flush(); } - // 2. Read the line + let mut input_buffer = String::new(); std::io::stdin() .read_line(&mut input_buffer) @@ -2198,7 +2170,7 @@ impl Evaluator { let new_value = Self::evaluate(value_expr, env)?; match target { - // Simple identifier assignment + ASTNode::Identifier { name, .. } => { if let Some(var_rc) = env.borrow().get(name) { *std::cell::RefCell::<_>::borrow_mut(&var_rc) = new_value; @@ -2255,11 +2227,11 @@ impl Evaluator { match object_val { RuntimeValue::Instance { fields, .. } => { if let Some(field_rc) = fields.get(member) { - // Update the field value + *field_rc.borrow_mut() = new_value; Ok(RuntimeValue::None) } else { - // Field doesn't exist on the instance + Err(format!("Runtime Error: Instance has no field named '{}'.", member)) } } @@ -2301,10 +2273,10 @@ impl Evaluator { let file_path = match path { ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - // file path + f.clone() } else { - // package name + format!("q_packages/{}/init.qc", f) } } @@ -2510,11 +2482,11 @@ impl Evaluator { ( RuntimeValue::QuantumRegister { size: size_a, - state: state_a_rc, + state: state_a_rc,.. }, RuntimeValue::QuantumRegister { size: size_b, - state: state_b_rc, + state: state_b_rc,.. }, ) => { let state_a = state_a_rc.borrow(); @@ -2538,6 +2510,8 @@ impl Evaluator { return Ok(RuntimeValue::QuantumRegister { size: new_size, state: Rc::new(RefCell::new(new_state)), + register_name: "tensor_result".to_string(), + global_start_index: None, }); } (l, r) => { @@ -2861,7 +2835,7 @@ impl Evaluator { Ok(RuntimeValue::None) } - //PLOT FUNCTIONS + fn builtin_graphics_create_plot(args: Vec) -> Result { let ptype_int = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => 0 }; @@ -2956,17 +2930,18 @@ impl Evaluator { Self::eval_recursive(node, env, lifecycle, simulator, &mut qubit_map, &mut next_alloc) } - fn resolve_qubits(args: &[ASTNode], map: &QubitMap) -> Result, String> { + fn resolve_qubits(args: &[ASTNode], env: &Rc>) -> Result, String> { let mut indices = Vec::new(); for arg in args { - if let ASTNode::ArrayAccess { array, index, .. } = arg { - if let ASTNode::Identifier { name, .. } = &**array { - if let ASTNode::IntLiteral(idx) = &**index { - if let Some(start) = map.get(name) { - indices.push(start + (*idx as usize)); - } - } + let val = Self::evaluate(arg, env)?; + if let RuntimeValue::Qubit { global_index, .. } = val { + if let Some(idx) = global_index { + indices.push(idx); + } else { + return Err("Runtime Error: Qubit has no hardware index (Simulator not active?).".to_string()); } + } else { + return Err("Runtime Error: Argument is not a Qubit.".to_string()); } } Ok(indices) @@ -2981,45 +2956,46 @@ impl Evaluator { next_alloc: &mut usize, ) -> Result { match node { - - ASTNode::Program(statements) => { + ASTNode::Program(statements) | ASTNode::Block(statements) => { let mut last = RuntimeValue::None; for stmt in statements { last = Self::eval_recursive(stmt, env, lifecycle, simulator, qubit_map, next_alloc)?; - if let RuntimeValue::ReturnValue(_) = last { + if let RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue = last { return Ok(last); } } Ok(last) } + ASTNode::QuantumDeclaration { name, size, .. } => { let count = if let Some(size_expr) = size { match Self::evaluate(size_expr, env)? { RuntimeValue::Int(n) => n as usize, _ => 1, } - } else { - 1 - }; + } else { 1 }; + qubit_map.insert(name.clone(), *next_alloc); - *next_alloc += count; lifecycle.register_qubits(name, count, QubitState::Classical(false)); + let dummy_state = Self::default_state_vector(count)?; let register = RuntimeValue::QuantumRegister { size: count, state: Rc::new(RefCell::new(dummy_state)), + register_name: name.clone(), + global_start_index: Some(*next_alloc), }; - env.borrow_mut().set(name.clone(), register); + env.borrow_mut().set(name.clone(), register); + *next_alloc += count; Ok(RuntimeValue::None) } ASTNode::Apply { gate_expr, arguments, loc } => { let qubit_ids = Self::extract_qubit_ids_runtime(arguments, env)?; - let (gate_name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; if Self::is_controlled_gate(gate_expr) { @@ -3034,7 +3010,8 @@ impl Evaluator { } } - let target_indices = Self::resolve_qubits(arguments, qubit_map)?; + + let target_indices = Self::resolve_qubits(arguments, env)?; let mut params = Vec::new(); Self::extract_gate_params(gate_expr, &mut params, env)?; @@ -3046,7 +3023,6 @@ impl Evaluator { } ASTNode::Measure(qubit_expr) => { - // 1. Lifecycle Check let qubit_ids = Self::extract_qubit_ids_runtime(&[*qubit_expr.clone()], env)?; let loc = Self::get_node_location(qubit_expr); for qubit_id in &qubit_ids { @@ -3054,14 +3030,11 @@ impl Evaluator { .map_err(|e| e.to_string())?; } - let indices = Self::resolve_qubits(&[*qubit_expr.clone()], qubit_map)?; - if indices.is_empty() { - return Err("Runtime Error: Could not resolve qubit for measurement.".to_string()); - } - let target = indices[0]; - let result = simulator.measure_single(target); + let indices = Self::resolve_qubits(&[*qubit_expr.clone()], env)?; + if indices.is_empty() { return Err("Runtime Error: Measure target invalid.".to_string()); } + let result = simulator.measure_single(indices[0]); Ok(RuntimeValue::Int(result as i64)) } @@ -3178,7 +3151,7 @@ impl Evaluator { match arg { ASTNode::Identifier { name: var_name, .. } => { if let Some(&start) = qubit_map.get(var_name) { - // Get size from environment + if let Some(val) = env.borrow().get(var_name) { if let RuntimeValue::QuantumRegister { size, .. } = &*val.borrow() { for i in 0..*size { @@ -3240,28 +3213,15 @@ impl Evaluator { env: &Rc>, ) -> Result, String> { let mut ids = Vec::new(); - for arg in arguments { let val = Self::evaluate(arg, env)?; - if let RuntimeValue::Qubit { index, .. } = val { - let reg_name = match arg { - ASTNode::ArrayAccess { array, .. } => { - if let ASTNode::Identifier { name, .. } = &**array { - name.clone() - } else { - "unknown".to_string() - } - } - ASTNode::Identifier { name, .. } => name.clone(), - _ => "unknown".to_string() - }; + if let RuntimeValue::Qubit { index, register_name, .. } = val { - ids.push(QubitId::new(reg_name, index)); + ids.push(QubitId::new(register_name, index)); } } - Ok(ids) } diff --git a/src/main.rs b/src/main.rs index 81c447b..57ed954 100644 --- a/src/main.rs +++ b/src/main.rs @@ -803,27 +803,37 @@ fn run_test_suite() { } fn run_test_file(filename: &str) -> Result<(), String> { - //Read source + // 1. Read source let source = fs::read_to_string(filename) .map_err(|e| format!("Failed to read file '{}': {}", filename, e))?; - // Lexical Analysis + // 2. Lexical Analysis let mut lexer = Lexer::new(&source); let tokens = lexer .tokenize() .map_err(|e| format!("Lexer error: {}", e))?; - // Syntax Analysis (Parsing) + // 3. Syntax Analysis (Parsing) let mut parser = Parser::new(tokens); let ast = parser.parse().map_err(|e| format!("Parser error: {}", e))?; - // Type Checking + // 4. Type Checking TypeChecker::check_program(&ast).map_err(|e| format!("Type error: {}", e))?; - //Interpretation (Evaluation) + // 5. Interpretation (Hybrid Engine) let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); Evaluator::register_builtins(&env); - Evaluator::evaluate_program(&ast, &env).map_err(|e| format!("Runtime Error: {}", e))?; + + let mut lifecycle = QubitLifecycleManager::new(true); + let total_qubits = count_total_qubits(&ast); + let mut simulator = NativeSimulator::new(total_qubits); + + Evaluator::evaluate_program_with_lifecycle( + &ast, + &env, + &mut lifecycle, + &mut simulator + ).map_err(|e| format!("Runtime Error: {}", e))?; Ok(()) } diff --git a/src/qubit_lifecycle.rs b/src/qubit_lifecycle.rs index 98bd9e1..b2265dd 100644 --- a/src/qubit_lifecycle.rs +++ b/src/qubit_lifecycle.rs @@ -242,7 +242,7 @@ impl QubitLifecycleManager { loc, }), - + /* (QubitState::Entangled(entangled_set), QubitOperation::ApplyGate(gate_name)) if self.strict_mode => { @@ -252,7 +252,7 @@ impl QubitLifecycleManager { operation: gate_name.clone(), loc, }) - } + }*/ _ => Ok(()), diff --git a/src/runtime.rs b/src/runtime.rs index d2a36ec..8d7851f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,4 +1,4 @@ -// src/runtime.rs +/* src/runtime.rs */ use crate::environment::GateDefinition; use crate::environment::{Environment, RuntimeValue}; @@ -19,9 +19,12 @@ pub extern "C" fn quantica_rt_new_state(num_qubits: c_int) -> StatePtr { let mut state_map: HashMap = HashMap::new(); state_map.insert(0, (1.0, 0.0)); + let register = RuntimeValue::QuantumRegister { state: Rc::new(RefCell::new(state_map)), size: num_qubits as usize, + register_name: "runtime_reg".to_string(), + global_start_index: None, }; let boxed_register = Box::new(register); @@ -35,21 +38,35 @@ pub extern "C" fn quantica_rt_measure(state_ptr: StatePtr, qubit_index: c_int) - return -1; } + let register = unsafe { &*(state_ptr as *mut RuntimeValue) }; match register { - RuntimeValue::QuantumRegister { state, size } => { + + RuntimeValue::QuantumRegister { state, size, .. } => { + let idx = qubit_index as usize; + if idx >= *size { + eprintln!("(Runtime Error) Qubit index {} out of bounds (size {}).", idx, size); + return -1; + } + + + let qubit_handle = RuntimeValue::Qubit { state: state.clone(), - index: qubit_index as usize, + index: idx, size: *size, + register_name: "runtime_reg".to_string(), + global_index: None, }; - let result = Evaluator::builtin_measure(vec![qubit_handle]); - match result { - Ok(RuntimeValue::Int(r)) => r as c_int, - Ok(_) => -2, + + let args = vec![qubit_handle]; + + match Evaluator::builtin_measure(args) { + Ok(RuntimeValue::Int(val)) => val as c_int, + Ok(_) => -1, Err(e) => { eprintln!("(Runtime Error) Measurement failed: {}", e); -1 @@ -134,8 +151,8 @@ pub extern "C" fn quantica_rt_debug_state(state_ptr: StatePtr) { let register = unsafe { &*(state_ptr as *mut RuntimeValue) }; - // 2. Check the type - if let RuntimeValue::QuantumRegister { state, size } = register { + + if let RuntimeValue::QuantumRegister { state, size,.. } = register { println!("(Quantum Runtime) Debugging state ({} qubits):", size); Evaluator::print_quantum_state(state, *size, 10); } else { @@ -188,14 +205,14 @@ unsafe fn apply_gate_unsafe( } let register = &*(state_ptr as *mut RuntimeValue); let (state_rc, reg_size) = match register { - RuntimeValue::QuantumRegister { state, size } => (state.clone(), *size), + RuntimeValue::QuantumRegister { state, size ,..} => (state.clone(), *size), _ => return Err("Invalid state pointer passed to apply_gate.".to_string()), }; let gate_name_cstr = CStr::from_ptr(gate_name_ptr); let gate_name = gate_name_cstr.to_str().unwrap_or("").to_string(); - // 3. Unwrap Dagger Flag + let is_dagger = is_dagger_int != 0; let params: Vec = if params_ptr.is_null() { From d098831e132ccb02fe91f475920ea4a4d0043205 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:05:09 +0530 Subject: [PATCH 18/27] Add files via upload --- tests/quantumv1.qc | 175 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 tests/quantumv1.qc diff --git a/tests/quantumv1.qc b/tests/quantumv1.qc new file mode 100644 index 0000000..2a33626 --- /dev/null +++ b/tests/quantumv1.qc @@ -0,0 +1,175 @@ +// Quantica Automated Regression Suite v1.1 + +let PI = 3.14159265 +print("\n🚀 STARTING AUTOMATED QUANTUM TEST SUITE\n") + + +// 1. Basic Pauli Gates (X, Y, Z) + +print("TEST 1: Pauli Gates...") +quantum q_pauli[3] + +// Test X: |0> -> |1> +apply X(q_pauli[0]) +let m_x = measure(q_pauli[0]) +assert(m_x == 1, "❌ Pauli X failed: Expected 1") + +// Test Y: |0> -> i|1> (Magnitude is 1) +apply Y(q_pauli[1]) +let m_y = measure(q_pauli[1]) +assert(m_y == 1, "❌ Pauli Y failed: Expected 1") + +// Test Z: H -> Z -> H (Should act like X) +apply Hadamard(q_pauli[2]) +apply Z(q_pauli[2]) +apply Hadamard(q_pauli[2]) +let m_z = measure(q_pauli[2]) +assert(m_z == 1, "❌ Pauli Z (Phase Flip) failed") + +print(" ✅ Pauli Gates Passed") + + +// 2. Reversibility (Hadamard & Swap) + +print("TEST 2: Reversibility (H & Swap)...") + +// --- Subtest A: Hadamard Reversibility --- +quantum q_rev_h[1] +apply Hadamard(q_rev_h[0]) +apply Hadamard(q_rev_h[0]) +let m_h = measure(q_rev_h[0]) +assert(m_h == 0, "❌ H-H Reversibility failed") + +// --- Subtest B: Swap Gate --- +quantum q_swap[2] +// Swap |10> -> |01> +apply X(q_swap[0]) // Set q0 to 1 +apply Swap(q_swap[0], q_swap[1]) // Swap q0 and q1 + +let m_s0 = measure(q_swap[0]) +let m_s1 = measure(q_swap[1]) +assert(m_s0 == 0, "❌ Swap q0 failed (Should be 0)") +assert(m_s1 == 1, "❌ Swap q1 failed (Should be 1)") + +print(" ✅ Reversibility Passed") + + +// 3. Phase Gates (S, T, RZ) via Interference + +print("TEST 3: Phase Gates (S, T)...") +quantum q_phase[2] + +// Test S: S*S = Z. So H*S*S*H = X -> |1> +apply Hadamard(q_phase[0]) +apply S(q_phase[0]) +apply S(q_phase[0]) +apply Hadamard(q_phase[0]) +let m_s = measure(q_phase[0]) +assert(m_s == 1, "❌ S Gate (Phase) failed") + +// Test T: T*T = S. So T^4 = Z. H*T^4*H = |1> +apply Hadamard(q_phase[1]) +apply T(q_phase[1]) +apply T(q_phase[1]) +apply T(q_phase[1]) +apply T(q_phase[1]) +apply Hadamard(q_phase[1]) +let m_t = measure(q_phase[1]) +assert(m_t == 1, "❌ T Gate (Phase) failed") + +print(" ✅ Phase Gates Passed") + + +// 4. Entanglement (Bell States) + +print("TEST 4: Entanglement (Bell Φ+)...") +quantum bell[2] + +// Create Bell State |Φ+> = (|00> + |11>)/√2 +apply Hadamard(bell[0]) +apply CNOT(bell[0], bell[1]) + +// Verify Entanglement (Deterministic Uncompute) +// Inverse of CNOT is CNOT. Inverse of H is H. +// Applying them in reverse should yield |00> +apply CNOT(bell[0], bell[1]) +apply Hadamard(bell[0]) + +let b0 = measure(bell[0]) +let b1 = measure(bell[1]) + +assert(b0 == 0, "❌ Bell Uncompute q0 failed") +assert(b1 == 0, "❌ Bell Uncompute q1 failed") + +print(" ✅ Bell State Passed") + + +// 5. Multi-Qubit Logic (Toffoli & GHZ) + +print("TEST 5: Multi-Qubit (Toffoli & GHZ)...") + +// Toffoli (CCX): |110> -> |111> +quantum tof[3] +apply X(tof[0]) +apply X(tof[1]) +apply CCX(tof[0], tof[1], tof[2]) + +let t0 = measure(tof[0]) +let t1 = measure(tof[1]) +let t2 = measure(tof[2]) + +assert(t0 == 1, "❌ Toffoli Control 1 corrupted") +assert(t1 == 1, "❌ Toffoli Control 2 corrupted") +assert(t2 == 1, "❌ Toffoli Target failed") + +// GHZ: |000> -> (|000> + |111>) -> Uncompute -> |000> +quantum ghz[3] +// Create +apply Hadamard(ghz[0]) +apply CNOT(ghz[0], ghz[1]) +apply CNOT(ghz[0], ghz[2]) +// Uncompute (Inverse Order) +apply CNOT(ghz[0], ghz[2]) +apply CNOT(ghz[0], ghz[1]) +apply Hadamard(ghz[0]) + +let g0 = measure(ghz[0]) +let g1 = measure(ghz[1]) +let g2 = measure(ghz[2]) + +assert(g0 == 0, "❌ GHZ q0 failed") +assert(g1 == 0, "❌ GHZ q1 failed") +assert(g2 == 0, "❌ GHZ q2 failed") + +print(" ✅ Multi-Qubit Passed") + + +// 6. Advanced Modifiers (Controlled & Dagger) + +print("TEST 6: Advanced Modifiers...") +quantum q_adv[2] + +// Test Controlled-Hadamard (CH) +// |10> -> |1> ⊗ H|0> -> |1+> +// Uncompute: Apply CH again -> |10> +apply X(q_adv[0]) // Control = 1 +apply controlled(Hadamard)(q_adv[0], q_adv[1]) // Target enters superposition +apply controlled(Hadamard)(q_adv[0], q_adv[1]) // Target exits superposition + +let c0 = measure(q_adv[0]) +let c1 = measure(q_adv[1]) +assert(c0 == 1, "❌ Controlled-H q0 failed") +assert(c1 == 0, "❌ Controlled-H q1 failed") + +// Test Dagger(S) +// S * Dagger(S) = I +quantum q_dag[1] +apply S(q_dag[0]) +apply dagger(S)(q_dag[0]) +let m_dag = measure(q_dag[0]) +assert(m_dag == 0, "❌ Dagger(S) failed") + +print(" ✅ Modifiers Passed") + + +print("\n🎉 ALL TESTS PASSED SUCCESSFULLY! 🎉") \ No newline at end of file From 83b0c7489a2d073bb02d73785637382dba09e1c0 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Fri, 12 Dec 2025 20:33:20 +0530 Subject: [PATCH 19/27] Fixed bugs to implement AI features --- src/evaluator/mod.rs | 7 + src/lexer/mod.rs | 270 ++++++++++++++++++---------------- src/main.rs | 25 ++-- src/parser/mod.rs | 334 +++++++++++++++++++++++-------------------- src/type_checker.rs | 169 +++++++++++++--------- 5 files changed, 444 insertions(+), 361 deletions(-) diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 6828bda..3062d37 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -2625,6 +2625,13 @@ impl Evaluator { (Add, RuntimeValue::String(l), RuntimeValue::String(r)) => { Ok(RuntimeValue::String(format!("{}{}", l, r))) } + + (Add, RuntimeValue::Register(l_arr), RuntimeValue::Register(r_arr)) => { + let mut new_arr = l_arr.clone(); + new_arr.extend(r_arr); + Ok(RuntimeValue::Register(new_arr)) + } + (Sub, RuntimeValue::Int(l), RuntimeValue::Int(r)) => { Ok(RuntimeValue::Int(l - r)) } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 1ef3508..d86e25d 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -1,4 +1,4 @@ -// src/lexer/mod.rs +/* src/lexer/mod.rs */ pub mod token; use token::{Token, TokenWithLocation}; @@ -39,18 +39,18 @@ impl Lexer { if self.current_char().ok() == Some('/') { if self.peek() == Some('/') { - // Single-line comment + self.advance(); self.skip_single_line_comment(); continue; } else if self.peek() == Some('*') { - // Multiline comment /* ... */ - self.advance(); // consume '/' - self.advance(); // consume '*' + + self.advance(); + self.advance(); self.skip_multiline_comment()?; continue; } else { - // Division operator + break; } } else { @@ -68,20 +68,20 @@ impl Lexer { continue; }*/ - // Check for and consume a newline *after* indentation + if self.current_char().ok() == Some('\n') { - self.advance(); // Consume the newline + self.advance(); if self.nesting_level == 0 { tokens.push(self.make_token(Token::Newline, 1)); } - continue; // Restart main loop + continue; } - // --- 3. Process the next actual token --- + let _start_col = self.column; let ch = self.current_char()?; - // 'token_result' must be a Result + let token_result = match ch { // Quantum notation '|' => { @@ -291,9 +291,9 @@ impl Lexer { tokens.push(token_result?); } - // --- Handle final dedents and EOF --- + while self.indent_stack.len() > 1 { - // Assumes 0 is root + self.indent_stack.pop(); tokens.push(self.make_token(Token::Dedent, 0)); } @@ -316,9 +316,9 @@ impl Lexer { let ch = self.current_char()?; if ch == '*' && self.peek() == Some('/') { - // Found closing */ - self.advance(); // consume '*' - self.advance(); // consume '/' + + self.advance(); + self.advance(); return Ok(()); } @@ -326,10 +326,18 @@ impl Lexer { } } + pub fn debug_print_tokens(&self, tokens: &[TokenWithLocation]) { + println!("=== TOKEN STREAM ==="); + for (i, tok) in tokens.iter().enumerate() { + println!("[{}] Line {}, Col {}: {:?}", i, tok.line, tok.column, tok.token); + } + println!("=== END TOKENS ==="); + } + fn handle_indentation(&mut self, tokens: &mut Vec) -> Result<(), String> { let mut indent_level = 0; - // 1. Consume whitespace and count indent + while !self.is_at_end() { let ch = self.current_char()?; match ch { @@ -338,40 +346,51 @@ impl Lexer { self.advance(); } '\t' => { - indent_level += 4; // Tab = 4 spaces + indent_level += 4; self.advance(); } '\r' => { - // FIX: Ignore Carriage Return, just skip it + self.advance(); } - _ => break, // Stop at content, newline, or comment + _ => break, } } - // 2. Check if the line is blank (only newline or comment ahead) - if !self.is_at_end() { - let ch = self.current_char()?; - // Check for newline OR a comment - // FIX: We don't need to check for \r here because we consumed it in the loop above - if ch == '\n' || (ch == '/' && self.peek() == Some('/')) { - // This is a blank line. Do NOT emit Indent/Dedent. - // The main loop will handle the newline or comment. - self.start_of_line = false; // Clear the flag + + if self.is_at_end() { + self.start_of_line = false; + return Ok(()); + } + + let ch = self.current_char()?; + + + if ch == '\n' { + self.start_of_line = false; + return Ok(()); + } + + + if ch == '/' { + if self.peek() == Some('/') || self.peek() == Some('*') { + self.start_of_line = false; return Ok(()); } } - // 3. Line is NOT blank. Clear start_of_line flag + self.start_of_line = false; - // 4. Process Indent/Dedent logic. + let current_indent = *self.indent_stack.last().unwrap(); if indent_level > current_indent { + self.indent_stack.push(indent_level); tokens.push(self.make_token(Token::Indent, 0)); } else if indent_level < current_indent { + while let Some(&stack_level) = self.indent_stack.last() { if stack_level <= indent_level { break; @@ -380,11 +399,16 @@ impl Lexer { tokens.push(self.make_token(Token::Dedent, 0)); } + if self.indent_stack.last() != Some(&indent_level) { - return Err(format!("Indentation error at line {}", self.line)); + return Err(format!( + "Indentation error at line {}: expected indentation to match a previous level", + self.line + )); } } + Ok(()) } @@ -393,31 +417,31 @@ impl Lexer { let start_col = self.column; let token = match ch { - // Comments + '#' => { self.skip_comment(); return Ok(None); } - // Newlines - // src/lexer/mod.rs (around line 190, the Newline case) - // Newlines + + + '\n' => { let token = Token::Newline; - // 1. ADVANCE (CONSUME THE \n) + self.advance(); - // 2. MAKE TOKEN (Length is 1, Column is now 2) - let length = 1; // \n is always one char long + + let length = 1; let token_with_loc = self.make_token(token, length); - // 3. RESET COLUMN STATE + self.line += 1; self.column = 1; - return Ok(Some(token_with_loc)); // Return the token + return Ok(Some(token_with_loc)); } // Quantum state notation |0}, |1}, |+}, |-} @@ -454,32 +478,32 @@ impl Lexer { Token::Plus } '-' => { - // Lookahead before advancing + if self.peek() == Some('>') { - self.advance(); // consume '-' - self.advance(); // consume '>' - Token::Arrow // Found '->' + self.advance(); + self.advance(); + Token::Arrow } else { - self.advance(); // consume '-' - Token::Minus // Found single '-' + self.advance(); + Token::Minus } } '*' => { - // Check for '***' (TensorProduct) first + if self.peek() == Some('*') && self.input.get(self.position + 2) == Some(&'*') { - self.advance(); // consume 1st * - self.advance(); // consume 2nd * - self.advance(); // consume 3rd * + self.advance(); + self.advance(); + self.advance(); Token::TensorProduct } - // Check for '**' (Optional: if you plan to use it for power/exponentiation) + /* else if self.peek() == Some('*') { - self.advance(); // consume 1st * - self.advance(); // consume 2nd * - Token::Power // Example: if you had a power token + self.advance(); + self.advance(); + Token::Power } */ else { - // Only consume one * + self.advance(); Token::Star } @@ -493,69 +517,69 @@ impl Lexer { Token::Percent } '=' => { - // Lookahead before advancing for all two- or three-char tokens starting with '=' + if self.peek() == Some('=') { - self.advance(); // consume first '=' - self.advance(); // consume second '=' - Token::EqualEqual // Handles '==' + self.advance(); + self.advance(); + Token::EqualEqual } else if self.peek() == Some('>') { - // Check for '=>>' or '=>' + if self.input.get(self.position + 2) == Some(&'>') { - self.advance(); // consume '=' - self.advance(); // consume '>' - self.advance(); // consume '>' - Token::PipeDouble // Handles '=>>' + self.advance(); + self.advance(); + self.advance(); + Token::PipeDouble } else { - self.advance(); // consume '=' - self.advance(); // consume '>' - Token::FatArrow // Handles '=>' + self.advance(); + self.advance(); + Token::FatArrow } } else { - self.advance(); // consume '=' - Token::Equal // Handles single '=' + self.advance(); + Token::Equal } } '!' => { - // Lookahead for '!=' + if self.peek() == Some('=') { - self.advance(); // consume ! - self.advance(); // consume = + self.advance(); + self.advance(); Token::NotEqual } else { - self.advance(); // consume single ! + self.advance(); Token::Bang } } '<' => { - // Lookahead for '<=' + if self.peek() == Some('=') { - self.advance(); // consume < - self.advance(); // consume = + self.advance(); + self.advance(); Token::LessEqual } else { - self.advance(); // consume single < + self.advance(); Token::Less } } '>' => { - // Lookahead for '>=' + if self.peek() == Some('=') { - self.advance(); // consume > - self.advance(); // consume = + self.advance(); + self.advance(); Token::GreaterEqual } else { - self.advance(); // consume single > + self.advance(); Token::Greater } } '?' => { - // Lookahead for '?.' (SafeNav) + if self.peek() == Some('.') { - self.advance(); // consume ? - self.advance(); // consume . + self.advance(); + self.advance(); Token::SafeNav } else { - self.advance(); // consume single ? + self.advance(); Token::Question } } @@ -600,37 +624,37 @@ impl Lexer { Token::Semicolon } ':' => { - // Lookahead for '::' or ':=' + if self.peek() == Some(':') { - self.advance(); // consume first : - self.advance(); // consume second : + self.advance(); + self.advance(); Token::DoubleColon } else if self.peek() == Some('=') { - self.advance(); // consume : - self.advance(); // consume = + self.advance(); + self.advance(); Token::ColonEqual } else { - self.advance(); // consume single : + self.advance(); Token::Colon } } '.' => { - // Check for '...' or '..=' + if self.peek() == Some('.') { - // Check for '..=' + if self.input.get(self.position + 2) == Some(&'=') { - self.advance(); // consume first . - self.advance(); // consume second . - self.advance(); // consume = + self.advance(); + self.advance(); + self.advance(); Token::RangeInclusive } else { - // Must be '..' (Range) - self.advance(); // consume first . - self.advance(); // consume second . + + self.advance(); + self.advance(); Token::Range } } else { - // Only consume single '.' + self.advance(); Token::Dot } @@ -657,7 +681,7 @@ impl Lexer { } fn is_quantum_bra(&self) -> bool { - // Check if it's {0| pattern + let mut i = self.position + 1; while i < self.input.len() { let ch = self.input[i]; @@ -674,7 +698,7 @@ impl Lexer { fn ket_notation(&mut self) -> Result { let start_col = self.column; - self.advance(); // skip | + self.advance(); let mut state = String::new(); while !self.is_at_end() && self.current_char()? != '}' { @@ -686,14 +710,14 @@ impl Lexer { return Err(format!("Unclosed quantum ket at line {}", self.line)); } - self.advance(); // skip } + self.advance(); let length = self.column - start_col; Ok(self.make_token(Token::KetState(state), length)) } fn bra_notation(&mut self) -> Result { let start_col = self.column; - self.advance(); // skip { + self.advance(); let mut state = String::new(); while !self.is_at_end() && self.current_char()? != '|' { @@ -705,7 +729,7 @@ impl Lexer { return Err(format!("Unclosed quantum bra at line {}", self.line)); } - self.advance(); // skip | + self.advance(); let length = self.column - start_col; Ok(self.make_token(Token::BraState(state), length)) } @@ -879,17 +903,17 @@ impl Lexer { let start_col = self.column; let mut is_float = false; - // Integer part + while !self.is_at_end() && self.current_char()?.is_ascii_digit() { self.advance(); } - // Decimal part + if !self.is_at_end() && self.current_char()? == '.' { let next = self.peek(); if next.is_some() && next.unwrap().is_ascii_digit() { is_float = true; - self.advance(); // skip . + self.advance(); while !self.is_at_end() && self.current_char()?.is_ascii_digit() { self.advance(); @@ -897,7 +921,7 @@ impl Lexer { } } - // Scientific notation + if !self.is_at_end() && (self.current_char()? == 'e' || self.current_char()? == 'E') { is_float = true; self.advance(); @@ -931,7 +955,7 @@ impl Lexer { fn string_literal(&mut self) -> Result { let start_col = self.column; - self.advance(); // skip opening " + self.advance(); let mut value = String::new(); @@ -965,14 +989,14 @@ impl Lexer { return Err("Unterminated string".to_string()); } - self.advance(); // skip closing " + self.advance(); let length = self.column - start_col; Ok(self.make_token(Token::StringLiteral(value), length)) } fn char_literal(&mut self) -> Result { let start_col = self.column; - self.advance(); // skip ' + self.advance(); if self.is_at_end() { return Err("Empty character literal".to_string()); @@ -985,12 +1009,12 @@ impl Lexer { return Err("Unterminated character literal".to_string()); } - self.advance(); // skip closing ' + self.advance(); let length = self.column - start_col; Ok(self.make_token(Token::IntLiteral(ch as i64), length)) } - // Helper methods + fn current_char(&self) -> Result { if self.is_at_end() { Err("Unexpected end of input".to_string()) @@ -1016,12 +1040,12 @@ impl Lexer { } fn skip_single_line_comment(&mut self) { - self.advance(); // Consume the second '/' + self.advance(); + - // Loop while 'current_char()' is Ok and not a newline while let Ok(ch) = self.current_char() { if ch == '\n' { - break; // Stop at the newline (don't consume it) + break; } self.advance(); } @@ -1038,7 +1062,7 @@ impl Lexer { self.start_of_line = true; } else { self.column += 1; - // Don't modify start_of_line here - let handle_indentation do it + } } } @@ -1047,13 +1071,13 @@ impl Lexer { self.position >= self.input.len() } - // src/lexer/mod.rs (around line 345) + fn make_token(&self, token: Token, length: usize) -> TokenWithLocation { - // Use saturating_sub to ensure the result does not underflow (result is 0 if length > column). + let calculated_start_column = self.column.saturating_sub(length); - // Columns start at 1. If the calculated start is 0, set it to 1. + let start_column = if calculated_start_column == 0 { 1 } else { @@ -1232,7 +1256,7 @@ mod tests { let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - // Should have: Let, Identifier, Equal, IntLiteral, Newline, Let, Identifier, Equal, IntLiteral + assert!(tokens.iter().any(|t| matches!(t.token, Token::Let))); assert!(tokens .iter() diff --git a/src/main.rs b/src/main.rs index 57ed954..bc93931 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -// src/main.rs +/* src/main.rs */ mod codegen; mod doc_generator; mod environment; @@ -55,7 +55,7 @@ enum CompilationTarget { } fn main() -> Result<(), Box> { - println!("=== Quantica Compiler v0.1 ===\n"); + println!("=== Quantica Compiler v0.1.2 ===\n"); let args: Vec = env::args().collect(); @@ -81,7 +81,7 @@ fn main() -> Result<(), Box> { } if enable_lifecycle_checking { - println!("✓ Qubit lifecycle checking enabled"); + println!("Note : Qubit lifecycle checking enabled"); } else { println!("⚠️ Qubit lifecycle checking disabled"); } @@ -204,7 +204,7 @@ fn main() -> Result<(), Box> { } if args.len() >= 2 && args[1] == "--version" { - println!("Quantica v0.1.0"); + println!("Quantica v0.1.2"); println!("Quantica By Quantica Foundation"); return Ok(()); } @@ -364,6 +364,7 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool std::process::exit(1); } }; + //lexer.debug_print_tokens(&tokens); if show_tokens { println!("=== TOKENS ==="); @@ -414,11 +415,11 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); let mut lifecycle = QubitLifecycleManager::new(true); // strict mode enabled - // 1. Scan for qubits + let total_qubits = count_total_qubits(&ast); if verbose { println!(" -> Allocated Quantum Memory: {} qubits", total_qubits); } - // 2. Initialize Native Backend + let mut simulator = NativeSimulator::new(total_qubits); if verbose { @@ -1472,13 +1473,13 @@ fn run_on_hardware( Ok(()) } fn parse_error_location(error_msg: &str) -> Option { - // Look for "at [Line X, Col Y]" + let start_bytes = error_msg.find("[Line ")?; let rest = &error_msg[start_bytes..]; let end_bytes = rest.find(']')?; - let content = &rest[6..end_bytes]; // skip "[Line " + let content = &rest[6..end_bytes]; + - // content is now "X, Col Y" let parts: Vec<&str> = content.split(", Col ").collect(); if parts.len() == 2 { let line = parts[0].parse().ok()?; @@ -1493,12 +1494,12 @@ fn count_total_qubits(node: &ASTNode) -> usize { stmts.iter().map(count_total_qubits).sum() } ASTNode::QuantumDeclaration { size, .. } => { - // If size is a literal integer, use it. Default to 1. + if let Some(size_expr) = size { if let ASTNode::IntLiteral(n) = &**size_expr { *n as usize } else { - 1 // Dynamic sizes might need more complex handling + 1 } } else { 1 @@ -1511,7 +1512,7 @@ fn count_total_qubits(node: &ASTNode) -> usize { sum } ASTNode::For { body, .. } | ASTNode::While { body, .. } => count_total_qubits(body), - // ... Recurse into other structures as needed ... + _ => 0, } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4b85d3b..8284f4b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,4 +1,4 @@ -// src/parser/mod.rs + pub mod ast; use crate::lexer::token::{Token, TokenWithLocation}; @@ -22,11 +22,15 @@ impl Parser { let mut statements = Vec::new(); while !self.is_at_end() { - // Skip newlines at top level + if self.match_token(&Token::Newline) { continue; } + if self.match_token(&Token::Dedent) { + continue; + } + statements.push(self.parse_statement()?); } @@ -35,36 +39,45 @@ impl Parser { fn parse_array_literal(&mut self) -> Result { let mut elements = Vec::new(); - // Check if the array is empty: [] + if self.check(&Token::RightBracket) { - self.advance(); // Consume ']' + self.advance(); return Ok(ASTNode::ArrayLiteral(elements)); } - // Parse elements + loop { - // Parse one element + elements.push(self.parse_expression()?); - // If the next token is ']', we are done + if self.check(&Token::RightBracket) { break; } - // Otherwise, expect a comma separator + self.expect(&Token::Comma)?; } - self.expect(&Token::RightBracket)?; // Consume ']' + self.expect(&Token::RightBracket)?; Ok(ASTNode::ArrayLiteral(elements)) } fn parse_statement(&mut self) -> Result { - // Skip newlines + + self.skip_newlines(); if self.is_at_end() { - return Err("Unexpected end of file".to_string()); + return Err("Unexpected end of file while parsing statement".to_string()); + } + + + if self.check(&Token::Dedent) { + return Err(format!( + "Unexpected Dedent token at line {}. This might indicate mismatched indentation.", + self.current()?.line + )); } match &self.current()?.token { @@ -95,10 +108,10 @@ impl Parser { Ok(ASTNode::Continue) } _ => { - // Expression statement or assignment + let expr = self.parse_expression()?; - // Check for assignment + if self.match_token(&Token::Equal) { let value = self.parse_expression()?; self.skip_newlines(); @@ -109,7 +122,7 @@ impl Parser { } self.skip_newlines(); - return Ok(expr); + Ok(expr) } } } @@ -118,7 +131,7 @@ impl Parser { let loc = self.get_loc(self.current()?); if self.match_token(&Token::Controlled) { - // Parse: controlled( ... ) + self.expect(&Token::LeftParen)?; let gate_expr = self.parse_gate_expression()?; self.expect(&Token::RightParen)?; @@ -127,7 +140,7 @@ impl Parser { loc, }) } else if self.match_token(&Token::Dagger) { - // Parse: dagger( ... ) + self.expect(&Token::LeftParen)?; let gate_expr = self.parse_gate_expression()?; self.expect(&Token::RightParen)?; @@ -136,13 +149,13 @@ impl Parser { loc, }) } else { - // Base case: A simple gate or parameterized gate + let gate_token = self.expect_identifier()?; let gate_name = self.extract_identifier_name(&gate_token)?; - // Check if it's parameterized (e.g., RX(PI)) + if self.check(&Token::LeftParen) { - self.advance(); // consume '(' + self.advance(); let mut params = Vec::new(); if !self.check(&Token::RightParen) { loop { @@ -159,7 +172,7 @@ impl Parser { loc: self.get_loc(&gate_token), }) } else { - // Simple gate (e.g., X, CNOT) + Ok(ASTNode::Gate { name: gate_name, loc: self.get_loc(&gate_token), @@ -170,18 +183,18 @@ impl Parser { fn parse_apply_statement(&mut self) -> Result { let loc = self.get_loc(self.current()?); - self.expect(&Token::Apply)?; // Consume 'apply' + self.expect(&Token::Apply)?; + + - // Check if this is "apply dagger(...)(args)" for CIRCUITS/FUNCTIONS (not gates) - // Detect pattern: dagger(identifier...)( if self.check(&Token::Dagger) { let saved_pos = self.position; - self.advance(); // consume 'dagger' + self.advance(); if self.check(&Token::LeftParen) { - self.advance(); // consume '(' + self.advance(); + - // Peek ahead - if it's a gate token or 'controlled', parse as gate let is_gate_expr = matches!( self.current().ok().map(|t| &t.token), Some(Token::X) @@ -207,16 +220,16 @@ impl Parser { | Some(Token::Dagger) ); - // Reset position + self.position = saved_pos; if is_gate_expr { - // Parse as: apply dagger(gate_expr)(qubit_args) - // This is a gate application, not a circuit call - // Fall through to regular gate parsing below + + + } else { - // Parse as: apply dagger(circuit_identifier)(args) - self.advance(); // consume 'dagger' + + self.advance(); self.expect(&Token::LeftParen)?; let callee_expr = self.parse_expression()?; self.expect(&Token::RightParen)?; @@ -232,12 +245,12 @@ impl Parser { }); } } else { - // Reset if no '(' after dagger + self.position = saved_pos; } } - // Lookahead to detect if this is a circuit/function call (not a gate) + let saved_pos = self.position; let mut is_function_call = false; @@ -299,17 +312,17 @@ impl Parser { }); } - // Parse as a gate application + let apply_node = self.parse_gate_application(loc)?; self.skip_newlines(); Ok(apply_node) } fn parse_gate_application(&mut self, loc: Loc) -> Result { - // Special handling for simple gate names like X, H, etc. - // These should parse as: apply X(qubit_args), not apply X(...)(qubit_args) - // Check if this is a simple gate (not controlled or dagger) + + + let is_simple_gate = matches!( self.current()?.token, Token::X @@ -333,12 +346,12 @@ impl Parser { Token::RX | Token::RY | Token::RZ | Token::U | Token::CPhase ); - // If it's a simple gate or parameterized gate at top level, parse as: GATE(qubit_args) + if is_simple_gate || is_parameterized_gate { let gate_token = self.expect_identifier()?; let gate_name = self.extract_identifier_name(&gate_token)?; - // For parameterized gates, parse parameters first + if is_parameterized_gate { self.expect(&Token::LeftParen)?; let mut params = Vec::new(); @@ -358,7 +371,7 @@ impl Parser { loc, }; - // Now parse qubit arguments + self.skip_newlines(); self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; @@ -369,13 +382,13 @@ impl Parser { loc, }); } else { - // Simple gate + let gate_expr = ASTNode::Gate { name: gate_name, loc, }; - // Now parse qubit arguments + self.skip_newlines(); self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; @@ -388,13 +401,13 @@ impl Parser { } } - // For controlled/dagger gates, use parse_gate_expression + let gate_expr = self.parse_gate_expression()?; - // Skip any newlines before the qubit arguments + self.skip_newlines(); - // Now expect '(' for the qubit arguments + if !self.check(&Token::LeftParen) { let current_token = self.current()?; return Err(format!( @@ -409,8 +422,8 @@ impl Parser { current_token.line, current_token.column, current_token.token )); } - self.advance(); // Consume '(' - let arguments = self.parse_arguments()?; // This consumes ')' + self.advance(); + let arguments = self.parse_arguments()?; Ok(ASTNode::Apply { gate_expr: Box::new(gate_expr), @@ -634,9 +647,16 @@ impl Parser { self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; + + + self.skip_newlines(); self.expect(&Token::RightParen)?; + + self.skip_layout_tokens(); + let return_type = if self.match_token(&Token::Arrow) { + self.skip_newlines(); Some(self.parse_type()?) } else { None @@ -664,7 +684,6 @@ impl Parser { let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; - // Optional inheritance let superclass = if self.match_token(&Token::LeftParen) { let super_loc = self.expect_identifier()?; let super_name = self.extract_identifier_name(&super_loc)?; @@ -688,65 +707,58 @@ impl Parser { while !self.check(&Token::Dedent) && !self.is_at_end() { self.skip_newlines(); - // --- FIX START: Handle spurious Dedent caused by empty lines --- + + if self.check(&Token::Class) { + break; + } + + if self.check(&Token::Dedent) { - // Peek ahead: If this Dedent is followed by Newlines and then an Indent, - // it's just a glitch caused by an empty line resetting indentation. + let mut temp_pos = self.position + 1; while temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Newline) { temp_pos += 1; } - if temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Indent) { - // It is spurious! Consume the Dedent, Newlines, and the restoring Indent. - self.advance(); // consume Dedent - self.skip_newlines(); // consume Newlines - self.advance(); // consume Indent - continue; // Continue parsing class members + self.advance(); + self.skip_newlines(); + self.advance(); + continue; } - - // If not followed by Indent, it's a real Dedent (end of class). break; } - // --- FIX END --- - // Check for visibility modifier - let is_public = if self.match_token(&Token::Public) { - true - } else if self.match_token(&Token::Private) { - false - } else { - true // default to public - }; - // Check for static modifier + let is_public = if self.match_token(&Token::Public) { true } + else if self.match_token(&Token::Private) { false } + else { true }; let is_static = self.match_token(&Token::Static); if self.check(&Token::Func) { - // Parse method self.advance(); let method_name_loc = self.expect_identifier()?; let method_name = self.extract_identifier_name(&method_name_loc)?; self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; + + + self.skip_newlines(); self.expect(&Token::RightParen)?; + + self.skip_layout_tokens(); + let return_type = if self.match_token(&Token::Arrow) { + self.skip_newlines(); Some(self.parse_type()?) } else { None }; - if self.check(&Token::Colon) { - self.advance(); - } - - // FIX: Do NOT call self.skip_newlines() here. - // Let parse_block handle the layout. + if self.check(&Token::Colon) { self.advance(); } let body = self.parse_block()?; - // Check if this is a constructor if method_name == "init" || method_name == "__init__" { constructor = Some(Box::new(ASTNode::FunctionDeclaration { name: method_name.clone(), @@ -765,11 +777,8 @@ impl Parser { }); } } else if self.check(&Token::Let) || self.check(&Token::Mut) || matches!(self.current()?.token, Token::Identifier(_)) { - // Parse field let is_mutable = self.match_token(&Token::Mut); - if !is_mutable { - self.match_token(&Token::Let); - } + if !is_mutable { self.match_token(&Token::Let); } let field_name_loc = self.expect_identifier()?; let field_name = self.extract_identifier_name(&field_name_loc)?; @@ -777,7 +786,7 @@ impl Parser { let field_type = if self.match_token(&Token::Colon) { self.parse_type()? } else { - Type::Any // Infer type as Any if not specified + Type::Any }; let default_value = if self.match_token(&Token::Equal) { @@ -785,29 +794,20 @@ impl Parser { } else { None }; - self.skip_newlines(); - - fields.push(ClassField { - name: field_name, - field_type, - default_value, - is_public, - }); + fields.push(ClassField { name: field_name, field_type, default_value, is_public }); } else { return Err(format!("Expected field or method declaration in class at {}", loc)); } } - self.expect(&Token::Dedent)?; + + if !self.check(&Token::Class) { + self.expect(&Token::Dedent)?; + } Ok(ASTNode::ClassDeclaration { - name, - superclass, - fields, - methods, - constructor, - loc, + name, superclass, fields, methods, constructor, loc, }) } @@ -836,9 +836,16 @@ impl Parser { self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; + + + self.skip_newlines(); self.expect(&Token::RightParen)?; + + self.skip_layout_tokens(); + let return_type = if self.match_token(&Token::Arrow) { + self.skip_newlines(); Some(self.parse_type()?) } else { None @@ -860,11 +867,14 @@ impl Parser { fn parse_parameters(&mut self) -> Result, String> { let mut params = Vec::new(); + self.skip_newlines(); + if self.check(&Token::RightParen) { return Ok(params); } loop { + self.skip_newlines(); let name_loc = self.expect_identifier()?; let name = self.extract_identifier_name(&name_loc)?; @@ -872,34 +882,39 @@ impl Parser { let param_type = self.parse_type()?; params.push(Parameter { name, param_type }); + self.skip_newlines(); if !self.match_token(&Token::Comma) { break; } } - + self.skip_newlines(); Ok(params) } fn parse_arguments(&mut self) -> Result, String> { let mut arguments = Vec::new(); + self.skip_newlines(); if !self.check(&Token::RightParen) { loop { + self.skip_newlines(); arguments.push(self.parse_expression()?); + self.skip_newlines(); if !self.match_token(&Token::Comma) { break; } } } - self.expect(&Token::RightParen)?; // <-- THIS MUST CONSUME THE FINAL ')' + self.skip_newlines(); + self.expect(&Token::RightParen)?; Ok(arguments) } fn parse_type(&mut self) -> Result { let type_token = self.current()?.token.clone(); - let base_type = match type_token { - // Built-in Type Tokens + let mut base_type = match type_token { + Token::Int => { self.advance(); Type::Int @@ -957,7 +972,7 @@ impl Parser { Type::Any } - // --- FIXED THIS LINE --- + Token::Quantum => { self.advance(); Type::QuantumRegister(None) @@ -975,20 +990,20 @@ impl Parser { _ => return Err(format!("Expected a type, but found {:?}", type_token)), }; - if self.match_token(&Token::LeftBracket) { + while self.match_token(&Token::LeftBracket) { if self.check(&Token::RightBracket) { self.advance(); - return Ok(Type::Array(Box::new(base_type))); - } - - let size = self.parse_expression()?; - self.expect(&Token::RightBracket)?; + base_type = Type::Array(Box::new(base_type)); + } else { - if let ASTNode::IntLiteral(n) = size { - return Ok(Type::QuantumArray(Box::new(base_type), Some(n as usize))); + let size = self.parse_expression()?; + self.expect(&Token::RightBracket)?; + if let ASTNode::IntLiteral(n) = size { + base_type = Type::QuantumArray(Box::new(base_type), Some(n as usize)); + } else { + base_type = Type::QuantumArray(Box::new(base_type), None); + } } - - return Ok(Type::QuantumArray(Box::new(base_type), None)); } Ok(base_type) @@ -1175,43 +1190,52 @@ impl Parser { let mut statements = Vec::new(); - // Expect Indent + if !self.check(&Token::Indent) { - // Empty block? But for functions, expect indent. + return Err("Expected indented block after ':'".to_string()); } - self.advance(); // consume Indent + self.advance(); loop { - // Skip newlines + self.skip_newlines(); - // *** REMOVED: if self.check(&Token::Dedent) { break; } <-- This blocked the fix below - // --- FIX START: Handle spurious Dedent caused by empty lines --- if self.check(&Token::Dedent) { + let mut temp_pos = self.position + 1; + + while temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Newline) { temp_pos += 1; } + + if temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Indent) { - self.advance(); // consume Dedent - self.skip_newlines(); // consume Newlines - self.advance(); // consume Indent + self.advance(); + self.skip_newlines(); + self.advance(); continue; } else { - // No, it's a real Dedent. The block is finished. + break; } } - // Parse the next statement + if self.is_at_end() { + break; + } + + statements.push(self.parse_statement()?); } - // Consume the final Dedent of the block - self.expect(&Token::Dedent)?; + + if self.check(&Token::Dedent) { + self.expect(&Token::Dedent)?; + } Ok(ASTNode::Block(statements)) } @@ -1351,7 +1375,7 @@ impl Parser { } fn parse_factor(&mut self) -> Result { - let mut expr = self.parse_power()?; // ← CHANGED: was parse_tensor_product + let mut expr = self.parse_power()?; while let Some(op_token) = self.match_tokens_loc(&[Token::Star, Token::Slash, Token::Percent]) { @@ -1362,7 +1386,7 @@ impl Parser { _ => unreachable!(), }; let loc = self.get_loc(&op_token); - let right = self.parse_power()?; // ← CHANGED: was parse_tensor_product + let right = self.parse_power()?; expr = ASTNode::Binary { operator, left: Box::new(expr), @@ -1412,11 +1436,11 @@ impl Parser { fn parse_power(&mut self) -> Result { let mut expr = self.parse_tensor_product()?; - // Power is RIGHT-associative: 2^3^4 = 2^(3^4) + if let Some(op_token) = self.match_tokens_loc(&[Token::Caret]) { let operator = BinaryOperator::Power; let loc = self.get_loc(&op_token); - let right = self.parse_power()?; // ← Recursive for right-associativity + let right = self.parse_power()?; expr = ASTNode::Binary { operator, left: Box::new(expr), @@ -1458,7 +1482,7 @@ impl Parser { let name_loc = self.expect_identifier()?; let member = self.extract_identifier_name(&name_loc)?; - // Check if this is a method call + if self.check(&Token::LeftParen) { let loc = self.get_loc(self.current()?); self.advance(); @@ -1496,7 +1520,7 @@ impl Parser { self.advance(); } - // If we hit Indent/Dedent here, something is wrong with statement parsing + if self.check(&Token::Indent) || self.check(&Token::Dedent) { let current_loc = self.current()?.clone(); return Err(format!( @@ -1509,7 +1533,7 @@ impl Parser { let token = ¤t_loc.token; let loc = self.get_loc(¤t_loc); - // This match must return a Result + match token { Token::If => self.parse_if(), Token::Print => { @@ -1527,23 +1551,23 @@ impl Parser { }) } - // --- *** MODIFIED: Token::Identifier *** --- + Token::Identifier(name) => { self.advance(); - // This arm NO LONGER handles calls or member access. - // It just returns the identifier. + + Ok(ASTNode::Identifier { name: name.clone(), loc, }) } - // --- *** END MODIFIED *** --- - // --- *** MODIFIED: Token::Dagger *** --- + + Token::Dagger => { - self.advance(); // Consume 'dagger' + self.advance(); self.expect(&Token::LeftParen)?; - let callee_expr = self.parse_expression()?; // e.g., 'my_circuit' or 'qstd.bell' + let callee_expr = self.parse_expression()?; self.expect(&Token::RightParen)?; let call_loc = self.get_loc(self.current()?); @@ -1571,7 +1595,7 @@ impl Parser { Ok(ASTNode::Measure(Box::new(qubit))) } - // --- LITERALS --- + Token::IntLiteral(n) => { self.advance(); Ok(ASTNode::IntLiteral(*n)) @@ -1605,7 +1629,7 @@ impl Parser { Ok(ASTNode::QuantumBra(state.clone())) } - // --- GROUPING & COLLECTIONS --- + Token::LeftParen => { self.advance(); let expr = self.parse_expression()?; @@ -1657,7 +1681,7 @@ impl Parser { } } - // --- *** DELETED 'parse_call_or_dagger' *** --- + fn skip_layout_tokens(&mut self) { while self.check(&Token::Newline) @@ -1703,7 +1727,7 @@ impl Parser { Ok(ASTNode::DictLiteral(pairs)) } - // Helper methods + fn current(&self) -> Result<&TokenWithLocation, String> { if self.is_at_end() { Err("Unexpected end of file".to_string()) @@ -1758,19 +1782,19 @@ impl Parser { self.advance(); Ok(()) } else { - // Logic to find where we are, even if we hit EOF + let (loc, current_str) = if self.position >= self.tokens.len() { - // We are past the end, use the last token's location + let last_loc = if !self.tokens.is_empty() { let last = self.tokens.last().unwrap(); - // Point to just after the last token + Loc { line: last.line, column: last.column + 1 } } else { Loc { line: 1, column: 1 } }; (last_loc, "EOF".to_string()) } else { - // We are at a valid token + let curr = &self.tokens[self.position]; ( Loc { line: curr.line, column: curr.column }, @@ -1778,7 +1802,7 @@ impl Parser { ) }; - // Return a standard formatted error string + Err(format!( "Syntax Error at {}: Expected {:?}, but found {}", loc, token, current_str @@ -1802,10 +1826,10 @@ impl Parser { Token::CS => Ok("cs".to_string()), Token::CT => Ok("ct".to_string()), Token::CPhase => Ok("cphase".to_string()), - Token::U => Ok("u".to_string()), // ← Add these - Token::RX => Ok("rx".to_string()), // ← Add these - Token::RY => Ok("ry".to_string()), // ← Add these - Token::RZ => Ok("rz".to_string()), // ← Add these + Token::U => Ok("u".to_string()), + Token::RX => Ok("rx".to_string()), + Token::RY => Ok("ry".to_string()), + Token::RZ => Ok("rz".to_string()), Token::CCX => Ok("ccx".to_string()), Token::Toffoli => Ok("toffoli".to_string()), _ => Err(format!( @@ -1823,7 +1847,7 @@ impl Parser { Token::Hadamard | Token::Cnot | Token::X | Token::Y | Token::Z | Token::S | Token::T | Token::Swap | Token::Reset | Token::CZ | Token::CS | Token::CT | Token::CPhase | - Token::U | Token::RX | Token::RY | Token::RZ | // ← Fixed: removed duplicate Token::U + Token::U | Token::RX | Token::RY | Token::RZ | Token::CCX | Token::Toffoli => { let owned_token_loc = current_loc.clone(); @@ -1838,7 +1862,7 @@ impl Parser { while self.match_token(&Token::Newline) {} } - // Tests + } #[cfg(test)] mod tests { diff --git a/src/type_checker.rs b/src/type_checker.rs index 95e72dd..765c4bb 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1,4 +1,4 @@ -//src/typechecker.rs + use crate::parser::ast::{ASTNode, BinaryOperator, ImportSpec, Type, UnaryOperator}; use std::cell::RefCell; @@ -367,6 +367,31 @@ impl TypeChecker { Self::prefill_environment(&env); + + for stmt in statements { + match stmt { + ASTNode::FunctionDeclaration { name, parameters, return_type, .. } => { + let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + let rt = return_type.clone().unwrap_or(Type::Any); + let func_type = Type::Function(param_types, Box::new(rt)); + env.borrow_mut().set(name.clone(), Self::immutable_info(func_type)); + }, + ASTNode::CircuitDeclaration { name, parameters, return_type, .. } => { + let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + let rt = return_type.clone().unwrap_or(Type::Any); + let func_type = Type::Function(param_types, Box::new(rt)); + env.borrow_mut().set(name.clone(), Self::immutable_info(func_type)); + }, + ASTNode::ClassDeclaration { name, .. } => { + let class_type = Type::Class(name.clone()); + env.borrow_mut().set(name.clone(), TypeInfo { var_type: class_type, is_mutable: false }); + }, + _ => {} + } + } + + + for stmt in statements { Self::check_with_lifecycle(stmt, &env, None, &mut lifecycle)?; } @@ -903,7 +928,6 @@ impl TypeChecker { let new_type = Self::check(value, env, Option::None)?; match target.as_ref() { - ASTNode::Identifier { name, .. } => { let original_info = match env.borrow().get(name) { Some(info) => info, @@ -912,11 +936,13 @@ impl TypeChecker { if !original_info.is_mutable { return Err(format!("Mutability Error: Cannot assign to immutable variable '{}'.", name)); } - if original_info.var_type != new_type && - original_info.var_type != Type::Any && - new_type != Type::None && - new_type != Type::Any { + + if original_info.var_type != new_type + && original_info.var_type != Type::Any + && new_type != Type::None + && new_type != Type::Any + { let is_quantum_compat = matches!( (&new_type, &original_info.var_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) @@ -927,14 +953,32 @@ impl TypeChecker { _ => false, }; - if !is_quantum_compat && !is_class_compat { + + let is_array_compat = match (&original_info.var_type, &new_type) { + (Type::Array(t1), Type::Array(t2)) => **t1 == Type::Any || **t2 == Type::Any, + _ => false + }; + + + if !is_quantum_compat && !is_class_compat && !is_array_compat { return Err(format!( "Type Error: Mismatched types in assignment. Cannot assign type {:?} to variable '{}' of type {:?}", new_type, name, original_info.var_type )); } + } + + + if let Type::Array(inner) = &original_info.var_type { + if **inner == Type::Any { + env.borrow_mut().set(name.clone(), TypeInfo { + var_type: new_type, + is_mutable: true + }); + } } + Ok(Type::None) } @@ -1006,6 +1050,11 @@ impl TypeChecker { (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); + let is_array_compat = match (&field_info.var_type, &new_type) { + (Type::Array(t1), Type::Array(t2)) => **t1 == Type::Any || **t2 == Type::Any, + _ => false + }; + if !is_compat && new_type != field_info.var_type { return Err(format!( "Type Error: Cannot assign type {:?} to field '{}.{}' of type {:?}", @@ -1059,44 +1108,46 @@ impl TypeChecker { } } ASTNode::ClassDeclaration { name, fields, methods, constructor, .. } => { - let class_type = Type::Class(name.clone()); env.borrow_mut().set( name.clone(), TypeInfo { var_type: class_type, is_mutable: false } ); - let class_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); for field in fields { class_env.borrow_mut().set( field.name.clone(), - TypeInfo { - var_type: field.field_type.clone(), - is_mutable: true, - } + TypeInfo { var_type: field.field_type.clone(), is_mutable: true } ); let global_field_key = format!("{}::{}", name, field.name); env.borrow_mut().set( global_field_key, - TypeInfo { - var_type: field.field_type.clone(), - is_mutable: true, - } + TypeInfo { var_type: field.field_type.clone(), is_mutable: true } ); } + for method in methods { + let method_param_types: Vec = method.parameters.iter() + .map(|p| p.param_type.clone()) + .collect(); + let method_return_type = method.return_type.clone().unwrap_or(Type::None); + let method_func_type = Type::Function(method_param_types, Box::new(method_return_type)); + + let global_method_key = format!("{}::{}", name, method.name); + env.borrow_mut().set( + global_method_key, + TypeInfo { var_type: method_func_type, is_mutable: false } + ); + } if let Some(constructor_node) = constructor { if let ASTNode::FunctionDeclaration { body, parameters, .. } = &**constructor_node { - let constructor_env = Rc::new(RefCell::new( - TypeEnvironment::new_enclosed(class_env.clone()) - )); - + let constructor_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(class_env.clone()))); let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); @@ -1106,68 +1157,31 @@ impl TypeChecker { TypeInfo { var_type: ctor_type, is_mutable: false } ); - - constructor_env.borrow_mut().set( - "self".to_string(), - TypeInfo { - var_type: Type::Instance(name.clone()), - is_mutable: false, - } - ); - + constructor_env.borrow_mut().set("self".to_string(), TypeInfo { + var_type: Type::Instance(name.clone()), is_mutable: false, + }); for param in parameters { - constructor_env.borrow_mut().set( - param.name.clone(), - TypeInfo { - var_type: param.param_type.clone(), - is_mutable: false, - } - ); + constructor_env.borrow_mut().set(param.name.clone(), TypeInfo { + var_type: param.param_type.clone(), is_mutable: false, + }); } - Self::check(body, &constructor_env, Some(&Type::None))?; } } for method in methods { + let method_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(class_env.clone()))); - let method_param_types: Vec = method.parameters.iter() - .map(|p| p.param_type.clone()) - .collect(); - let method_return_type = method.return_type.clone().unwrap_or(Type::None); - - - let method_func_type = Type::Function(method_param_types, Box::new(method_return_type)); - - - let global_method_key = format!("{}::{}", name, method.name); - env.borrow_mut().set( - global_method_key, - TypeInfo { var_type: method_func_type, is_mutable: false } - ); - let method_env = Rc::new(RefCell::new( - TypeEnvironment::new_enclosed(class_env.clone()) - )); - - method_env.borrow_mut().set( - "self".to_string(), - TypeInfo { - var_type: Type::Instance(name.clone()), - is_mutable: false, - } - ); - + method_env.borrow_mut().set("self".to_string(), TypeInfo { + var_type: Type::Instance(name.clone()), is_mutable: false, + }); for param in &method.parameters { - method_env.borrow_mut().set( - param.name.clone(), - TypeInfo { - var_type: param.param_type.clone(), - is_mutable: false, - } - ); + method_env.borrow_mut().set(param.name.clone(), TypeInfo { + var_type: param.param_type.clone(), is_mutable: false, + }); } let return_type = method.return_type.as_ref().unwrap_or(&Type::None); @@ -1579,6 +1593,19 @@ impl TypeChecker { (Type::Float, Type::Int) => Ok(Type::Float), (Type::String, Type::String) => Ok(Type::String), + (Type::Array(t1), Type::Array(t2)) => { + if **t1 == Type::Any { + Ok(Type::Array(t2.clone())) + } else if **t2 == Type::Any { + Ok(Type::Array(t1.clone())) + } else if t1 == t2 { + Ok(Type::Array(t1.clone())) + } else { + + Ok(Type::Array(Box::new(Type::Any))) + } + } + (Type::Any, _) | (_, Type::Any) => Ok(Type::Any), _ => Err(format!("Type Error at {}: Cannot add types {:?} and {:?}", loc, left_type, right_type)), } From 11f45f07edc3e2baeae6b0836cc021157c369faf Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:47:56 +0530 Subject: [PATCH 20/27] Tensor Support (***) for Ai and ML applications --- src/evaluator/mod.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 3062d37..de294b1 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -2488,6 +2488,8 @@ impl Evaluator { size: size_b, state: state_b_rc,.. }, + + ) => { let state_a = state_a_rc.borrow(); let state_b = state_b_rc.borrow(); @@ -2513,7 +2515,31 @@ impl Evaluator { register_name: "tensor_result".to_string(), global_start_index: None, }); + } + + (RuntimeValue::Register(vec_a), RuntimeValue::Register(vec_b)) => { + if vec_a.len() != vec_b.len() { + return Err("Dimension mismatch for dot product".to_string()); + } + + let mut sum = 0.0; + for (a_rc, b_rc) in vec_a.iter().zip(vec_b.iter()) { + let a = match *a_rc.borrow() { + RuntimeValue::Float(f) => f, + RuntimeValue::Int(i) => i as f64, + _ => return Err("Tensor product requires numbers".to_string()) + }; + let b = match *b_rc.borrow() { + RuntimeValue::Float(f) => f, + RuntimeValue::Int(i) => i as f64, + _ => return Err("Tensor product requires numbers".to_string()) + }; + sum += a * b; + } + return Ok(RuntimeValue::Float(sum)); + } + (l, r) => { return Err(format!( "Runtime Error at {}: Operator {:?} not defined for types {:?} and {:?}", From 47707d8560f0929aaa043e7fa20199c179385d49 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:00:28 +0530 Subject: [PATCH 21/27] bug fixed related to tensor --- src/type_checker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/type_checker.rs b/src/type_checker.rs index 765c4bb..e1d7617 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1572,9 +1572,9 @@ impl TypeChecker { } (Type::Array(t1), Type::Array(t2)) => { if t1 == t2 { - return Ok(Type::Array(t1)); + return Ok(*t1); } else { - return Ok(Type::Array(Box::new(Type::Any))); + return Ok(Type::Float); } } _ => return Err(format!( From 29891cb4fa7e80ff96fd7986fc5aa3eb1c686a82 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:03:11 +0530 Subject: [PATCH 22/27] Add files via upload --- tests/saveload.qc | 33 +++++++++++++++++++++++++++++++++ tests/tensortest.qc | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 tests/saveload.qc create mode 100644 tests/tensortest.qc diff --git a/tests/saveload.qc b/tests/saveload.qc new file mode 100644 index 0000000..a739227 --- /dev/null +++ b/tests/saveload.qc @@ -0,0 +1,33 @@ +import "stdlib/ai.qc" as ai + +print("--- 1. Training a Model ---") +let nn = new ai.NeuralNetwork(0.5) +nn.add_layer(2, 2, "sigmoid") +nn.add_layer(2, 1, "sigmoid") + +// Train on XOR (briefly) +let dataset = new ai.Dataset() +dataset.add_sample([0.0, 0.0], [0.0]) +dataset.add_sample([0.0, 1.0], [1.0]) +dataset.add_sample([1.0, 0.0], [1.0]) +dataset.add_sample([1.0, 1.0], [0.0]) + +ai.train_network(nn, dataset, 500, 1, False) + +print("Prediction BEFORE save (0,1): " + to_string(nn.predict([0.0, 1.0])[0])) + +// Save +nn.save("xor_model.qmodel") + +print("\n--- 2. Loading into NEW Model ---") +let nn2 = new ai.NeuralNetwork(0.1) // Dummy LR +nn2.load("xor_model.qmodel") + +print("Prediction AFTER load (0,1): " + to_string(nn2.predict([0.0, 1.0])[0])) + +// Verify +let diff = nn.predict([0.0, 1.0])[0] - nn2.predict([0.0, 1.0])[0] +if diff < 0.00001: + print("✅ SUCCESS: Model restored perfectly.") +else: + print("❌ FAILURE: Models do not match.") \ No newline at end of file diff --git a/tests/tensortest.qc b/tests/tensortest.qc new file mode 100644 index 0000000..c37fdf0 --- /dev/null +++ b/tests/tensortest.qc @@ -0,0 +1,38 @@ +print("=== QUANTICA GENERAL TENSOR TEST ===\n") +print("--- 1. Finance: Calculating Total Cost ---") +let quantities = [2.0, 5.0, 3.0] +let prices = [0.50, 0.20, 0.80] +let total_cost = quantities *** prices + +print("Quantities: " + to_string(quantities)) +print("Prices: " + to_string(prices)) +print("Total Cost: $" + to_string(total_cost)) +assert(total_cost == 4.4, "Shopping bill calculation") + +print("\n--- 2. Physics: Work Calculation ---") +let force = [10.0, 20.0, 5.0] +let displacement = [2.0, 0.0, 4.0] +let work_done = force *** displacement + +print("Force Vector: " + to_string(force)) +print("Move Vector: " + to_string(displacement)) +print("Work Done: " + to_string(work_done) + " Joules") + +assert(work_done == 40.0, "Work calculation") + + + + + +print("\n--- 3. Grading: Weighted Average ---") +let scores = [90.0, 80.0, 95.0] +let weights = [0.2, 0.3, 0.5] + +let final_grade = scores *** weights + +print("Scores: " + to_string(scores)) +print("Weights: " + to_string(weights)) +print("Weighted Grade: " + to_string(final_grade)) + +assert(final_grade == 89.5, "Weighted grade calculation") +print("\n ALL GENERAL TENSOR TESTS PASSED!") \ No newline at end of file From f7bd6b592d089b52774688b68b6c7263303aa3eb Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:03:45 +0530 Subject: [PATCH 23/27] Add files via upload --- stdlib/ai.qc | 911 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 911 insertions(+) create mode 100644 stdlib/ai.qc diff --git a/stdlib/ai.qc b/stdlib/ai.qc new file mode 100644 index 0000000..b07244f --- /dev/null +++ b/stdlib/ai.qc @@ -0,0 +1,911 @@ +// stdlib/ai.qc +/* + Quantica Standard AI/ML Library + Provides neural networks, training algorithms, and ML utilities +*/ + +import "stdlib/math.qc" as math + +/* ============================================ + ACTIVATION FUNCTIONS + ============================================ */ + +/* Sigmoid activation function */ +func sigmoid(x: Float) -> Float: + return 1.0 / (1.0 + math.exp(-x)) + +/* Sigmoid derivative */ +func sigmoid_derivative(x: Float) -> Float: + let s = sigmoid(x) + return s * (1.0 - s) + +/* Hyperbolic tangent activation */ +func tanh(x: Float) -> Float: + return math.tanh(x) + +/* Tanh derivative */ +func tanh_derivative(x: Float) -> Float: + let t = math.tanh(x) + return 1.0 - t * t + +/* ReLU (Rectified Linear Unit) */ +func relu(x: Float) -> Float: + return math.relu(x) + +/* ReLU derivative */ +func relu_derivative(x: Float) -> Float: + if x > 0.0: + return 1.0 + return 0.0 + +/* Leaky ReLU */ +func leaky_relu(x: Float, alpha: Float) -> Float: + return math.leaky_relu(x, alpha) + +/* Leaky ReLU derivative */ +func leaky_relu_derivative(x: Float, alpha: Float) -> Float: + if x > 0.0: + return 1.0 + return alpha + +/* Softmax for output layer */ +func softmax(inputs: Float[]) -> Float[]: + let n = len(inputs) + mut max_val = inputs[0] + + // Find max for numerical stability + mut i = 1 + while i < n: + if inputs[i] > max_val: + max_val = inputs[i] + i = i + 1 + + // Compute exp(x - max) and sum + mut exp_sum = 0.0 + mut exp_values = [] + i = 0 + while i < n: + let exp_val = math.exp(inputs[i] - max_val) + exp_values = exp_values + [exp_val] + exp_sum = exp_sum + exp_val + i = i + 1 + + // Normalize + mut result = [] + i = 0 + while i < n: + result = result + [exp_values[i] / exp_sum] + i = i + 1 + + return result + +/* ============================================ + LOSS FUNCTIONS + ============================================ */ + +/* Mean Squared Error loss */ +func mse_loss(predicted: Float[], actual: Float[]) -> Float: + let n = len(predicted) + mut sum = 0.0 + mut i = 0 + + while i < n: + let diff = predicted[i] - actual[i] + sum = sum + diff * diff + i = i + 1 + + return sum / to_float(n) + +/* Mean Squared Error derivative */ +func mse_loss_derivative(predicted: Float[], actual: Float[]) -> Float[]: + let n = len(predicted) + mut result = [] + mut i = 0 + + while i < n: + result = result + [2.0 * (predicted[i] - actual[i]) / to_float(n)] + i = i + 1 + + return result + +/* Cross-Entropy loss */ +func cross_entropy_loss(predicted: Float[], actual: Float[]) -> Float: + let n = len(predicted) + mut sum = 0.0 + mut i = 0 + + while i < n: + let p = math.max(predicted[i], 1e-15) // Prevent log(0) + sum = sum - actual[i] * math.ln(p) + i = i + 1 + + return sum + +/* Binary Cross-Entropy loss */ +func binary_cross_entropy(predicted: Float, actual: Float) -> Float: + let p = math.clamp(predicted, 1e-15, 1.0 - 1e-15) + return -(actual * math.ln(p) + (1.0 - actual) * math.ln(1.0 - p)) + +/* ============================================ + NEURAL NETWORK LAYER + ============================================ */ + +Class Layer: + public mut weights: Float[][] + public mut biases: Float[] + public mut input_size: Int + public mut output_size: Int + public mut activation: String + + private mut last_input: Float[] + private mut last_output: Float[] + private mut last_z: Float[] + + func init(in_size: Int, out_size: Int, activation_func: String): + input_size = in_size + output_size = out_size + activation = activation_func + + weights = [] + let scale = math.sqrt(2.0 / to_float(in_size + out_size)) + + mut i = 0 + while i < out_size: + mut row = [] + mut j = 0 + while j < in_size: + let seed = i * in_size + j + let rand_val = (to_float(seed % 100) / 100.0 - 0.5) * 2.0 * scale + row = row + [rand_val] + j = j + 1 + weights = weights + [row] + i = i + 1 + + biases = [] + i = 0 + while i < out_size: + biases = biases + [0.0] + i = i + 1 + + last_input = [] + last_output = [] + last_z = [] + + func forward(input: Float[]) -> Float[]: + last_input = input + mut z = [] + + mut i = 0 + while i < output_size: + let sum = biases[i] + (weights[i] *** input) + z = z + [sum] + i = i + 1 + // --------------------------- + + last_z = z + + mut output = [] + i = 0 + while i < output_size: + let activated = self.apply_activation(z[i]) + output = output + [activated] + i = i + 1 + + last_output = output + return output + + func apply_activation(x: Float) -> Float: + if activation == "sigmoid": + return sigmoid(x) + elif activation == "tanh": + return tanh(x) + elif activation == "relu": + return relu(x) + elif activation == "linear": + return x + return x + + func activation_derivative(x: Float) -> Float: + if activation == "sigmoid": + return sigmoid_derivative(x) + elif activation == "tanh": + return tanh_derivative(x) + elif activation == "relu": + return relu_derivative(x) + elif activation == "linear": + return 1.0 + return 1.0 + + func backward(output_gradient: Float[], learning_rate: Float) -> Float[]: + mut delta = [] + mut i = 0 + while i < output_size: + let d = output_gradient[i] * self.activation_derivative(last_z[i]) + delta = delta + [d] + i = i + 1 + + i = 0 + while i < output_size: + mut j = 0 + while j < input_size: + let grad = delta[i] * last_input[j] + weights[i][j] = weights[i][j] - learning_rate * grad + j = j + 1 + + // Update bias + biases[i] = biases[i] - learning_rate * delta[i] + i = i + 1 + + mut input_gradient = [] + mut j = 0 + while j < input_size: + mut sum = 0.0 + i = 0 + while i < output_size: + sum = sum + weights[i][j] * delta[i] + i = i + 1 + input_gradient = input_gradient + [sum] + j = j + 1 + + return input_gradient + +/* ============================================ + NEURAL NETWORK + ============================================ */ + +Class NeuralNetwork: + private mut layers: Layer[] + private mut num_layers: Int + public mut learning_rate: Float + + func init(lr: Float): + layers = [] + num_layers = 0 + learning_rate = lr + + func add_layer(in_size: Int, out_size: Int, activation: String): + let layer = Layer(in_size, out_size, activation) + layers = layers + [layer] + num_layers = num_layers + 1 + + func forward(input: Float[]) -> Float[]: + //print("DEBUG forward: num_layers=" + to_string(num_layers) + ", layers.length=" + to_string(len(layers))) + mut output = input + mut i = 0 + while i < self.num_layers: + output = self.layers[i].forward(output) + i = i + 1 + return output + + func backward(loss_gradient: Float[]): + mut gradient = loss_gradient + mut i = self.num_layers - 1 + + while i >= 0: + gradient = self.layers[i].backward(gradient, learning_rate) + i = i - 1 + + func train_step(input: Float[], target: Float[]) -> Float: + // Forward pass + let predicted = self.forward(input) + + // Compute loss + let loss = mse_loss(predicted, target) + + // Backward pass + let loss_grad = mse_loss_derivative(predicted, target) + self.backward(loss_grad) + + return loss + + func predict(input: Float[]) -> Float[]: + return self.forward(input) + + func save(filename: String): + print("Saving model to " + filename + "...") + mut data = "" + + data = data + "lr:" + to_string(learning_rate) + "\n" + data = data + "layers:" + to_string(num_layers) + "\n" + + mut i = 0 + while i < self.num_layers: + let layer = self.layers[i] + + data = data + "L" + to_string(i) + ":" + data = data + to_string(layer.input_size) + "," + data = data + to_string(layer.output_size) + "," + data = data + layer.activation + "\n" + + mut w_str = "" + mut r = 0 + mut total_weights = layer.output_size * layer.input_size + mut weight_count = 0 + while r < layer.output_size: + mut c = 0 + while c < layer.input_size: + w_str = w_str + to_string(layer.weights[r][c]) + weight_count = weight_count + 1 + if weight_count < total_weights: + w_str = w_str + "," + c = c + 1 + r = r + 1 + data = data + w_str + "\n" + + mut b_str = "" + let b_len = len(layer.biases) + mut k = 0 + while k < b_len: + b_str = b_str + to_string(layer.biases[k]) + if k < b_len - 1: + b_str = b_str + "," + k = k + 1 + data = data + b_str + + if i < num_layers - 1: + data = data + "\n" + + i = i + 1 + + file_write(filename, data) + print("Model saved successfully.") + + func load(filename: String): + print("Loading model from " + filename + "...") + let content = file_read(filename) + let lines = split(content, "\n") + + let lr_line = split(lines[0], ":") + self.learning_rate = to_float(lr_line[1]) + + let count_line = split(lines[1], ":") + let total_layers = to_int(count_line[1]) + + self.layers=[] + self.num_layers = 0 + mut line_idx = 2 + mut i = 0 + while i < total_layers: + let config_line_parts = split(lines[line_idx], ":") + let config_str = config_line_parts[1] + let config_parts = split(config_str, ",") + let in_size = to_int(config_parts[0]) + let out_size = to_int(config_parts[1]) + let act = config_parts[2] + + self.add_layer(in_size, out_size, act) + + let w_vals = split(lines[line_idx + 1], ",") + mut w_idx = 0 + mut r = 0 + while r < out_size: + mut c = 0 + while c < in_size: + self.layers[i].weights[r][c] = to_float(w_vals[w_idx]) + w_idx = w_idx + 1 + c = c + 1 + r = r + 1 + let b_vals = split(lines[line_idx + 2], ",") + mut k = 0 + while k < out_size: + self.layers[i].biases[k] = to_float(b_vals[k]) + k = k + 1 + + line_idx = line_idx + 3 + i = i + 1 + + print("Model loaded.") + +/* ============================================ + DATASET UTILITIES + ============================================ */ +Class Dataset: + public mut inputs: Float[][] + public mut targets: Float[][] + public mut size: Int + + func init(): + inputs = [] + targets = [] + size = 0 + + func add_sample(input: Float[], target: Float[]): + inputs = inputs + [input] + targets = targets + [target] + size = size + 1 + + func get_sample(index: Int) -> Dict: + return { + "input": inputs[index], + "target": targets[index] + } + + func shuffle(): + // Simple shuffle algorithm (Fisher-Yates) + mut i = size - 1 + while i > 0: + // In real implementation, use proper random number + let j = i % (i + 1) + + // Swap inputs + let temp_input = inputs[i] + inputs[i] = inputs[j] + inputs[j] = temp_input + + // Swap targets + let temp_target = targets[i] + targets[i] = targets[j] + targets[j] = temp_target + + i = i - 1 + +/* ============================================ + TRAINING UTILITIES + ============================================ */ + +/* Train a neural network */ +func train_network( + network: NeuralNetwork, + dataset: Dataset, + epochs: Int, + batch_size: Int, + verbose: Bool +) -> Float[]: + mut losses = [] + + mut epoch = 0 + while epoch < epochs: + dataset.shuffle() + + mut epoch_loss = 0.0 + mut i = 0 + + while i < dataset.size: + let sample = dataset.get_sample(i) + let loss = network.train_step(sample["input"], sample["target"]) + epoch_loss = epoch_loss + loss + i = i + 1 + + epoch_loss = epoch_loss / to_float(dataset.size) + losses = losses + [epoch_loss] + + if verbose: + print("Epoch " + to_string(epoch + 1) + "/" + to_string(epochs) + + " - Loss: " + to_string(epoch_loss)) + + epoch = epoch + 1 + + return losses + +/* Evaluate network accuracy */ +func evaluate_network( + network: NeuralNetwork, + dataset: Dataset, + threshold: Float +) -> Float: + mut correct = 0 + mut i = 0 + + while i < dataset.size: + let sample = dataset.get_sample(i) + let predicted = network.predict(sample["input"]) + let target = sample["target"] + + // Check if prediction is correct (for classification) + let pred_class = argmax(predicted) + let true_class = argmax(target) + + if pred_class == true_class: + correct = correct + 1 + + i = i + 1 + + return to_float(correct) / to_float(dataset.size) + +/* Find index of maximum value */ +func argmax(values: Float[]) -> Int: + mut max_idx = 0 + mut max_val = values[0] + mut i = 1 + + while i < len(values): + if values[i] > max_val: + max_val = values[i] + max_idx = i + i = i + 1 + + return max_idx + +/* ============================================ + COMMON ARCHITECTURES + ============================================ */ + +/* Create a simple feedforward network */ +func create_feedforward_network( + input_size: Int, + hidden_sizes: Int[], + output_size: Int, + learning_rate: Float +) -> NeuralNetwork: + let network = NeuralNetwork(learning_rate) + + // Input to first hidden layer + network.add_layer(input_size, hidden_sizes[0], "relu") + + // Hidden layers + mut i = 1 + while i < len(hidden_sizes): + network.add_layer(hidden_sizes[i - 1], hidden_sizes[i], "relu") + i = i + 1 + + // Hidden to output layer + let last_hidden = hidden_sizes[len(hidden_sizes) - 1] + network.add_layer(last_hidden, output_size, "sigmoid") + + return network + +/* ============================================ + K-MEANS CLUSTERING + ============================================ */ + +Class KMeans: + private mut k: Int + private mut centroids: Float[][] + private mut max_iterations: Int + + func init(num_clusters: Int, max_iter: Int): + k = num_clusters + max_iterations = max_iter + centroids = [] + + func fit(data: Float[][], features: Int): + // Initialize centroids (use first k data points) + centroids = [] + mut i = 0 + while i < k: + centroids = centroids + [data[i]] + i = i + 1 + + // Iterate until convergence or max iterations + mut iteration = 0 + while iteration < max_iterations: + // Assign points to clusters + let assignments = self.assign_clusters(data, features) + + // Update centroids + let new_centroids = self.update_centroids(data, assignments, features) + + // Check for convergence + if self.centroids_equal(centroids, new_centroids, features): + centroids = new_centroids + iteration = max_iterations // Break + else: + centroids = new_centroids + + iteration = iteration + 1 + + func assign_clusters(data: Float[][], features: Int) -> Int[]: + let n = len(data) + mut assignments = [] + + mut i = 0 + while i < n: + mut min_dist = self.euclidean_distance(data[i], centroids[0], features) + mut best_cluster = 0 + + mut j = 1 + while j < k: + let dist = self.euclidean_distance(data[i], centroids[j], features) + if dist < min_dist: + min_dist = dist + best_cluster = j + j = j + 1 + + assignments = assignments + [best_cluster] + i = i + 1 + + return assignments + + func update_centroids(data: Float[][], assignments: Int[], features: Int) -> Float[][]: + mut new_centroids = [] + + mut cluster = 0 + while cluster < k: + mut centroid = [] + mut counts = [] + + // Initialize to zeros + mut f = 0 + while f < features: + centroid = centroid + [0.0] + counts = counts + [0] + f = f + 1 + + // Sum all points in this cluster + mut i = 0 + while i < len(data): + if assignments[i] == cluster: + f = 0 + while f < features: + centroid[f] = centroid[f] + data[i][f] + counts[f] = counts[f] + 1 + f = f + 1 + i = i + 1 + + // Average + f = 0 + while f < features: + if counts[f] > 0: + centroid[f] = centroid[f] / to_float(counts[f]) + f = f + 1 + + new_centroids = new_centroids + [centroid] + cluster = cluster + 1 + + return new_centroids + + func euclidean_distance(point1: Float[], point2: Float[], features: Int) -> Float: + mut sum = 0.0 + mut i = 0 + while i < features: + let diff = point1[i] - point2[i] + sum = sum + diff * diff + i = i + 1 + return math.sqrt(sum) + + func centroids_equal(c1: Float[][], c2: Float[][], features: Int) -> Bool: + mut i = 0 + while i < k: + mut j = 0 + while j < features: + if math.abs(c1[i][j] - c2[i][j]) > 1e-6: + return False + j = j + 1 + i = i + 1 + return True + + func predict(point: Float[], features: Int) -> Int: + mut min_dist = self.euclidean_distance(point, centroids[0], features) + mut best_cluster = 0 + + mut i = 1 + while i < k: + let dist = self.euclidean_distance(point, centroids[i], features) + if dist < min_dist: + min_dist = dist + best_cluster = i + i = i + 1 + + return best_cluster + +/* ============================================ + LINEAR REGRESSION + ============================================ */ + +Class LinearRegression: + private mut weights: Float[] + private mut bias: Float + private mut learning_rate: Float + + func init(features: Int, lr: Float): + learning_rate = lr + bias = 0.0 + + // Initialize weights + weights = [] + mut i = 0 + while i < features: + weights = weights + [0.0] + i = i + 1 + + func predict(x: Float[]) -> Float: + mut y = bias + mut i = 0 + while i < len(weights): + y = y + weights[i] * x[i] + i = i + 1 + return y + + func train_step(x: Float[], y_true: Float): + let y_pred = self.predict(x) + let error = y_pred - y_true + + // Update weights + mut i = 0 + while i < len(weights): + weights[i] = weights[i] - learning_rate * error * x[i] + i = i + 1 + + // Update bias + bias = bias - learning_rate * error + + func fit(X: Float[][], y: Float[], epochs: Int): + let n = len(X) + + mut epoch = 0 + while epoch < epochs: + mut i = 0 + while i < n: + self.train_step(X[i], y[i]) + i = i + 1 + epoch = epoch + 1 + +/* ============================================ + GRADIENT DESCENT OPTIMIZER + ============================================ */ + +Class Optimizer: + private mut learning_rate: Float + private mut momentum: Float + private mut velocity: Float[] + + func init(lr: Float, mom: Float): + learning_rate = lr + momentum = mom + velocity = [] + + func update(weights: Float[], gradients: Float[]) -> Float[]: + let n = len(weights) + + // Initialize velocity if needed + if len(velocity) == 0: + mut i = 0 + while i < n: + velocity = velocity + [0.0] + i = i + 1 + + // Update with momentum + mut new_weights = [] + mut i = 0 + while i < n: + velocity[i] = momentum * velocity[i] - learning_rate * gradients[i] + new_weights = new_weights + [weights[i] + velocity[i]] + i = i + 1 + + return new_weights + +/* ============================================ + FEATURE ENGINEERING + ============================================ */ + +/* Normalize features to [0, 1] */ +func normalize_features(data: Float[][], features: Int) -> Float[][]: + let n = len(data) + + // Find min and max for each feature + mut mins = [] + mut maxs = [] + mut i = 0 + while i < features: + mins = mins + [data[0][i]] + maxs = maxs + [data[0][i]] + i = i + 1 + + mut row = 1 + while row < n: + mut col = 0 + while col < features: + if data[row][col] < mins[col]: + mins[col] = data[row][col] + if data[row][col] > maxs[col]: + maxs[col] = data[row][col] + col = col + 1 + row = row + 1 + + // Normalize + mut normalized = [] + row = 0 + while row < n: + mut norm_row = [] + mut col = 0 + while col < features: + let range = maxs[col] - mins[col] + if range > 1e-10: + let norm_val = (data[row][col] - mins[col]) / range + norm_row = norm_row + [norm_val] + else: + norm_row = norm_row + [0.0] + col = col + 1 + normalized = normalized + [norm_row] + row = row + 1 + + return normalized + +/* Standardize features (zero mean, unit variance) */ +func standardize_features(data: Float[][], features: Int) -> Float[][]: + let n = len(data) + + // Compute mean for each feature + mut means = [] + mut i = 0 + while i < features: + means = means + [0.0] + i = i + 1 + + mut row = 0 + while row < n: + mut col = 0 + while col < features: + means[col] = means[col] + data[row][col] + col = col + 1 + row = row + 1 + + mut col = 0 + while col < features: + means[col] = means[col] / to_float(n) + col = col + 1 + + // Compute standard deviation + mut stds = [] + col = 0 + while col < features: + stds = stds + [0.0] + col = col + 1 + + row = 0 + while row < n: + col = 0 + while col < features: + let diff = data[row][col] - means[col] + stds[col] = stds[col] + diff * diff + col = col + 1 + row = row + 1 + + col = 0 + while col < features: + stds[col] = math.sqrt(stds[col] / to_float(n)) + col = col + 1 + + // Standardize + mut standardized = [] + row = 0 + while row < n: + mut std_row = [] + col = 0 + while col < features: + if stds[col] > 1e-10: + let std_val = (data[row][col] - means[col]) / stds[col] + std_row = std_row + [std_val] + else: + std_row = std_row + [0.0] + col = col + 1 + standardized = standardized + [std_row] + row = row + 1 + + return standardized + +/* ============================================ + EXAMPLE USAGE + ============================================ */ + +/* Example: Train a simple XOR network */ +/* +func example_xor_network(): + print("Training XOR Neural Network...") + + // Create network: 2 inputs -> 4 hidden -> 1 output + let network = NeuralNetwork(0.5) + network.add_layer(2, 4, "sigmoid") + network.add_layer(4, 1, "sigmoid") + + // Create dataset + let dataset = Dataset() + dataset.add_sample([0.0, 0.0], [0.0]) + dataset.add_sample([0.0, 1.0], [1.0]) + dataset.add_sample([1.0, 0.0], [1.0]) + dataset.add_sample([1.0, 1.0], [0.0]) + + // Train + let losses = train_network(network, dataset, 1000, 1, False) + + // Test + print("Testing XOR network:") + print("0 XOR 0 = " + to_string(network.predict([0.0, 0.0])[0])) + print("0 XOR 1 = " + to_string(network.predict([0.0, 1.0])[0])) + print("1 XOR 0 = " + to_string(network.predict([1.0, 0.0])[0])) + print("1 XOR 1 = " + to_string(network.predict([1.0, 1.0])[0])) + + + +example_xor_network() +*/ \ No newline at end of file From 28f37eb40567a08540b6c2adb7d2254a130488ad Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:09:00 +0530 Subject: [PATCH 24/27] added Stdlib upport for AI and ML and fixed bugs and also save load functions --- src/environment/mod.rs | 21 ++++++ src/evaluator/mod.rs | 164 +++++++++++++++++++++++++---------------- src/parser/mod.rs | 13 +++- src/type_checker.rs | 131 ++++++++++++++++++++++++-------- 4 files changed, 231 insertions(+), 98 deletions(-) diff --git a/src/environment/mod.rs b/src/environment/mod.rs index 3cd9d23..efb1540 100644 --- a/src/environment/mod.rs +++ b/src/environment/mod.rs @@ -64,6 +64,7 @@ pub enum RuntimeValue { fields: Vec, methods: HashMap, constructor: Option>, + definition_env: Option>>, }, @@ -71,6 +72,7 @@ pub enum RuntimeValue { class_name: String, fields: HashMap>>, methods: HashMap, + definition_env: Option>>, }, Register(Vec>>), @@ -252,6 +254,25 @@ impl Environment { self.store.clone() } + pub fn define(&mut self, name: String, value: RuntimeValue) { + self.store.insert(name, Rc::new(RefCell::new(value))); + } + + pub fn define_rc(&mut self, name: String, value: Rc>) { + self.store.insert(name, value); + } + + pub fn assign(&mut self, name: &str, value: RuntimeValue) -> bool { + if let Some(slot) = self.store.get(name) { + *slot.borrow_mut() = value; + true + } else if let Some(outer) = &self.outer { + outer.borrow_mut().assign(name, value) + } else { + false + } + } + pub fn get_quantum_state(&self) -> Option { for (_, value_rc) in self.store.iter() { let value = value_rc.borrow(); diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index de294b1..cb358e6 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -198,6 +198,10 @@ impl Evaluator { e.set("_graphics_plot_set_title".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_title".to_string())); e.set("_graphics_plot_render".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_render".to_string())); e.set("_graphics_destroy_plot".to_string(), RuntimeValue::BuiltinFunction("_graphics_destroy_plot".to_string())); + + e.set("file_write".to_string(), RuntimeValue::BuiltinFunction("file_write".to_string())); + e.set("file_read".to_string(), RuntimeValue::BuiltinFunction("file_read".to_string())); + } fn is_truthy(val: &RuntimeValue) -> bool { match val { @@ -221,6 +225,7 @@ impl Evaluator { methods: &Vec, constructor: &Option>, env: &Rc>, + ) -> Result { let mut method_map = HashMap::new(); @@ -234,6 +239,7 @@ impl Evaluator { fields: fields.clone(), methods: method_map, constructor: constructor.clone(), + definition_env: Some(env.clone()), }; env.borrow_mut().set(name.to_string(), class_value); @@ -247,22 +253,38 @@ impl Evaluator { env: &Rc>, ) -> Result { - let class_value = env.borrow().get(class_name) - .ok_or_else(|| format!("Runtime Error at {}: Unknown class '{}'", loc, class_name))?; + let parts: Vec<&str> = class_name.split('.').collect(); + + let class_value_rc = if parts.len() > 1 { + let module_name = parts[0]; + let target_class = parts[1]; + + let module_rc = env.borrow().get(module_name) + .ok_or_else(|| format!("Runtime Error at {}: Unknown module '{}'", loc, module_name))?; - let class_value_borrowed = class_value.borrow(); - let (fields_def, methods, constructor) = match &*class_value_borrowed { - RuntimeValue::Class { fields, methods, constructor, .. } => { - (fields.clone(), methods.clone(), constructor.clone()) + let module_val = module_rc.borrow(); + if let RuntimeValue::Module(mod_env) = &*module_val { + mod_env.borrow().get(target_class) + .ok_or_else(|| format!("Runtime Error at {}: Module '{}' has no class '{}'", loc, module_name, target_class))? + } else { + return Err(format!("Runtime Error at {}: '{}' is not a module.", loc, module_name)); } - _ => return Err(format!("Runtime Error at {}: '{}' is not a class", loc, class_name)), + } else { + env.borrow().get(class_name) + .ok_or_else(|| format!("Runtime Error at {}: Unknown class '{}'", loc, class_name))? }; - drop(class_value_borrowed); + let class_borrow = class_value_rc.borrow(); + let (fields_def, methods, constructor, def_env) = match &*class_borrow { + RuntimeValue::Class { fields, methods, constructor, definition_env, .. } => { + (fields.clone(), methods.clone(), constructor.clone(), definition_env.clone()) + } + _ => return Err(format!("Runtime Error at {}: '{}' is not a class", loc, class_name)), + }; + drop(class_borrow); let mut instance_fields = HashMap::new(); let mut initial_values = HashMap::new(); - for field in &fields_def { let value = if let Some(default_expr) = &field.default_value { Self::evaluate(default_expr, env)? @@ -270,22 +292,20 @@ impl Evaluator { RuntimeValue::None }; - initial_values.insert(field.name.clone(), value.clone()); - - instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); + let shared_ptr = Rc::new(RefCell::new(value)); + initial_values.insert(field.name.clone(), shared_ptr.clone()); + instance_fields.insert(field.name.clone(), shared_ptr); } - let instance = RuntimeValue::Instance { class_name: class_name.to_string(), fields: instance_fields.clone(), methods: methods.clone(), + definition_env: def_env.clone(), }; - if let Some(constructor_node) = constructor { if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { - let mut evaluated_args = Vec::new(); for arg in arguments { evaluated_args.push(Self::evaluate(arg, env)?); @@ -295,39 +315,21 @@ impl Evaluator { return Err(format!("Runtime Error: Constructor arg mismatch.")); } - let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); - + let parent_env = def_env.unwrap_or(env.clone()); + let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(parent_env))); constructor_env.borrow_mut().set("self".to_string(), instance.clone()); - - for (name, val) in &initial_values { - constructor_env.borrow_mut().set(name.clone(), val.clone()); + for (name, val_rc) in &initial_values { + constructor_env.borrow_mut().define_rc(name.clone(), val_rc.clone()); } - for (param, arg_value) in parameters.iter().zip(evaluated_args) { constructor_env.borrow_mut().set(param.name.clone(), arg_value); } - Self::evaluate(&body, &constructor_env)?; - - for (field_name, field_rc) in &instance_fields { - if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { - let local_val = local_val_rc.borrow().clone(); - - - if let Some(initial_val) = initial_values.get(field_name) { - - if !Self::are_values_equal(&local_val, initial_val) { - - *field_rc.borrow_mut() = local_val; - } - } - } - } } } @@ -361,8 +363,8 @@ impl Evaluator { class_name, fields, methods, + definition_env, } => { - let method = methods.get(method_name).ok_or_else(|| { format!( "Runtime Error at {}: Class '{}' has no method '{}'", @@ -370,13 +372,11 @@ impl Evaluator { ) })?; - let mut evaluated_args = Vec::new(); for arg in arguments { evaluated_args.push(Self::evaluate(arg, env)?); } - if method.parameters.len() != evaluated_args.len() { return Err(format!( "Runtime Error at {}: Method '{}' expected {} arguments, got {}", @@ -387,41 +387,33 @@ impl Evaluator { )); } - - let method_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); + let parent_env = definition_env.unwrap_or(env.clone()); + let method_env = Rc::new(RefCell::new(Environment::new_enclosed(parent_env))); let self_instance = RuntimeValue::Instance { class_name: class_name.clone(), fields: fields.clone(), methods: methods.clone(), + definition_env: None, }; method_env.borrow_mut().set("self".to_string(), self_instance); - - for (field_name, field_value) in &fields { + for (field_name, field_value_rc) in &fields { method_env .borrow_mut() - .set(field_name.clone(), field_value.borrow().clone()); + .define_rc(field_name.clone(), field_value_rc.clone()); } - for (param, arg_value) in method.parameters.iter().zip(evaluated_args) { method_env.borrow_mut().set(param.name.clone(), arg_value); } - let result = Self::evaluate(&method.body, &method_env)?; - - for (field_name, field_rc) in &fields { - - if let Some(env_val_rc) = method_env.borrow().get(field_name) { - - let new_val = env_val_rc.borrow().clone(); - - - *field_rc.borrow_mut() = new_val; - } + for (field_name, field_value_rc) in &fields { + method_env + .borrow_mut() + .define_rc(field_name.clone(), field_value_rc.clone()); } match result { RuntimeValue::ReturnValue(val) => Ok(*val), @@ -487,8 +479,10 @@ impl Evaluator { } else { RuntimeValue::None }; - initial_values.insert(field.name.clone(), value.clone()); - instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); + + let shared_ptr = Rc::new(RefCell::new(value)); + initial_values.insert(field.name.clone(), shared_ptr.clone()); + instance_fields.insert(field.name.clone(), shared_ptr); } @@ -496,6 +490,7 @@ impl Evaluator { class_name: name.clone(), fields: instance_fields.clone(), methods: methods.clone(), + definition_env: None, }; @@ -512,7 +507,7 @@ impl Evaluator { for (name, val) in &initial_values { - constructor_env.borrow_mut().set(name.clone(), val.clone()); + constructor_env.borrow_mut().define_rc(name.clone(), val.clone()); } @@ -523,25 +518,27 @@ impl Evaluator { Self::evaluate(&body, &constructor_env)?; - + /* for (field_name, field_rc) in &instance_fields { if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { let local_val = local_val_rc.borrow().clone(); - if let Some(initial_val) = initial_values.get(field_name) { + if let Some(initial_val_rc) = initial_values.get(field_name) { + let initial_val = initial_val_rc.borrow(); - if !Self::are_values_equal(&local_val, initial_val) { + if !Self::are_values_equal(&local_val, &*initial_val) { *field_rc.borrow_mut() = local_val; } } } } - + */ instance = RuntimeValue::Instance { class_name: name.clone(), fields: instance_fields, methods, + definition_env: None, }; } } @@ -1616,6 +1613,10 @@ impl Evaluator { "_graphics_plot_set_title" => Self::builtin_graphics_plot_set_title(evaluated_args), "_graphics_plot_render" => Self::builtin_graphics_plot_render(evaluated_args), "_graphics_destroy_plot" => Self::builtin_graphics_destroy_plot(evaluated_args), + + "file_write" => Self::builtin_file_write(evaluated_args), + "file_read" => Self::builtin_file_read(evaluated_args), + _ => Err(format!( "Runtime Error at {}: Unknown built-in function '{}'.", loc, func_name @@ -1684,6 +1685,7 @@ impl Evaluator { class_name: name.clone(), fields: instance_fields.clone(), methods: methods.clone(), + definition_env: None, }; @@ -1730,6 +1732,7 @@ impl Evaluator { class_name: name.clone(), fields: instance_fields, methods, + definition_env: None, }; } } @@ -1930,6 +1933,39 @@ impl Evaluator { } } + fn builtin_file_write(args: Vec) -> Result { + if args.len() != 2 { + return Err("Runtime Error: 'file_write' expects 2 arguments (path, content).".to_string()); + } + + let path = match &args[0] { + RuntimeValue::String(s) => s, + _ => return Err("Runtime Error: File path must be a String.".to_string()) + }; + + let content = match &args[1] { + RuntimeValue::String(s) => s, + _ => return Err("Runtime Error: File content must be a String.".to_string()) + }; + + std::fs::write(path, content).map_err(|e| format!("File Write Error: {}", e))?; + Ok(RuntimeValue::None) + } + + fn builtin_file_read(args: Vec) -> Result { + if args.len() != 1 { + return Err("Runtime Error: 'file_read' expects 1 argument (path).".to_string()); + } + + let path = match &args[0] { + RuntimeValue::String(s) => s, + _ => return Err("Runtime Error: File path must be a String.".to_string()) + }; + + let content = std::fs::read_to_string(path).map_err(|e| format!("File Read Error: {}", e))?; + Ok(RuntimeValue::String(content)) + } + pub fn builtin_measure(args: Vec) -> Result { if args.len() != 1 { return Err("Runtime Error: 'measure' expects exactly one qubit.".to_string()); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 8284f4b..1f5a269 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,4 +1,4 @@ - +/* src/parser/mod.rs */ pub mod ast; use crate::lexer::token::{Token, TokenWithLocation}; @@ -815,8 +815,15 @@ impl Parser { let loc = self.get_loc(self.current()?); self.expect(&Token::New)?; - let class_name_loc = self.expect_identifier()?; - let class_name = self.extract_identifier_name(&class_name_loc)?; + let name_loc = self.expect_identifier()?; + let mut class_name = self.extract_identifier_name(&name_loc)?; + + while self.match_token(&Token::Dot) { + let part_loc = self.expect_identifier()?; + let part_name = self.extract_identifier_name(&part_loc)?; + class_name.push('.'); + class_name.push_str(&part_name); + } self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; diff --git a/src/type_checker.rs b/src/type_checker.rs index e1d7617..c42757e 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1,4 +1,4 @@ - +/* src/type_checker.rs */ use crate::parser::ast::{ASTNode, BinaryOperator, ImportSpec, Type, UnaryOperator}; use std::cell::RefCell; @@ -358,6 +358,23 @@ impl TypeChecker { "_graphics_destroy_plot".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), ); + + env_mut.set( + "file_write".to_string(), + immut(Type::Function( + vec![Type::String, Type::String], // Arguments: (path, content) + Box::new(Type::None), // Returns: None + )), + ); + + env_mut.set( + "file_read".to_string(), + immut(Type::Function( + vec![Type::String], // Argument: (path) + Box::new(Type::String), // Returns: String (content) + )), + ); + } pub fn check_program(node: &ASTNode) -> Result<(), String> { @@ -367,31 +384,39 @@ impl TypeChecker { Self::prefill_environment(&env); - for stmt in statements { match stmt { ASTNode::FunctionDeclaration { name, parameters, return_type, .. } => { let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); let rt = return_type.clone().unwrap_or(Type::Any); let func_type = Type::Function(param_types, Box::new(rt)); - env.borrow_mut().set(name.clone(), Self::immutable_info(func_type)); + + env.borrow_mut().set(name.clone(), TypeInfo { + var_type: func_type, + is_mutable: false + }); }, ASTNode::CircuitDeclaration { name, parameters, return_type, .. } => { let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); let rt = return_type.clone().unwrap_or(Type::Any); let func_type = Type::Function(param_types, Box::new(rt)); - env.borrow_mut().set(name.clone(), Self::immutable_info(func_type)); + + env.borrow_mut().set(name.clone(), TypeInfo { + var_type: func_type, + is_mutable: false + }); }, ASTNode::ClassDeclaration { name, .. } => { let class_type = Type::Class(name.clone()); - env.borrow_mut().set(name.clone(), TypeInfo { var_type: class_type, is_mutable: false }); + env.borrow_mut().set(name.clone(), TypeInfo { + var_type: class_type, + is_mutable: false + }); }, _ => {} } } - - for stmt in statements { Self::check_with_lifecycle(stmt, &env, None, &mut lifecycle)?; } @@ -641,43 +666,64 @@ impl TypeChecker { fn check_module(path: &ImportPath) -> Result, String> { let file_path = match path { - ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - f.clone() } else { - - format!("q_packages/{}/init.qc", f) } } ImportPath::Module(m) => { - m.join("/") + ".qc" } }; let source = fs::read_to_string(&file_path).map_err(|e| { - format!( - "Type Check Error: Failed to read module '{}': {}", - file_path, e - ) + format!("Type Check Error: Failed to read module '{}': {}", file_path, e) })?; let mut lexer = Lexer::new(&source); - let tokens = lexer - .tokenize() - .map_err(|e| format!("Module Lexer Error: {}", e))?; + let tokens = lexer.tokenize().map_err(|e| format!("Module Lexer Error: {}", e))?; let mut parser = Parser::new(tokens); - let ast = parser - .parse() - .map_err(|e| format!("Module Parser Error: {}", e))?; + let ast = parser.parse().map_err(|e| format!("Module Parser Error: {}", e))?; let module_env = Rc::new(RefCell::new(TypeEnvironment::new())); Self::prefill_environment(&module_env); if let ASTNode::Program(statements) = ast { + for stmt in &statements { + match stmt { + ASTNode::FunctionDeclaration { name, parameters, return_type, .. } => { + let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + let rt = return_type.clone().unwrap_or(Type::Any); + let func_type = Type::Function(param_types, Box::new(rt)); + + module_env.borrow_mut().set(name.clone(), TypeInfo { + var_type: func_type, + is_mutable: false + }); + }, + ASTNode::CircuitDeclaration { name, parameters, return_type, .. } => { + let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + let rt = return_type.clone().unwrap_or(Type::Any); + let func_type = Type::Function(param_types, Box::new(rt)); + + module_env.borrow_mut().set(name.clone(), TypeInfo { + var_type: func_type, + is_mutable: false + }); + }, + ASTNode::ClassDeclaration { name, .. } => { + let class_type = Type::Class(name.clone()); + module_env.borrow_mut().set(name.clone(), TypeInfo { + var_type: class_type, + is_mutable: false + }); + }, + _ => {} + } + } + for stmt in statements { Self::check(&stmt, &module_env, None)?; } @@ -685,10 +731,7 @@ impl TypeChecker { return Err("Module root is not a Program node".to_string()); } - let module_types = module_env - .borrow() - .store - .iter() + let module_types = module_env.borrow().store.iter() .map(|(k, v)| (k.clone(), v.var_type.clone())) .collect(); @@ -1055,7 +1098,7 @@ impl TypeChecker { _ => false }; - if !is_compat && new_type != field_info.var_type { + if !is_compat && !is_array_compat && new_type != field_info.var_type { return Err(format!( "Type Error: Cannot assign type {:?} to field '{}.{}' of type {:?}", new_type, class_name, member, field_info.var_type @@ -1192,20 +1235,46 @@ impl TypeChecker { } ASTNode::NewInstance { class_name, arguments, loc } => { + let parts: Vec<&str> = class_name.split('.').collect(); - let class_info = env.borrow().get(class_name) - .ok_or_else(|| format!("Type Error at {}: Unknown class '{}'", loc, class_name))?; + let class_type = if parts.len() > 1 { + let module_name = parts[0]; + let target_class = parts[1]; + + let module_info = env.borrow().get(module_name) + .ok_or_else(|| format!("Type Error at {}: Unknown module '{}'", loc, module_name))?; + + if let Type::Module(mod_types) = &module_info.var_type { + mod_types.get(target_class).cloned() + .ok_or_else(|| format!("Type Error at {}: Module '{}' has no class '{}'", loc, module_name, target_class))? + } else { + return Err(format!("Type Error at {}: '{}' is not a module", loc, module_name)); + } + } else { + env.borrow().get(class_name) + .map(|info| info.var_type.clone()) + .ok_or_else(|| format!("Type Error at {}: Unknown class '{}'", loc, class_name))? + }; - if !matches!(class_info.var_type, Type::Class(_)) { + if !matches!(class_type, Type::Class(_)) { return Err(format!("Type Error at {}: '{}' is not a class", loc, class_name)); } + let ctor_params: Vec = if let Type::Class(real_name) = &class_type { + vec![] + } else { + vec![] + }; for arg in arguments { Self::check(arg, env, None)?; } - Ok(Type::Instance(class_name.clone())) + if let Type::Class(name) = class_type { + Ok(Type::Instance(name)) + } else { + Ok(Type::Instance(class_name.clone())) + } } ASTNode::MethodCall { From 565d436610697385dbdd30a51a398ccf7c9abae6 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:08:22 +0530 Subject: [PATCH 25/27] Fixed bugs related to AI implementation --- src/evaluator/mod.rs | 992 +++++++++++++++++++++++-------------- src/lexer/mod.rs | 83 +--- src/main.rs | 97 ++-- src/parser/mod.rs | 233 ++++----- src/type_checker.rs | 1105 +++++++++++++++++++++++++----------------- 5 files changed, 1467 insertions(+), 1043 deletions(-) diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index cb358e6..7c6a9a8 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -3,31 +3,31 @@ use crate::environment::{Environment, GateDefinition, RuntimeValue}; use crate::lexer::Lexer; use crate::parser::ast::ASTNode; use crate::parser::ast::BinaryOperator; +use crate::parser::ast::ClassField; +use crate::parser::ast::ClassMethod; use crate::parser::ast::ImportPath; use crate::parser::ast::ImportSpec; use crate::parser::ast::Loc; use crate::parser::Parser; +use num_complex::Complex; use rand::Rng; use std::cell::RefCell; use std::collections::HashMap; use std::fs; use std::path::PathBuf; use std::rc::Rc; -use crate::parser::ast::ClassField; -use crate::parser::ast::ClassMethod; -use num_complex::Complex; type C64 = Complex; use crate::graphics::{Canvas, Color, Plot, PlotType}; -use std::sync::Mutex; use std::io::Write; +use std::sync::Mutex; type QubitMap = HashMap; lazy_static::lazy_static! { static ref INTERPRETER_CANVASES: Mutex> = Mutex::new(HashMap::new()); static ref INTERPRETER_PLOTS: Mutex> = Mutex::new(HashMap::new()); static ref NEXT_ID: Mutex = Mutex::new(0); } -use crate::qubit_lifecycle::{QubitLifecycleManager, QubitId, QubitOperation, QubitState}; use crate::quantum_backend::native_simulator::NativeSimulator; +use crate::qubit_lifecycle::{QubitId, QubitLifecycleManager, QubitOperation, QubitState}; pub struct Evaluator; @@ -168,40 +168,119 @@ impl Evaluator { pub fn register_builtins(env: &Rc>) { let mut e = env.borrow_mut(); + e.set("time".to_string(), RuntimeValue::BuiltinFunction("time".to_string())); - e.set("print".to_string(), RuntimeValue::BuiltinFunction("print".to_string())); - e.set("len".to_string(), RuntimeValue::BuiltinFunction("len".to_string())); - e.set("to_string".to_string(), RuntimeValue::BuiltinFunction("to_string".to_string())); - e.set("to_int".to_string(), RuntimeValue::BuiltinFunction("to_int".to_string())); - e.set("to_float".to_string(), RuntimeValue::BuiltinFunction("to_float".to_string())); - e.set("type_of".to_string(), RuntimeValue::BuiltinFunction("type_of".to_string())); - e.set("assert".to_string(), RuntimeValue::BuiltinFunction("assert".to_string())); - e.set("echo".to_string(), RuntimeValue::BuiltinFunction("echo".to_string())); - e.set("maybe".to_string(), RuntimeValue::BuiltinFunction("maybe".to_string())); - e.set("sample".to_string(), RuntimeValue::BuiltinFunction("sample".to_string())); - e.set("debug_state".to_string(), RuntimeValue::BuiltinFunction("debug_state".to_string())); - e.set("measure".to_string(), RuntimeValue::BuiltinFunction("measure".to_string())); - - - e.set("_graphics_create_canvas".to_string(), RuntimeValue::BuiltinFunction("_graphics_create_canvas".to_string())); - e.set("_graphics_set_background".to_string(), RuntimeValue::BuiltinFunction("_graphics_set_background".to_string())); - e.set("_graphics_draw_rect".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_rect".to_string())); - e.set("_graphics_draw_circle".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_circle".to_string())); - e.set("_graphics_draw_line".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_line".to_string())); - e.set("_graphics_draw_text".to_string(), RuntimeValue::BuiltinFunction("_graphics_draw_text".to_string())); - e.set("_graphics_save_svg".to_string(), RuntimeValue::BuiltinFunction("_graphics_save_svg".to_string())); - e.set("_graphics_destroy_canvas".to_string(), RuntimeValue::BuiltinFunction("_graphics_destroy_canvas".to_string())); - + e.set( + "print".to_string(), + RuntimeValue::BuiltinFunction("print".to_string()), + ); + e.set( + "len".to_string(), + RuntimeValue::BuiltinFunction("len".to_string()), + ); + e.set( + "to_string".to_string(), + RuntimeValue::BuiltinFunction("to_string".to_string()), + ); + e.set( + "to_int".to_string(), + RuntimeValue::BuiltinFunction("to_int".to_string()), + ); + e.set( + "to_float".to_string(), + RuntimeValue::BuiltinFunction("to_float".to_string()), + ); + e.set( + "type_of".to_string(), + RuntimeValue::BuiltinFunction("type_of".to_string()), + ); + e.set( + "assert".to_string(), + RuntimeValue::BuiltinFunction("assert".to_string()), + ); + e.set( + "echo".to_string(), + RuntimeValue::BuiltinFunction("echo".to_string()), + ); + e.set( + "maybe".to_string(), + RuntimeValue::BuiltinFunction("maybe".to_string()), + ); + e.set( + "sample".to_string(), + RuntimeValue::BuiltinFunction("sample".to_string()), + ); + e.set( + "debug_state".to_string(), + RuntimeValue::BuiltinFunction("debug_state".to_string()), + ); + e.set( + "measure".to_string(), + RuntimeValue::BuiltinFunction("measure".to_string()), + ); - e.set("_graphics_create_plot".to_string(), RuntimeValue::BuiltinFunction("_graphics_create_plot".to_string())); - e.set("_graphics_plot_set_data".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_data".to_string())); - e.set("_graphics_plot_set_title".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_set_title".to_string())); - e.set("_graphics_plot_render".to_string(), RuntimeValue::BuiltinFunction("_graphics_plot_render".to_string())); - e.set("_graphics_destroy_plot".to_string(), RuntimeValue::BuiltinFunction("_graphics_destroy_plot".to_string())); + e.set( + "_graphics_create_canvas".to_string(), + RuntimeValue::BuiltinFunction("_graphics_create_canvas".to_string()), + ); + e.set( + "_graphics_set_background".to_string(), + RuntimeValue::BuiltinFunction("_graphics_set_background".to_string()), + ); + e.set( + "_graphics_draw_rect".to_string(), + RuntimeValue::BuiltinFunction("_graphics_draw_rect".to_string()), + ); + e.set( + "_graphics_draw_circle".to_string(), + RuntimeValue::BuiltinFunction("_graphics_draw_circle".to_string()), + ); + e.set( + "_graphics_draw_line".to_string(), + RuntimeValue::BuiltinFunction("_graphics_draw_line".to_string()), + ); + e.set( + "_graphics_draw_text".to_string(), + RuntimeValue::BuiltinFunction("_graphics_draw_text".to_string()), + ); + e.set( + "_graphics_save_svg".to_string(), + RuntimeValue::BuiltinFunction("_graphics_save_svg".to_string()), + ); + e.set( + "_graphics_destroy_canvas".to_string(), + RuntimeValue::BuiltinFunction("_graphics_destroy_canvas".to_string()), + ); - e.set("file_write".to_string(), RuntimeValue::BuiltinFunction("file_write".to_string())); - e.set("file_read".to_string(), RuntimeValue::BuiltinFunction("file_read".to_string())); + e.set( + "_graphics_create_plot".to_string(), + RuntimeValue::BuiltinFunction("_graphics_create_plot".to_string()), + ); + e.set( + "_graphics_plot_set_data".to_string(), + RuntimeValue::BuiltinFunction("_graphics_plot_set_data".to_string()), + ); + e.set( + "_graphics_plot_set_title".to_string(), + RuntimeValue::BuiltinFunction("_graphics_plot_set_title".to_string()), + ); + e.set( + "_graphics_plot_render".to_string(), + RuntimeValue::BuiltinFunction("_graphics_plot_render".to_string()), + ); + e.set( + "_graphics_destroy_plot".to_string(), + RuntimeValue::BuiltinFunction("_graphics_destroy_plot".to_string()), + ); + e.set( + "file_write".to_string(), + RuntimeValue::BuiltinFunction("file_write".to_string()), + ); + e.set( + "file_read".to_string(), + RuntimeValue::BuiltinFunction("file_read".to_string()), + ); } fn is_truthy(val: &RuntimeValue) -> bool { match val { @@ -225,9 +304,7 @@ impl Evaluator { methods: &Vec, constructor: &Option>, env: &Rc>, - ) -> Result { - let mut method_map = HashMap::new(); for method in methods { method_map.insert(method.name.clone(), method.clone()); @@ -252,34 +329,56 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - let parts: Vec<&str> = class_name.split('.').collect(); let class_value_rc = if parts.len() > 1 { let module_name = parts[0]; let target_class = parts[1]; - let module_rc = env.borrow().get(module_name) - .ok_or_else(|| format!("Runtime Error at {}: Unknown module '{}'", loc, module_name))?; + let module_rc = env.borrow().get(module_name).ok_or_else(|| { + format!("Runtime Error at {}: Unknown module '{}'", loc, module_name) + })?; let module_val = module_rc.borrow(); if let RuntimeValue::Module(mod_env) = &*module_val { - mod_env.borrow().get(target_class) - .ok_or_else(|| format!("Runtime Error at {}: Module '{}' has no class '{}'", loc, module_name, target_class))? + mod_env.borrow().get(target_class).ok_or_else(|| { + format!( + "Runtime Error at {}: Module '{}' has no class '{}'", + loc, module_name, target_class + ) + })? } else { - return Err(format!("Runtime Error at {}: '{}' is not a module.", loc, module_name)); + return Err(format!( + "Runtime Error at {}: '{}' is not a module.", + loc, module_name + )); } } else { - env.borrow().get(class_name) - .ok_or_else(|| format!("Runtime Error at {}: Unknown class '{}'", loc, class_name))? + env.borrow().get(class_name).ok_or_else(|| { + format!("Runtime Error at {}: Unknown class '{}'", loc, class_name) + })? }; let class_borrow = class_value_rc.borrow(); let (fields_def, methods, constructor, def_env) = match &*class_borrow { - RuntimeValue::Class { fields, methods, constructor, definition_env, .. } => { - (fields.clone(), methods.clone(), constructor.clone(), definition_env.clone()) + RuntimeValue::Class { + fields, + methods, + constructor, + definition_env, + .. + } => ( + fields.clone(), + methods.clone(), + constructor.clone(), + definition_env.clone(), + ), + _ => { + return Err(format!( + "Runtime Error at {}: '{}' is not a class", + loc, class_name + )) } - _ => return Err(format!("Runtime Error at {}: '{}' is not a class", loc, class_name)), }; drop(class_borrow); @@ -305,7 +404,10 @@ impl Evaluator { }; if let Some(constructor_node) = constructor { - if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { + if let ASTNode::FunctionDeclaration { + parameters, body, .. + } = &*constructor_node + { let mut evaluated_args = Vec::new(); for arg in arguments { evaluated_args.push(Self::evaluate(arg, env)?); @@ -318,18 +420,23 @@ impl Evaluator { let parent_env = def_env.unwrap_or(env.clone()); let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(parent_env))); - constructor_env.borrow_mut().set("self".to_string(), instance.clone()); + constructor_env + .borrow_mut() + .set("self".to_string(), instance.clone()); for (name, val_rc) in &initial_values { - constructor_env.borrow_mut().define_rc(name.clone(), val_rc.clone()); + constructor_env + .borrow_mut() + .define_rc(name.clone(), val_rc.clone()); } for (param, arg_value) in parameters.iter().zip(evaluated_args) { - constructor_env.borrow_mut().set(param.name.clone(), arg_value); + constructor_env + .borrow_mut() + .set(param.name.clone(), arg_value); } Self::evaluate(&body, &constructor_env)?; - } } @@ -354,11 +461,9 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - let object = Self::evaluate(object_expr, env)?; match object { - RuntimeValue::Instance { class_name, fields, @@ -396,7 +501,9 @@ impl Evaluator { methods: methods.clone(), definition_env: None, }; - method_env.borrow_mut().set("self".to_string(), self_instance); + method_env + .borrow_mut() + .set("self".to_string(), self_instance); for (field_name, field_value_rc) in &fields { method_env @@ -421,7 +528,6 @@ impl Evaluator { } } - RuntimeValue::Module(module_env) => { let member_val = module_env.borrow().get(method_name).ok_or_else(|| { format!( @@ -430,7 +536,6 @@ impl Evaluator { ) })?; - let member_val = member_val.borrow().clone(); let evaluated_args = Self::eval_arguments(arguments, env)?; @@ -567,10 +672,7 @@ impl Evaluator { } } - fn eval_self_ref( - loc: &Loc, - env: &Rc>, - ) -> Result { + fn eval_self_ref(loc: &Loc, env: &Rc>) -> Result { Err(format!( "Runtime Error at {}: 'self' can only be used inside class methods", loc @@ -670,7 +772,6 @@ impl Evaluator { Ok(state) } - fn complex_mul((a, b): (f64, f64), (c, d): (f64, f64)) -> (f64, f64) { (a * c - b * d, a * d + b * c) } @@ -693,13 +794,11 @@ impl Evaluator { loc: &Loc, env: &Rc>, ) -> Result { - let mut qubit_args = Vec::new(); for arg_node in arg_nodes { qubit_args.push(Self::evaluate(arg_node, env)?); } - let gate_val = Self::eval_gate_expression(gate_expr_node, env)?; let (base_name, is_dagger, num_controls) = match gate_val { @@ -716,7 +815,6 @@ impl Evaluator { } }; - let mut controls = Vec::new(); let mut targets = Vec::new(); @@ -731,7 +829,9 @@ impl Evaluator { for (i, qubit_val) in qubit_args.iter().enumerate() { let (q_state_rc, q_index, q_size) = match qubit_val { - RuntimeValue::Qubit { state, index, size, .. } => (state.clone(), *index, *size), + RuntimeValue::Qubit { + state, index, size, .. + } => (state.clone(), *index, *size), _ => { return Err(format!( "Runtime Error at {}: Gate arguments must be Qubits, but argument {} was {}.", @@ -758,11 +858,9 @@ impl Evaluator { } } - let mut params = Vec::new(); Self::extract_gate_params(gate_expr_node, &mut params, env)?; - let gate_def = GateDefinition { name: base_name, params, @@ -772,7 +870,6 @@ impl Evaluator { state_rc: state_rc.unwrap(), }; - Self::apply_multi_controlled_gate(gate_def, is_dagger) } @@ -799,13 +896,11 @@ impl Evaluator { base_name, is_dagger, num_controls, - } => { - Ok(RuntimeValue::Gate { - base_name, - is_dagger: !is_dagger, - num_controls, - }) - } + } => Ok(RuntimeValue::Gate { + base_name, + is_dagger: !is_dagger, + num_controls, + }), _ => Err("Internal Error: 'dagger' did not receive a valid gate.".to_string()), } } @@ -816,13 +911,11 @@ impl Evaluator { base_name, is_dagger, num_controls, - } => { - Ok(RuntimeValue::Gate { - base_name, - is_dagger, - num_controls: num_controls + 1, - }) - } + } => Ok(RuntimeValue::Gate { + base_name, + is_dagger, + num_controls: num_controls + 1, + }), _ => { Err("Internal Error: 'controlled' did not receive a valid gate." .to_string()) @@ -838,19 +931,17 @@ impl Evaluator { ASTNode::Gate { name, .. } => Ok((name.to_lowercase(), false)), ASTNode::ParameterizedGate { name, .. } => Ok((name.to_lowercase(), false)), - ASTNode::Dagger { gate_expr, .. } => { let (name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; Ok((name, !is_dagger)) - }, - + } ASTNode::Controlled { gate_expr, .. } => { let (name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; Ok((format!("c{}", name), is_dagger)) - }, + } - _ => Err("Invalid gate expression".to_string()) + _ => Err("Invalid gate expression".to_string()), } } @@ -897,13 +988,11 @@ impl Evaluator { loc, .. } => { - let gate_name = match &**callee { ASTNode::Identifier { name, .. } => name.to_lowercase(), _ => return Err(format!("Runtime Error at {}: Gate expression must be a simple identifier inside the call.", loc)), }; - for arg_node in arguments.iter() { let arg_val = Self::evaluate(arg_node, env)?; let float_param = match arg_val { @@ -974,18 +1063,14 @@ impl Evaluator { let mut processed = std::collections::HashSet::new(); - for (&basis_state, &_tuple) in old_state_map.iter() { if processed.contains(&basis_state) { continue; } - if (basis_state & control_mask) == control_mask { - let partner_state = basis_state ^ target_mask; - let amp_self_tuple = amp_tuple; let amp_partner_tuple = old_state_map .get(&partner_state) @@ -997,35 +1082,27 @@ impl Evaluator { let (amp0, amp1, idx0, idx1); - if (basis_state & target_mask) == 0 { - amp0 = amp_self; amp1 = amp_partner; idx0 = basis_state; idx1 = partner_state; } else { - amp0 = amp_partner; amp1 = amp_self; idx0 = partner_state; idx1 = basis_state; } - let new_amp0 = matrix[0][0] * amp0 + matrix[0][1] * amp1; let new_amp1 = matrix[1][0] * amp0 + matrix[1][1] * amp1; - Self::insert_if_nonzero(&mut new_state_map, idx0, new_amp0); Self::insert_if_nonzero(&mut new_state_map, idx1, new_amp1); - processed.insert(idx0); processed.insert(idx1); } else { - - new_state_map.insert(basis_state, amp_tuple); processed.insert(basis_state); } @@ -1064,9 +1141,7 @@ impl Evaluator { let angle = if is_dagger { -phi } else { phi }; let phase_factor = C64::from_polar(1.0, angle); - for (&basis_state, amp_tuple) in state_map_guard.iter_mut() { - if (basis_state & control_mask) != 0 && (basis_state & target_mask) != 0 { let amp = C64::new(amp_tuple.0, amp_tuple.1); let new_amp = amp * phase_factor; @@ -1081,11 +1156,9 @@ impl Evaluator { let bit_b = (basis_state & target_mask) != 0; if bit_a != bit_b { - let swapped_idx = basis_state ^ control_mask ^ target_mask; new_state_map.insert(swapped_idx, amp_tuple); } else { - new_state_map.insert(basis_state, amp_tuple); } } @@ -1094,21 +1167,17 @@ impl Evaluator { "cnot" | "cx" => { let mut new_state_map = HashMap::new(); for (&basis_state, &_tuple) in state_map_guard.iter() { - if (basis_state & control_mask) != 0 { let swapped_idx = basis_state ^ target_mask; new_state_map.insert(swapped_idx, amp_tuple); } else { - new_state_map.insert(basis_state, amp_tuple); } } *state_map_guard = new_state_map; } "cz" => { - for (&basis_state, amp_tuple) in state_map_guard.iter_mut() { - if (basis_state & control_mask) != 0 && (basis_state & target_mask) != 0 { *amp_tuple = (-amp_tuple.0, -amp_tuple.1); } @@ -1125,7 +1194,6 @@ impl Evaluator { Ok(RuntimeValue::None) } - fn get_gate_matrix( name: &str, params: &[f64], @@ -1135,7 +1203,6 @@ impl Evaluator { let eff_dagger = if is_dagger { -1.0 } else { 1.0 }; match name { - "x" | "not" | "cnot" | "ccx" | "toffoli" => Ok([ [C64::new(0.0, 0.0), C64::new(1.0, 0.0)], [C64::new(1.0, 0.0), C64::new(0.0, 0.0)], @@ -1231,8 +1298,9 @@ impl Evaluator { ) -> Result { let target_val = Self::evaluate(target_expr, env)?; let (state_rc, target_index, reg_size) = match target_val { - - RuntimeValue::Qubit { state, index, size, .. } => (state, index, size), + RuntimeValue::Qubit { + state, index, size, .. + } => (state, index, size), _ => { return Err(format!( "Runtime Error: 'measure' can only be used on a single Qubit, got {}.", @@ -1262,10 +1330,8 @@ impl Evaluator { let target_mask = 1 << target_index; let mut prob0 = 0.0; - for (&basis_state, &_tuple) in old_state_map.iter() { if (basis_state & target_mask) == 0 { - prob0 += amp_tuple.0.powi(2) + amp_tuple.1.powi(2); } } @@ -1291,29 +1357,21 @@ impl Evaluator { let mut new_state_map = HashMap::new(); - for (&basis_state, &_tuple) in old_state_map.iter() { let bit_at_index = (basis_state >> target_index) & 1; - if bit_at_index as i64 == measured_result { let (real, imag) = amp_tuple; let normalized_amp = Self::complex_scalar_mul((real, imag), norm_factor); new_state_map.insert(basis_state, normalized_amp); } - } - *state_map_guard = new_state_map; Ok(RuntimeValue::Int(measured_result)) } - - - - fn eval_array_access( collection_expr: &Box, index_expr: &Box, @@ -1345,7 +1403,12 @@ impl Evaluator { )) } - RuntimeValue::QuantumRegister { size, state, register_name, global_start_index } => { + RuntimeValue::QuantumRegister { + size, + state, + register_name, + global_start_index, + } => { let index = match index_val { RuntimeValue::Int(i) => i, _ => return Err(format!("Runtime Error at {}: Index must be int.", loc)), @@ -1354,7 +1417,6 @@ impl Evaluator { return Err(format!("Runtime Error at {}: Index out of bounds.", loc)); } - let global_index = global_start_index.map(|start| start + (index as usize)); Ok(RuntimeValue::Qubit { @@ -1388,22 +1450,20 @@ impl Evaluator { ) -> Result { let object = Self::evaluate(object_expr, env)?; match object { - - RuntimeValue::Module(module_env) => { - match module_env.borrow().get(member) { - Some(value_rc) => Ok(value_rc.borrow().clone()), - None => Err(format!( - "Runtime Error: Module does not have a member named '{}'", - member - )), - } - } - RuntimeValue::Instance { fields, .. } => { - match fields.get(member) { - Some(value_rc) => Ok(value_rc.borrow().clone()), - None => Err(format!("Runtime Error: Instance has no field named '{}'", member)) - } - } + RuntimeValue::Module(module_env) => match module_env.borrow().get(member) { + Some(value_rc) => Ok(value_rc.borrow().clone()), + None => Err(format!( + "Runtime Error: Module does not have a member named '{}'", + member + )), + }, + RuntimeValue::Instance { fields, .. } => match fields.get(member) { + Some(value_rc) => Ok(value_rc.borrow().clone()), + None => Err(format!( + "Runtime Error: Instance has no field named '{}'", + member + )), + }, RuntimeValue::QuantumRegister { size, .. } => match member { "length" => Ok(RuntimeValue::Int(size as i64)), @@ -1589,6 +1649,7 @@ impl Evaluator { match func_name.as_str() { "print" => Self::builtin_print(evaluated_args), + "time" => Self::builtin_time(evaluated_args), "input" => Self::builtin_input(evaluated_args), "split" => Self::builtin_split(evaluated_args), "echo" => Self::builtin_echo(evaluated_args), @@ -1601,16 +1662,24 @@ impl Evaluator { "len" => Self::builtin_len(evaluated_args), "debug_state" => Self::builtin_debug_state(evaluated_args), "assert" => Self::builtin_assert(evaluated_args), - "_graphics_create_canvas" => Self::builtin_graphics_create_canvas(evaluated_args), - "_graphics_set_background" => Self::builtin_graphics_set_background(evaluated_args), + "_graphics_create_canvas" => { + Self::builtin_graphics_create_canvas(evaluated_args) + } + "_graphics_set_background" => { + Self::builtin_graphics_set_background(evaluated_args) + } "_graphics_draw_rect" => Self::builtin_graphics_draw_rect(evaluated_args), "_graphics_draw_circle" => Self::builtin_graphics_draw_circle(evaluated_args), "_graphics_draw_line" => Self::builtin_graphics_draw_line(evaluated_args), "_graphics_save_svg" => Self::builtin_graphics_save_svg(evaluated_args), "_graphics_destroy_canvas" => Ok(RuntimeValue::None), "_graphics_create_plot" => Self::builtin_graphics_create_plot(evaluated_args), - "_graphics_plot_set_data" => Self::builtin_graphics_plot_set_data(evaluated_args), - "_graphics_plot_set_title" => Self::builtin_graphics_plot_set_title(evaluated_args), + "_graphics_plot_set_data" => { + Self::builtin_graphics_plot_set_data(evaluated_args) + } + "_graphics_plot_set_title" => { + Self::builtin_graphics_plot_set_title(evaluated_args) + } "_graphics_plot_render" => Self::builtin_graphics_plot_render(evaluated_args), "_graphics_destroy_plot" => Self::builtin_graphics_destroy_plot(evaluated_args), @@ -1666,78 +1735,82 @@ impl Evaluator { } } - RuntimeValue::Class { name, fields, methods, constructor, .. } => { - - let mut instance_fields = HashMap::new(); - let mut initial_values = HashMap::new(); - for field in &fields { - let value = if let Some(default_expr) = &field.default_value { - Self::evaluate(default_expr, env)? - } else { - RuntimeValue::None - }; - initial_values.insert(field.name.clone(), value.clone()); - instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); - } - - - let mut instance = RuntimeValue::Instance { - class_name: name.clone(), - fields: instance_fields.clone(), - methods: methods.clone(), - definition_env: None, - }; - - - if let Some(constructor_node) = constructor { - if let ASTNode::FunctionDeclaration { parameters, body, .. } = &*constructor_node { - if parameters.len() != evaluated_args.len() { - return Err(format!("Runtime Error: Constructor arg mismatch.")); - } - - let constructor_env = Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); - - - constructor_env.borrow_mut().set("self".to_string(), instance.clone()); - + RuntimeValue::Class { + name, + fields, + methods, + constructor, + .. + } => { + let mut instance_fields = HashMap::new(); + let mut initial_values = HashMap::new(); + for field in &fields { + let value = if let Some(default_expr) = &field.default_value { + Self::evaluate(default_expr, env)? + } else { + RuntimeValue::None + }; + initial_values.insert(field.name.clone(), value.clone()); + instance_fields.insert(field.name.clone(), Rc::new(RefCell::new(value))); + } - for (name, val) in &initial_values { - constructor_env.borrow_mut().set(name.clone(), val.clone()); - } + let mut instance = RuntimeValue::Instance { + class_name: name.clone(), + fields: instance_fields.clone(), + methods: methods.clone(), + definition_env: None, + }; + if let Some(constructor_node) = constructor { + if let ASTNode::FunctionDeclaration { + parameters, body, .. + } = &*constructor_node + { + if parameters.len() != evaluated_args.len() { + return Err(format!("Runtime Error: Constructor arg mismatch.")); + } - for (param, arg_value) in parameters.iter().zip(evaluated_args) { - constructor_env.borrow_mut().set(param.name.clone(), arg_value); - } + let constructor_env = + Rc::new(RefCell::new(Environment::new_enclosed(env.clone()))); + constructor_env + .borrow_mut() + .set("self".to_string(), instance.clone()); - Self::evaluate(&body, &constructor_env)?; + for (name, val) in &initial_values { + constructor_env.borrow_mut().set(name.clone(), val.clone()); + } + for (param, arg_value) in parameters.iter().zip(evaluated_args) { + constructor_env + .borrow_mut() + .set(param.name.clone(), arg_value); + } - for (field_name, field_rc) in &instance_fields { - if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { - let local_val = local_val_rc.borrow().clone(); + Self::evaluate(&body, &constructor_env)?; - if let Some(initial_val) = initial_values.get(field_name) { + for (field_name, field_rc) in &instance_fields { + if let Some(local_val_rc) = constructor_env.borrow().get(field_name) { + let local_val = local_val_rc.borrow().clone(); - if !Self::are_values_equal(&local_val, initial_val) { - *field_rc.borrow_mut() = local_val; - } - } + if let Some(initial_val) = initial_values.get(field_name) { + if !Self::are_values_equal(&local_val, initial_val) { + *field_rc.borrow_mut() = local_val; } } - - - instance = RuntimeValue::Instance { - class_name: name.clone(), - fields: instance_fields, - methods, - definition_env: None, - }; } } - Ok(instance) + + instance = RuntimeValue::Instance { + class_name: name.clone(), + fields: instance_fields, + methods, + definition_env: None, + }; } + } + Ok(instance) + } _ => Err(format!( "Runtime Error at {}: '{}' is not a callable function.", loc, name @@ -1795,7 +1868,6 @@ impl Evaluator { loc, } => { let daggered_gate_expr = match &**gate_expr { - ASTNode::Gate { name, loc, .. } => ASTNode::Dagger { gate_expr: Box::new(ASTNode::Gate { name: name.clone(), @@ -1885,7 +1957,6 @@ impl Evaluator { Ok(last_result) } - pub fn print_quantum_state( state: &Rc>>, size: usize, @@ -1913,6 +1984,18 @@ impl Evaluator { } println!("-----------------------------------"); } + + fn builtin_time(args: Vec) -> Result { + if !args.is_empty() { + return Err("Runtime Error: 'time' expects 0 arguments.".to_string()); + } + let start = std::time::SystemTime::now(); + let since_the_epoch = start + .duration_since(std::time::UNIX_EPOCH) + .map_err(|e| format!("Time Error: {}", e))?; + Ok(RuntimeValue::Float(since_the_epoch.as_secs_f64())) + } + fn builtin_debug_state(args: Vec) -> Result { if args.len() != 1 { return Err( @@ -1921,8 +2004,7 @@ impl Evaluator { ); } match &args[0] { - RuntimeValue::QuantumRegister { size, state ,..} => { - + RuntimeValue::QuantumRegister { size, state, .. } => { Self::print_quantum_state(state, *size, 10); Ok(RuntimeValue::None) } @@ -1935,17 +2017,19 @@ impl Evaluator { fn builtin_file_write(args: Vec) -> Result { if args.len() != 2 { - return Err("Runtime Error: 'file_write' expects 2 arguments (path, content).".to_string()); + return Err( + "Runtime Error: 'file_write' expects 2 arguments (path, content).".to_string(), + ); } let path = match &args[0] { RuntimeValue::String(s) => s, - _ => return Err("Runtime Error: File path must be a String.".to_string()) + _ => return Err("Runtime Error: File path must be a String.".to_string()), }; let content = match &args[1] { RuntimeValue::String(s) => s, - _ => return Err("Runtime Error: File content must be a String.".to_string()) + _ => return Err("Runtime Error: File content must be a String.".to_string()), }; std::fs::write(path, content).map_err(|e| format!("File Write Error: {}", e))?; @@ -1959,10 +2043,11 @@ impl Evaluator { let path = match &args[0] { RuntimeValue::String(s) => s, - _ => return Err("Runtime Error: File path must be a String.".to_string()) + _ => return Err("Runtime Error: File path must be a String.".to_string()), }; - let content = std::fs::read_to_string(path).map_err(|e| format!("File Read Error: {}", e))?; + let content = + std::fs::read_to_string(path).map_err(|e| format!("File Read Error: {}", e))?; Ok(RuntimeValue::String(content)) } @@ -1972,8 +2057,10 @@ impl Evaluator { } let (state_rc, target_index, reg_size) = match &args[0] { - RuntimeValue::Qubit { state, index, size, .. } => (state, *index, *size), - _ => return Err(format!("Runtime Error: Not a qubit.")) + RuntimeValue::Qubit { + state, index, size, .. + } => (state, *index, *size), + _ => return Err(format!("Runtime Error: Not a qubit.")), }; Self::perform_measurement(state_rc, target_index, reg_size) } @@ -2006,25 +2093,21 @@ impl Evaluator { }; if condition { - Ok(RuntimeValue::None) } else { - Err(format!("Assertion Failed: {}", message)) } } fn builtin_print(args: Vec) -> Result { let output: Vec = args .into_iter() - .map(|val| { - match val { - RuntimeValue::String(s) => s, - RuntimeValue::Int(n) => n.to_string(), - RuntimeValue::Float(f) => f.to_string(), - RuntimeValue::Bool(b) => b.to_string(), - RuntimeValue::None => "None".to_string(), - _ => format!("{}", val), - } + .map(|val| match val { + RuntimeValue::String(s) => s, + RuntimeValue::Int(n) => n.to_string(), + RuntimeValue::Float(f) => f.to_string(), + RuntimeValue::Bool(b) => b.to_string(), + RuntimeValue::None => "None".to_string(), + _ => format!("{}", val), }) .collect(); println!("{}", output.join(" ")); @@ -2032,20 +2115,21 @@ impl Evaluator { } fn builtin_split(args: Vec) -> Result { if args.len() != 2 { - return Err("Runtime Error: 'split' expects 2 arguments (string, delimiter).".to_string()); + return Err( + "Runtime Error: 'split' expects 2 arguments (string, delimiter).".to_string(), + ); } let target_str = match &args[0] { RuntimeValue::String(s) => s, - _ => return Err("Runtime Error: First argument to split must be a String.".to_string()) + _ => return Err("Runtime Error: First argument to split must be a String.".to_string()), }; let delim = match &args[1] { RuntimeValue::String(s) => s, - _ => return Err("Runtime Error: Delimiter must be a String.".to_string()) + _ => return Err("Runtime Error: Delimiter must be a String.".to_string()), }; - let elements: Vec>> = target_str .split(delim) .map(|s| { @@ -2057,19 +2141,16 @@ impl Evaluator { Ok(RuntimeValue::Register(elements)) } fn builtin_input(args: Vec) -> Result { - if let Some(val) = args.get(0) { print!("{}", val); let _ = std::io::stdout().flush(); } - let mut input_buffer = String::new(); std::io::stdin() .read_line(&mut input_buffer) .map_err(|e| format!("Runtime Error: Failed to read input. {}", e))?; - Ok(RuntimeValue::String(input_buffer.trim().to_string())) } @@ -2187,7 +2268,6 @@ impl Evaluator { } } - fn eval_let_declaration( name: &str, value_expr: &ASTNode, @@ -2206,7 +2286,6 @@ impl Evaluator { let new_value = Self::evaluate(value_expr, env)?; match target { - ASTNode::Identifier { name, .. } => { if let Some(var_rc) = env.borrow().get(name) { *std::cell::RefCell::<_>::borrow_mut(&var_rc) = new_value; @@ -2219,7 +2298,6 @@ impl Evaluator { } } - ASTNode::ArrayAccess { array, index, loc } => { let collection_val = Self::evaluate(array, env)?; let index_val = Self::evaluate(index, env)?; @@ -2257,23 +2335,24 @@ impl Evaluator { } ASTNode::MemberAccess { object, member } => { - let object_val = Self::evaluate(object, env)?; match object_val { RuntimeValue::Instance { fields, .. } => { if let Some(field_rc) = fields.get(member) { - *field_rc.borrow_mut() = new_value; Ok(RuntimeValue::None) } else { - - Err(format!("Runtime Error: Instance has no field named '{}'.", member)) + Err(format!( + "Runtime Error: Instance has no field named '{}'.", + member + )) } } _ => Err(format!( "Runtime Error: Cannot assign to member '{}' of non-instance type {}.", - member, object_val.type_name() + member, + object_val.type_name() )), } } @@ -2305,14 +2384,11 @@ impl Evaluator { alias: &str, env: &Rc>, ) -> Result { - let file_path = match path { ImportPath::File(f) => { if f.ends_with(".qc") || f.contains('/') || f.contains('\\') { - f.clone() } else { - format!("q_packages/{}/init.qc", f) } } @@ -2335,15 +2411,12 @@ impl Evaluator { .parse() .map_err(|e| format!("Module Parser Error: {}", e))?; - let module_env = Rc::new(RefCell::new(Environment::new())); Self::register_builtins(&module_env); Self::evaluate_program(&ast, &module_env)?; - let module_val = RuntimeValue::Module(module_env); - env.borrow_mut().set(alias.to_string(), module_val); Ok(RuntimeValue::None) @@ -2518,14 +2591,14 @@ impl Evaluator { ( RuntimeValue::QuantumRegister { size: size_a, - state: state_a_rc,.. + state: state_a_rc, + .. }, RuntimeValue::QuantumRegister { size: size_b, - state: state_b_rc,.. + state: state_b_rc, + .. }, - - ) => { let state_a = state_a_rc.borrow(); let state_b = state_b_rc.borrow(); @@ -2533,14 +2606,11 @@ impl Evaluator { let mut new_state = HashMap::new(); - - for (&i, &_a) in state_a.iter() { for (&j, &_b) in state_b.iter() { let new_index = (i << size_b) | j; let new_amp_tuple = Self::complex_mul(amp_a, amp_b); - new_state.insert(new_index, new_amp_tuple); } } @@ -2551,12 +2621,11 @@ impl Evaluator { register_name: "tensor_result".to_string(), global_start_index: None, }); - } (RuntimeValue::Register(vec_a), RuntimeValue::Register(vec_b)) => { if vec_a.len() != vec_b.len() { - return Err("Dimension mismatch for dot product".to_string()); + return Err("Dimension mismatch for dot product".to_string()); } let mut sum = 0.0; @@ -2564,12 +2633,12 @@ impl Evaluator { let a = match *a_rc.borrow() { RuntimeValue::Float(f) => f, RuntimeValue::Int(i) => i as f64, - _ => return Err("Tensor product requires numbers".to_string()) + _ => return Err("Tensor product requires numbers".to_string()), }; let b = match *b_rc.borrow() { RuntimeValue::Float(f) => f, RuntimeValue::Int(i) => i as f64, - _ => return Err("Tensor product requires numbers".to_string()) + _ => return Err("Tensor product requires numbers".to_string()), }; sum += a * b; } @@ -2766,8 +2835,14 @@ impl Evaluator { } } fn builtin_graphics_create_canvas(args: Vec) -> Result { - let width = match args.get(0) { Some(RuntimeValue::Int(i)) => *i as u32, _ => return Err("Width must be Int".to_string()) }; - let height = match args.get(1) { Some(RuntimeValue::Int(i)) => *i as u32, _ => return Err("Height must be Int".to_string()) }; + let width = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i as u32, + _ => return Err("Width must be Int".to_string()), + }; + let height = match args.get(1) { + Some(RuntimeValue::Int(i)) => *i as u32, + _ => return Err("Height must be Int".to_string()), + }; let mut id_guard = NEXT_ID.lock().unwrap(); let id = *id_guard; @@ -2780,11 +2855,26 @@ impl Evaluator { } fn builtin_graphics_set_background(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; - let r = match args.get(1) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; - let g = match args.get(2) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; - let b = match args.get(3) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; - let a = match args.get(4) { Some(RuntimeValue::Int(i)) => *i as u8, _ => 255 }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; + let r = match args.get(1) { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; + let g = match args.get(2) { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; + let b = match args.get(3) { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; + let a = match args.get(4) { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 255, + }; let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); if let Some(canvas) = canvases.get_mut(&id) { @@ -2794,10 +2884,20 @@ impl Evaluator { } fn builtin_graphics_draw_rect(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; - let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; - let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let to_f = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Float(f)) => *f, + Some(RuntimeValue::Int(i)) => *i as f64, + _ => 0.0, + }; + let to_u8 = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; let x = to_f(args.get(1)); let y = to_f(args.get(2)); @@ -2807,7 +2907,10 @@ impl Evaluator { let g = to_u8(args.get(6)); let b = to_u8(args.get(7)); let a = to_u8(args.get(8)); - let filled = match args.get(9) { Some(RuntimeValue::Int(i)) => *i != 0, _ => false }; + let filled = match args.get(9) { + Some(RuntimeValue::Int(i)) => *i != 0, + _ => false, + }; let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); if let Some(canvas) = canvases.get_mut(&id) { @@ -2817,10 +2920,20 @@ impl Evaluator { } fn builtin_graphics_draw_circle(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; - let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; - let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let to_f = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Float(f)) => *f, + Some(RuntimeValue::Int(i)) => *i as f64, + _ => 0.0, + }; + let to_u8 = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; let x = to_f(args.get(1)); let y = to_f(args.get(2)); @@ -2829,7 +2942,10 @@ impl Evaluator { let g = to_u8(args.get(5)); let b = to_u8(args.get(6)); let a = to_u8(args.get(7)); - let filled = match args.get(8) { Some(RuntimeValue::Int(i)) => *i != 0, _ => false }; + let filled = match args.get(8) { + Some(RuntimeValue::Int(i)) => *i != 0, + _ => false, + }; let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); if let Some(canvas) = canvases.get_mut(&id) { @@ -2839,10 +2955,20 @@ impl Evaluator { } fn builtin_graphics_draw_line(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; - let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; - let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let to_f = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Float(f)) => *f, + Some(RuntimeValue::Int(i)) => *i as f64, + _ => 0.0, + }; + let to_u8 = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; let x1 = to_f(args.get(1)); let y1 = to_f(args.get(2)); @@ -2862,14 +2988,27 @@ impl Evaluator { } fn builtin_graphics_draw_text(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; - let to_f = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Float(f)) => *f, Some(RuntimeValue::Int(i)) => *i as f64, _ => 0.0 }; - let to_u8 = |v: Option<&RuntimeValue>| match v { Some(RuntimeValue::Int(i)) => *i as u8, _ => 0 }; + let to_f = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Float(f)) => *f, + Some(RuntimeValue::Int(i)) => *i as f64, + _ => 0.0, + }; + let to_u8 = |v: Option<&RuntimeValue>| match v { + Some(RuntimeValue::Int(i)) => *i as u8, + _ => 0, + }; let x = to_f(args.get(1)); let y = to_f(args.get(2)); - let text = match args.get(3) { Some(RuntimeValue::String(s)) => s.clone(), _ => "".to_string() }; + let text = match args.get(3) { + Some(RuntimeValue::String(s)) => s.clone(), + _ => "".to_string(), + }; let r = to_u8(args.get(4)); let g = to_u8(args.get(5)); let b = to_u8(args.get(6)); @@ -2884,8 +3023,14 @@ impl Evaluator { } fn builtin_graphics_save_svg(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; - let filename = match args.get(1) { Some(RuntimeValue::String(s)) => s.clone(), _ => return Err("Filename error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; + let filename = match args.get(1) { + Some(RuntimeValue::String(s)) => s.clone(), + _ => return Err("Filename error".to_string()), + }; let canvases = INTERPRETER_CANVASES.lock().unwrap(); if let Some(canvas) = canvases.get(&id) { @@ -2899,35 +3044,42 @@ impl Evaluator { } fn builtin_graphics_destroy_canvas(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; INTERPRETER_CANVASES.lock().unwrap().remove(&id); Ok(RuntimeValue::None) } - - fn builtin_graphics_create_plot(args: Vec) -> Result { - let ptype_int = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => 0 }; - let ptype = match ptype_int { - 0 => PlotType::Line, - 1 => PlotType::Scatter, - 2 => PlotType::Bar, - 3 => PlotType::Histogram, - 4 => PlotType::Heatmap, - _ => PlotType::Line, - }; + let ptype_int = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => 0, + }; + let ptype = match ptype_int { + 0 => PlotType::Line, + 1 => PlotType::Scatter, + 2 => PlotType::Bar, + 3 => PlotType::Histogram, + 4 => PlotType::Heatmap, + _ => PlotType::Line, + }; - let mut id_guard = NEXT_ID.lock().unwrap(); - let id = *id_guard; - *id_guard += 1; + let mut id_guard = NEXT_ID.lock().unwrap(); + let id = *id_guard; + *id_guard += 1; - let plot = Plot::new(ptype); - INTERPRETER_PLOTS.lock().unwrap().insert(id, plot); - Ok(RuntimeValue::Int(id)) + let plot = Plot::new(ptype); + INTERPRETER_PLOTS.lock().unwrap().insert(id, plot); + Ok(RuntimeValue::Int(id)) } fn builtin_graphics_plot_set_data(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; let extract_vec = |val: Option<&RuntimeValue>| -> Vec { match val { @@ -2935,12 +3087,15 @@ impl Evaluator { let mut res = Vec::new(); for item in arr_rc.iter() { let inner = item.borrow(); - if let RuntimeValue::Float(f) = *inner { res.push(f); } - else if let RuntimeValue::Int(i) = *inner { res.push(i as f64); } + if let RuntimeValue::Float(f) = *inner { + res.push(f); + } else if let RuntimeValue::Int(i) = *inner { + res.push(i as f64); + } } res } - _ => Vec::new() + _ => Vec::new(), } }; @@ -2955,8 +3110,14 @@ impl Evaluator { } fn builtin_graphics_plot_set_title(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; - let title = match args.get(1) { Some(RuntimeValue::String(s)) => s.clone(), _ => "".to_string() }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; + let title = match args.get(1) { + Some(RuntimeValue::String(s)) => s.clone(), + _ => "".to_string(), + }; let mut plots = INTERPRETER_PLOTS.lock().unwrap(); if let Some(plot) = plots.get_mut(&id) { @@ -2966,8 +3127,14 @@ impl Evaluator { } fn builtin_graphics_plot_render(args: Vec) -> Result { - let plot_id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; - let canvas_id = match args.get(1) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let plot_id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; + let canvas_id = match args.get(1) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; let plots = INTERPRETER_PLOTS.lock().unwrap(); let mut canvases = INTERPRETER_CANVASES.lock().unwrap(); @@ -2983,7 +3150,10 @@ impl Evaluator { } fn builtin_graphics_destroy_plot(args: Vec) -> Result { - let id = match args.get(0) { Some(RuntimeValue::Int(i)) => *i, _ => return Err("ID error".to_string()) }; + let id = match args.get(0) { + Some(RuntimeValue::Int(i)) => *i, + _ => return Err("ID error".to_string()), + }; INTERPRETER_PLOTS.lock().unwrap().remove(&id); Ok(RuntimeValue::None) } @@ -2997,9 +3167,19 @@ impl Evaluator { let mut qubit_map: QubitMap = HashMap::new(); let mut next_alloc = 0; - Self::eval_recursive(node, env, lifecycle, simulator, &mut qubit_map, &mut next_alloc) + Self::eval_recursive( + node, + env, + lifecycle, + simulator, + &mut qubit_map, + &mut next_alloc, + ) } - fn resolve_qubits(args: &[ASTNode], env: &Rc>) -> Result, String> { + fn resolve_qubits( + args: &[ASTNode], + env: &Rc>, + ) -> Result, String> { let mut indices = Vec::new(); for arg in args { let val = Self::evaluate(arg, env)?; @@ -3007,7 +3187,10 @@ impl Evaluator { if let Some(idx) = global_index { indices.push(idx); } else { - return Err("Runtime Error: Qubit has no hardware index (Simulator not active?).".to_string()); + return Err( + "Runtime Error: Qubit has no hardware index (Simulator not active?)." + .to_string(), + ); } } else { return Err("Runtime Error: Argument is not a Qubit.".to_string()); @@ -3028,8 +3211,13 @@ impl Evaluator { ASTNode::Program(statements) | ASTNode::Block(statements) => { let mut last = RuntimeValue::None; for stmt in statements { - last = Self::eval_recursive(stmt, env, lifecycle, simulator, qubit_map, next_alloc)?; - if let RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue = last { + last = Self::eval_recursive( + stmt, env, lifecycle, simulator, qubit_map, next_alloc, + )?; + if let RuntimeValue::ReturnValue(_) + | RuntimeValue::Break + | RuntimeValue::Continue = last + { return Ok(last); } } @@ -3042,14 +3230,14 @@ impl Evaluator { RuntimeValue::Int(n) => n as usize, _ => 1, } - } else { 1 }; - + } else { + 1 + }; qubit_map.insert(name.clone(), *next_alloc); lifecycle.register_qubits(name, count, QubitState::Classical(false)); - let dummy_state = Self::default_state_vector(count)?; let register = RuntimeValue::QuantumRegister { size: count, @@ -3063,29 +3251,39 @@ impl Evaluator { Ok(RuntimeValue::None) } - ASTNode::Apply { gate_expr, arguments, loc } => { + ASTNode::Apply { + gate_expr, + arguments, + loc, + } => { let qubit_ids = Self::extract_qubit_ids_runtime(arguments, env)?; let (gate_name, is_dagger) = Self::extract_gate_info_runtime(gate_expr)?; if Self::is_controlled_gate(gate_expr) { - let num_controls = Self::count_controls(gate_expr); - let (controls, targets) = qubit_ids.split_at(num_controls); - lifecycle.record_controlled_gate(controls, targets, &gate_name, *loc) + let num_controls = Self::count_controls(gate_expr); + let (controls, targets) = qubit_ids.split_at(num_controls); + lifecycle + .record_controlled_gate(controls, targets, &gate_name, *loc) .map_err(|e| e.to_string())?; } else { for qubit_id in &qubit_ids { - lifecycle.record_operation(qubit_id, QubitOperation::ApplyGate(gate_name.clone()), *loc) + lifecycle + .record_operation( + qubit_id, + QubitOperation::ApplyGate(gate_name.clone()), + *loc, + ) .map_err(|e| e.to_string())?; } } - let target_indices = Self::resolve_qubits(arguments, env)?; let mut params = Vec::new(); Self::extract_gate_params(gate_expr, &mut params, env)?; - simulator.apply_gate(&gate_name, &target_indices, ¶ms, is_dagger) + simulator + .apply_gate(&gate_name, &target_indices, ¶ms, is_dagger) .map_err(|e| format!("Simulator Error: {}", e))?; Ok(RuntimeValue::None) @@ -3095,13 +3293,15 @@ impl Evaluator { let qubit_ids = Self::extract_qubit_ids_runtime(&[*qubit_expr.clone()], env)?; let loc = Self::get_node_location(qubit_expr); for qubit_id in &qubit_ids { - lifecycle.record_operation(qubit_id, QubitOperation::Measure, loc) + lifecycle + .record_operation(qubit_id, QubitOperation::Measure, loc) .map_err(|e| e.to_string())?; } - let indices = Self::resolve_qubits(&[*qubit_expr.clone()], env)?; - if indices.is_empty() { return Err("Runtime Error: Measure target invalid.".to_string()); } + if indices.is_empty() { + return Err("Runtime Error: Measure target invalid.".to_string()); + } let result = simulator.measure_single(indices[0]); Ok(RuntimeValue::Int(result as i64)) @@ -3110,35 +3310,54 @@ impl Evaluator { ASTNode::Block(statements) => { let mut last = RuntimeValue::None; for stmt in statements { - last = Self::eval_recursive(stmt, env, lifecycle, simulator, qubit_map, next_alloc)?; + last = Self::eval_recursive( + stmt, env, lifecycle, simulator, qubit_map, next_alloc, + )?; match last { - RuntimeValue::ReturnValue(_) | RuntimeValue::Break | RuntimeValue::Continue => return Ok(last), + RuntimeValue::ReturnValue(_) + | RuntimeValue::Break + | RuntimeValue::Continue => return Ok(last), _ => {} } } Ok(last) } - ASTNode::If { condition, then_block, elif_blocks, else_block } => { + ASTNode::If { + condition, + then_block, + elif_blocks, + else_block, + } => { let cond_val = Self::evaluate(condition, env)?; if Self::is_truthy(&cond_val) { - return Self::eval_recursive(then_block, env, lifecycle, simulator, qubit_map, next_alloc); + return Self::eval_recursive( + then_block, env, lifecycle, simulator, qubit_map, next_alloc, + ); } for (cond, body) in elif_blocks { if Self::is_truthy(&Self::evaluate(cond, env)?) { - return Self::eval_recursive(body, env, lifecycle, simulator, qubit_map, next_alloc); + return Self::eval_recursive( + body, env, lifecycle, simulator, qubit_map, next_alloc, + ); } } if let Some(else_body) = else_block { - return Self::eval_recursive(else_body, env, lifecycle, simulator, qubit_map, next_alloc); + return Self::eval_recursive( + else_body, env, lifecycle, simulator, qubit_map, next_alloc, + ); } Ok(RuntimeValue::None) } ASTNode::While { condition, body } => { loop { - if !Self::is_truthy(&Self::evaluate(condition, env)?) { break; } - let res = Self::eval_recursive(body, env, lifecycle, simulator, qubit_map, next_alloc)?; + if !Self::is_truthy(&Self::evaluate(condition, env)?) { + break; + } + let res = Self::eval_recursive( + body, env, lifecycle, simulator, qubit_map, next_alloc, + )?; match res { RuntimeValue::Break => break, RuntimeValue::Continue => continue, @@ -3149,7 +3368,11 @@ impl Evaluator { Ok(RuntimeValue::None) } - ASTNode::For { variable, iterator, body } => { + ASTNode::For { + variable, + iterator, + body, + } => { let iter_val = Self::evaluate(iterator, env)?; let iterable: Vec = match iter_val { RuntimeValue::Range(r) => r.into_iter().map(RuntimeValue::Int).collect(), @@ -3158,7 +3381,9 @@ impl Evaluator { }; for item in iterable { env.borrow_mut().set(variable.clone(), item); - let res = Self::eval_recursive(body, env, lifecycle, simulator, qubit_map, next_alloc)?; + let res = Self::eval_recursive( + body, env, lifecycle, simulator, qubit_map, next_alloc, + )?; match res { RuntimeValue::Break => break, RuntimeValue::Continue => continue, @@ -3170,33 +3395,39 @@ impl Evaluator { } ASTNode::LetDeclaration { name, value, .. } => { - let val = Self::eval_recursive(value, env, lifecycle, simulator, qubit_map, next_alloc)?; + let val = + Self::eval_recursive(value, env, lifecycle, simulator, qubit_map, next_alloc)?; env.borrow_mut().set(name.clone(), val); Ok(RuntimeValue::None) } ASTNode::Assignment { target, value } => { - let new_value = Self::eval_recursive(value, env, lifecycle, simulator, qubit_map, next_alloc)?; + let new_value = + Self::eval_recursive(value, env, lifecycle, simulator, qubit_map, next_alloc)?; match &**target { ASTNode::Identifier { name, .. } => { - if let Some(var_rc) = env.borrow().get(name) { + if let Some(var_rc) = env.borrow().get(name) { *var_rc.borrow_mut() = new_value; Ok(RuntimeValue::None) } else { Err(format!("Runtime Error: Undefined variable '{}'.", name)) } } - _ => Self::eval_assignment(target, value, env) + _ => Self::eval_assignment(target, value, env), } } - ASTNode::FunctionCall { callee, arguments, loc, is_dagger } => { - Self::eval_function_call_with_lifecycle_recursive( - callee, arguments, loc, env, *is_dagger, lifecycle, simulator, qubit_map, next_alloc - ) - } + ASTNode::FunctionCall { + callee, + arguments, + loc, + is_dagger, + } => Self::eval_function_call_with_lifecycle_recursive( + callee, arguments, loc, env, *is_dagger, lifecycle, simulator, qubit_map, + next_alloc, + ), - _ => Self::evaluate(node, env) + _ => Self::evaluate(node, env), } } @@ -3211,7 +3442,6 @@ impl Evaluator { qubit_map: &mut QubitMap, next_alloc: &mut usize, ) -> Result { - if let ASTNode::Identifier { name, .. } = callee_expr { if name == "debug_state" { if let Some(arg) = arguments.get(0) { @@ -3220,25 +3450,26 @@ impl Evaluator { match arg { ASTNode::Identifier { name: var_name, .. } => { if let Some(&start) = qubit_map.get(var_name) { - if let Some(val) = env.borrow().get(var_name) { - if let RuntimeValue::QuantumRegister { size, .. } = &*val.borrow() { + if let RuntimeValue::QuantumRegister { size, .. } = + &*val.borrow() + { for i in 0..*size { indices.push(start + i); } } } } - }, + } ASTNode::ArrayAccess { array, index, .. } => { - if let ASTNode::Identifier { name: var_name, .. } = &**array { - if let ASTNode::IntLiteral(idx) = &**index { - if let Some(&start) = qubit_map.get(var_name) { - indices.push(start + (*idx as usize)); - } - } - } - }, + if let ASTNode::Identifier { name: var_name, .. } = &**array { + if let ASTNode::IntLiteral(idx) = &**index { + if let Some(&start) = qubit_map.get(var_name) { + indices.push(start + (*idx as usize)); + } + } + } + } _ => {} } @@ -3254,7 +3485,11 @@ impl Evaluator { let function = Self::evaluate(callee_expr, env)?; match function { - RuntimeValue::Function { parameters, body, env: func_env } => { + RuntimeValue::Function { + parameters, + body, + env: func_env, + } => { let mut function_scope = Environment::new_enclosed(func_env); if parameters.len() != evaluated_args.len() { return Err(format!("Runtime Error: Arg count mismatch at {}", loc)); @@ -3264,7 +3499,14 @@ impl Evaluator { } let function_scope_rc = Rc::new(RefCell::new(function_scope)); - let result = Self::eval_recursive(&body, &function_scope_rc, lifecycle, simulator, qubit_map, next_alloc)?; + let result = Self::eval_recursive( + &body, + &function_scope_rc, + lifecycle, + simulator, + qubit_map, + next_alloc, + )?; if let RuntimeValue::ReturnValue(val) = result { Ok(*val) @@ -3272,11 +3514,10 @@ impl Evaluator { Ok(result) } } - _ => Self::eval_function_call(callee_expr, arguments, loc, env, is_dagger) + _ => Self::eval_function_call(callee_expr, arguments, loc, env, is_dagger), } } - fn extract_qubit_ids_runtime( arguments: &[ASTNode], env: &Rc>, @@ -3285,9 +3526,12 @@ impl Evaluator { for arg in arguments { let val = Self::evaluate(arg, env)?; - - if let RuntimeValue::Qubit { index, register_name, .. } = val { - + if let RuntimeValue::Qubit { + index, + register_name, + .. + } = val + { ids.push(QubitId::new(register_name, index)); } } @@ -3300,34 +3544,34 @@ impl Evaluator { ASTNode::ParameterizedGate { name, .. } => Ok(name.to_lowercase()), ASTNode::Controlled { gate_expr, .. } => Self::extract_gate_name_runtime(gate_expr), ASTNode::Dagger { gate_expr, .. } => Self::extract_gate_name_runtime(gate_expr), - _ => Err("Invalid gate expression".to_string()) + _ => Err("Invalid gate expression".to_string()), } } fn is_controlled_gate(gate_expr: &ASTNode) -> bool { match gate_expr { ASTNode::Controlled { .. } => true, - ASTNode::Dagger { gate_expr: inner, .. } => Self::is_controlled_gate(inner), - _ => false + ASTNode::Dagger { + gate_expr: inner, .. + } => Self::is_controlled_gate(inner), + _ => false, } } - fn count_controls(gate_expr: &ASTNode) -> usize { match gate_expr { ASTNode::Controlled { gate_expr, .. } => 1 + Self::count_controls(gate_expr), ASTNode::Dagger { gate_expr, .. } => Self::count_controls(gate_expr), - _ => 0 + _ => 0, } } - fn get_node_location(node: &ASTNode) -> Loc { match node { ASTNode::ArrayAccess { loc, .. } => *loc, ASTNode::Identifier { loc, .. } => *loc, ASTNode::Apply { loc, .. } => *loc, ASTNode::Measure(inner) => Self::get_node_location(inner), - _ => Loc { line: 0, column: 0 } + _ => Loc { line: 0, column: 0 }, } } } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index d86e25d..fa24ea0 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -39,18 +39,15 @@ impl Lexer { if self.current_char().ok() == Some('/') { if self.peek() == Some('/') { - self.advance(); self.skip_single_line_comment(); continue; } else if self.peek() == Some('*') { - self.advance(); self.advance(); self.skip_multiline_comment()?; continue; } else { - break; } } else { @@ -68,7 +65,6 @@ impl Lexer { continue; }*/ - if self.current_char().ok() == Some('\n') { self.advance(); if self.nesting_level == 0 { @@ -77,11 +73,9 @@ impl Lexer { continue; } - let _start_col = self.column; let ch = self.current_char()?; - let token_result = match ch { // Quantum notation '|' => { @@ -224,7 +218,9 @@ impl Lexer { } ')' => { self.advance(); - if self.nesting_level > 0 { self.nesting_level -= 1; } + if self.nesting_level > 0 { + self.nesting_level -= 1; + } Ok(self.make_token(Token::RightParen, 1)) } '[' => { @@ -234,12 +230,16 @@ impl Lexer { } ']' => { self.advance(); - if self.nesting_level > 0 { self.nesting_level -= 1; } + if self.nesting_level > 0 { + self.nesting_level -= 1; + } Ok(self.make_token(Token::RightBracket, 1)) } '}' => { self.advance(); - if self.nesting_level > 0 { self.nesting_level -= 1; } + if self.nesting_level > 0 { + self.nesting_level -= 1; + } Ok(self.make_token(Token::RightBrace, 1)) } ',' => { @@ -289,11 +289,20 @@ impl Lexer { }; tokens.push(token_result?); - } + if let Some(last_tok) = tokens.last() { + if matches!(last_tok.token, Token::Colon) { + // Peek ahead to see if there's a newline + if !self.is_at_end() && self.current_char().ok() == Some('\n') { + // Reset nesting level so indentation handling works + self.nesting_level = 0; + } + } + } + + } while self.indent_stack.len() > 1 { - self.indent_stack.pop(); tokens.push(self.make_token(Token::Dedent, 0)); } @@ -316,7 +325,6 @@ impl Lexer { let ch = self.current_char()?; if ch == '*' && self.peek() == Some('/') { - self.advance(); self.advance(); return Ok(()); @@ -329,7 +337,10 @@ impl Lexer { pub fn debug_print_tokens(&self, tokens: &[TokenWithLocation]) { println!("=== TOKEN STREAM ==="); for (i, tok) in tokens.iter().enumerate() { - println!("[{}] Line {}, Col {}: {:?}", i, tok.line, tok.column, tok.token); + println!( + "[{}] Line {}, Col {}: {:?}", + i, tok.line, tok.column, tok.token + ); } println!("=== END TOKENS ==="); } @@ -337,7 +348,6 @@ impl Lexer { fn handle_indentation(&mut self, tokens: &mut Vec) -> Result<(), String> { let mut indent_level = 0; - while !self.is_at_end() { let ch = self.current_char()?; match ch { @@ -350,14 +360,12 @@ impl Lexer { self.advance(); } '\r' => { - self.advance(); } _ => break, } } - if self.is_at_end() { self.start_of_line = false; return Ok(()); @@ -365,13 +373,11 @@ impl Lexer { let ch = self.current_char()?; - if ch == '\n' { self.start_of_line = false; return Ok(()); } - if ch == '/' { if self.peek() == Some('/') || self.peek() == Some('*') { self.start_of_line = false; @@ -379,18 +385,14 @@ impl Lexer { } } - self.start_of_line = false; - let current_indent = *self.indent_stack.last().unwrap(); if indent_level > current_indent { - self.indent_stack.push(indent_level); tokens.push(self.make_token(Token::Indent, 0)); } else if indent_level < current_indent { - while let Some(&stack_level) = self.indent_stack.last() { if stack_level <= indent_level { break; @@ -399,7 +401,6 @@ impl Lexer { tokens.push(self.make_token(Token::Dedent, 0)); } - if self.indent_stack.last() != Some(&indent_level) { return Err(format!( "Indentation error at line {}: expected indentation to match a previous level", @@ -408,7 +409,6 @@ impl Lexer { } } - Ok(()) } @@ -417,27 +417,19 @@ impl Lexer { let start_col = self.column; let token = match ch { - '#' => { self.skip_comment(); return Ok(None); } - - - - '\n' => { let token = Token::Newline; - self.advance(); - let length = 1; let token_with_loc = self.make_token(token, length); - self.line += 1; self.column = 1; @@ -478,7 +470,6 @@ impl Lexer { Token::Plus } '-' => { - if self.peek() == Some('>') { self.advance(); self.advance(); @@ -489,21 +480,18 @@ impl Lexer { } } '*' => { - if self.peek() == Some('*') && self.input.get(self.position + 2) == Some(&'*') { self.advance(); self.advance(); self.advance(); Token::TensorProduct } - /* else if self.peek() == Some('*') { self.advance(); self.advance(); Token::Power } */ else { - self.advance(); Token::Star } @@ -517,13 +505,11 @@ impl Lexer { Token::Percent } '=' => { - if self.peek() == Some('=') { self.advance(); self.advance(); Token::EqualEqual } else if self.peek() == Some('>') { - if self.input.get(self.position + 2) == Some(&'>') { self.advance(); self.advance(); @@ -540,7 +526,6 @@ impl Lexer { } } '!' => { - if self.peek() == Some('=') { self.advance(); self.advance(); @@ -551,7 +536,6 @@ impl Lexer { } } '<' => { - if self.peek() == Some('=') { self.advance(); self.advance(); @@ -562,7 +546,6 @@ impl Lexer { } } '>' => { - if self.peek() == Some('=') { self.advance(); self.advance(); @@ -573,7 +556,6 @@ impl Lexer { } } '?' => { - if self.peek() == Some('.') { self.advance(); self.advance(); @@ -624,7 +606,6 @@ impl Lexer { Token::Semicolon } ':' => { - if self.peek() == Some(':') { self.advance(); self.advance(); @@ -639,22 +620,18 @@ impl Lexer { } } '.' => { - if self.peek() == Some('.') { - if self.input.get(self.position + 2) == Some(&'=') { self.advance(); self.advance(); self.advance(); Token::RangeInclusive } else { - self.advance(); self.advance(); Token::Range } } else { - self.advance(); Token::Dot } @@ -681,7 +658,6 @@ impl Lexer { } fn is_quantum_bra(&self) -> bool { - let mut i = self.position + 1; while i < self.input.len() { let ch = self.input[i]; @@ -903,12 +879,10 @@ impl Lexer { let start_col = self.column; let mut is_float = false; - while !self.is_at_end() && self.current_char()?.is_ascii_digit() { self.advance(); } - if !self.is_at_end() && self.current_char()? == '.' { let next = self.peek(); if next.is_some() && next.unwrap().is_ascii_digit() { @@ -921,7 +895,6 @@ impl Lexer { } } - if !self.is_at_end() && (self.current_char()? == 'e' || self.current_char()? == 'E') { is_float = true; self.advance(); @@ -1014,7 +987,6 @@ impl Lexer { Ok(self.make_token(Token::IntLiteral(ch as i64), length)) } - fn current_char(&self) -> Result { if self.is_at_end() { Err("Unexpected end of input".to_string()) @@ -1042,7 +1014,6 @@ impl Lexer { fn skip_single_line_comment(&mut self) { self.advance(); - while let Ok(ch) = self.current_char() { if ch == '\n' { break; @@ -1062,7 +1033,6 @@ impl Lexer { self.start_of_line = true; } else { self.column += 1; - } } } @@ -1071,13 +1041,9 @@ impl Lexer { self.position >= self.input.len() } - - fn make_token(&self, token: Token, length: usize) -> TokenWithLocation { - let calculated_start_column = self.column.saturating_sub(length); - let start_column = if calculated_start_column == 0 { 1 } else { @@ -1256,7 +1222,6 @@ mod tests { let mut lexer = Lexer::new(input); let tokens = lexer.tokenize().unwrap(); - assert!(tokens.iter().any(|t| matches!(t.token, Token::Let))); assert!(tokens .iter() diff --git a/src/main.rs b/src/main.rs index bc93931..ca7c510 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,34 +2,37 @@ mod codegen; mod doc_generator; mod environment; +mod error; +mod error_bridge; +mod error_codes; mod evaluator; mod lexer; mod linker; mod parser; +mod qubit_lifecycle; mod runtime; mod type_checker; -mod error; -mod error_codes; -mod error_bridge; -mod qubit_lifecycle; -mod hardware_integration; -mod quantum_backend; mod graphics; mod graphics_runtime; +mod hardware_integration; +mod quantum_backend; +use crate::quantum_backend::native_simulator::NativeSimulator; +use crate::qubit_lifecycle::QubitLifecycleManager; use hardware_integration::{parse_hardware_config, HardwareExecutor}; use quantum_backend::QuantumConfig; -use crate::qubit_lifecycle::QubitLifecycleManager; -use crate::quantum_backend::native_simulator::NativeSimulator; use crate::codegen::Compiler; use crate::doc_generator::DocGenerator; use crate::environment::Environment; use crate::environment::RuntimeValue; +use crate::error::{Diagnostic, ErrorCategory, ErrorReporter}; +use crate::error_bridge::report_string_error; use crate::evaluator::Evaluator; use crate::lexer::token::Token; use crate::linker::Linker; use crate::parser::ast::ASTNode; +use crate::parser::ast::Loc; use crate::type_checker::TypeChecker; use crate::type_checker::TypeEnvironment; use inkwell::context::Context; @@ -43,9 +46,6 @@ use std::fs; use std::io::{self, Write}; use std::path::Path; use std::time::Instant; -use crate::error_bridge::report_string_error; -use crate::error::{ErrorReporter, Diagnostic,ErrorCategory}; -use crate::parser::ast::Loc; #[derive(Debug, Clone, Copy, PartialEq)] enum CompilationTarget { @@ -86,16 +86,13 @@ fn main() -> Result<(), Box> { println!("⚠️ Qubit lifecycle checking disabled"); } - let mut i = 1; while i < args.len() { match args[i].as_str() { "--hardware" => { - i += 2; } "--device" | "--shots" | "--api-token" => { - i += 2; } "--no-optimize" => { @@ -152,7 +149,6 @@ fn main() -> Result<(), Box> { i += 1; } _ => { - i += 1; } } @@ -181,7 +177,13 @@ fn main() -> Result<(), Box> { if let Some(file) = filename { println!("🚀 Executing on Quantum Hardware: {:?}\n", config.provider); println!("📄 File: {}", file); - println!("🎯 Device: {}", config.device_name.as_ref().unwrap_or(&"default".to_string())); + println!( + "🎯 Device: {}", + config + .device_name + .as_ref() + .unwrap_or(&"default".to_string()) + ); println!("🎲 Shots: {}\n", config.shots); return run_on_hardware(&file, config); } else { @@ -265,14 +267,7 @@ fn main() -> Result<(), Box> { println!("🚀 Compiling to executable: {} -> {}", fname, output_exe); - match compile_file_llvm( - fname, - object_file, - emit_llvm, - opt_level, - enable_lto, - target, - ) { + match compile_file_llvm(fname, object_file, emit_llvm, opt_level, enable_lto, target) { Ok(()) => { println!("✓ LLVM compilation successful!\n"); @@ -349,7 +344,6 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool println!("{:-<60}\n", ""); } - let _reporter = ErrorReporter::new(&source, filename); // Lexical Analysis @@ -394,7 +388,7 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } match TypeChecker::check_program(&ast) { - Ok(()) => {}, + Ok(()) => {} Err(e) => { report_string_error(e, &source, filename); std::process::exit(1); @@ -413,12 +407,13 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool } let env = std::rc::Rc::new(std::cell::RefCell::new(Environment::new())); + Evaluator::register_builtins(&env); let mut lifecycle = QubitLifecycleManager::new(true); // strict mode enabled - let total_qubits = count_total_qubits(&ast); - if verbose { println!(" -> Allocated Quantum Memory: {} qubits", total_qubits); } - + if verbose { + println!(" -> Allocated Quantum Memory: {} qubits", total_qubits); + } let mut simulator = NativeSimulator::new(total_qubits); @@ -430,12 +425,8 @@ fn compile_file(filename: &str, show_ast: bool, show_tokens: bool, verbose: bool let start_time = Instant::now(); //Evaluator (Hybrid Mode) - let result = Evaluator::evaluate_program_with_lifecycle( - &ast, - &env, - &mut lifecycle, - &mut simulator, - ); + let result = + Evaluator::evaluate_program_with_lifecycle(&ast, &env, &mut lifecycle, &mut simulator); let duration = start_time.elapsed(); @@ -494,7 +485,9 @@ fn print_help() { println!("QUANTUM HARDWARE OPTIONS:"); println!(" --hardware Run on quantum hardware/simulator"); println!(" Providers:"); - println!(" - native : Native Rust simulator (default, fast, no deps)"); + println!( + " - native : Native Rust simulator (default, fast, no deps)" + ); println!(" - ibm : IBM Quantum (requires Python + qiskit)"); println!(" - google : Google Cirq (requires Python + cirq)"); println!(" - aws : AWS Braket"); @@ -543,7 +536,9 @@ fn print_help() { println!(" Qiskit: ✅ Real IBM hardware, ❌ requires Python"); println!(" Cirq: ✅ Google integration, ❌ requires Python"); println!(); - println!("For more information, visit: https://github.com/Quantica-Foundation/quantica-lang.git"); + println!( + "For more information, visit: https://github.com/Quantica-Foundation/quantica-lang.git" + ); } fn compile_file_llvm( filename: &str, @@ -829,12 +824,8 @@ fn run_test_file(filename: &str) -> Result<(), String> { let total_qubits = count_total_qubits(&ast); let mut simulator = NativeSimulator::new(total_qubits); - Evaluator::evaluate_program_with_lifecycle( - &ast, - &env, - &mut lifecycle, - &mut simulator - ).map_err(|e| format!("Runtime Error: {}", e))?; + Evaluator::evaluate_program_with_lifecycle(&ast, &env, &mut lifecycle, &mut simulator) + .map_err(|e| format!("Runtime Error: {}", e))?; Ok(()) } @@ -1292,7 +1283,6 @@ fn run_repl() { continue; } - // Lexer let mut lexer = Lexer::new(&source_buffer); let tokens = match lexer.tokenize() { @@ -1331,7 +1321,6 @@ fn run_repl() { } }; - if let ASTNode::Program(statements) = ast { for stmt in statements { //Type Check the single statement @@ -1473,13 +1462,11 @@ fn run_on_hardware( Ok(()) } fn parse_error_location(error_msg: &str) -> Option { - let start_bytes = error_msg.find("[Line ")?; let rest = &error_msg[start_bytes..]; let end_bytes = rest.find(']')?; let content = &rest[6..end_bytes]; - let parts: Vec<&str> = content.split(", Col ").collect(); if parts.len() == 2 { let line = parts[0].parse().ok()?; @@ -1494,7 +1481,6 @@ fn count_total_qubits(node: &ASTNode) -> usize { stmts.iter().map(count_total_qubits).sum() } ASTNode::QuantumDeclaration { size, .. } => { - if let Some(size_expr) = size { if let ASTNode::IntLiteral(n) = &**size_expr { *n as usize @@ -1505,10 +1491,19 @@ fn count_total_qubits(node: &ASTNode) -> usize { 1 } } - ASTNode::If { then_block, elif_blocks, else_block, .. } => { + ASTNode::If { + then_block, + elif_blocks, + else_block, + .. + } => { let mut sum = count_total_qubits(then_block); - for (_, block) in elif_blocks { sum += count_total_qubits(block); } - if let Some(blk) = else_block { sum += count_total_qubits(blk); } + for (_, block) in elif_blocks { + sum += count_total_qubits(block); + } + if let Some(blk) = else_block { + sum += count_total_qubits(blk); + } sum } ASTNode::For { body, .. } | ASTNode::While { body, .. } => count_total_qubits(body), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1f5a269..0d3a244 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18,11 +18,29 @@ impl Parser { } } + fn debug_current_context(&self, msg: &str) { + if self.position < self.tokens.len() { + eprintln!("DEBUG {}: pos={}, token={:?}, line={}, col={}", + msg, + self.position, + self.tokens[self.position].token, + self.tokens[self.position].line, + self.tokens[self.position].column + ); + + // Show next 5 tokens + for i in 0..5 { + if self.position + i < self.tokens.len() { + eprintln!(" [+{}] {:?}", i, self.tokens[self.position + i].token); + } + } + } + } + pub fn parse(&mut self) -> Result { let mut statements = Vec::new(); while !self.is_at_end() { - if self.match_token(&Token::Newline) { continue; } @@ -39,23 +57,18 @@ impl Parser { fn parse_array_literal(&mut self) -> Result { let mut elements = Vec::new(); - if self.check(&Token::RightBracket) { self.advance(); return Ok(ASTNode::ArrayLiteral(elements)); } - loop { - elements.push(self.parse_expression()?); - if self.check(&Token::RightBracket) { break; } - self.expect(&Token::Comma)?; } @@ -65,14 +78,12 @@ impl Parser { } fn parse_statement(&mut self) -> Result { - self.skip_newlines(); if self.is_at_end() { return Err("Unexpected end of file while parsing statement".to_string()); } - if self.check(&Token::Dedent) { return Err(format!( "Unexpected Dedent token at line {}. This might indicate mismatched indentation.", @@ -108,10 +119,8 @@ impl Parser { Ok(ASTNode::Continue) } _ => { - let expr = self.parse_expression()?; - if self.match_token(&Token::Equal) { let value = self.parse_expression()?; self.skip_newlines(); @@ -131,7 +140,6 @@ impl Parser { let loc = self.get_loc(self.current()?); if self.match_token(&Token::Controlled) { - self.expect(&Token::LeftParen)?; let gate_expr = self.parse_gate_expression()?; self.expect(&Token::RightParen)?; @@ -140,7 +148,6 @@ impl Parser { loc, }) } else if self.match_token(&Token::Dagger) { - self.expect(&Token::LeftParen)?; let gate_expr = self.parse_gate_expression()?; self.expect(&Token::RightParen)?; @@ -149,11 +156,9 @@ impl Parser { loc, }) } else { - let gate_token = self.expect_identifier()?; let gate_name = self.extract_identifier_name(&gate_token)?; - if self.check(&Token::LeftParen) { self.advance(); let mut params = Vec::new(); @@ -172,7 +177,6 @@ impl Parser { loc: self.get_loc(&gate_token), }) } else { - Ok(ASTNode::Gate { name: gate_name, loc: self.get_loc(&gate_token), @@ -185,8 +189,6 @@ impl Parser { let loc = self.get_loc(self.current()?); self.expect(&Token::Apply)?; - - if self.check(&Token::Dagger) { let saved_pos = self.position; self.advance(); @@ -194,7 +196,6 @@ impl Parser { if self.check(&Token::LeftParen) { self.advance(); - let is_gate_expr = matches!( self.current().ok().map(|t| &t.token), Some(Token::X) @@ -220,15 +221,10 @@ impl Parser { | Some(Token::Dagger) ); - self.position = saved_pos; if is_gate_expr { - - - } else { - self.advance(); self.expect(&Token::LeftParen)?; let callee_expr = self.parse_expression()?; @@ -245,12 +241,10 @@ impl Parser { }); } } else { - self.position = saved_pos; } } - let saved_pos = self.position; let mut is_function_call = false; @@ -312,17 +306,12 @@ impl Parser { }); } - let apply_node = self.parse_gate_application(loc)?; self.skip_newlines(); Ok(apply_node) } fn parse_gate_application(&mut self, loc: Loc) -> Result { - - - - let is_simple_gate = matches!( self.current()?.token, Token::X @@ -346,12 +335,10 @@ impl Parser { Token::RX | Token::RY | Token::RZ | Token::U | Token::CPhase ); - if is_simple_gate || is_parameterized_gate { let gate_token = self.expect_identifier()?; let gate_name = self.extract_identifier_name(&gate_token)?; - if is_parameterized_gate { self.expect(&Token::LeftParen)?; let mut params = Vec::new(); @@ -371,7 +358,6 @@ impl Parser { loc, }; - self.skip_newlines(); self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; @@ -382,13 +368,11 @@ impl Parser { loc, }); } else { - let gate_expr = ASTNode::Gate { name: gate_name, loc, }; - self.skip_newlines(); self.expect(&Token::LeftParen)?; let arguments = self.parse_arguments()?; @@ -401,13 +385,10 @@ impl Parser { } } - let gate_expr = self.parse_gate_expression()?; - self.skip_newlines(); - if !self.check(&Token::LeftParen) { let current_token = self.current()?; return Err(format!( @@ -648,11 +629,9 @@ impl Parser { self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; - self.skip_newlines(); self.expect(&Token::RightParen)?; - self.skip_layout_tokens(); let return_type = if self.match_token(&Token::Arrow) { @@ -707,19 +686,20 @@ impl Parser { while !self.check(&Token::Dedent) && !self.is_at_end() { self.skip_newlines(); - if self.check(&Token::Class) { break; } - if self.check(&Token::Dedent) { - let mut temp_pos = self.position + 1; - while temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Newline) { + while temp_pos < self.tokens.len() + && matches!(self.tokens[temp_pos].token, Token::Newline) + { temp_pos += 1; } - if temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Indent) { + if temp_pos < self.tokens.len() + && matches!(self.tokens[temp_pos].token, Token::Indent) + { self.advance(); self.skip_newlines(); self.advance(); @@ -728,10 +708,13 @@ impl Parser { break; } - - let is_public = if self.match_token(&Token::Public) { true } - else if self.match_token(&Token::Private) { false } - else { true }; + let is_public = if self.match_token(&Token::Public) { + true + } else if self.match_token(&Token::Private) { + false + } else { + true + }; let is_static = self.match_token(&Token::Static); if self.check(&Token::Func) { @@ -742,11 +725,9 @@ impl Parser { self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; - self.skip_newlines(); self.expect(&Token::RightParen)?; - self.skip_layout_tokens(); let return_type = if self.match_token(&Token::Arrow) { @@ -756,7 +737,9 @@ impl Parser { None }; - if self.check(&Token::Colon) { self.advance(); } + if self.check(&Token::Colon) { + self.advance(); + } let body = self.parse_block()?; if method_name == "init" || method_name == "__init__" { @@ -776,9 +759,14 @@ impl Parser { is_static, }); } - } else if self.check(&Token::Let) || self.check(&Token::Mut) || matches!(self.current()?.token, Token::Identifier(_)) { + } else if self.check(&Token::Let) + || self.check(&Token::Mut) + || matches!(self.current()?.token, Token::Identifier(_)) + { let is_mutable = self.match_token(&Token::Mut); - if !is_mutable { self.match_token(&Token::Let); } + if !is_mutable { + self.match_token(&Token::Let); + } let field_name_loc = self.expect_identifier()?; let field_name = self.extract_identifier_name(&field_name_loc)?; @@ -795,19 +783,31 @@ impl Parser { None }; self.skip_newlines(); - fields.push(ClassField { name: field_name, field_type, default_value, is_public }); + fields.push(ClassField { + name: field_name, + field_type, + default_value, + is_public, + }); } else { - return Err(format!("Expected field or method declaration in class at {}", loc)); + return Err(format!( + "Expected field or method declaration in class at {}", + loc + )); } } - if !self.check(&Token::Class) { self.expect(&Token::Dedent)?; } Ok(ASTNode::ClassDeclaration { - name, superclass, fields, methods, constructor, loc, + name, + superclass, + fields, + methods, + constructor, + loc, }) } @@ -844,11 +844,9 @@ impl Parser { self.expect(&Token::LeftParen)?; let parameters = self.parse_parameters()?; - self.skip_newlines(); self.expect(&Token::RightParen)?; - self.skip_layout_tokens(); let return_type = if self.match_token(&Token::Arrow) { @@ -921,7 +919,6 @@ impl Parser { let type_token = self.current()?.token.clone(); let mut base_type = match type_token { - Token::Int => { self.advance(); Type::Int @@ -978,31 +975,38 @@ impl Parser { self.advance(); Type::Any } - - Token::Quantum => { self.advance(); Type::QuantumRegister(None) } - Token::Identifier(name) => { self.advance(); - match name.as_str() { + + // Handle module-qualified types like ai.Dataset or math.Vector + let mut full_name = name.clone(); + while self.check(&Token::Dot) { + self.advance(); // consume the dot + let next_name_loc = self.expect_identifier()?; + let next_name = self.extract_identifier_name(&next_name_loc)?; + full_name.push('.'); + full_name.push_str(&next_name); + } + + match full_name.as_str() { "Qubit" => Type::Qubit, "QuantumRegister" => Type::QuantumRegister(None), - _ => Type::Custom(name), + _ => Type::Custom(full_name), } } - _ => return Err(format!("Expected a type, but found {:?}", type_token)), }; + // Handle array types while self.match_token(&Token::LeftBracket) { if self.check(&Token::RightBracket) { self.advance(); base_type = Type::Array(Box::new(base_type)); } else { - let size = self.parse_expression()?; self.expect(&Token::RightBracket)?; if let ASTNode::IntLiteral(n) = size { @@ -1195,51 +1199,46 @@ impl Parser { fn parse_block(&mut self) -> Result { self.skip_newlines(); - let mut statements = Vec::new(); + /* self.debug_current_context("parse_block entry"); */ + let mut statements = Vec::new(); if !self.check(&Token::Indent) { - return Err("Expected indented block after ':'".to_string()); } self.advance(); loop { - self.skip_newlines(); - if self.check(&Token::Dedent) { - let mut temp_pos = self.position + 1; - - while temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Newline) { + while temp_pos < self.tokens.len() + && matches!(self.tokens[temp_pos].token, Token::Newline) + { temp_pos += 1; } - - if temp_pos < self.tokens.len() && matches!(self.tokens[temp_pos].token, Token::Indent) { + if temp_pos < self.tokens.len() + && matches!(self.tokens[temp_pos].token, Token::Indent) + { self.advance(); self.skip_newlines(); self.advance(); continue; } else { - break; } } - if self.is_at_end() { break; } - statements.push(self.parse_statement()?); } - if self.check(&Token::Dedent) { self.expect(&Token::Dedent)?; } @@ -1247,7 +1246,6 @@ impl Parser { Ok(ASTNode::Block(statements)) } - fn parse_expression(&mut self) -> Result { self.parse_pipeline() } @@ -1443,7 +1441,6 @@ impl Parser { fn parse_power(&mut self) -> Result { let mut expr = self.parse_tensor_product()?; - if let Some(op_token) = self.match_tokens_loc(&[Token::Caret]) { let operator = BinaryOperator::Power; let loc = self.get_loc(&op_token); @@ -1489,7 +1486,6 @@ impl Parser { let name_loc = self.expect_identifier()?; let member = self.extract_identifier_name(&name_loc)?; - if self.check(&Token::LeftParen) { let loc = self.get_loc(self.current()?); self.advance(); @@ -1527,7 +1523,6 @@ impl Parser { self.advance(); } - if self.check(&Token::Indent) || self.check(&Token::Dedent) { let current_loc = self.current()?.clone(); return Err(format!( @@ -1540,7 +1535,6 @@ impl Parser { let token = ¤t_loc.token; let loc = self.get_loc(¤t_loc); - match token { Token::If => self.parse_if(), Token::Print => { @@ -1558,19 +1552,15 @@ impl Parser { }) } - Token::Identifier(name) => { self.advance(); - Ok(ASTNode::Identifier { name: name.clone(), loc, }) } - - Token::Dagger => { self.advance(); self.expect(&Token::LeftParen)?; @@ -1602,7 +1592,6 @@ impl Parser { Ok(ASTNode::Measure(Box::new(qubit))) } - Token::IntLiteral(n) => { self.advance(); Ok(ASTNode::IntLiteral(*n)) @@ -1636,7 +1625,6 @@ impl Parser { Ok(ASTNode::QuantumBra(state.clone())) } - Token::LeftParen => { self.advance(); let expr = self.parse_expression()?; @@ -1688,8 +1676,6 @@ impl Parser { } } - - fn skip_layout_tokens(&mut self) { while self.check(&Token::Newline) || self.check(&Token::Indent) @@ -1734,7 +1720,6 @@ impl Parser { Ok(ASTNode::DictLiteral(pairs)) } - fn current(&self) -> Result<&TokenWithLocation, String> { if self.is_at_end() { Err("Unexpected end of file".to_string()) @@ -1789,27 +1774,29 @@ impl Parser { self.advance(); Ok(()) } else { - let (loc, current_str) = if self.position >= self.tokens.len() { - let last_loc = if !self.tokens.is_empty() { let last = self.tokens.last().unwrap(); - Loc { line: last.line, column: last.column + 1 } + Loc { + line: last.line, + column: last.column + 1, + } } else { Loc { line: 1, column: 1 } }; (last_loc, "EOF".to_string()) } else { - let curr = &self.tokens[self.position]; ( - Loc { line: curr.line, column: curr.column }, - format!("{:?}", curr.token) + Loc { + line: curr.line, + column: curr.column, + }, + format!("{:?}", curr.token), ) }; - Err(format!( "Syntax Error at {}: Expected {:?}, but found {}", loc, token, current_str @@ -1850,26 +1837,40 @@ impl Parser { let current_loc = self.current()?; match ¤t_loc.token { - Token::Identifier(_) | - Token::Hadamard | Token::Cnot | Token::X | Token::Y | Token::Z | - Token::S | Token::T | Token::Swap | Token::Reset | - Token::CZ | Token::CS | Token::CT | Token::CPhase | - Token::U | Token::RX | Token::RY | Token::RZ | - Token::CCX | Token::Toffoli - => { - let owned_token_loc = current_loc.clone(); - self.advance(); - Ok(owned_token_loc) + Token::Identifier(_) + | Token::Hadamard + | Token::Cnot + | Token::X + | Token::Y + | Token::Z + | Token::S + | Token::T + | Token::Swap + | Token::Reset + | Token::CZ + | Token::CS + | Token::CT + | Token::CPhase + | Token::U + | Token::RX + | Token::RY + | Token::RZ + | Token::CCX + | Token::Toffoli => { + let owned_token_loc = current_loc.clone(); + self.advance(); + Ok(owned_token_loc) + } + _ => Err(format!( + "Syntax Error: Expected Identifier or Gate name, found {:?}", + current_loc.token + )), } - _ => Err(format!("Syntax Error: Expected Identifier or Gate name, found {:?}", current_loc.token)) - } } fn skip_newlines(&mut self) { while self.match_token(&Token::Newline) {} } - - } #[cfg(test)] mod tests { diff --git a/src/type_checker.rs b/src/type_checker.rs index c42757e..e2e6156 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1,18 +1,16 @@ /* src/type_checker.rs */ - -use crate::parser::ast::{ASTNode, BinaryOperator, ImportSpec, Type, UnaryOperator}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; -use std::string::String; use crate::error::{find_similar_names, levenshtein_distance}; use crate::lexer::Lexer; use crate::parser::ast::ImportPath; use crate::parser::ast::Loc; +use crate::parser::ast::{ASTNode, BinaryOperator, ImportSpec, Type, UnaryOperator}; use crate::parser::Parser; -use crate::qubit_lifecycle::{QubitLifecycleManager, QubitId, QubitOperation, QubitState}; +use crate::qubit_lifecycle::{QubitId, QubitLifecycleManager, QubitOperation, QubitState}; +use std::cell::RefCell; +use std::collections::HashMap; use std::fs; - +use std::rc::Rc; +use std::string::String; #[derive(Debug, Clone, PartialEq)] pub struct TypeInfo { @@ -27,7 +25,6 @@ pub struct TypeEnvironment { } impl TypeEnvironment { - pub fn new() -> Self { TypeEnvironment { store: HashMap::new(), @@ -57,10 +54,11 @@ impl TypeEnvironment { } } -pub struct TypeChecker{lifecycle_manager: QubitLifecycleManager,} +pub struct TypeChecker { + lifecycle_manager: QubitLifecycleManager, +} impl TypeChecker { - pub fn prefill_environment(env: &Rc>) { let mut env_mut = env.borrow_mut(); @@ -74,22 +72,21 @@ impl TypeChecker { let qubit_type = Type::Qubit; let none_type = Box::new(none.clone()); - env_mut.set( "print".to_string(), immut(Type::Function(vec![], Box::new(none.clone()))), ); - env_mut.set("input".to_string(), immut( - - Type::Function(vec![Type::Any], Box::new(Type::String)) - )); - env_mut.set("split".to_string(), immut( - - Type::Function( + env_mut.set( + "input".to_string(), + immut(Type::Function(vec![Type::Any], Box::new(Type::String))), + ); + env_mut.set( + "split".to_string(), + immut(Type::Function( vec![Type::String, Type::String], - Box::new(Type::Array(Box::new(Type::String))) - ) - )); + Box::new(Type::Array(Box::new(Type::String))), + )), + ); env_mut.set( "debug_state".to_string(), immut(Type::Function( @@ -140,7 +137,6 @@ impl TypeChecker { immut(Type::Function(vec![any.clone()], Box::new(Type::Float))), ); - let single_qubit_gate = Type::Function(vec![qubit_type.clone()], none_type.clone()); env_mut.set("hadamard".to_string(), immut(single_qubit_gate.clone())); env_mut.set("x".to_string(), immut(single_qubit_gate.clone())); @@ -150,7 +146,6 @@ impl TypeChecker { env_mut.set("t".to_string(), immut(single_qubit_gate.clone())); env_mut.set("reset".to_string(), immut(single_qubit_gate.clone())); - let two_qubit_gate = Type::Function( vec![qubit_type.clone(), qubit_type.clone()], none_type.clone(), @@ -168,7 +163,6 @@ impl TypeChecker { env_mut.set("ccx".to_string(), immut(three_qubit_gate.clone())); env_mut.set("toffoli".to_string(), immut(three_qubit_gate)); - let cphase_gate = Type::Function( vec![Type::Float, qubit_type.clone(), qubit_type.clone()], none_type.clone(), @@ -187,9 +181,6 @@ impl TypeChecker { env_mut.set("ry".to_string(), immut(parameterized_gate.clone())); env_mut.set("rz".to_string(), immut(parameterized_gate.clone())); - - - env_mut.set( "_graphics_create_canvas".to_string(), immut(Type::Function( @@ -198,7 +189,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_set_background".to_string(), immut(Type::Function( @@ -207,13 +197,11 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_clear".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), ); - env_mut.set( "_graphics_draw_line".to_string(), immut(Type::Function( @@ -233,7 +221,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_draw_rect".to_string(), immut(Type::Function( @@ -253,7 +240,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_draw_circle".to_string(), immut(Type::Function( @@ -272,7 +258,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_draw_text".to_string(), immut(Type::Function( @@ -291,7 +276,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_save_svg".to_string(), immut(Type::Function( @@ -300,7 +284,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_save_png".to_string(), immut(Type::Function( @@ -309,19 +292,16 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_destroy_canvas".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), ); - env_mut.set( "_graphics_create_plot".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::Int))), ); - env_mut.set( "_graphics_plot_set_data".to_string(), immut(Type::Function( @@ -335,7 +315,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_plot_set_title".to_string(), immut(Type::Function( @@ -344,7 +323,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_plot_render".to_string(), immut(Type::Function( @@ -353,7 +331,6 @@ impl TypeChecker { )), ); - env_mut.set( "_graphics_destroy_plot".to_string(), immut(Type::Function(vec![Type::Int], Box::new(Type::None))), @@ -370,8 +347,16 @@ impl TypeChecker { env_mut.set( "file_read".to_string(), immut(Type::Function( - vec![Type::String], // Argument: (path) - Box::new(Type::String), // Returns: String (content) + vec![Type::String], // Argument: (path) + Box::new(Type::String), // Returns: String (content) + )), + ); + + env_mut.set( + "time".to_string(), + immut(Type::Function( + vec![], // No arguments + Box::new(Type::Float), // Returns Float )), ); @@ -379,11 +364,43 @@ impl TypeChecker { pub fn check_program(node: &ASTNode) -> Result<(), String> { if let ASTNode::Program(statements) = node { + // 1. Create the Environment let env = Rc::new(RefCell::new(TypeEnvironment::new())); let mut lifecycle = QubitLifecycleManager::new(true); + // 2. Prefill Standard Built-ins (print, etc.) Self::prefill_environment(&env); + // 3. FORCE Register file I/O (Fixes the 'Undefined variable' error) + { + let mut env_mut = env.borrow_mut(); + + // Manually add file_read + env_mut.set( + "file_read".to_string(), + TypeInfo { + var_type: Type::Function( + vec![Type::String], // Arg: path + Box::new(Type::String) // Ret: content + ), + is_mutable: false + } + ); + + // Manually add file_write + env_mut.set( + "file_write".to_string(), + TypeInfo { + var_type: Type::Function( + vec![Type::String, Type::String], // Args: path, content + Box::new(Type::None) + ), + is_mutable: false + } + ); + } + + // 4. Pass 1: Register Global Functions & Classes for stmt in statements { match stmt { ASTNode::FunctionDeclaration { name, parameters, return_type, .. } => { @@ -417,6 +434,7 @@ impl TypeChecker { } } + // 5. Pass 2: Check Statements (Bodies) for stmt in statements { Self::check_with_lifecycle(stmt, &env, None, &mut lifecycle)?; } @@ -426,6 +444,7 @@ impl TypeChecker { Err("Expected Program node".to_string()) } } + fn check_with_lifecycle( node: &ASTNode, env: &Rc>, @@ -433,8 +452,11 @@ impl TypeChecker { lifecycle: &mut QubitLifecycleManager, ) -> Result { match node { - - ASTNode::QuantumDeclaration { name, size, initial_state } => { + ASTNode::QuantumDeclaration { + name, + size, + initial_state, + } => { let register_size = if let Some(size_expr) = size { if let ASTNode::IntLiteral(n) = &**size_expr { *n as usize @@ -445,19 +467,16 @@ impl TypeChecker { 1 }; - lifecycle.register_qubits( - name, - register_size, - QubitState::Classical(false) - ); - + lifecycle.register_qubits(name, register_size, QubitState::Classical(false)); Self::check(node, env, expected_return_type) } - - ASTNode::Apply { gate_expr, arguments, loc } => { - + ASTNode::Apply { + gate_expr, + arguments, + loc, + } => { let qubit_ids = Self::extract_qubit_ids(arguments)?; let gate_name = Self::extract_gate_name(gate_expr)?; let is_controlled = Self::is_controlled_gate(gate_expr); @@ -465,59 +484,73 @@ impl TypeChecker { if is_controlled { let num_controls = Self::count_controls(gate_expr); let (controls, targets) = qubit_ids.split_at(num_controls); - lifecycle.record_controlled_gate(controls, targets, &gate_name, *loc) + lifecycle + .record_controlled_gate(controls, targets, &gate_name, *loc) .map_err(|e| e.to_string())?; } else { for qubit_id in &qubit_ids { - lifecycle.record_operation( - qubit_id, - QubitOperation::ApplyGate(gate_name.clone()), - *loc - ).map_err(|e| e.to_string())?; + lifecycle + .record_operation( + qubit_id, + QubitOperation::ApplyGate(gate_name.clone()), + *loc, + ) + .map_err(|e| e.to_string())?; } } - Self::check(node, env, expected_return_type) } - ASTNode::Measure(qubit_expr) => { let qubit_ids = Self::extract_qubit_ids(&[*qubit_expr.clone()])?; let loc = Self::get_node_location(qubit_expr); for qubit_id in &qubit_ids { - lifecycle.record_operation( - qubit_id, - QubitOperation::Measure, - loc - ).map_err(|e| e.to_string())?; + lifecycle + .record_operation(qubit_id, QubitOperation::Measure, loc) + .map_err(|e| e.to_string())?; } Self::check(node, env, expected_return_type) } - - ASTNode::LetDeclaration { name, type_annotation, value, is_mutable, .. } => { - + ASTNode::LetDeclaration { + name, + type_annotation, + value, + is_mutable, + .. + } => { let value_type = Self::check_with_lifecycle(value, env, None, lifecycle)?; - let final_type = match type_annotation { Some(expected_type) => { - if value_type != *expected_type && value_type != Type::None && *expected_type != Type::Any && value_type != Type::Any { - return Err(format!("Type Error: Variable '{}' annotated as {:?} but got {:?}", name, expected_type, value_type)); + if value_type != *expected_type + && value_type != Type::None + && *expected_type != Type::Any + && value_type != Type::Any + { + return Err(format!( + "Type Error: Variable '{}' annotated as {:?} but got {:?}", + name, expected_type, value_type + )); } expected_type.clone() } None => value_type, }; - env.borrow_mut().set(name.clone(), TypeInfo { var_type: final_type, is_mutable: *is_mutable }); + env.borrow_mut().set( + name.clone(), + TypeInfo { + var_type: final_type, + is_mutable: *is_mutable, + }, + ); Ok(Type::None) } - ASTNode::Block(statements) => { for stmt in statements { Self::check_with_lifecycle(stmt, env, expected_return_type, lifecycle)?; @@ -525,21 +558,30 @@ impl TypeChecker { Ok(Type::None) } - - ASTNode::If { condition, then_block, elif_blocks, else_block } => { - + ASTNode::If { + condition, + then_block, + elif_blocks, + else_block, + } => { let cond_type = Self::check(condition, env, None)?; if cond_type != Type::Bool { - return Err(format!("Type Error: 'if' condition must be Bool, got {:?}", cond_type)); + return Err(format!( + "Type Error: 'if' condition must be Bool, got {:?}", + cond_type + )); } - - let then_type = Self::check_with_lifecycle(then_block, env, expected_return_type, lifecycle)?; + let then_type = + Self::check_with_lifecycle(then_block, env, expected_return_type, lifecycle)?; for (elif_cond, elif_body) in elif_blocks { let elif_cond_type = Self::check(elif_cond, env, None)?; if elif_cond_type != Type::Bool { - return Err(format!("Type Error: 'elif' condition must be Bool, got {:?}", elif_cond_type)); + return Err(format!( + "Type Error: 'elif' condition must be Bool, got {:?}", + elif_cond_type + )); } Self::check_with_lifecycle(elif_body, env, expected_return_type, lifecycle)?; } @@ -550,19 +592,23 @@ impl TypeChecker { Ok(then_type) } - ASTNode::While { condition, body } => { let cond_type = Self::check(condition, env, None)?; if cond_type != Type::Bool { - return Err(format!("Type Error: 'while' condition must be Bool, got {:?}", cond_type)); + return Err(format!( + "Type Error: 'while' condition must be Bool, got {:?}", + cond_type + )); } Self::check_with_lifecycle(body, env, expected_return_type, lifecycle)?; Ok(Type::None) } - - ASTNode::For { variable, iterator, body } => { - + ASTNode::For { + variable, + iterator, + body, + } => { let iterator_type = Self::check(iterator, env, None)?; let element_type = match iterator_type { Type::Array(inner) => *inner, @@ -570,34 +616,39 @@ impl TypeChecker { Type::Dict => Type::String, Type::Custom(name) if name == "range" => Type::Int, Type::Any => Type::Any, - _ => return Err(format!("Type Error: Cannot iterate over {:?}", iterator_type)), + _ => { + return Err(format!( + "Type Error: Cannot iterate over {:?}", + iterator_type + )) + } }; - let loop_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); - loop_env.borrow_mut().set(variable.clone(), TypeInfo { var_type: element_type, is_mutable: false }); - + loop_env.borrow_mut().set( + variable.clone(), + TypeInfo { + var_type: element_type, + is_mutable: false, + }, + ); Self::check_with_lifecycle(body, &loop_env, expected_return_type, lifecycle)?; Ok(Type::None) } - - ASTNode::Return(value_expr) => { + ASTNode::Return(value_expr) => { if let Some(expr) = value_expr { - - let _ = Self::check_with_lifecycle(expr, env, None, lifecycle)?; + let _ = Self::check_with_lifecycle(expr, env, None, lifecycle)?; } Self::check(node, env, expected_return_type) } - - _ => Self::check(node, env, expected_return_type) + _ => Self::check(node, env, expected_return_type), } } - fn extract_qubit_ids(arguments: &[ASTNode]) -> Result, String> { let mut ids = Vec::new(); @@ -611,7 +662,6 @@ impl TypeChecker { } } ASTNode::Identifier { name, .. } => { - ids.push(QubitId::new(name.clone(), 0)); } _ => {} @@ -621,39 +671,35 @@ impl TypeChecker { Ok(ids) } - fn extract_gate_name(gate_expr: &ASTNode) -> Result { match gate_expr { ASTNode::Gate { name, .. } => Ok(name.clone()), ASTNode::ParameterizedGate { name, .. } => Ok(name.clone()), ASTNode::Controlled { gate_expr, .. } => Self::extract_gate_name(gate_expr), ASTNode::Dagger { gate_expr, .. } => Self::extract_gate_name(gate_expr), - _ => Err("Invalid gate expression".to_string()) + _ => Err("Invalid gate expression".to_string()), } } - fn is_controlled_gate(gate_expr: &ASTNode) -> bool { - matches!(gate_expr, ASTNode::Controlled { .. }) || - matches!(gate_expr, ASTNode::Dagger { gate_expr: inner, .. } + matches!(gate_expr, ASTNode::Controlled { .. }) + || matches!(gate_expr, ASTNode::Dagger { gate_expr: inner, .. } if Self::is_controlled_gate(inner)) } - fn count_controls(gate_expr: &ASTNode) -> usize { match gate_expr { ASTNode::Controlled { gate_expr, .. } => 1 + Self::count_controls(gate_expr), ASTNode::Dagger { gate_expr, .. } => Self::count_controls(gate_expr), - _ => 0 + _ => 0, } } - fn get_node_location(node: &ASTNode) -> Loc { match node { ASTNode::ArrayAccess { loc, .. } => *loc, ASTNode::Identifier { loc, .. } => *loc, - _ => Loc { line: 0, column: 0 } + _ => Loc { line: 0, column: 0 }, } } @@ -673,19 +719,24 @@ impl TypeChecker { format!("q_packages/{}/init.qc", f) } } - ImportPath::Module(m) => { - m.join("/") + ".qc" - } + ImportPath::Module(m) => m.join("/") + ".qc", }; let source = fs::read_to_string(&file_path).map_err(|e| { - format!("Type Check Error: Failed to read module '{}': {}", file_path, e) + format!( + "Type Check Error: Failed to read module '{}': {}", + file_path, e + ) })?; let mut lexer = Lexer::new(&source); - let tokens = lexer.tokenize().map_err(|e| format!("Module Lexer Error: {}", e))?; + let tokens = lexer + .tokenize() + .map_err(|e| format!("Module Lexer Error: {}", e))?; let mut parser = Parser::new(tokens); - let ast = parser.parse().map_err(|e| format!("Module Parser Error: {}", e))?; + let ast = parser + .parse() + .map_err(|e| format!("Module Parser Error: {}", e))?; let module_env = Rc::new(RefCell::new(TypeEnvironment::new())); Self::prefill_environment(&module_env); @@ -693,33 +744,56 @@ impl TypeChecker { if let ASTNode::Program(statements) = ast { for stmt in &statements { match stmt { - ASTNode::FunctionDeclaration { name, parameters, return_type, .. } => { - let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + ASTNode::FunctionDeclaration { + name, + parameters, + return_type, + .. + } => { + let param_types: Vec = + parameters.iter().map(|p| p.param_type.clone()).collect(); let rt = return_type.clone().unwrap_or(Type::Any); let func_type = Type::Function(param_types, Box::new(rt)); - module_env.borrow_mut().set(name.clone(), TypeInfo { - var_type: func_type, - is_mutable: false - }); - }, - ASTNode::CircuitDeclaration { name, parameters, return_type, .. } => { - let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); - let rt = return_type.clone().unwrap_or(Type::Any); - let func_type = Type::Function(param_types, Box::new(rt)); + module_env.borrow_mut().set( + name.clone(), + TypeInfo { + var_type: func_type, + is_mutable: false, + }, + ); + } + ASTNode::CircuitDeclaration { + name, + parameters, + return_type, + .. + } => { + let param_types: Vec = + parameters.iter().map(|p| p.param_type.clone()).collect(); + let rt = return_type.clone().unwrap_or(Type::Any); + let func_type = Type::Function(param_types, Box::new(rt)); - module_env.borrow_mut().set(name.clone(), TypeInfo { - var_type: func_type, - is_mutable: false - }); - }, + module_env.borrow_mut().set( + name.clone(), + TypeInfo { + var_type: func_type, + is_mutable: false, + }, + ); + } ASTNode::ClassDeclaration { name, .. } => { let class_type = Type::Class(name.clone()); - module_env.borrow_mut().set(name.clone(), TypeInfo { - var_type: class_type, - is_mutable: false - }); - }, + module_env.borrow_mut().set( + name.clone(), + TypeInfo { + var_type: class_type, + is_mutable: false, + }, + ); + + /* Self::check(stmt, &module_env, None)?; */ + } _ => {} } } @@ -731,48 +805,40 @@ impl TypeChecker { return Err("Module root is not a Program node".to_string()); } - let module_types = module_env.borrow().store.iter() + let module_types = module_env + .borrow() + .store + .iter() .map(|(k, v)| (k.clone(), v.var_type.clone())) .collect(); Ok(module_types) } - fn resolve_method( - env: &Rc>, - method_key: &str, - ) -> Option { - + fn resolve_method(env: &Rc>, method_key: &str) -> Option { if let Some(info) = env.borrow().get(method_key) { return Some(info.var_type.clone()); } - - let mut current_env = Some(env.clone()); while let Some(env_rc) = current_env { let env_ref = env_rc.borrow(); - for info in env_ref.store.values() { - if let Type::Module(module_map) = &info.var_type { - if let Some(method_type) = module_map.get(method_key) { return Some(method_type.clone()); } } } - current_env = env_ref.outer.clone(); } None } - fn check_gate_expression( node: &ASTNode, env: &Rc>, @@ -795,7 +861,6 @@ impl TypeChecker { } } - Err(format!( "Type Error at {}: Unknown quantum gate '{}'.", loc, name @@ -803,7 +868,6 @@ impl TypeChecker { } ASTNode::Dagger { gate_expr, .. } => { - let inner_gate_type = Self::check_gate_expression(gate_expr, env)?; if let Type::Function(..) = inner_gate_type { @@ -817,23 +881,16 @@ impl TypeChecker { } ASTNode::Controlled { gate_expr, loc } => { - let inner_gate_type = Self::check_gate_expression(gate_expr, env)?; match inner_gate_type { Type::Function(mut params, ret_type) => { - - - - if params.is_empty() { return Err(format!("Type Error at {}: 'controlled' target gate must take at least one argument.", loc)); } - params.insert(0, Type::Qubit); - Ok(Type::Function(params, ret_type)) } _ => Err(format!( @@ -850,7 +907,6 @@ impl TypeChecker { } => { let gate_name_lower = name.to_lowercase(); - let gate_info = env.borrow().get(&gate_name_lower).ok_or_else(|| { format!( "Type Error at {}: Unknown parameterized gate '{}'.", @@ -860,9 +916,6 @@ impl TypeChecker { match gate_info.var_type { Type::Function(param_types, return_type) => { - - - if parameters.len() > param_types.len() { return Err(format!( "Type Error at {}: Gate '{}' takes at most {} parameters, but got {}", @@ -870,12 +923,10 @@ impl TypeChecker { )); } - for (i, param_expr) in parameters.iter().enumerate() { let param_type = Self::check(param_expr, env, None)?; let expected_type = ¶m_types[i]; - if param_type != *expected_type && !(*expected_type == Type::Float && param_type == Type::Int) { @@ -886,7 +937,6 @@ impl TypeChecker { } } - let remaining_params = param_types[parameters.len()..].to_vec(); Ok(Type::Function(remaining_params, return_type)) } @@ -901,14 +951,12 @@ impl TypeChecker { } } - pub fn check( node: &ASTNode, env: &Rc>, expected_return_type: Option<&Type>, ) -> Result { match node { - ASTNode::IntLiteral(_) => Ok(Type::Int), ASTNode::FloatLiteral(_) => Ok(Type::Float), ASTNode::StringLiteral(_) => Ok(Type::String), @@ -918,7 +966,6 @@ impl TypeChecker { ASTNode::QuantumKet(_) => Ok(Type::QuantumRegister(Some(1))), ASTNode::QuantumBra(_) => Err("Bra notation is not yet supported.".to_string()), - ASTNode::LetDeclaration { name, type_annotation, @@ -935,7 +982,6 @@ impl TypeChecker { && *expected_type != Type::Any && value_type != Type::Any { - let is_quantum_compat = matches!( (&value_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) @@ -952,7 +998,6 @@ impl TypeChecker { name, expected_type, value_type )); } - } expected_type.clone() } @@ -1066,165 +1111,233 @@ impl TypeChecker { } } ASTNode::MemberAccess { object, member } => { - let object_type = Self::check(object, env, Option::None)?; - - let class_name = match object_type { - Type::Instance(name) | Type::Custom(name) => name, - _ => return Err(format!("Type Error: Cannot assign to member '{}' of non-object type {:?}", member, object_type)), + let class_name_opt = match &object_type { + Type::Instance(name) | Type::Custom(name) => Some(name.clone()), + _ => None, }; - - let field_key = format!("{}::{}", class_name, member); - - - if let Some(field_info) = env.borrow().get(&field_key) { - - if !field_info.is_mutable { - return Err(format!("Mutability Error: Field '{}.{}' is immutable.", class_name, member)); + if let Some(mut class_name) = class_name_opt { + // Handle aliased class names (e.g. ai.Dataset -> Dataset) + if class_name.contains('.') { + if let Some(real_name) = class_name.split('.').last() { + class_name = real_name.to_string(); + } } + let field_key = format!("{}::{}", class_name, member); - if field_info.var_type != new_type && field_info.var_type != Type::Any { - - let is_compat = matches!((&new_type, &field_info.var_type), - (Type::Int, Type::Float) | - (Type::QuantumRegister(_), Type::QuantumRegister(None)) - ); - - let is_array_compat = match (&field_info.var_type, &new_type) { - (Type::Array(t1), Type::Array(t2)) => **t1 == Type::Any || **t2 == Type::Any, - _ => false - }; + // Check current environment first + if let Some(field_info) = env.borrow().get(&field_key) { + return Ok(field_info.var_type.clone()); + } - if !is_compat && !is_array_compat && new_type != field_info.var_type { - return Err(format!( - "Type Error: Cannot assign type {:?} to field '{}.{}' of type {:?}", - new_type, class_name, member, field_info.var_type - )); + // CRITICAL FIX: Check inside imported modules + // The field definition might be inside a module's type map + let mut found_type = None; + let mut current_env = Some(env.clone()); + + while let Some(env_rc) = current_env { + let env_ref = env_rc.borrow(); + for info in env_ref.store.values() { + if let Type::Module(mod_types) = &info.var_type { + if let Some(field_type) = mod_types.get(&field_key) { + found_type = Some(field_type.clone()); + break; + } + } + } + if found_type.is_some() { + break; } + current_env = env_ref.outer.clone(); } - Ok(Type::None) - } else { + if let Some(t) = found_type { + return Ok(t); + } + return Err(format!( + "Type Error: Class '{}' has no member named '{}'", + class_name, member + )); + } - Err(format!("Type Error: Class '{}' has no field named '{}'", class_name, member)) + if let Type::Module(module_types) = object_type { + match module_types.get(member) { + Some(t) => Ok(t.clone()), + None => Err(format!( + "Type Error: Module has no member named '{}'", + member + )), + } + } else if member == "length" { + match object_type { + Type::Array(_) | Type::String | Type::Dict => Ok(Type::Int), + Type::QuantumRegister(Some(_size)) => Ok(Type::Int), + Type::QuantumRegister(None) => Ok(Type::Int), + _ => Err(format!( + "Type Error: Cannot get .length of type {:?}", + object_type + )), + } + } else { + if let Type::Dict = object_type { + Ok(Type::Any) + } else { + Err(format!( + "Type Error: Type {:?} has no member named '{}'", + object_type, member + )) + } } } _ => Err("Type Error: Assignment target must be an identifier or subscript expression.".to_string()) } } - ASTNode::Identifier { name, loc } => { - match env.borrow().get(name) { - Some(info) => Ok(info.var_type.clone()), - None => { - - let mut all_names = Vec::new(); - let mut current_env = Some(env.clone()); - - while let Some(env_rc) = current_env { - let env_ref = env_rc.borrow(); - for var_name in env_ref.store.keys() { - all_names.push(var_name.clone()); - } - current_env = env_ref.outer.clone(); + ASTNode::Identifier { name, loc } => match env.borrow().get(name) { + Some(info) => Ok(info.var_type.clone()), + None => { + let mut all_names = Vec::new(); + let mut current_env = Some(env.clone()); + + while let Some(env_rc) = current_env { + let env_ref = env_rc.borrow(); + for var_name in env_ref.store.keys() { + all_names.push(var_name.clone()); } + current_env = env_ref.outer.clone(); + } + let suggestions = find_similar_names(name, &all_names); - let suggestions = find_similar_names(name, &all_names); - - let mut error_msg = format!("Undefined variable '{}'", name); - if !suggestions.is_empty() { - error_msg.push_str(&format!( - ". Did you mean: {}?", - suggestions.join(", ") - )); - } - - Err(format!( - "Type Error at {}: {}", - loc, error_msg - )) + let mut error_msg = format!("Undefined variable '{}'", name); + if !suggestions.is_empty() { + error_msg.push_str(&format!(". Did you mean: {}?", suggestions.join(", "))); } + + Err(format!("Type Error at {}: {}", loc, error_msg)) } - } - ASTNode::ClassDeclaration { name, fields, methods, constructor, .. } => { + }, + ASTNode::ClassDeclaration { + name, + fields, + methods, + constructor, + .. + } => { let class_type = Type::Class(name.clone()); env.borrow_mut().set( name.clone(), - TypeInfo { var_type: class_type, is_mutable: false } + TypeInfo { + var_type: class_type, + is_mutable: false, + }, ); let class_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(env.clone()))); - for field in fields { class_env.borrow_mut().set( field.name.clone(), - TypeInfo { var_type: field.field_type.clone(), is_mutable: true } + TypeInfo { + var_type: field.field_type.clone(), + is_mutable: true, + }, ); let global_field_key = format!("{}::{}", name, field.name); env.borrow_mut().set( global_field_key, - TypeInfo { var_type: field.field_type.clone(), is_mutable: true } + TypeInfo { + var_type: field.field_type.clone(), + is_mutable: true, + }, ); } - for method in methods { - let method_param_types: Vec = method.parameters.iter() + let method_param_types: Vec = method + .parameters + .iter() .map(|p| p.param_type.clone()) .collect(); let method_return_type = method.return_type.clone().unwrap_or(Type::None); - let method_func_type = Type::Function(method_param_types, Box::new(method_return_type)); + let method_func_type = + Type::Function(method_param_types, Box::new(method_return_type)); let global_method_key = format!("{}::{}", name, method.name); env.borrow_mut().set( global_method_key, - TypeInfo { var_type: method_func_type, is_mutable: false } + TypeInfo { + var_type: method_func_type, + is_mutable: false, + }, ); } - if let Some(constructor_node) = constructor { - if let ASTNode::FunctionDeclaration { body, parameters, .. } = &**constructor_node { - let constructor_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(class_env.clone()))); - - - let param_types: Vec = parameters.iter().map(|p| p.param_type.clone()).collect(); + if let ASTNode::FunctionDeclaration { + body, parameters, .. + } = &**constructor_node + { + let constructor_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed( + class_env.clone(), + ))); + + let param_types: Vec = + parameters.iter().map(|p| p.param_type.clone()).collect(); let ctor_type = Type::Function(param_types, Box::new(Type::None)); env.borrow_mut().set( format!("{}::init", name), - TypeInfo { var_type: ctor_type, is_mutable: false } + TypeInfo { + var_type: ctor_type, + is_mutable: false, + }, ); - constructor_env.borrow_mut().set("self".to_string(), TypeInfo { - var_type: Type::Instance(name.clone()), is_mutable: false, - }); + constructor_env.borrow_mut().set( + "self".to_string(), + TypeInfo { + var_type: Type::Instance(name.clone()), + is_mutable: false, + }, + ); for param in parameters { - constructor_env.borrow_mut().set(param.name.clone(), TypeInfo { - var_type: param.param_type.clone(), is_mutable: false, - }); + constructor_env.borrow_mut().set( + param.name.clone(), + TypeInfo { + var_type: param.param_type.clone(), + is_mutable: false, + }, + ); } Self::check(body, &constructor_env, Some(&Type::None))?; } } - for method in methods { - let method_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed(class_env.clone()))); - - method_env.borrow_mut().set("self".to_string(), TypeInfo { - var_type: Type::Instance(name.clone()), is_mutable: false, - }); + let method_env = Rc::new(RefCell::new(TypeEnvironment::new_enclosed( + class_env.clone(), + ))); + + method_env.borrow_mut().set( + "self".to_string(), + TypeInfo { + var_type: Type::Instance(name.clone()), + is_mutable: false, + }, + ); for param in &method.parameters { - method_env.borrow_mut().set(param.name.clone(), TypeInfo { - var_type: param.param_type.clone(), is_mutable: false, - }); + method_env.borrow_mut().set( + param.name.clone(), + TypeInfo { + var_type: param.param_type.clone(), + is_mutable: false, + }, + ); } let return_type = method.return_type.as_ref().unwrap_or(&Type::None); @@ -1234,30 +1347,48 @@ impl TypeChecker { Ok(Type::None) } - ASTNode::NewInstance { class_name, arguments, loc } => { + ASTNode::NewInstance { + class_name, + arguments, + loc, + } => { let parts: Vec<&str> = class_name.split('.').collect(); let class_type = if parts.len() > 1 { let module_name = parts[0]; let target_class = parts[1]; - let module_info = env.borrow().get(module_name) - .ok_or_else(|| format!("Type Error at {}: Unknown module '{}'", loc, module_name))?; + let module_info = env.borrow().get(module_name).ok_or_else(|| { + format!("Type Error at {}: Unknown module '{}'", loc, module_name) + })?; if let Type::Module(mod_types) = &module_info.var_type { - mod_types.get(target_class).cloned() - .ok_or_else(|| format!("Type Error at {}: Module '{}' has no class '{}'", loc, module_name, target_class))? + mod_types.get(target_class).cloned().ok_or_else(|| { + format!( + "Type Error at {}: Module '{}' has no class '{}'", + loc, module_name, target_class + ) + })? } else { - return Err(format!("Type Error at {}: '{}' is not a module", loc, module_name)); + return Err(format!( + "Type Error at {}: '{}' is not a module", + loc, module_name + )); } } else { - env.borrow().get(class_name) + env.borrow() + .get(class_name) .map(|info| info.var_type.clone()) - .ok_or_else(|| format!("Type Error at {}: Unknown class '{}'", loc, class_name))? + .ok_or_else(|| { + format!("Type Error at {}: Unknown class '{}'", loc, class_name) + })? }; if !matches!(class_type, Type::Class(_)) { - return Err(format!("Type Error at {}: '{}' is not a class", loc, class_name)); + return Err(format!( + "Type Error at {}: '{}' is not a class", + loc, class_name + )); } let ctor_params: Vec = if let Type::Class(real_name) = &class_type { @@ -1286,16 +1417,11 @@ impl TypeChecker { let object_type = Self::check(object, env, None)?; match object_type { - Type::Instance(class_name) | Type::Custom(class_name) => { - let method_key = format!("{}::{}", class_name, method_name); - - if let Some(method_type) = Self::resolve_method(env, &method_key) { - if let Type::Function(param_types, return_type) = method_type { - + if let Type::Function(param_types, return_type) = method_type { if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Method '{}.{}' expected {} arguments, but got {}", @@ -1303,7 +1429,6 @@ impl TypeChecker { )); } - for (i, arg_node) in arguments.iter().enumerate() { let arg_type = Self::check(arg_node, env, None)?; let expected_type = ¶m_types[i]; @@ -1312,22 +1437,35 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - let is_quantum_compat = matches!( (&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); let is_class_compat = match (&arg_type, expected_type) { - (Type::Instance(got), Type::Custom(expected)) => got == expected, + (Type::Instance(got), Type::Custom(expected)) => { + got == expected || + got.ends_with(&format!(".{}", expected)) || + expected.ends_with(&format!(".{}", got)) + } + (Type::Custom(got), Type::Custom(expected)) => { + got == expected || + got.ends_with(&format!(".{}", expected)) || + expected.ends_with(&format!(".{}", got)) + } _ => false, }; let is_dict_compat = match (&arg_type, expected_type) { - (Type::Dict, Type::Custom(name)) if name == "Dict" => true, - (Type::Custom(name), Type::Dict) if name == "Dict" => true, + (Type::Dict, Type::Custom(name)) if name == "Dict" => { + true + } + (Type::Custom(name), Type::Dict) if name == "Dict" => { + true + } _ => false, }; - if !is_quantum_compat && !is_class_compat && !is_dict_compat { + if !is_quantum_compat && !is_class_compat && !is_dict_compat + { return Err(format!( "Type Error at {}: Argument {} of '{}.{}' is wrong type. Expected {:?}, got {:?}", loc, (i + 1), class_name, method_name, expected_type, arg_type @@ -1336,7 +1474,6 @@ impl TypeChecker { } } - Ok(*return_type) } else { Ok(Type::Any) @@ -1349,14 +1486,10 @@ impl TypeChecker { } } - - Type::Module(module_types) => { if let Some(member_type) = module_types.get(method_name) { match member_type { - Type::Function(param_types, return_type) => { - if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Module function '{}.{}' expected {} arguments, but got {}", @@ -1364,7 +1497,6 @@ impl TypeChecker { )); } - for (i, arg_node) in arguments.iter().enumerate() { let arg_type = Self::check(arg_node, env, Option::None)?; let expected_type = ¶m_types[i]; @@ -1373,23 +1505,53 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - - let is_quantum_compat = matches!((&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None))); + let is_quantum_compat = matches!( + (&arg_type, expected_type), + ( + Type::QuantumRegister(_), + Type::QuantumRegister(None) + ) + ); let is_class_compat = match (&arg_type, expected_type) { - (Type::Instance(got), Type::Custom(expected)) => got == expected, + (Type::Instance(got), Type::Custom(expected)) => { + got == expected || + got.ends_with(&format!(".{}", expected)) || + expected.ends_with(&format!(".{}", got)) + } + (Type::Custom(got), Type::Custom(expected)) => { + got == expected || + got.ends_with(&format!(".{}", expected)) || + expected.ends_with(&format!(".{}", got)) + } _ => false, }; let is_dict_compat = match (&arg_type, expected_type) { - (Type::Dict, Type::Custom(name)) if name == "Dict" => true, - (Type::Custom(name), Type::Dict) if name == "Dict" => true, + (Type::Dict, Type::Custom(name)) + if name == "Dict" => + { + true + } + (Type::Custom(name), Type::Dict) + if name == "Dict" => + { + true + } _ => false, }; let is_func_compat = match (&arg_type, expected_type) { - (Type::Function(..), Type::Custom(name)) if name == "Function" => true, + (Type::Function(..), Type::Custom(name)) + if name == "Function" => + { + true + } _ => false, }; - if !is_quantum_compat && !is_class_compat && !is_dict_compat && !is_func_compat { + if !is_quantum_compat + && !is_class_compat + && !is_dict_compat + && !is_func_compat + { return Err(format!( "Type Error at {}: Argument {} of '{}.{}' is wrong type. Expected {:?}, got {:?}", loc, (i + 1), "module", method_name, expected_type, arg_type @@ -1400,20 +1562,18 @@ impl TypeChecker { Ok(*return_type.clone()) } - Type::Class(class_name) => { - let init_key = format!("{}::init", class_name); - let constructor_params = if let Some(ctor_type) = module_types.get(&init_key) { - if let Type::Function(params, _) = ctor_type { - params.clone() + let constructor_params = + if let Some(ctor_type) = module_types.get(&init_key) { + if let Type::Function(params, _) = ctor_type { + params.clone() + } else { + vec![] + } } else { vec![] - } - } else { - vec![] - }; - + }; if arguments.len() != constructor_params.len() { return Err(format!( @@ -1430,19 +1590,37 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - - let is_quantum_compat = matches!((&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None))); + let is_quantum_compat = matches!( + (&arg_type, expected_type), + ( + Type::QuantumRegister(_), + Type::QuantumRegister(None) + ) + ); let is_class_compat = match (&arg_type, expected_type) { - (Type::Instance(got), Type::Custom(expected)) => got == expected, + (Type::Instance(got), Type::Custom(expected)) => { + got == expected + } _ => false, }; let is_dict_compat = match (&arg_type, expected_type) { - (Type::Dict, Type::Custom(name)) if name == "Dict" => true, - (Type::Custom(name), Type::Dict) if name == "Dict" => true, - _ => false, + (Type::Dict, Type::Custom(name)) + if name == "Dict" => + { + true + } + (Type::Custom(name), Type::Dict) + if name == "Dict" => + { + true + } + _ => false, }; - if !is_quantum_compat && !is_class_compat && !is_dict_compat { + if !is_quantum_compat + && !is_class_compat + && !is_dict_compat + { return Err(format!( "Type Error at {}: Argument {} of constructor is wrong type. Expected {:?}, got {:?}", loc, (i + 1), expected_type, arg_type @@ -1457,7 +1635,7 @@ impl TypeChecker { _ => Err(format!( "Type Error at {}: '{}.{}' is not a function or class.", loc, "module", method_name - )) + )), } } else { Err(format!( @@ -1474,11 +1652,10 @@ impl TypeChecker { } } - ASTNode::SelfRef { loc } => { - - - Err(format!("Type Error at {}: 'self' can only be used inside class methods", loc)) - } + ASTNode::SelfRef { loc } => Err(format!( + "Type Error at {}: 'self' can only be used inside class methods", + loc + )), ASTNode::FunctionCall { callee, @@ -1490,21 +1667,18 @@ impl TypeChecker { let name = format!("{:?}", callee); match callee_type { - Type::Class(class_name) => { - let init_key = format!("{}::init", class_name); - let constructor_params = if let Some(ctor_info) = env.borrow().get(&init_key) { - if let Type::Function(params, _) = ctor_info.var_type { - params + let constructor_params = + if let Some(ctor_info) = env.borrow().get(&init_key) { + if let Type::Function(params, _) = ctor_info.var_type { + params + } else { + vec![] + } } else { vec![] - } - } else { - - vec![] - }; - + }; if arguments.len() != constructor_params.len() { return Err(format!( @@ -1513,7 +1687,6 @@ impl TypeChecker { )); } - for (i, arg_node) in arguments.iter().enumerate() { let arg_type = Self::check(arg_node, env, Option::None)?; let expected_type = &constructor_params[i]; @@ -1522,18 +1695,18 @@ impl TypeChecker { && *expected_type != Type::Any && arg_type != Type::Any { - let is_quantum_compat = matches!( (&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); let is_class_compat = match (&arg_type, expected_type) { - (Type::Instance(got), Type::Custom(expected)) => got == expected, + (Type::Instance(got), Type::Custom(expected)) => { + got == expected + } _ => false, }; - let is_dict_compat = match (&arg_type, expected_type) { (Type::Dict, Type::Custom(name)) if name == "Dict" => true, (Type::Custom(name), Type::Dict) if name == "Dict" => true, @@ -1549,13 +1722,10 @@ impl TypeChecker { } } - Ok(Type::Instance(class_name)) } Type::Custom(ref name) if name == "Function" => { - - for arg in arguments { Self::check(arg, env, Option::None)?; } @@ -1565,7 +1735,6 @@ impl TypeChecker { Type::Function(param_types, return_type) => { if arguments.len() > 0 && param_types.len() == 0 { - } else if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Function '{}' expected {} arguments, but got {}", @@ -1584,33 +1753,35 @@ impl TypeChecker { if arg_type != *expected_type && *expected_type != Type::Any - && arg_type != Type::Any { - + && arg_type != Type::Any + { let is_quantum_compat = matches!( (&arg_type, expected_type), (Type::QuantumRegister(_), Type::QuantumRegister(None)) ); let is_func_compat = match (&arg_type, expected_type) { - - (Type::Function(..), Type::Custom(name)) if name == "Function" => true, + (Type::Function(..), Type::Custom(name)) + if name == "Function" => + { + true + } _ => false, }; let is_class_compat = match (&arg_type, expected_type) { - (Type::Instance(got), Type::Custom(expected)) => got == expected, + (Type::Instance(got), Type::Custom(expected)) => { + got == expected + } _ => false, }; - - if !is_quantum_compat && !is_class_compat { return Err(format!( "Type Error at {}: Argument {} of function call is wrong type. Expected {:?}, got {:?}", loc, (i + 1), expected_type, arg_type )); } - } } Ok(*return_type) @@ -1783,99 +1954,126 @@ impl TypeChecker { let index_type = Self::check(index, env, Option::None)?; match array_type { - - Type::QuantumRegister(size_opt) => { - if index_type != Type::Int { - return Err(format!( + Type::QuantumRegister(size_opt) => { + if index_type != Type::Int { + return Err(format!( "Type Error at {}: Quantum register index must be an Int, but got {:?}", loc, index_type )); - } - if let (Some(size), ASTNode::IntLiteral(idx)) = (size_opt, &**index) { - if *idx < 0 || *idx as usize >= size { - return Err(format!( + } + if let (Some(size), ASTNode::IntLiteral(idx)) = (size_opt, &**index) { + if *idx < 0 || *idx as usize >= size { + return Err(format!( "Type Error at {}: Qubit index {} is out of bounds for register of size {}.", loc, idx, size )); + } } + Ok(Type::Qubit) } - Ok(Type::Qubit) - } - - Type::Array(inner_type) => { - if index_type != Type::Int { - return Err(format!( - "Type Error at {}: Array index must be an Int, but got {:?}", - loc, index_type - )); + Type::Array(inner_type) => { + if index_type != Type::Int { + return Err(format!( + "Type Error at {}: Array index must be an Int, but got {:?}", + loc, index_type + )); + } + Ok(*inner_type) } - Ok(*inner_type) - } - - Type::String => { - if index_type != Type::Int { - return Err(format!( - "Type Error at {}: String index must be an Int, but got {:?}", - loc, index_type - )); + Type::String => { + if index_type != Type::Int { + return Err(format!( + "Type Error at {}: String index must be an Int, but got {:?}", + loc, index_type + )); + } + Ok(Type::String) } - Ok(Type::String) - } - - Type::Dict => { - if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { - return Err(format!( + Type::Dict => { + if index_type != Type::String + && index_type != Type::Int + && index_type != Type::Bool + { + return Err(format!( "Type Error at {}: Dictionary key must be String, Int, or Bool, but got {:?}", loc, index_type )); + } + Ok(Type::Any) } - Ok(Type::Any) - } - - Type::Custom(ref name) if name == "Dict" => { - if index_type != Type::String && index_type != Type::Int && index_type != Type::Bool { - return Err(format!( + Type::Custom(ref name) if name == "Dict" => { + if index_type != Type::String + && index_type != Type::Int + && index_type != Type::Bool + { + return Err(format!( "Type Error at {}: Dictionary key must be String, Int, or Bool, but got {:?}", loc, index_type )); + } + Ok(Type::Any) } - Ok(Type::Any) - } - _ => Err(format!( - "Type Error at {}: Cannot perform array access '[]' on type {:?}", - loc, array_type - )), - } + _ => Err(format!( + "Type Error at {}: Cannot perform array access '[]' on type {:?}", + loc, array_type + )), + } } ASTNode::MemberAccess { object, member } => { let object_type = Self::check(object, env, Option::None)?; let class_name_opt = match &object_type { - Type::Instance(name) | Type::Custom(name) => Some(name), + Type::Instance(name) | Type::Custom(name) => Some(name.clone()), _ => None, }; - if let Some(class_name) = class_name_opt { + if let Some(mut class_name) = class_name_opt { + if class_name.contains('.') { + if let Some(real_name) = class_name.split('.').last() { + class_name = real_name.to_string(); + } + } let field_key = format!("{}::{}", class_name, member); - if let Some(field_info) = env.borrow().get(&field_key) { return Ok(field_info.var_type.clone()); - } else { - return Err(format!( - "Type Error: Class '{}' has no member named '{}'", - class_name, member - )); } + + let mut found_type = None; + let mut current_env = Some(env.clone()); + + while let Some(env_rc) = current_env { + let env_ref = env_rc.borrow(); + for info in env_ref.store.values() { + if let Type::Module(mod_types) = &info.var_type { + if let Some(field_type) = mod_types.get(&field_key) { + found_type = Some(field_type.clone()); + break; + } + } + } + if found_type.is_some() { break; } + current_env = env_ref.outer.clone(); + } + + if let Some(t) = found_type { + return Ok(t); + } + + return Err(format!( + "Type Error: Class '{}' has no member named '{}'", + class_name, member + )); } + // Handle Module Access if let Type::Module(module_types) = object_type { match module_types.get(member) { Some(t) => Ok(t.clone()), @@ -1884,11 +2082,11 @@ impl TypeChecker { member )), } + // Handle .length property } else if member == "length" { match object_type { Type::Array(_) | Type::String | Type::Dict => Ok(Type::Int), - Type::QuantumRegister(Some(_size)) => Ok(Type::Int), - Type::QuantumRegister(None) => Ok(Type::Int), + Type::QuantumRegister(_) => Ok(Type::Int), _ => Err(format!( "Type Error: Cannot get .length of type {:?}", object_type @@ -1912,13 +2110,10 @@ impl TypeChecker { arguments, loc, } => { - let gate_type = Self::check_gate_expression(gate_expr, env)?; - match gate_type { Type::Function(param_types, return_type) => { - if arguments.len() != param_types.len() { return Err(format!( "Type Error at {}: Gate requires {} arguments, but got {}", @@ -1949,17 +2144,9 @@ impl TypeChecker { } } + ASTNode::Gate { .. } => Self::check_gate_expression(node, env), - ASTNode::Gate { .. } => { - - Self::check_gate_expression(node, env) - } - - - ASTNode::Controlled { gate_expr, loc } => { - - Self::check_gate_expression(node, env) - } + ASTNode::Controlled { gate_expr, loc } => Self::check_gate_expression(node, env), ASTNode::Measure(target_expr) => { let target_type = Self::check(target_expr, env, Option::None)?; @@ -2001,7 +2188,6 @@ impl TypeChecker { Ok(Type::None) } - ASTNode::FunctionDeclaration { name, parameters, @@ -2056,38 +2242,17 @@ impl TypeChecker { } else { Type::None }; + match expected_return_type { Option::None => Err( "Type Error: 'return' statement found outside of a function.".to_string(), ), Some(expected) => { - - if value_type != *expected - && *expected != Type::Any - && value_type != Type::Any - { - - let is_quantum_compat = matches!( - (&value_type, expected), - (Type::QuantumRegister(_), Type::QuantumRegister(None)) - ); - - - let is_class_compat = match (&value_type, expected) { - (Type::Instance(got), Type::Custom(expected_name)) => got == expected_name, - _ => false, - }; - - - let is_dict_compat = match (&value_type, expected) { - (Type::Dict, Type::Custom(name)) if name == "Dict" => true, - (Type::Custom(name), Type::Dict) if name == "Dict" => true, - _ => false, - }; - - if !is_quantum_compat && !is_class_compat && !is_dict_compat { - return Err(format!("Type Error: Function expected return type {:?}, but found return with type {:?}", expected, value_type)); - } + if !Self::is_type_compatible(&value_type, expected) { + return Err(format!( + "Type Error: Function expected return type {:?}, but found return with type {:?}", + expected, value_type + )); } Ok(Type::None) } @@ -2138,7 +2303,6 @@ impl TypeChecker { )); } - Ok(Type::Custom("range".to_string())) } @@ -2224,4 +2388,59 @@ impl TypeChecker { )), } } + + fn is_type_compatible(actual: &Type, expected: &Type) -> bool { + if actual == expected { + return true; + } + + match (actual, expected) { + (Type::Any, _) | (_, Type::Any) => true, + + (Type::None, _) => true, + + (Type::QuantumRegister(_), Type::QuantumRegister(None)) => true, + + (Type::Instance(actual_class), Type::Custom(expected_class)) => { + if actual_class == expected_class { + return true; + } + if expected_class.contains('.') { + if let Some(class_name) = expected_class.split('.').last() { + return actual_class == class_name; + } + } + false + } + + (Type::Custom(actual_class), Type::Instance(expected_class)) => { + if actual_class == expected_class { + return true; + } + if actual_class.contains('.') { + if let Some(class_name) = actual_class.split('.').last() { + return class_name == expected_class; + } + } + false + } + + (Type::Custom(actual_class), Type::Custom(expected_class)) => { + actual_class == expected_class || + actual_class.ends_with(&format!(".{}", expected_class)) || + expected_class.ends_with(&format!(".{}", actual_class)) + } + + (Type::Dict, Type::Custom(name)) | (Type::Custom(name), Type::Dict) + if name == "Dict" => true, + + (Type::Array(t1), Type::Array(t2)) => { + **t1 == Type::Any || **t2 == Type::Any || Self::is_type_compatible(t1, t2) + } + + (Type::Int, Type::Float) => true, + + _ => false, + } + } } From 30b82f194d6fff56e5a4ef11b02f4a69e12eb0b8 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:40:04 +0530 Subject: [PATCH 26/27] Optimized ai training speed upto 2x --- Cargo.toml | 2 +- src/evaluator/mod.rs | 120 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 +- src/type_checker.rs | 25 +++++++++ stdlib/ai.qc | 21 ++++---- 5 files changed, 158 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2f21c10..cddb197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "quantica" -version = "0.1.0" +version = "0.2.0" edition = "2021" # This new [lib] section tells Cargo to build libquantica.a diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 7c6a9a8..e18304d 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -169,6 +169,8 @@ impl Evaluator { let mut e = env.borrow_mut(); e.set("time".to_string(), RuntimeValue::BuiltinFunction("time".to_string())); + e.set("matrix_update".to_string(), RuntimeValue::BuiltinFunction("matrix_update".to_string())); + e.set("compute_input_gradient".to_string(), RuntimeValue::BuiltinFunction("compute_input_gradient".to_string())); e.set( "print".to_string(), @@ -1649,6 +1651,8 @@ impl Evaluator { match func_name.as_str() { "print" => Self::builtin_print(evaluated_args), + "matrix_update" => Self::builtin_matrix_update(evaluated_args), + "compute_input_gradient" => Self::builtin_compute_input_gradient(evaluated_args), "time" => Self::builtin_time(evaluated_args), "input" => Self::builtin_input(evaluated_args), "split" => Self::builtin_split(evaluated_args), @@ -1985,6 +1989,65 @@ impl Evaluator { println!("-----------------------------------"); } + fn builtin_compute_input_gradient(args: Vec) -> Result { + if args.len() != 2 { + return Err("Runtime Error: 'compute_input_gradient' requires 2 args (weights, delta)".to_string()); + } + + let weights_matrix = match &args[0] { + RuntimeValue::Register(v) => v, + _ => return Err("Runtime Error: Weights must be a matrix".to_string()) + }; + + let delta_vec = match &args[1] { + RuntimeValue::Register(v) => v, + _ => return Err("Runtime Error: Delta must be an array".to_string()) + }; + + let output_size = delta_vec.len(); + if output_size == 0 { return Ok(RuntimeValue::Register(vec![])); } + + if weights_matrix.is_empty() { return Ok(RuntimeValue::Register(vec![])); } + let input_size = match &*weights_matrix[0].borrow() { + RuntimeValue::Register(row) => row.len(), + _ => 0 + }; + + let mut input_gradient = vec![0.0; input_size]; + + //result[j] = sum(weights[i][j] * delta[i]) + for (i, row_rc) in weights_matrix.iter().enumerate() { + if i >= output_size { break; } + + // delta[i] + let d_val = match *delta_vec[i].borrow() { + RuntimeValue::Float(f) => f, + _ => 0.0 + }; + + let row_val = row_rc.borrow(); + if let RuntimeValue::Register(row) = &*row_val { + for (j, weight_rc) in row.iter().enumerate() { + if j >= input_size { break; } + + let w_val = match *weight_rc.borrow() { + RuntimeValue::Float(f) => f, + _ => 0.0 + }; + + input_gradient[j] += w_val * d_val; + } + } + } + + let result_objs: Vec>> = input_gradient + .into_iter() + .map(|f| Rc::new(RefCell::new(RuntimeValue::Float(f)))) + .collect(); + + Ok(RuntimeValue::Register(result_objs)) + } + fn builtin_time(args: Vec) -> Result { if !args.is_empty() { return Err("Runtime Error: 'time' expects 0 arguments.".to_string()); @@ -1996,6 +2059,63 @@ impl Evaluator { Ok(RuntimeValue::Float(since_the_epoch.as_secs_f64())) } + fn builtin_matrix_update(args: Vec) -> Result { + if args.len() != 4 { + return Err("Runtime Error: 'matrix_update' requires 4 args (weights, delta, input, lr)".to_string()); + } + + let lr = match args[3] { + RuntimeValue::Float(f) => f, + _ => return Err("Runtime Error: Learning rate must be a float".to_string()) + }; + + let delta_vec = match &args[1] { + RuntimeValue::Register(v) => v, + _ => return Err("Runtime Error: Delta must be an array".to_string()) + }; + let input_vec = match &args[2] { + RuntimeValue::Register(v) => v, + _ => return Err("Runtime Error: Input must be an array".to_string()) + }; + + let weights_matrix = match &args[0] { + RuntimeValue::Register(v) => v, + _ => return Err("Runtime Error: Weights must be a matrix".to_string()) + }; + + // weights[i][j] -= lr * delta[i] * input[j] + for (i, row_rc) in weights_matrix.iter().enumerate() { + if i >= delta_vec.len() { break; } + + // Get delta[i] + let d_val = match *delta_vec[i].borrow() { + RuntimeValue::Float(f) => f, + _ => 0.0 + }; + + let row_val = row_rc.borrow(); + if let RuntimeValue::Register(row) = &*row_val { + for (j, weight_rc) in row.iter().enumerate() { + if j >= input_vec.len() { break; } + + // Get input[j] + let in_val = match *input_vec[j].borrow() { + RuntimeValue::Float(f) => f, + _ => 0.0 + }; + + // Update Weight + let mut w_guard = weight_rc.borrow_mut(); + if let RuntimeValue::Float(w) = *w_guard { + *w_guard = RuntimeValue::Float(w - (lr * d_val * in_val)); + } + } + } + } + + Ok(RuntimeValue::None) + } + fn builtin_debug_state(args: Vec) -> Result { if args.len() != 1 { return Err( diff --git a/src/main.rs b/src/main.rs index ca7c510..c1ff102 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,7 +55,7 @@ enum CompilationTarget { } fn main() -> Result<(), Box> { - println!("=== Quantica Compiler v0.1.2 ===\n"); + println!("=== Quantica Compiler v0.2.0 ===\n"); let args: Vec = env::args().collect(); diff --git a/src/type_checker.rs b/src/type_checker.rs index e2e6156..0579be9 100644 --- a/src/type_checker.rs +++ b/src/type_checker.rs @@ -1,4 +1,5 @@ /* src/type_checker.rs */ + use crate::error::{find_similar_names, levenshtein_distance}; use crate::lexer::Lexer; use crate::parser::ast::ImportPath; @@ -360,6 +361,30 @@ impl TypeChecker { )), ); + env_mut.set( + "matrix_update".to_string(), + immut(Type::Function( + vec![ + Type::Array(Box::new(Type::Array(Box::new(Type::Float)))), // weights + Type::Array(Box::new(Type::Float)), // delta + Type::Array(Box::new(Type::Float)), // input + Type::Float // lr + ], + Box::new(Type::None), + )), + ); + + env_mut.set( + "compute_input_gradient".to_string(), + immut(Type::Function( + vec![ + Type::Array(Box::new(Type::Array(Box::new(Type::Float)))), // weights + Type::Array(Box::new(Type::Float)) // delta + ], + Box::new(Type::Array(Box::new(Type::Float))), // returns input_gradient + )), + ); + } pub fn check_program(node: &ASTNode) -> Result<(), String> { diff --git a/stdlib/ai.qc b/stdlib/ai.qc index b07244f..762fe82 100644 --- a/stdlib/ai.qc +++ b/stdlib/ai.qc @@ -224,6 +224,8 @@ Class Layer: delta = delta + [d] i = i + 1 + + /* i = 0 while i < output_size: mut j = 0 @@ -235,17 +237,16 @@ Class Layer: // Update bias biases[i] = biases[i] - learning_rate * delta[i] i = i + 1 + */ - mut input_gradient = [] - mut j = 0 - while j < input_size: - mut sum = 0.0 - i = 0 - while i < output_size: - sum = sum + weights[i][j] * delta[i] - i = i + 1 - input_gradient = input_gradient + [sum] - j = j + 1 + matrix_update(weights, delta, last_input, learning_rate) + + i = 0 + while i < output_size: + biases[i] = biases[i] - learning_rate * delta[i] + i = i + 1 + + let input_gradient = compute_input_gradient(weights, delta) return input_gradient From ea363502cb872e471b88b23b47213036cd20e3f7 Mon Sep 17 00:00:00 2001 From: Gurukasi <163010968+gurukasi-2006@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:41:59 +0530 Subject: [PATCH 27/27] added mnist examples --- examples/mnist.qc | 107 ++++++++++++++++++++++++++++++++++++++++++++ examples/predict.qc | 98 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 examples/mnist.qc create mode 100644 examples/predict.qc diff --git a/examples/mnist.qc b/examples/mnist.qc new file mode 100644 index 0000000..cd97cd2 --- /dev/null +++ b/examples/mnist.qc @@ -0,0 +1,107 @@ +import "stdlib/ai.qc" as ai +import "stdlib/math.qc" as math + +print("=== QUANTICA MNIST TRAINING ===") + +/* --- Helper: One-Hot Encode Labels --- */ +func one_hot(label: Int) -> Float[]: + mut target = [] + mut i = 0 + while i < 10: + if i == label: + target = target + [1.0] + else: + target = target + [0.0] + i = i + 1 + return target + +/* --- Helper: Load CSV Data --- */ +func load_mnist(filename: String, max_samples: Int) -> ai.Dataset: + print("DEBUG: Reading " + filename + "...") + let content = file_read(filename) + let lines = split(content, "\n") + + let dataset = new ai.Dataset() + + mut count = max_samples + if len(lines) < max_samples: + count = len(lines) + if max_samples <= 0: + count = len(lines) + + print("DEBUG: Parsing " + to_string(count) + " samples...") + + mut i = 0 + mut samples_loaded = 0 + + while i < count: + let line = lines[i] + + if len(line) > 10: + let values = split(line, ",") + + // Header Check + if values[0] == "label": + i = i + 1 + count = count + 1 + if count > len(lines): + count = len(lines) + else: + let label = to_int(values[0]) + let target = one_hot(label) + + mut input = [] + mut p = 1 + while p < 785: + let pixel = to_float(values[p]) / 255.0 + input = input + [pixel] + p = p + 1 + + dataset.add_sample(input, target) + samples_loaded = samples_loaded + 1 + + if samples_loaded > 0: + if (samples_loaded % 500) == 0: + print("DEBUG: Loaded " + to_string(samples_loaded) + " samples...") + + i = i + 1 + + print("SUCCESS: Loaded " + to_string(dataset.size) + " samples.") + return dataset + +/* --- MAIN TRAINING --- */ + +// Loading Data +print("--- 1. Loading Training Set ---") +let train_data = load_mnist("mnist_train.csv", 1000) + +print("\n--- 2. Loading Test Set ---") +let test_data = load_mnist("mnist_test.csv", 100) + +// Creating Network +print("\n--- 3. Initializing Network ---") +let nn = new ai.NeuralNetwork(0.1) +nn.add_layer(784, 128, "relu") +nn.add_layer(128, 10, "sigmoid") + +// Train with Timer +print("\n--- 4. Starting Training ---") + +// Start timer +let start_time = time() + +ai.train_network(nn, train_data, 6, 1, True) + +// Stop timer +let end_time = time() +let duration = end_time - start_time +print(">> Total Training Time: " + to_string(duration) + " seconds") + +//Evaluate +print("\n--- 5. Evaluating ---") +let acc = ai.evaluate_network(nn, test_data, 0.5) +print(">> TEST ACCURACY: " + to_string(acc * 100.0) + "%") + +// Save +print("\n--- 6. Saving Model ---") +nn.save("mnist_brain.qmodel") \ No newline at end of file diff --git a/examples/predict.qc b/examples/predict.qc new file mode 100644 index 0000000..d64a7c1 --- /dev/null +++ b/examples/predict.qc @@ -0,0 +1,98 @@ +import "stdlib/ai.qc" as ai +import "stdlib/math.qc" as math + +print("=== QUANTICA DIGIT PREDICTOR ===") + +/* --- Helper: Draw Digit as ASCII Art --- */ +func draw_digit(pixels: Float[]): + print("--- IMAGE ---") + mut row = 0 + while row < 28: + mut line = "" + mut col = 0 + while col < 28: + let idx = row * 28 + col + let val = pixels[idx] + + if val > 0.5: + line = line + " # " + elif val > 0.2: + line = line + " . " + else: + line = line + " " + col = col + 1 + print(line) + row = row + 1 + print("-------------") + +/* --- Helper: Find index of max value --- */ +func argmax(values: Float[]) -> Int: + mut max_idx = 0 + mut max_val = values[0] + mut i = 1 + while i < len(values): + if values[i] > max_val: + max_val = values[i] + max_idx = i + i = i + 1 + return max_idx + +/* --- Helper: Load CSV Data (Single Sample) --- */ +func load_sample(filename: String, line_index: Int) -> Dict: + let content = file_read(filename) + let lines = split(content, "\n") + mut real_index = line_index + let first = split(lines[0], ",") + if first[0] == "label": + real_index = real_index + 1 + + let line = lines[real_index] + let values = split(line, ",") + + let label = to_int(values[0]) + + mut input = [] + mut p = 1 + while p < 785: + let pixel = to_float(values[p]) / 255.0 + input = input + [pixel] + p = p + 1 + + return {"input": input, "label": label} + +/* --- MAIN PREDICTION LOGIC --- */ + +print("Initializing Network Structure...") +let nn = new ai.NeuralNetwork(0.1) +nn.add_layer(784, 128, "relu") +nn.add_layer(128, 10, "sigmoid") + +print("Loading 'mnist_brain.qmodel'...") +nn.load("mnist_brain.qmodel") + +print("\n--- TESTING MULTIPLE DIGITS ---") + +mut i = 0 +while i < 5: + print("\n[ Test Sample #" + to_string(i) + " ]") + + // Load + let sample = load_sample("mnist_test.csv", i) + + // Predict + let prediction_probs = nn.predict(sample["input"]) + let predicted_digit = argmax(prediction_probs) + + // Visualize + draw_digit(sample["input"]) + + // Result + print("TRUE LABEL: " + to_string(sample["label"])) + print("PREDICTION: " + to_string(predicted_digit)) + + if sample["label"] == predicted_digit: + print("RESULT: ✅ PASS") + else: + print("RESULT: ❌ FAIL") + + i = i + 1 \ No newline at end of file