Skip to content

Commit

Permalink
introduce the concept of language backend
Browse files Browse the repository at this point in the history
  • Loading branch information
fredszaq authored and emilio committed Apr 14, 2024
1 parent 8ea849e commit 3354606
Show file tree
Hide file tree
Showing 19 changed files with 2,058 additions and 1,435 deletions.
381 changes: 22 additions & 359 deletions src/bindgen/bindings.rs

Large diffs are not rendered by default.

61 changes: 45 additions & 16 deletions src/bindgen/cdecl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::io::Write;
use crate::bindgen::config::Layout;
use crate::bindgen::declarationtyperesolver::DeclarationType;
use crate::bindgen::ir::{ConstExpr, Function, GenericArgument, Type};
use crate::bindgen::language_backend::LanguageBackend;
use crate::bindgen::writer::{ListType, SourceWriter};
use crate::bindgen::{Config, Language};

Expand Down Expand Up @@ -191,7 +192,13 @@ impl CDecl {
}
}

fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
fn write<F: Write, LB: LanguageBackend>(
&self,
language_backend: &mut LB,
out: &mut SourceWriter<F>,
ident: Option<&str>,
config: &Config,
) {
// Write the type-specifier and type-qualifier first
if !self.type_qualifers.is_empty() {
write!(out, "{} ", self.type_qualifers);
Expand All @@ -207,7 +214,15 @@ impl CDecl {

if !self.type_generic_args.is_empty() {
out.write("<");
out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
out.write_horizontal_source_list(
language_backend,
&self.type_generic_args,
ListType::Join(", "),
|language_backend, out, g| match *g {
GenericArgument::Type(ref ty) => language_backend.write_type(out, ty),
GenericArgument::Const(ref expr) => write!(out, "{}", expr.as_str()),
},
);
out.write(">");
}

Expand Down Expand Up @@ -289,7 +304,8 @@ impl CDecl {
out.write("void");
}

fn write_vertical<F: Write>(
fn write_vertical<F: Write, LB: LanguageBackend>(
language_backend: &mut LB,
out: &mut SourceWriter<F>,
config: &Config,
args: &[(Option<String>, CDecl)],
Expand All @@ -305,12 +321,13 @@ impl CDecl {
// Convert &Option<String> to Option<&str>
let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());

arg_ty.write(out, arg_ident, config);
arg_ty.write(language_backend, out, arg_ident, config);
}
out.pop_tab();
}

fn write_horizontal<F: Write>(
fn write_horizontal<F: Write, LB: LanguageBackend>(
language_backend: &mut LB,
out: &mut SourceWriter<F>,
config: &Config,
args: &[(Option<String>, CDecl)],
Expand All @@ -323,19 +340,19 @@ impl CDecl {
// Convert &Option<String> to Option<&str>
let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());

arg_ty.write(out, arg_ident, config);
arg_ty.write(language_backend, out, arg_ident, config);
}
}

match layout {
Layout::Vertical => write_vertical(out, config, args),
Layout::Horizontal => write_horizontal(out, config, args),
Layout::Vertical => write_vertical(language_backend, out, config, args),
Layout::Horizontal => write_horizontal(language_backend, out, config, args),
Layout::Auto => {
if !out.try_write(
|out| write_horizontal(out, config, args),
|out| write_horizontal(language_backend, out, config, args),
config.line_length,
) {
write_vertical(out, config, args)
write_vertical(language_backend, out, config, args)
}
}
}
Expand All @@ -354,19 +371,31 @@ impl CDecl {
}
}

pub fn write_func<F: Write>(
pub fn write_func<F: Write, LB: LanguageBackend>(
language_backend: &mut LB,
out: &mut SourceWriter<F>,
f: &Function,
layout: Layout,
config: &Config,
) {
CDecl::from_func(f, layout, config).write(out, Some(f.path().name()), config);
CDecl::from_func(f, layout, config).write(language_backend, out, Some(f.path().name()), config);
}

pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
CDecl::from_type(t, config).write(out, Some(ident), config);
pub fn write_field<F: Write, LB: LanguageBackend>(
language_backend: &mut LB,
out: &mut SourceWriter<F>,
t: &Type,
ident: &str,
config: &Config,
) {
CDecl::from_type(t, config).write(language_backend, out, Some(ident), config);
}

pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
CDecl::from_type(t, config).write(out, None, config);
pub fn write_type<F: Write, LB: LanguageBackend>(
language_backend: &mut LB,
out: &mut SourceWriter<F>,
t: &Type,
config: &Config,
) {
CDecl::from_type(t, config).write(language_backend, out, None, config);
}
126 changes: 14 additions & 112 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
Struct, ToCondition, Type,
};
use crate::bindgen::language_backend::LanguageBackend;
use crate::bindgen::library::Library;
use crate::bindgen::writer::{Source, SourceWriter};
use crate::bindgen::writer::SourceWriter;
use crate::bindgen::Bindings;

fn member_to_ident(member: &syn::Member) -> String {
Expand All @@ -28,7 +29,7 @@ fn member_to_ident(member: &syn::Member) -> String {
}

// TODO: Maybe add support to more std associated constants.
fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
pub(crate) fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
use crate::bindgen::ir::{IntKind, PrimitiveType};

if name != "MAX" && name != "MIN" {
Expand Down Expand Up @@ -472,107 +473,6 @@ impl Literal {
_ => Err(format!("Unsupported expression. {:?}", *expr)),
}
}

pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
match self {
Literal::Expr(v) => match (&**v, config.language) {
("true", Language::Cython) => write!(out, "True"),
("false", Language::Cython) => write!(out, "False"),
(v, _) => write!(out, "{}", v),
},
Literal::Path {
ref associated_to,
ref name,
} => {
if let Some((ref path, ref export_name)) = associated_to {
if let Some(known) = to_known_assoc_constant(path, name) {
return write!(out, "{}", known);
}
let path_separator = match config.language {
Language::Cython | Language::C => "_",
Language::Cxx => {
if config.structure.associated_constants_in_body {
"::"
} else {
"_"
}
}
};
write!(out, "{}{}", export_name, path_separator)
}
write!(out, "{}", name)
}
Literal::FieldAccess {
ref base,
ref field,
} => {
write!(out, "(");
base.write(config, out);
write!(out, ").{}", field);
}
Literal::PostfixUnaryOp { op, ref value } => {
write!(out, "{}", op);
value.write(config, out);
}
Literal::BinOp {
ref left,
op,
ref right,
} => {
write!(out, "(");
left.write(config, out);
write!(out, " {} ", op);
right.write(config, out);
write!(out, ")");
}
Literal::Cast { ref ty, ref value } => {
out.write(if config.language == Language::Cython {
"<"
} else {
"("
});
ty.write(config, out);
out.write(if config.language == Language::Cython {
">"
} else {
")"
});
value.write(config, out);
}
Literal::Struct {
export_name,
fields,
path,
} => {
match config.language {
Language::C => write!(out, "({})", export_name),
Language::Cxx => write!(out, "{}", export_name),
Language::Cython => write!(out, "<{}>", export_name),
}

write!(out, "{{ ");
let mut is_first_field = true;
// In C++, same order as defined is required.
let ordered_fields = out.bindings().struct_field_names(path);
for ordered_key in ordered_fields.iter() {
if let Some(lit) = fields.get(ordered_key) {
if !is_first_field {
write!(out, ", ");
} else {
is_first_field = false;
}
match config.language {
Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
Language::C => write!(out, ".{} = ", ordered_key),
Language::Cython => {}
}
lit.write(config, out);
}
}
write!(out, " }}");
}
}
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -692,9 +592,10 @@ impl Item for Constant {
}

impl Constant {
pub fn write_declaration<F: Write>(
pub fn write_declaration<F: Write, LB: LanguageBackend>(
&self,
config: &Config,
language_backend: &mut LB,
out: &mut SourceWriter<F>,
associated_to_struct: &Struct,
) {
Expand All @@ -709,13 +610,14 @@ impl Constant {
} else {
out.write("static const ");
}
self.ty.write(config, out);
language_backend.write_type(out, &self.ty);
write!(out, " {};", self.export_name())
}

pub fn write<F: Write>(
pub fn write<F: Write, LB: LanguageBackend>(
&self,
config: &Config,
language_backend: &mut LB,
out: &mut SourceWriter<F>,
associated_to_struct: Option<&Struct>,
) {
Expand Down Expand Up @@ -770,7 +672,7 @@ impl Constant {
_ => &self.value,
};

self.documentation.write(config, out);
language_backend.write_documentation(out, &self.documentation);

let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr();
match config.language {
Expand All @@ -789,22 +691,22 @@ impl Constant {
out.write("const ");
}

self.ty.write(config, out);
language_backend.write_type(out, &self.ty);
write!(out, " {} = ", name);
value.write(config, out);
language_backend.write_literal(out, value);
write!(out, ";");
}
Language::Cxx | Language::C => {
write!(out, "#define {} ", name);
value.write(config, out);
language_backend.write_literal(out, value);
}
Language::Cython => {
out.write("const ");
self.ty.write(config, out);
language_backend.write_type(out, &self.ty);
// For extern Cython declarations the initializer is ignored,
// but still useful as documentation, so we write it as a comment.
write!(out, " {} # = ", name);
value.write(config, out);
language_backend.write_literal(out, value);
}
}

Expand Down
Loading

0 comments on commit 3354606

Please sign in to comment.