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