use super::{CodecTarget, Constant, Handle, Handoff, Routine, Scalar};
use crate::adapter::ada83::{ada83_map_entry_name, component_name};
use crate::default_common::resolve_alias;
use crate::encode::EncodeError;
use zerodds_ast::{Ast, MemberNode, PrimitiveKind, TypeKind, TypeNode};
use zerodds_ast_core::Id;
use zerodds_lang_cst::ada83;
use zerodds_lang_cst::common::{Doc, Ident, Literal, Path};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ada83;
const RUNTIME: &str = "Zd_Wire83";
const SIGNED_CHAR: &str = "signed char";
const OWN_SAMPLE: &str = "Zd83_Sample";
const OWN_ARRAY: &str = "Zd83_Own_Array";
const RECEIVERLESS: &[&str] = &["bitfield", "trim_utf8", "trim_utf16"];
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdaType {
pub mark: String,
pub is_bool: bool,
pub is_union: bool,
pub own_array: Option<String>,
pub counts: bool,
}
impl AdaType {
fn bare(mark: &str) -> Self {
Self {
mark: mark.to_string(),
is_bool: false,
is_union: false,
own_array: None,
counts: false,
}
}
}
#[derive(Debug, Clone)]
pub enum AExpr {
Plain(ada83::Expr),
OptionalSlot(ada83::Expr),
Discriminant(ada83::Expr),
RuntimeEnum(ada83::Expr),
ReferenceSlot {
place: ada83::Expr,
mark: Path,
},
Assignment {
place: Box<AExpr>,
value: ada83::Expr,
},
}
fn inner(expr: AExpr) -> ada83::Expr {
match expr {
AExpr::Plain(e)
| AExpr::OptionalSlot(e)
| AExpr::Discriminant(e)
| AExpr::RuntimeEnum(e) => e,
AExpr::ReferenceSlot { place, .. } => place,
AExpr::Assignment { place, value } => ada83::Expr::Binary {
op: "=",
lhs: Box::new(inner(*place)),
rhs: Box::new(value),
},
}
}
fn plain(e: ada83::Expr) -> AExpr {
AExpr::Plain(e)
}
fn pattern_literal(value: i128) -> ada83::Expr {
const WIDTH: i128 = 1 << 64;
if value > i128::from(i64::MAX) && value <= i128::from(u64::MAX) {
return ada83::Expr::int(value - WIDTH);
}
ada83::Expr::int(value)
}
const fn bitwise_binary(op: &str) -> Option<&'static str> {
match op.as_bytes() {
b"&" => Some("Bits_And"),
b"|" => Some("Bits_Or"),
b"^" => Some("Bits_Xor"),
b">>" => Some("Shift_Right"),
b"<<" => Some("Shift_Left"),
_ => None,
}
}
fn mark_path(mark: &str) -> Path {
Path::new(&mark.split('.').collect::<Vec<&str>>())
}
impl Ada83 {
fn runtime_path(name: &str) -> Path {
Path::new(&[RUNTIME, name])
}
fn runtime_constant(name: &str) -> AExpr {
AExpr::RuntimeEnum(ada83::Expr::Name(Self::runtime_path(name)))
}
fn operation(method: &str) -> String {
ada83::ada_case(method)
}
fn selected(value: ada83::Expr, name: &str) -> ada83::Expr {
ada83::Expr::Selected {
value: Box::new(value),
name: Ident::new(name),
}
}
fn call_path(path: Path, args: Vec<ada83::Expr>) -> ada83::Expr {
ada83::Expr::Call { name: path, args }
}
fn narrow_integer(method: &str, arguments: &[ada83::Expr]) -> Option<ada83::Expr> {
let (width, signed) = match method {
"write_i8" | "read_i8" => (1, true),
"write_u8" | "read_u8" => (1, false),
"write_i16" | "read_i16" => (2, true),
"write_u16" | "read_u16" => (2, false),
"write_i32" | "read_i32" => (4, true),
"write_u32" | "read_u32" => (4, false),
_ => return None,
};
let bits = width * 8;
let (low, high) = if signed {
(-(1_i64 << (bits - 1)), (1_i64 << (bits - 1)) - 1)
} else {
(0, (1_i64 << bits) - 1)
};
let number = |value: i64| ada83::Expr::int(i128::from(value));
if method.starts_with("read_") {
let name = if signed { "Read_Int" } else { "Read_Uint" };
let mut args = arguments.to_vec();
args.push(number(width));
return Some(Self::call_path(Self::runtime_path(name), args));
}
let mut args = arguments.to_vec();
args.push(number(width));
args.push(number(low));
args.push(number(high));
Some(Self::call_path(Self::runtime_path("Write_Int"), args))
}
fn to_natural(value: ada83::Expr) -> ada83::Expr {
ada83::Expr::Convert {
mark: Path::single("Natural"),
value: Box::new(value),
}
}
fn engaged(value: ada83::Expr) -> ada83::Expr {
ada83::Expr::Aggregate(vec![
ada83::Association {
choice: Ident::new(crate::adapter::ada83::PRESENT),
value: ada83::Expr::Literal(Literal::Bool(true)),
},
ada83::Association {
choice: Ident::new(crate::adapter::ada83::VALUE),
value,
},
])
}
fn discriminant_record(place: ada83::Expr) -> ada83::Expr {
match place {
ada83::Expr::Selected { value, .. } => *value,
other => other,
}
}
fn assign_discriminant(place: ada83::Expr, value: ada83::Expr) -> ada83::Stmt {
ada83::Stmt::Declare {
vars: vec![ada83::VarDecl {
name: Ident::new("zd_nd"),
mark: Path::single(OWN_SAMPLE),
constraint: Some(value),
value: None,
}],
body: vec![ada83::Stmt::Assign {
place: Self::discriminant_record(place),
value: ada83::Expr::name("zd_nd"),
}],
}
}
}
impl CodecTarget for Ada83 {
type Expr = AExpr;
type Stmt = ada83::Stmt;
type Block = Vec<ada83::Stmt>;
type Type = AdaType;
type Decl = ada83::Decl;
fn scalar(kind: Scalar) -> Self::Type {
match kind {
Scalar::Bool => AdaType {
is_bool: true,
..AdaType::bare("Boolean")
},
Scalar::Float => AdaType::bare("Float"),
Scalar::Double => AdaType::bare("Long_Float"),
Scalar::LongDouble => AdaType::bare("Zd_Wire83.Text"),
Scalar::Index | Scalar::Size => AdaType::bare("Natural"),
Scalar::UInt32 => AdaType {
counts: true,
..AdaType::bare("Long_Integer")
},
_ => AdaType::bare("Long_Integer"),
}
}
fn named(name: &str) -> Self::Type {
AdaType::bare(name)
}
fn nothing() -> Self::Type {
AdaType::bare("Zd83_No_Type")
}
fn handle(kind: Handle) -> Self::Type {
AdaType::bare(match kind {
Handle::DHeader => "Zd_Wire83.Dheader",
Handle::EmHeader => "Zd_Wire83.Emheader",
Handle::Pid => "Zd_Wire83.Pid_Write",
Handle::TryConstruct => "Zd_Wire83.Try_Construct",
Handle::ReaderMark => "Zd_Wire83.Reader_Mark",
Handle::PidRead => "Zd_Wire83.Pid_Read",
Handle::EmHeaderRead => "Zd_Wire83.Emheader_Read",
})
}
fn code_point(value: Self::Expr, _narrow: bool) -> Self::Expr {
value
}
fn constant(which: Constant) -> Self::Expr {
match which {
Constant::WcharWidth2 => plain(ada83::Expr::int(2)),
Constant::WcharWidth4 => plain(ada83::Expr::int(4)),
Constant::WcharFixed => Self::runtime_constant("Wchar_Fixed"),
Constant::WcharGiopLengthPrefixed => {
Self::runtime_constant("Wchar_Giop_Length_Prefixed")
}
Constant::WstringUnitsStream => Self::runtime_constant("Unit_Stream"),
Constant::WstringUnitsBigEndianAlways => {
Self::runtime_constant("Unit_Big_Endian_Always")
}
Constant::WstringUnitsByteOrderMark => Self::runtime_constant("Unit_Byte_Order_Mark"),
Constant::WstringUnitsHostUnswapped => Self::runtime_constant("Unit_Host_Unswapped"),
Constant::WstringSpecification => Self::runtime_constant("Wstr_Octets_Unterminated_2"),
Constant::WstringCharacterCount => {
Self::runtime_constant("Wstr_Characters_Unterminated_2")
}
Constant::WstringCharacterCountTerminated => {
Self::runtime_constant("Wstr_Characters_Terminated_4")
}
Constant::WstringSpecificationWideUnits => {
Self::runtime_constant("Wstr_Octets_Unterminated_4")
}
Constant::WstringRosTypeSupport => {
Self::runtime_constant("Wstr_Characters_Unterminated_4")
}
Constant::WstringOctetsTerminated2 => {
Self::runtime_constant("Wstr_Octets_Terminated_2")
}
Constant::WstringOctetsTerminated4 => {
Self::runtime_constant("Wstr_Octets_Terminated_4")
}
Constant::WstringCharactersTerminated2 => {
Self::runtime_constant("Wstr_Characters_Terminated_2")
}
Constant::FixedDeclared => Self::runtime_constant("Fixed_Declared"),
Constant::FixedValuePrecision => Self::runtime_constant("Fixed_Value_Precision"),
Constant::FixedValueStringDigits => Self::runtime_constant("Fixed_Value_String_Digits"),
Constant::ParameterMember => Self::runtime_constant("Role_Member"),
Constant::ParameterOptional => Self::runtime_constant("Role_Optional"),
Constant::TryConstructDiscard => Self::runtime_constant("Tc_Discard"),
Constant::TryConstructTrim => Self::runtime_constant("Tc_Trim"),
Constant::TryConstructUseDefault => Self::runtime_constant("Tc_Use_Default"),
Constant::StatusOk => Self::runtime_constant("Status_Ok"),
Constant::StatusDiscarded => Self::runtime_constant("Status_Discarded"),
Constant::StatusImpossibleLength => Self::runtime_constant("Status_Impossible_Length"),
Constant::StatusOutOfRange => Self::runtime_constant("Status_Out_Of_Range"),
Constant::StatusTruncated => Self::runtime_constant("Status_Truncated"),
}
}
fn unsigned_holder(_bits: u16) -> Self::Type {
AdaType::bare("Long_Integer")
}
fn carries_hdds_axes() -> bool {
true
}
fn value_type(ast: &Ast, id: Id<TypeNode>) -> Self::Type {
let mark = match crate::adapter::ada83::ada83_value_path(ast, id) {
Ok(path) => path
.segments()
.iter()
.map(Ident::as_str)
.collect::<Vec<&str>>()
.join("."),
Err(_) => "Zd83_No_Ada_Form".to_string(),
};
let resolved = resolve_alias(ast, id);
let kind = ast.type_(resolved).map(|node| node.kind.clone());
AdaType {
mark,
is_bool: matches!(kind, Ok(TypeKind::Primitive(PrimitiveKind::Boolean))),
is_union: matches!(kind, Ok(TypeKind::Union { .. })),
own_array: match kind {
Ok(TypeKind::Sequence { .. } | TypeKind::Map { .. }) => Some(format!(
"{}_Array",
crate::adapter::ada83::ada83_type_name(ast, resolved)
)),
_ => None,
},
counts: false,
}
}
fn type_name(ast: &Ast, id: Id<TypeNode>) -> String {
crate::adapter::ada83::type_tag(ast, id)
}
fn map_entry_name(ast: &Ast, id: Id<TypeNode>) -> String {
ada83_map_entry_name(ast, id)
}
fn module_prefix(_ast: &Ast, _id: Id<TypeNode>) -> String {
String::new()
}
fn escape(name: &str) -> String {
component_name(name)
}
fn inherited_path(depth: usize) -> Option<String> {
Some(format!("{}.", component_name("parent")).repeat(depth))
}
fn name(text: &str) -> Self::Expr {
plain(ada83::Expr::name(local_name(text)))
}
fn int(value: i128) -> Self::Expr {
plain(ada83::Expr::int(value))
}
fn literal(value: Literal) -> Self::Expr {
let Literal::Int(text) = &value else {
return plain(ada83::Expr::Literal(value));
};
match text.as_str() {
"true" => plain(ada83::Expr::Literal(Literal::Bool(true))),
"false" => plain(ada83::Expr::Literal(Literal::Bool(false))),
_ => plain(ada83::Expr::Literal(value)),
}
}
fn wide_int(value: i128) -> Self::Expr {
plain(pattern_literal(value))
}
fn unsigned_int(value: i128) -> Self::Expr {
plain(pattern_literal(value))
}
fn wide_unsigned(value: u64) -> Self::Expr {
plain(pattern_literal(i128::from(value)))
}
fn wide_signed(value: i128) -> Self::Expr {
plain(pattern_literal(value))
}
fn member(base: &str, field: &str) -> Self::Expr {
if field == "_d" {
return AExpr::Discriminant(Self::selected(
ada83::Expr::name(local_name(base)),
crate::adapter::ada83::DISCRIMINANT,
));
}
plain(Self::selected(ada83::Expr::name(local_name(base)), field))
}
fn whole(base: &str) -> Self::Expr {
plain(ada83::Expr::name(local_name(base)))
}
fn field_of(value: Self::Expr, field: &str) -> Self::Expr {
plain(Self::selected(inner(value), field))
}
fn header_field(value: Self::Expr, field: &str) -> Self::Expr {
let name = if field == "end" {
"End_Flag".to_string()
} else {
ada83::ada_case(field)
};
plain(Self::selected(inner(value), &name))
}
fn index(value: Self::Expr, at: Self::Expr) -> Self::Expr {
plain(ada83::Expr::Indexed {
value: Box::new(inner(value)),
index: Box::new(inner(at)),
})
}
fn count_of(base: &str, fixed: Option<u64>) -> Self::Expr {
match fixed {
Some(bound) => plain(ada83::Expr::int(i128::from(bound))),
None => plain(ada83::Expr::Convert {
mark: mark_path("Long_Integer"),
value: Box::new(ada83::Expr::Attribute {
value: Box::new(ada83::Expr::Deref(Box::new(ada83::Expr::name(local_name(
base,
))))),
name: Ident::new("Length"),
args: Vec::new(),
}),
}),
}
}
fn elements(base: &str) -> Self::Expr {
plain(ada83::Expr::name(local_name(base)))
}
fn case_member(base: &str, field: &str) -> Self::Expr {
Self::member(base, field)
}
fn entry_half(entry: Self::Expr, key: bool) -> Self::Expr {
plain(Self::selected(
inner(entry),
if key {
"Key"
} else {
crate::adapter::ada83::VALUE
},
))
}
fn absent() -> Self::Expr {
plain(ada83::Expr::Literal(Literal::Null))
}
fn materialised(_value: Self::Expr) -> Option<Self::Expr> {
None
}
fn optional_value(_ast: &Ast, _id: Id<TypeNode>, base: &str, field: &str) -> Self::Expr {
plain(Self::selected(
inner(Self::member(base, field)),
crate::adapter::ada83::VALUE,
))
}
fn present(base: &str, field: &str) -> Self::Expr {
plain(Self::selected(
inner(Self::member(base, field)),
crate::adapter::ada83::PRESENT,
))
}
fn holds_by_reference(
ast: &Ast,
aggregate: Id<TypeNode>,
member_type: Id<TypeNode>,
optional: bool,
external: bool,
) -> bool {
crate::adapter::ada83::held_by_reference(ast, aggregate, member_type, optional, external)
}
fn reference_present(base: &str, field: &str) -> Self::Expr {
Self::binary(
"!=",
Self::member(base, field),
plain(ada83::Expr::Literal(Literal::Null)),
)
}
fn reference_value(_ast: &Ast, _id: Id<TypeNode>, base: &str, field: &str) -> Self::Expr {
plain(ada83::Expr::Deref(Box::new(inner(Self::member(
base, field,
)))))
}
fn reference_destination(
ast: &Ast,
id: Id<TypeNode>,
base: &str,
field: &str,
) -> (Vec<Self::Stmt>, Self::Expr) {
let designated = crate::adapter::ada83::reference_designated(ast, id).unwrap_or(id);
(
Vec::new(),
AExpr::ReferenceSlot {
place: inner(Self::member(base, field)),
mark: Path::single(&crate::adapter::ada83::ada83_type_name(ast, designated)),
},
)
}
fn reference_absent(base: &str, field: &str) -> Vec<Self::Stmt> {
vec![ada83::Stmt::Assign {
place: inner(Self::member(base, field)),
value: ada83::Expr::Literal(Literal::Null),
}]
}
fn set_present(base: &str, field: &str, present: bool) -> Vec<Self::Stmt> {
if present {
return Vec::new();
}
vec![ada83::Stmt::Assign {
place: inner(Self::member(base, field)),
value: ada83::Expr::Aggregate(vec![ada83::Association {
choice: Ident::new(crate::adapter::ada83::PRESENT),
value: ada83::Expr::Literal(Literal::Bool(false)),
}]),
}]
}
fn optional_destination(base: &str, field: &str) -> (Vec<Self::Stmt>, Self::Expr) {
(
Vec::new(),
AExpr::OptionalSlot(inner(Self::member(base, field))),
)
}
fn binary(op: &'static str, lhs: Self::Expr, rhs: Self::Expr) -> Self::Expr {
if op == "=" {
return AExpr::Assignment {
place: Box::new(lhs),
value: inner(rhs),
};
}
if (op == "==" || op == "!=")
&& (matches!(lhs, AExpr::RuntimeEnum(_)) || matches!(rhs, AExpr::RuntimeEnum(_)))
{
let call =
Self::call_path(Path::new(&[RUNTIME, "\"=\""]), vec![inner(lhs), inner(rhs)]);
if op == "!=" {
return plain(ada83::Expr::Unary {
op: "not",
value: Box::new(call),
});
}
return plain(call);
}
if let Some(name) = bitwise_binary(op) {
let rhs = if op == ">>" || op == "<<" {
Self::to_natural(inner(rhs))
} else {
inner(rhs)
};
return plain(Self::call_path(
Self::runtime_path(name),
vec![inner(lhs), rhs],
));
}
plain(ada83::Expr::Binary {
op,
lhs: Box::new(inner(lhs)),
rhs: Box::new(inner(rhs)),
})
}
fn unary(op: &'static str, value: Self::Expr) -> Self::Expr {
if op == "~" {
return plain(Self::call_path(
Self::runtime_path("Bits_Not"),
vec![inner(value)],
));
}
plain(ada83::Expr::Unary {
op,
value: Box::new(inner(value)),
})
}
fn paren(value: Self::Expr) -> Self::Expr {
value
}
fn cast(type_: Self::Type, value: Self::Expr) -> Self::Expr {
if type_.mark == SIGNED_CHAR {
return plain(Self::call_path(
Self::runtime_path("As_Signed"),
vec![inner(value)],
));
}
if type_.is_bool {
if let AExpr::Plain(ada83::Expr::Literal(Literal::Int(text))) = &value {
return plain(ada83::Expr::Literal(Literal::Bool(text != "0")));
}
}
plain(ada83::Expr::Convert {
mark: mark_path(&type_.mark),
value: Box::new(inner(value)),
})
}
fn handoff(value: Self::Expr, _form: Handoff) -> Self::Expr {
value
}
fn call(callee: &str, args: Vec<Self::Expr>) -> Self::Expr {
let arguments: Vec<ada83::Expr> = args.into_iter().map(inner).collect();
if callee == "strcmp" {
return plain(Self::call_path(Self::runtime_path("Strcmp"), arguments));
}
plain(Self::call_path(Path::single(callee), arguments))
}
fn runtime(receiver: &str, method: &str, args: Vec<Self::Expr>) -> Self::Expr {
let mut arguments: Vec<ada83::Expr> = if RECEIVERLESS.contains(&method) {
Vec::new()
} else {
vec![ada83::Expr::name(receiver)]
};
arguments.extend(args.into_iter().map(inner));
if let Some(call) = Self::narrow_integer(method, &arguments) {
return plain(call);
}
plain(Self::call_path(
Self::runtime_path(&Self::operation(method)),
arguments,
))
}
fn runtime_static(method: &str, args: Vec<Self::Expr>) -> Self::Expr {
plain(Self::call_path(
Self::runtime_path(&Self::operation(method)),
args.into_iter().map(inner).collect(),
))
}
fn unsigned_less(octets: u8, left: Self::Expr, right: Self::Expr) -> Self::Expr {
if octets < 8 {
return Self::binary("<", left, right);
}
Self::runtime_static("u64_less", vec![left, right])
}
fn bit_pattern(source: super::BitPatternSource, value: Self::Expr) -> Self::Expr {
match source {
super::BitPatternSource::Int { octets } => Self::runtime_static(
"bit_pattern_int",
vec![value, Self::int(i128::from(octets))],
),
super::BitPatternSource::Bool => Self::runtime_static("bit_pattern_bool", vec![value]),
super::BitPatternSource::Float32 => {
Self::runtime_static("bit_pattern_f32", vec![value])
}
super::BitPatternSource::Float64 => {
Self::runtime_static("bit_pattern_f64_overlay", vec![value])
}
}
}
fn declare(type_: Self::Type, name: &str, value: Option<Self::Expr>) -> Self::Stmt {
let counts = type_.counts
&& type_.mark == "Long_Integer"
&& matches!(
&value,
Some(AExpr::Plain(ada83::Expr::Literal(Literal::Int(_))))
);
ada83::Stmt::Var(ada83::VarDecl {
name: Ident::new(local_name(name)),
mark: mark_path(if counts { "Natural" } else { &type_.mark }),
constraint: None,
value: value.map(inner),
})
}
fn perform(value: Self::Expr) -> Self::Stmt {
match value {
AExpr::Assignment { place, value } => match *place {
AExpr::OptionalSlot(field) => ada83::Stmt::Assign {
place: field,
value: Self::engaged(value),
},
AExpr::Discriminant(field) => Self::assign_discriminant(field, value),
other => ada83::Stmt::Assign {
place: inner(other),
value,
},
},
other => ada83::Stmt::Call(inner(other)),
}
}
fn give_back(value: Option<Self::Expr>) -> Self::Stmt {
ada83::Stmt::Return(Some(match value {
Some(value) => inner(value),
None => ada83::Expr::name("v"),
}))
}
fn when(
condition: Self::Expr,
then: Self::Block,
otherwise: Option<Self::Block>,
) -> Self::Stmt {
ada83::Stmt::If {
condition: inner(condition),
then,
otherwise: otherwise.filter(|body| !body.is_empty()),
}
}
fn count_up(counter: &str, bound: Self::Expr, body: Self::Block) -> Self::Stmt {
ada83::Stmt::For {
counter: Ident::new(local_name(counter)),
low: ada83::Expr::int(0),
high: ada83::Expr::Binary {
op: "-",
lhs: Box::new(Self::to_natural(inner(bound))),
rhs: Box::new(ada83::Expr::int(1)),
},
body,
}
}
fn until(condition: Self::Expr, body: Self::Block) -> Self::Stmt {
ada83::Stmt::While {
condition: inner(condition),
body,
}
}
fn leave() -> Self::Stmt {
ada83::Stmt::Exit
}
fn note(doc: Doc) -> Self::Stmt {
ada83::Stmt::Comment(doc)
}
fn drop_value(value: Self::Expr) -> Self::Stmt {
ada83::Stmt::Call(Self::call_path(
Self::runtime_path("Discard"),
vec![inner(value)],
))
}
fn mark_used(_name: &str) -> Vec<Self::Stmt> {
Vec::new()
}
fn read_call(
callee: &str,
place: Self::Expr,
_form: Handoff,
behaviour: Self::Expr,
) -> Self::Stmt {
let decoded = Self::call_path(
Path::single(callee),
vec![ada83::Expr::name("r"), inner(behaviour)],
);
match place {
AExpr::OptionalSlot(field) => ada83::Stmt::Assign {
place: field,
value: Self::engaged(decoded),
},
AExpr::Discriminant(field) => Self::assign_discriminant(field, decoded),
AExpr::ReferenceSlot { place, mark } => ada83::Stmt::Assign {
place,
value: ada83::Expr::New {
mark,
init: ada83::AllocInit::Qualified(Box::new(decoded)),
},
},
other => ada83::Stmt::Assign {
place: inner(other),
value: decoded,
},
}
}
fn read_forward(callee: &str) -> Self::Stmt {
ada83::Stmt::Assign {
place: ada83::Expr::name("v"),
value: Self::call_path(
Path::single(callee),
vec![ada83::Expr::name("r"), ada83::Expr::name("zd_tc")],
),
}
}
fn block(statements: Vec<Self::Stmt>) -> Self::Block {
statements
}
fn into_statements(block: Self::Block) -> Vec<Self::Stmt> {
block
}
fn make_room(
base: &str,
_element: Self::Type,
count: Self::Expr,
_assigned_whole: bool,
fill: Vec<Self::Stmt>,
) -> Vec<Self::Stmt> {
let mut out = vec![ada83::Stmt::Assign {
place: ada83::Expr::name(local_name(base)),
value: ada83::Expr::New {
mark: Path::single(OWN_ARRAY),
init: ada83::AllocInit::Range {
low: Box::new(ada83::Expr::int(0)),
high: Box::new(ada83::Expr::Binary {
op: "-",
lhs: Box::new(Self::to_natural(inner(count))),
rhs: Box::new(ada83::Expr::int(1)),
}),
},
},
}];
out.extend(fill);
out
}
fn keep_first(base: &str, count: Self::Expr) -> Vec<Self::Stmt> {
vec![ada83::Stmt::Assign {
place: ada83::Expr::name(local_name(base)),
value: ada83::Expr::New {
mark: Path::single(OWN_ARRAY),
init: ada83::AllocInit::Qualified(Box::new(ada83::Expr::Slice {
value: Box::new(ada83::Expr::Deref(Box::new(ada83::Expr::name(local_name(
base,
))))),
low: Box::new(ada83::Expr::int(0)),
high: Box::new(ada83::Expr::Binary {
op: "-",
lhs: Box::new(Self::to_natural(inner(count))),
rhs: Box::new(ada83::Expr::int(1)),
}),
})),
},
}]
}
fn sequence_element(
base: &str,
index: Self::Expr,
_element: Self::Type,
) -> (Vec<Self::Stmt>, Self::Expr, Vec<Self::Stmt>) {
(
Vec::new(),
Self::index(Self::elements(base), index),
Vec::new(),
)
}
fn routine(spec: Routine<Self>) -> Option<Self::Decl> {
let mut params = vec![ada83::Param {
name: Ident::new(if spec.writing { "w" } else { "r" }),
mode: ada83::Mode::In,
type_: Self::runtime_path(if spec.writing {
"Writer_Access"
} else {
"Reader_Access"
}),
}];
if spec.writing {
params.push(ada83::Param {
name: Ident::new("v"),
mode: ada83::Mode::In,
type_: mark_path(&spec.sample.mark),
});
}
for (type_, name) in &spec.extra {
params.push(ada83::Param {
name: Ident::new(local_name(name)),
mode: ada83::Mode::In,
type_: mark_path(&type_.mark),
});
}
let profile = ada83::Subprogram {
doc: spec.doc.clone(),
kind: if spec.writing {
ada83::SubprogramKind::Procedure
} else {
ada83::SubprogramKind::Function
},
name: Ident::new(&spec.name),
params,
result: if spec.writing {
None
} else {
Some(mark_path(&spec.sample.mark))
},
};
let Some(body) = spec.body else {
return Some(ada83::Decl::Subprogram(profile));
};
if spec.writing {
return Some(ada83::Decl::SubprogramBody(ada83::SubprogramBody {
spec: profile,
body,
}));
}
let mut statements = Vec::new();
if spec.sample.is_union && names_mark(&body, OWN_SAMPLE) {
statements.push(ada83::Stmt::Subtype {
name: Ident::new(OWN_SAMPLE),
mark: mark_path(&spec.sample.mark),
});
}
if let Some(array) = &spec.sample.own_array {
if names_mark(&body, OWN_ARRAY) {
statements.push(ada83::Stmt::Subtype {
name: Ident::new(OWN_ARRAY),
mark: Path::single(array),
});
}
}
let defaulter = crate::default_ada83::defaulter_from_decoder(&spec.name);
statements.push(ada83::Stmt::Var(ada83::VarDecl {
name: Ident::new("v"),
mark: mark_path(&spec.sample.mark),
constraint: None,
value: Some(Self::call_path(Path::single(&defaulter), Vec::new())),
}));
let mut body = body;
statements.append(&mut body);
statements.push(ada83::Stmt::Return(Some(ada83::Expr::name("v"))));
Some(ada83::Decl::SubprogramBody(ada83::SubprogramBody {
spec: profile,
body: statements,
}))
}
fn default_into(
ast: &Ast,
id: Id<TypeNode>,
place: Self::Expr,
) -> Result<Self::Stmt, EncodeError> {
let defaulted = Self::call_path(
Path::single(&crate::default_ada83::defaulter_name(ast, id)),
Vec::new(),
);
Ok(match place {
AExpr::OptionalSlot(field) => ada83::Stmt::Assign {
place: field,
value: Self::engaged(defaulted),
},
AExpr::Discriminant(field) => Self::assign_discriminant(field, defaulted),
other => ada83::Stmt::Assign {
place: inner(other),
value: defaulted,
},
})
}
fn member_default(
ast: &Ast,
node: &MemberNode,
type_: Id<TypeNode>,
place: Self::Expr,
) -> Result<Vec<Self::Stmt>, EncodeError> {
crate::default_ada83::member_default(ast, node, type_, inner(place))
}
fn enumerator_constant(_ast: &Ast, _id: Id<TypeNode>, enumerator: &str) -> Self::Expr {
plain(ada83::Expr::name(&component_name(enumerator)))
}
}
fn local_name(text: &str) -> &str {
if text.eq_ignore_ascii_case("zd_wire") {
return "zd_onwire";
}
text
}
fn names_mark(body: &[ada83::Stmt], mark: &str) -> bool {
body.iter().any(|statement| stmt_names(statement, mark))
}
fn stmt_names(statement: &ada83::Stmt, mark: &str) -> bool {
match statement {
ada83::Stmt::Null | ada83::Stmt::Exit | ada83::Stmt::Comment(_) => false,
ada83::Stmt::Var(var) => {
path_is(&var.mark, mark)
|| var.constraint.as_ref().is_some_and(|e| expr_names(e, mark))
|| var.value.as_ref().is_some_and(|e| expr_names(e, mark))
}
ada83::Stmt::Subtype { mark: target, .. } => path_is(target, mark),
ada83::Stmt::Assign { place, value } => expr_names(place, mark) || expr_names(value, mark),
ada83::Stmt::Call(value) => expr_names(value, mark),
ada83::Stmt::Return(value) => value.as_ref().is_some_and(|e| expr_names(e, mark)),
ada83::Stmt::If {
condition,
then,
otherwise,
} => {
expr_names(condition, mark)
|| names_mark(then, mark)
|| otherwise
.as_ref()
.is_some_and(|body| names_mark(body, mark))
}
ada83::Stmt::For {
low, high, body, ..
} => expr_names(low, mark) || expr_names(high, mark) || names_mark(body, mark),
ada83::Stmt::While { condition, body } => {
expr_names(condition, mark) || names_mark(body, mark)
}
ada83::Stmt::Declare { vars, body } => {
vars.iter().any(|var| {
path_is(&var.mark, mark)
|| var.constraint.as_ref().is_some_and(|e| expr_names(e, mark))
|| var.value.as_ref().is_some_and(|e| expr_names(e, mark))
}) || names_mark(body, mark)
}
}
}
fn path_is(path: &Path, mark: &str) -> bool {
path.segments().len() == 1
&& path
.segments()
.first()
.is_some_and(|segment| segment.as_str() == mark)
}
fn expr_names(expr: &ada83::Expr, mark: &str) -> bool {
match expr {
ada83::Expr::Literal(_) => false,
ada83::Expr::Name(path) => path_is(path, mark),
ada83::Expr::Call { name, args } => {
path_is(name, mark) || args.iter().any(|arg| expr_names(arg, mark))
}
ada83::Expr::Negate(value)
| ada83::Expr::Unary { value, .. }
| ada83::Expr::Deref(value) => expr_names(value, mark),
ada83::Expr::Binary { lhs, rhs, .. } => expr_names(lhs, mark) || expr_names(rhs, mark),
ada83::Expr::Selected { value, .. } => expr_names(value, mark),
ada83::Expr::Indexed { value, index } => expr_names(value, mark) || expr_names(index, mark),
ada83::Expr::Slice { value, low, high } => {
expr_names(value, mark) || expr_names(low, mark) || expr_names(high, mark)
}
ada83::Expr::Attribute { value, args, .. } => {
expr_names(value, mark) || args.iter().any(|arg| expr_names(arg, mark))
}
ada83::Expr::Convert {
mark: target,
value,
} => path_is(target, mark) || expr_names(value, mark),
ada83::Expr::Aggregate(parts) => parts.iter().any(|part| expr_names(&part.value, mark)),
ada83::Expr::New { mark: target, init } => {
path_is(target, mark)
|| match init {
ada83::AllocInit::Range { low, high } => {
expr_names(low, mark) || expr_names(high, mark)
}
ada83::AllocInit::Qualified(value) => expr_names(value, mark),
}
}
}
}