add: dfs test, and code fixes
This commit is contained in:
68
samples/dfs.rpg
Normal file
68
samples/dfs.rpg
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
**Free
|
||||||
|
Ctl-Opt DftActGrp(*No) Main(MainLine);
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Global Graph Data (15 Nodes)
|
||||||
|
// --------------------------------------------------
|
||||||
|
Dcl-S AdjMatrix Ind Dim(15: 15) Inz(*Off);
|
||||||
|
Dcl-S Visited Ind Dim(15) Inz(*Off);
|
||||||
|
Dcl-S Found Ind Inz(*Off);
|
||||||
|
|
||||||
|
Dcl-Proc MainLine;
|
||||||
|
// 1. Setup a simple graph (Node 1 connected to 2 & 3, etc.)
|
||||||
|
AdjMatrix(1: 2) = *On; AdjMatrix(2: 4) = *On; AdjMatrix(4: 8) = *On;
|
||||||
|
AdjMatrix(1: 3) = *On; AdjMatrix(3: 5) = *On; AdjMatrix(5: 9) = *On; // Path to 9
|
||||||
|
AdjMatrix(3: 6) = *On; AdjMatrix(6: 10) = *On;
|
||||||
|
|
||||||
|
Dsply 'Starting DFS to find Node 9...';
|
||||||
|
|
||||||
|
// 2. Start Search from Node 1
|
||||||
|
DFS(1: 9);
|
||||||
|
|
||||||
|
If Not Found;
|
||||||
|
Dsply 'Node 9 was not found.';
|
||||||
|
EndIf;
|
||||||
|
|
||||||
|
Return;
|
||||||
|
End-Proc;
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Recursive DFS Subprocedure
|
||||||
|
// --------------------------------------------------
|
||||||
|
Dcl-Proc DFS;
|
||||||
|
Dcl-Pi *N;
|
||||||
|
CurrentNode Int(10) Value;
|
||||||
|
TargetNode Int(10) Value;
|
||||||
|
End-Pi;
|
||||||
|
|
||||||
|
Dcl-S Neighbor Int(10);
|
||||||
|
|
||||||
|
// If already found elsewhere, stop exploring
|
||||||
|
If Found;
|
||||||
|
Return;
|
||||||
|
EndIf;
|
||||||
|
|
||||||
|
// Mark and Print current step
|
||||||
|
Visited(CurrentNode) = *On;
|
||||||
|
Dsply ('Visiting: ' + %Char(CurrentNode));
|
||||||
|
|
||||||
|
// Check if this is our target
|
||||||
|
If CurrentNode = TargetNode;
|
||||||
|
Dsply '*** MATCH FOUND! ***';
|
||||||
|
Found = *On;
|
||||||
|
Return;
|
||||||
|
EndIf;
|
||||||
|
|
||||||
|
// Explore Neighbors (1 to 15)
|
||||||
|
For Neighbor = 1 to 15;
|
||||||
|
If AdjMatrix(CurrentNode: Neighbor) And Not Visited(Neighbor);
|
||||||
|
DFS(Neighbor: TargetNode);
|
||||||
|
|
||||||
|
// If the recursive call found it, stop looping here too
|
||||||
|
If Found;
|
||||||
|
Return;
|
||||||
|
EndIf;
|
||||||
|
EndIf;
|
||||||
|
EndFor;
|
||||||
|
|
||||||
|
End-Proc;
|
||||||
9
samples/dfs.rpg.stdout
Normal file
9
samples/dfs.rpg.stdout
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
DSPLY Starting DFS to find Node 9...
|
||||||
|
DSPLY Visiting: 1
|
||||||
|
DSPLY Visiting: 2
|
||||||
|
DSPLY Visiting: 4
|
||||||
|
DSPLY Visiting: 8
|
||||||
|
DSPLY Visiting: 3
|
||||||
|
DSPLY Visiting: 5
|
||||||
|
DSPLY Visiting: 9
|
||||||
|
DSPLY *** MATCH FOUND! ***
|
||||||
@@ -220,11 +220,14 @@ pub enum VarKeyword {
|
|||||||
/// `INZ(*named-constant)` — initialise to named constant.
|
/// `INZ(*named-constant)` — initialise to named constant.
|
||||||
InzNamed(NamedConstant),
|
InzNamed(NamedConstant),
|
||||||
Static,
|
Static,
|
||||||
/// `DIM(n)` — declares the variable as an array with `n` elements.
|
/// `DIM(n)` — declares the variable as a 1-D array with `n` elements.
|
||||||
Dim(Expression),
|
Dim(Expression),
|
||||||
|
/// `DIM(rows: cols)` — declares the variable as a 2-D array.
|
||||||
|
Dim2(Expression, Expression),
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Procedures
|
// Procedures
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
314
src/codegen.rs
314
src/codegen.rs
@@ -78,6 +78,7 @@ pub fn compile_to_object(
|
|||||||
builder,
|
builder,
|
||||||
globals: HashMap::new(),
|
globals: HashMap::new(),
|
||||||
array_dims: HashMap::new(),
|
array_dims: HashMap::new(),
|
||||||
|
array_cols: HashMap::new(),
|
||||||
string_cache: HashMap::new(),
|
string_cache: HashMap::new(),
|
||||||
global_inits: Vec::new(),
|
global_inits: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -141,6 +142,7 @@ pub fn emit_ir(program: &Program) -> Result<String, CodegenError> {
|
|||||||
builder,
|
builder,
|
||||||
globals: HashMap::new(),
|
globals: HashMap::new(),
|
||||||
array_dims: HashMap::new(),
|
array_dims: HashMap::new(),
|
||||||
|
array_cols: HashMap::new(),
|
||||||
string_cache: HashMap::new(),
|
string_cache: HashMap::new(),
|
||||||
global_inits: Vec::new(),
|
global_inits: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -182,9 +184,14 @@ struct Codegen<'ctx> {
|
|||||||
builder: Builder<'ctx>,
|
builder: Builder<'ctx>,
|
||||||
/// Module-scope global variables name -> (alloca/global ptr, TypeSpec)
|
/// Module-scope global variables name -> (alloca/global ptr, TypeSpec)
|
||||||
globals: HashMap<String, (PointerValue<'ctx>, TypeSpec)>,
|
globals: HashMap<String, (PointerValue<'ctx>, TypeSpec)>,
|
||||||
/// Array dimension table: variable name -> number of elements.
|
/// Array dimension table: variable name -> total number of elements.
|
||||||
/// Populated when a `DIM(n)` keyword is encountered.
|
/// Populated when a `DIM(n)` or `DIM(rows:cols)` keyword is encountered.
|
||||||
array_dims: HashMap<String, u64>,
|
array_dims: HashMap<String, u64>,
|
||||||
|
/// Column count for 2-D arrays: variable name -> number of columns.
|
||||||
|
/// Only present for variables declared with `DIM(rows: cols)`.
|
||||||
|
/// Used to convert a (row, col) subscript into a flat 0-based index:
|
||||||
|
/// flat = (row - 1) * cols + (col - 1)
|
||||||
|
array_cols: HashMap<String, u64>,
|
||||||
/// Interned string literal globals (content -> global ptr).
|
/// Interned string literal globals (content -> global ptr).
|
||||||
string_cache: HashMap<String, PointerValue<'ctx>>,
|
string_cache: HashMap<String, PointerValue<'ctx>>,
|
||||||
/// Global declarations that need runtime initialisation (INZ with a value).
|
/// Global declarations that need runtime initialisation (INZ with a value).
|
||||||
@@ -252,7 +259,23 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate each procedure.
|
// ── Pass 1: forward-declare all procedure signatures ──────────────────
|
||||||
|
// This ensures that any procedure can call any other procedure regardless
|
||||||
|
// of source order (e.g. mainline calling dfs before dfs is defined).
|
||||||
|
for proc in &program.procedures {
|
||||||
|
let fn_name = if proc.exported {
|
||||||
|
format!("rpg_{}", proc.name)
|
||||||
|
} else {
|
||||||
|
proc.name.clone()
|
||||||
|
};
|
||||||
|
// Only declare if not already in the module (runtime fns, etc.).
|
||||||
|
if self.module.get_function(&fn_name).is_none() {
|
||||||
|
let fn_ty = self.build_proc_fn_type(proc);
|
||||||
|
self.module.add_function(&fn_name, fn_ty, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pass 2: emit procedure bodies ────────────────────────────────────
|
||||||
let mut exported_name: Option<String> = None;
|
let mut exported_name: Option<String> = None;
|
||||||
for proc in &program.procedures {
|
for proc in &program.procedures {
|
||||||
if proc.exported && exported_name.is_none() {
|
if proc.exported && exported_name.is_none() {
|
||||||
@@ -332,8 +355,35 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
fn gen_global_decl(&mut self, decl: &Declaration) -> Result<(), CodegenError> {
|
fn gen_global_decl(&mut self, decl: &Declaration) -> Result<(), CodegenError> {
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Standalone(sd) => {
|
Declaration::Standalone(sd) => {
|
||||||
let size = sd.ty.byte_size().unwrap_or(8);
|
let elem_size = sd.ty.byte_size().unwrap_or(8);
|
||||||
let arr_ty = self.context.i8_type().array_type(size as u32);
|
|
||||||
|
// Check for DIM(n) or DIM(rows:cols) keywords.
|
||||||
|
let dim1 = sd.keywords.iter().find_map(|kw| {
|
||||||
|
if let VarKeyword::Dim(expr) = kw { const_int_from_expr(expr) } else { None }
|
||||||
|
});
|
||||||
|
let dim2 = sd.keywords.iter().find_map(|kw| {
|
||||||
|
if let VarKeyword::Dim2(r, c) = kw {
|
||||||
|
match (const_int_from_expr(r), const_int_from_expr(c)) {
|
||||||
|
(Some(rows), Some(cols)) => Some((rows, cols)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let total_bytes = if let Some((rows, cols)) = dim2 {
|
||||||
|
self.array_dims.insert(sd.name.clone(), rows * cols);
|
||||||
|
self.array_cols.insert(sd.name.clone(), cols);
|
||||||
|
elem_size * rows * cols
|
||||||
|
} else if let Some(n) = dim1 {
|
||||||
|
self.array_dims.insert(sd.name.clone(), n);
|
||||||
|
elem_size * n
|
||||||
|
} else {
|
||||||
|
elem_size
|
||||||
|
};
|
||||||
|
|
||||||
|
let arr_ty = self.context.i8_type().array_type(total_bytes as u32);
|
||||||
let global = self.module.add_global(arr_ty, Some(AddressSpace::default()), &sd.name);
|
let global = self.module.add_global(arr_ty, Some(AddressSpace::default()), &sd.name);
|
||||||
global.set_initializer(&arr_ty.const_zero());
|
global.set_initializer(&arr_ty.const_zero());
|
||||||
let ptr = global.as_pointer_value();
|
let ptr = global.as_pointer_value();
|
||||||
@@ -375,12 +425,44 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
proc.name.clone()
|
proc.name.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let function = self.module.add_function(&fn_name, fn_ty, None);
|
// Re-use the forward declaration emitted in pass 1 rather than adding
|
||||||
|
// a duplicate function with the same name.
|
||||||
|
let function = self.module.get_function(&fn_name)
|
||||||
|
.unwrap_or_else(|| self.module.add_function(&fn_name, fn_ty, None));
|
||||||
|
|
||||||
let entry_bb = self.context.append_basic_block(function, "entry");
|
let entry_bb = self.context.append_basic_block(function, "entry");
|
||||||
self.builder.position_at_end(entry_bb);
|
self.builder.position_at_end(entry_bb);
|
||||||
|
|
||||||
let mut state = FnState::new(function);
|
let mut state = FnState::new(function);
|
||||||
|
|
||||||
|
// ── Wire PI parameters into state.locals ──────────────────────────────
|
||||||
|
// Each incoming LLVM argument gets its own alloca slot so that the body
|
||||||
|
// can read (and write) the parameter by name just like any other local.
|
||||||
|
if let Some(pi) = &proc.pi {
|
||||||
|
for (i, param) in pi.params.iter().enumerate() {
|
||||||
|
if let Some(arg_val) = function.get_nth_param(i as u32) {
|
||||||
|
// Allocate a slot of the right size in the entry block.
|
||||||
|
let ptr = self.alloca_for_type(¶m.ty, ¶m.name);
|
||||||
|
// Store the incoming argument value.
|
||||||
|
let i64_t = self.context.i64_type();
|
||||||
|
match arg_val {
|
||||||
|
BasicValueEnum::IntValue(iv) => {
|
||||||
|
// Extend/truncate to i64, then store at the right width.
|
||||||
|
let extended = self.builder
|
||||||
|
.build_int_s_extend_or_bit_cast(iv, i64_t, "param_ext")
|
||||||
|
.unwrap_or(iv);
|
||||||
|
self.store_value(ptr, extended.into(), ¶m.ty);
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
// Pointer / float — store as-is.
|
||||||
|
self.store_value(ptr, other, ¶m.ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.locals.insert(param.name.clone(), (ptr, param.ty.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate locals for DCL-S inside the proc.
|
// Allocate locals for DCL-S inside the proc.
|
||||||
for decl in &proc.locals {
|
for decl in &proc.locals {
|
||||||
self.gen_local_decl(decl, &mut state)?;
|
self.gen_local_decl(decl, &mut state)?;
|
||||||
@@ -431,25 +513,40 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
fn gen_local_decl(&mut self, decl: &Declaration, state: &mut FnState<'ctx>) -> Result<(), CodegenError> {
|
fn gen_local_decl(&mut self, decl: &Declaration, state: &mut FnState<'ctx>) -> Result<(), CodegenError> {
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Standalone(sd) => {
|
Declaration::Standalone(sd) => {
|
||||||
// Check if a DIM(n) keyword is present — if so we allocate a
|
// Check for DIM(n) or DIM(rows:cols) keywords.
|
||||||
// contiguous block of `n * elem_size` bytes.
|
let dim1 = sd.keywords.iter().find_map(|kw| {
|
||||||
let dim = sd.keywords.iter().find_map(|kw| {
|
|
||||||
if let VarKeyword::Dim(expr) = kw {
|
if let VarKeyword::Dim(expr) = kw {
|
||||||
const_int_from_expr(expr)
|
const_int_from_expr(expr)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let dim2 = sd.keywords.iter().find_map(|kw| {
|
||||||
|
if let VarKeyword::Dim2(r, c) = kw {
|
||||||
|
match (const_int_from_expr(r), const_int_from_expr(c)) {
|
||||||
|
(Some(rows), Some(cols)) => Some((rows, cols)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let ptr = if let Some(n) = dim {
|
let ptr = if let Some((rows, cols)) = dim2 {
|
||||||
// Array: allocate n elements of the element type.
|
// 2-D array: allocate rows*cols elements of the element type.
|
||||||
|
self.alloca_for_type_dim(&sd.ty, &sd.name, rows * cols)
|
||||||
|
} else if let Some(n) = dim1 {
|
||||||
|
// 1-D array: allocate n elements of the element type.
|
||||||
self.alloca_for_type_dim(&sd.ty, &sd.name, n)
|
self.alloca_for_type_dim(&sd.ty, &sd.name, n)
|
||||||
} else {
|
} else {
|
||||||
self.alloca_for_type(&sd.ty, &sd.name)
|
self.alloca_for_type(&sd.ty, &sd.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record the dimension so %Elem and indexing can use it.
|
// Record the dimension(s) so %Elem and indexing can use them.
|
||||||
if let Some(n) = dim {
|
if let Some((rows, cols)) = dim2 {
|
||||||
|
self.array_dims.insert(sd.name.clone(), rows * cols);
|
||||||
|
self.array_cols.insert(sd.name.clone(), cols);
|
||||||
|
} else if let Some(n) = dim1 {
|
||||||
self.array_dims.insert(sd.name.clone(), n);
|
self.array_dims.insert(sd.name.clone(), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,6 +652,81 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load a single element of type `ty` from `elem_ptr`, returning it sign-/zero-extended
|
||||||
|
/// to i64. Handles `Ind` (stored as i8, returned as i64 0/1), `Int`/`Uns` (sign-extended),
|
||||||
|
/// and falls back to returning the pointer itself for `Char` and other pointer-like types.
|
||||||
|
fn load_array_elem(
|
||||||
|
&self,
|
||||||
|
elem_ptr: PointerValue<'ctx>,
|
||||||
|
ty: &TypeSpec,
|
||||||
|
) -> Result<BasicValueEnum<'ctx>, CodegenError> {
|
||||||
|
let i64_t = self.context.i64_type();
|
||||||
|
match ty {
|
||||||
|
TypeSpec::Ind => {
|
||||||
|
// Stored as i8 (1 = *On, 0 = *Off).
|
||||||
|
let i8_t = self.context.i8_type();
|
||||||
|
let cast_ptr = self.builder.build_pointer_cast(
|
||||||
|
elem_ptr,
|
||||||
|
self.context.ptr_type(inkwell::AddressSpace::default()),
|
||||||
|
"ind_ptr_cast",
|
||||||
|
).unwrap_or(elem_ptr);
|
||||||
|
if let Ok(v) = self.builder.build_load(i8_t, cast_ptr, "ind_elem") {
|
||||||
|
let iv = v.into_int_value();
|
||||||
|
let ext = self.builder.build_int_z_extend(iv, i64_t, "ind_ext").unwrap_or(iv);
|
||||||
|
return Ok(ext.into());
|
||||||
|
}
|
||||||
|
Ok(i64_t.const_zero().into())
|
||||||
|
}
|
||||||
|
TypeSpec::Int(_) | TypeSpec::Uns(_) => {
|
||||||
|
let bytes = ty.byte_size().unwrap_or(8);
|
||||||
|
let int_ty = self.context.custom_width_int_type((bytes * 8) as u32);
|
||||||
|
let cast_ptr = self.builder.build_pointer_cast(
|
||||||
|
elem_ptr,
|
||||||
|
self.context.ptr_type(inkwell::AddressSpace::default()),
|
||||||
|
"int_ptr_cast",
|
||||||
|
).unwrap_or(elem_ptr);
|
||||||
|
if let Ok(v) = self.builder.build_load(int_ty, cast_ptr, "int_elem") {
|
||||||
|
let iv = v.into_int_value();
|
||||||
|
let ext = self.builder.build_int_s_extend(iv, i64_t, "int_ext").unwrap_or(iv);
|
||||||
|
return Ok(ext.into());
|
||||||
|
}
|
||||||
|
Ok(i64_t.const_zero().into())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Char / pointer-like — return the pointer itself.
|
||||||
|
Ok(elem_ptr.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a pointer to element `(row, col)` (both 1-based, RPG convention)
|
||||||
|
/// of a 2-D array stored in row-major order.
|
||||||
|
///
|
||||||
|
/// flat index = (row - 1) * cols + (col - 1)
|
||||||
|
fn array_elem_ptr_2d(
|
||||||
|
&self,
|
||||||
|
base_ptr: PointerValue<'ctx>,
|
||||||
|
row: inkwell::values::IntValue<'ctx>,
|
||||||
|
col: inkwell::values::IntValue<'ctx>,
|
||||||
|
cols: u64,
|
||||||
|
elem_size: u64,
|
||||||
|
) -> PointerValue<'ctx> {
|
||||||
|
let i64_t = self.context.i64_type();
|
||||||
|
let one = i64_t.const_int(1, false);
|
||||||
|
let cols_val = i64_t.const_int(cols, false);
|
||||||
|
let row0 = self.builder.build_int_sub(row, one, "row0").unwrap();
|
||||||
|
let col0 = self.builder.build_int_sub(col, one, "col0").unwrap();
|
||||||
|
let row_off = self.builder.build_int_mul(row0, cols_val, "row_off").unwrap();
|
||||||
|
let flat = self.builder.build_int_add(row_off, col0, "flat").unwrap();
|
||||||
|
let elem_bytes = i64_t.const_int(elem_size, false);
|
||||||
|
let byte_off = self.builder.build_int_mul(flat, elem_bytes, "byte_off2d").unwrap();
|
||||||
|
unsafe {
|
||||||
|
self.builder
|
||||||
|
.build_gep(self.context.i8_type(), base_ptr, &[byte_off], "elem_ptr_2d")
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn zero_init_var(&self, ptr: PointerValue<'ctx>, ty: &TypeSpec) -> Result<(), CodegenError> {
|
fn zero_init_var(&self, ptr: PointerValue<'ctx>, ty: &TypeSpec) -> Result<(), CodegenError> {
|
||||||
let size = ty.byte_size().unwrap_or(0);
|
let size = ty.byte_size().unwrap_or(0);
|
||||||
if size == 0 { return Ok(()); }
|
if size == 0 { return Ok(()); }
|
||||||
@@ -952,8 +1124,16 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
|
|
||||||
// If the LValue has an index (array assignment), compute the element pointer.
|
// If the LValue has an index (array assignment), compute the element pointer.
|
||||||
let dest_ptr = if let LValue::Index(_, indices) = &a.target {
|
let dest_ptr = if let LValue::Index(_, indices) = &a.target {
|
||||||
if let Some(idx_expr) = indices.first() {
|
|
||||||
let elem_size = ty.byte_size().unwrap_or(8);
|
let elem_size = ty.byte_size().unwrap_or(8);
|
||||||
|
if indices.len() >= 2 {
|
||||||
|
// 2-D subscript: name(row: col) — look up the column stride.
|
||||||
|
let cols = self.array_cols.get(name).copied().unwrap_or(1);
|
||||||
|
let row_val = self.gen_expression(&indices[0], state)?;
|
||||||
|
let col_val = self.gen_expression(&indices[1], state)?;
|
||||||
|
let row_i = self.coerce_to_i64(row_val);
|
||||||
|
let col_i = self.coerce_to_i64(col_val);
|
||||||
|
self.array_elem_ptr_2d(ptr, row_i, col_i, cols, elem_size)
|
||||||
|
} else if let Some(idx_expr) = indices.first() {
|
||||||
let idx_val = self.gen_expression(idx_expr, state)?;
|
let idx_val = self.gen_expression(idx_expr, state)?;
|
||||||
let idx_i = self.coerce_to_i64(idx_val);
|
let idx_i = self.coerce_to_i64(idx_val);
|
||||||
self.array_elem_ptr(ptr, idx_i, elem_size)
|
self.array_elem_ptr(ptr, idx_i, elem_size)
|
||||||
@@ -1323,8 +1503,6 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
Expression::Variable(qname) => {
|
Expression::Variable(qname) => {
|
||||||
let name = qname.leaf();
|
let name = qname.leaf();
|
||||||
if let Some((ptr, ty)) = self.resolve_var(name, state) {
|
if let Some((ptr, ty)) = self.resolve_var(name, state) {
|
||||||
let llvm_ty = self.type_spec_to_llvm(&ty)
|
|
||||||
.unwrap_or(BasicTypeEnum::IntType(i64_t));
|
|
||||||
match &ty {
|
match &ty {
|
||||||
TypeSpec::Int(_) | TypeSpec::Uns(_) => {
|
TypeSpec::Int(_) | TypeSpec::Uns(_) => {
|
||||||
// Use byte_size() to get the real storage width — the
|
// Use byte_size() to get the real storage width — the
|
||||||
@@ -1337,14 +1515,23 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
let ext = self.builder.build_int_s_extend(iv, i64_t, "sext").unwrap_or(iv);
|
let ext = self.builder.build_int_s_extend(iv, i64_t, "sext").unwrap_or(iv);
|
||||||
return Ok(ext.into());
|
return Ok(ext.into());
|
||||||
}
|
}
|
||||||
|
Err(CodegenError::new(format!("could not load variable '{}'", name)))
|
||||||
|
}
|
||||||
|
TypeSpec::Ind => {
|
||||||
|
// Stored as i8 (1 = *On, 0 = *Off); zero-extend to i64.
|
||||||
|
let i8_t = self.context.i8_type();
|
||||||
|
if let Ok(v) = self.builder.build_load(i8_t, ptr, name) {
|
||||||
|
let iv = v.into_int_value();
|
||||||
|
let ext = self.builder.build_int_z_extend(iv, i64_t, "ind_zext").unwrap_or(iv);
|
||||||
|
return Ok(ext.into());
|
||||||
|
}
|
||||||
|
Ok(i64_t.const_zero().into())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// For CHAR / other types, return the pointer itself.
|
// For CHAR / pointer-like types, return the pointer itself.
|
||||||
return Ok(ptr.into());
|
Ok(ptr.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = llvm_ty;
|
|
||||||
Err(CodegenError::new(format!("could not load variable '{}'", name)))
|
|
||||||
} else {
|
} else {
|
||||||
// Return 0 for unknown variables.
|
// Return 0 for unknown variables.
|
||||||
Ok(i64_t.const_zero().into())
|
Ok(i64_t.const_zero().into())
|
||||||
@@ -1407,33 +1594,32 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No function found — check if `name` is an array variable and
|
// No function found — check if `name` is an array variable and
|
||||||
// the call is actually a subscript read: name(idx).
|
// the call is actually a subscript read: name(idx) or name(row:col).
|
||||||
if let Some((ptr, ty)) = self.resolve_var(name, state) {
|
if let Some((ptr, ty)) = self.resolve_var(name, state) {
|
||||||
let elem_size = ty.byte_size().unwrap_or(8);
|
let elem_size = ty.byte_size().unwrap_or(8);
|
||||||
if let Some(Arg::Expr(idx_expr)) = args.first() {
|
|
||||||
|
// Collect up to two Expr arguments.
|
||||||
|
let exprs: Vec<&Expression> = args.iter()
|
||||||
|
.filter_map(|a| if let Arg::Expr(e) = a { Some(e) } else { None })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let elem_ptr = if exprs.len() >= 2 {
|
||||||
|
// 2-D subscript: name(row: col)
|
||||||
|
let cols = self.array_cols.get(name).copied().unwrap_or(1);
|
||||||
|
let row_val = self.gen_expression(exprs[0], state)?;
|
||||||
|
let col_val = self.gen_expression(exprs[1], state)?;
|
||||||
|
let row_i = self.coerce_to_i64(row_val);
|
||||||
|
let col_i = self.coerce_to_i64(col_val);
|
||||||
|
self.array_elem_ptr_2d(ptr, row_i, col_i, cols, elem_size)
|
||||||
|
} else if let Some(idx_expr) = exprs.first() {
|
||||||
let idx_val = self.gen_expression(idx_expr, state)?;
|
let idx_val = self.gen_expression(idx_expr, state)?;
|
||||||
let idx_i = self.coerce_to_i64(idx_val);
|
let idx_i = self.coerce_to_i64(idx_val);
|
||||||
let elem_ptr = self.array_elem_ptr(ptr, idx_i, elem_size);
|
self.array_elem_ptr(ptr, idx_i, elem_size)
|
||||||
match &ty {
|
} else {
|
||||||
TypeSpec::Int(_) | TypeSpec::Uns(_) => {
|
ptr
|
||||||
let bytes = ty.byte_size().unwrap_or(8);
|
};
|
||||||
let int_ty = self.context.custom_width_int_type((bytes * 8) as u32);
|
|
||||||
let cast_ptr = self.builder.build_pointer_cast(
|
return self.load_array_elem(elem_ptr, &ty);
|
||||||
elem_ptr,
|
|
||||||
self.context.ptr_type(AddressSpace::default()),
|
|
||||||
"call_elem_ptr_cast",
|
|
||||||
).unwrap_or(elem_ptr);
|
|
||||||
if let Ok(v) = self.builder.build_load(int_ty, cast_ptr, "call_elem") {
|
|
||||||
let iv = v.into_int_value();
|
|
||||||
let ext = self.builder
|
|
||||||
.build_int_s_extend(iv, i64_t, "call_elem_ext")
|
|
||||||
.unwrap_or(iv);
|
|
||||||
return Ok(ext.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Ok(elem_ptr.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(i64_t.const_zero().into())
|
Ok(i64_t.const_zero().into())
|
||||||
@@ -1446,35 +1632,29 @@ impl<'ctx> Codegen<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expression::Index(qname, indices) => {
|
Expression::Index(qname, indices) => {
|
||||||
// Array element read: name(i) — RPG uses 1-based indexing.
|
// Array element read: name(i) or name(row: col) — RPG uses 1-based indexing.
|
||||||
let name = qname.leaf();
|
let name = qname.leaf();
|
||||||
if let Some((ptr, ty)) = self.resolve_var(name, state) {
|
if let Some((ptr, ty)) = self.resolve_var(name, state) {
|
||||||
let elem_size = ty.byte_size().unwrap_or(8);
|
let elem_size = ty.byte_size().unwrap_or(8);
|
||||||
if let Some(idx_expr) = indices.first() {
|
|
||||||
|
let elem_ptr = if indices.len() >= 2 {
|
||||||
|
// 2-D subscript: name(row: col)
|
||||||
|
let cols = self.array_cols.get(name).copied().unwrap_or(1);
|
||||||
|
let row_val = self.gen_expression(&indices[0], state)?;
|
||||||
|
let col_val = self.gen_expression(&indices[1], state)?;
|
||||||
|
let row_i = self.coerce_to_i64(row_val);
|
||||||
|
let col_i = self.coerce_to_i64(col_val);
|
||||||
|
self.array_elem_ptr_2d(ptr, row_i, col_i, cols, elem_size)
|
||||||
|
} else if let Some(idx_expr) = indices.first() {
|
||||||
let idx_val = self.gen_expression(idx_expr, state)?;
|
let idx_val = self.gen_expression(idx_expr, state)?;
|
||||||
let idx_i = self.coerce_to_i64(idx_val);
|
let idx_i = self.coerce_to_i64(idx_val);
|
||||||
let elem_ptr = self.array_elem_ptr(ptr, idx_i, elem_size);
|
self.array_elem_ptr(ptr, idx_i, elem_size)
|
||||||
// Load the element with the element's integer type.
|
} else {
|
||||||
match &ty {
|
ptr
|
||||||
TypeSpec::Int(_) | TypeSpec::Uns(_) => {
|
};
|
||||||
let bytes = ty.byte_size().unwrap_or(8);
|
|
||||||
let int_ty = self.context.custom_width_int_type((bytes * 8) as u32);
|
// Load the element with the appropriate type.
|
||||||
let cast_ptr = self.builder.build_pointer_cast(
|
return self.load_array_elem(elem_ptr, &ty);
|
||||||
elem_ptr,
|
|
||||||
self.context.ptr_type(AddressSpace::default()),
|
|
||||||
"elem_ptr_cast",
|
|
||||||
).unwrap_or(elem_ptr);
|
|
||||||
if let Ok(v) = self.builder.build_load(int_ty, cast_ptr, "elem") {
|
|
||||||
let iv = v.into_int_value();
|
|
||||||
let ext = self.builder.build_int_s_extend(iv, i64_t, "sext").unwrap_or(iv);
|
|
||||||
return Ok(ext.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Ok(elem_ptr.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(i64_t.const_zero().into())
|
Ok(i64_t.const_zero().into())
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/lower.rs
13
src/lower.rs
@@ -1352,9 +1352,18 @@ impl Parser {
|
|||||||
self.advance(); // KwDim
|
self.advance(); // KwDim
|
||||||
if self.peek() == &Token::LParen {
|
if self.peek() == &Token::LParen {
|
||||||
self.advance(); // (
|
self.advance(); // (
|
||||||
if let Ok(expr) = self.parse_expression() {
|
if let Ok(rows) = self.parse_expression() {
|
||||||
|
if self.eat(&Token::Colon) {
|
||||||
|
// DIM(rows: cols) — 2-D array
|
||||||
|
if let Ok(cols) = self.parse_expression() {
|
||||||
self.eat(&Token::RParen);
|
self.eat(&Token::RParen);
|
||||||
return VarKeyword::Dim(expr);
|
return VarKeyword::Dim2(rows, cols);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// DIM(n) — 1-D array
|
||||||
|
self.eat(&Token::RParen);
|
||||||
|
return VarKeyword::Dim(rows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.eat(&Token::RParen);
|
self.eat(&Token::RParen);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user