use super::{Lower, LowerError, R};
use crate::layout::{Binding, Section};
use rt::lir::{self, GlobalAt, RangeKind};
use syntax::Pos;
use syntax::ast::DataEntry;
fn ordinal(n: usize) -> R<u16> {
u16::try_from(n).map_err(|_| LowerError::Exceeds("LINKAGE records", Pos::default()))
}
fn file_index(k: usize) -> R<u16> {
u16::try_from(k).map_err(|_| LowerError::Exceeds("files", Pos::default()))
}
fn global_names(entries: &[DataEntry]) -> impl Iterator<Item = &str> {
entries.iter().filter(|e| matches!(e.level, 1 | 77) && e.global).filter_map(|e| e.name.as_deref())
}
impl Lower<'_> {
pub(super) fn scope(&mut self) -> R<lir::Scope> {
let (layout, program) = (self.layout, self.program);
let containers = program.containers.iter().map(|c| self.sym(&c.id)).collect();
let mut records = Vec::new();
for (n, binding) in layout.bindings.iter().enumerate() {
let binding = match binding {
Binding::Argument => continue,
Binding::External { name, size } => lir::Binding::External { name: self.sym(name), size: *size },
Binding::ExternalFile(k) => lir::Binding::ExternalFile(*k),
Binding::Global { program, record, section } => {
let (section, name) = match section {
Section::WorkingStorage => (lir::Section::WorkingStorage, record),
Section::LocalStorage => (lir::Section::LocalStorage, record),
Section::Linkage => (lir::Section::Linkage, record),
Section::File(file) => (lir::Section::File, file),
};
lir::Binding::Global { program: self.sym(program), section, name: self.sym(name) }
}
};
records.push((ordinal(n)?, binding));
}
let mut files = Vec::new();
for (k, f) in program.files.iter().enumerate().filter(|(_, f)| f.external || f.declared_in.is_some()) {
let declared_in = f.declared_in.as_deref().map(|d| self.sym(d));
files.push(lir::SharedFile { file: file_index(k)?, external: f.external, declared_in });
}
let mut areas = Vec::new();
for (k, area) in layout.bound_areas.iter().enumerate() {
if let Some(record) = area {
areas.push((file_index(k)?, *record));
}
}
let mut scope = lir::Scope { containers, records, files, areas, ..lir::Scope::default() };
if !program.nested.is_empty() {
scope.globals = self.globals()?;
let table = &self.c.declaratives;
for (k, span) in table.global_files.iter().enumerate() {
if let Some(span) = span {
scope.global_files.push((file_index(k)?, self.span_range(*span, RangeKind::UseProcedure)?));
}
}
for (mode, span) in scope.global_modes.iter_mut().zip(table.global_modes) {
*mode = span.map(|s| self.span_range(s, RangeKind::UseProcedure)).transpose()?;
}
}
Ok(scope)
}
fn globals(&mut self) -> R<Vec<lir::Global>> {
let (layout, program) = (self.layout, self.program);
let root = |name: &str, local: bool| {
layout.items.iter().find(|i| i.parent.is_none() && i.linkage.is_none() && i.file.is_none() && i.local == local && i.name.as_deref() == Some(name))
};
let mut found = Vec::new();
for (entries, local) in [(&program.working_storage, false), (&program.local_storage, true)] {
for name in global_names(entries) {
if let Some(item) = root(name, local) {
let (section, at) = if local { (lir::Section::LocalStorage, GlobalAt::Local(item.offset)) } else { (lir::Section::WorkingStorage, GlobalAt::Program(item.offset)) };
found.push((section, name, at));
}
}
}
for name in global_names(&program.linkage) {
if let Some(o) = layout.linkage_roots.iter().position(|&i| layout.items[i].name.as_deref() == Some(name)) {
found.push((lir::Section::Linkage, name, GlobalAt::Linkage(ordinal(o)?)));
}
}
for f in program.files.iter().filter(|f| f.global && f.declared_in.is_none()) {
let Some(j) = program.files.iter().position(|g| g.name == f.name) else { continue };
let at = match layout.bound_areas[j] {
Some(record) => GlobalAt::Linkage(record),
None => GlobalAt::Program(layout.file_areas[j].0),
};
found.push((lir::Section::File, &f.name, at));
}
Ok(found.into_iter().map(|(section, name, at)| lir::Global { section, name: self.sym(name), at }).collect())
}
}