Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add C support #38

Merged
merged 14 commits into from
May 8, 2023
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ The following are languages which have built-in support in genco.
* [🌐 <b>JavaScript</b>][js]<br>
<small>[Example][js-example]</small>

* [🇨 <b>C</b>][c]<br>
<small>[Example][c-example]</small>

* [🐍 <b>Python</b>][python]<br>
<small>[Example][python-example]</small><br>
**Requires a `nightly` compiler**
Expand Down Expand Up @@ -136,6 +139,8 @@ fn main() {
[dart-example]: https://github.com/udoprog/genco/blob/master/examples/dart.rs
[js]: https://docs.rs/genco/latest/genco/lang/js/index.html
[js-example]: https://github.com/udoprog/genco/blob/master/examples/js.rs
[c]: https://docs.rs/genco/latest/genco/lang/c/index.html
[c-example]: https://github.com/udoprog/genco/blob/master/examples/c.rs
[python]: https://docs.rs/genco/latest/genco/lang/python/index.html
[python-example]: https://github.com/udoprog/genco/blob/master/examples/python.rs
[solve namespace conflicts]: https://docs.rs/genco/latest/genco/lang/csharp/fn.import.html
Expand Down
30 changes: 30 additions & 0 deletions examples/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use genco::fmt;
use genco::prelude::*;

fn main() -> anyhow::Result<()> {
let printf = &c::include_system("stdio.h", "printf");

let day = "tuesday";
let name = "George";

let tokens = quote! {
const char* greet_user() {
return $(quoted(format!("Hello {}!", name)));
}

int main() {
const char* current_day = $(quoted(day));
$printf("%s\n", current_day);
$printf("%s\n", greet_user());
}
};

let stdout = std::io::stdout();
let mut w = fmt::IoWriter::new(stdout.lock());

let fmt = fmt::Config::from_lang::<C>();
let config = c::Config::default().with_package("main");

tokens.format_file(&mut w.as_formatter(&fmt), &config)?;
Ok(())
}
178 changes: 178 additions & 0 deletions src/lang/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//! Specialization for C code generation.

use crate as genco;
use crate::fmt;
use crate::quote_in;
use crate::tokens::{quoted, ItemStr};
use std::collections::BTreeSet;
use std::fmt::Write as _;

/// Tokens container specialization for C.
pub type Tokens = crate::Tokens<C>;

impl_lang! {
/// Language specialization for C.
pub C {
type Config = Config;
type Format = Format;
type Item = Import;

fn write_quoted(out: &mut fmt::Formatter<'_>, input: &str) -> fmt::Result {
super::c_family_write_quoted(out, input)
}

fn format_file(
tokens: &Tokens,
out: &mut fmt::Formatter<'_>,
config: &Self::Config,
) -> fmt::Result {
let mut header = Tokens::new();

if let Some(package) = &config.package {
udoprog marked this conversation as resolved.
Show resolved Hide resolved
quote_in!(header => package $package);
header.line();
}

Self::imports(&mut header, tokens);
let format = Format::default();
header.format(out, config, &format)?;
tokens.format(out, config, &format)?;
Ok(())
}
}

Import {
fn format(&self, out: &mut fmt::Formatter<'_>, _: &Config, _: &Format) -> fmt::Result {
out.write_str(&self.item)?;
Ok(())
}
}
}

/// The include statement for a C header file such as `#include "foo/bar.h"` or
/// `#include <stdio.h>`.
///
/// Created using the [include()] function.
#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Import {
/// Path to included file.
path: ItemStr,
/// Item declared in the included file.
item: ItemStr,
/// True if the include is specified as a system header using `<>`, false if a local header using `""`.
system: bool,
}

/// Format for C.
#[derive(Debug, Default)]
pub struct Format {}

/// Config data for C.
#[derive(Debug, Default)]
pub struct Config {
package: Option<ItemStr>,
udoprog marked this conversation as resolved.
Show resolved Hide resolved
}

impl Config {
/// Configure the specified package.
pub fn with_package<P: Into<ItemStr>>(self, package: P) -> Self {
Self {
package: Some(package.into()),
}
}
}

impl C {
fn imports(out: &mut Tokens, tokens: &Tokens) {
let mut includes = BTreeSet::new();

for include in tokens.walk_imports() {
includes.insert((&include.path, include.system));
}

if includes.is_empty() {
return;
}

for (file, system_header) in includes {
if system_header {
quote_in!(*out => #include <$(file)>);
} else {
quote_in!(*out => #include $(quoted(file)));
}
out.push();
}

out.line();
}
}

/// Include an item declared in a local C header file such as `#include "foo/bar.h"`
///
/// # Examples
///
/// ```
/// use genco::prelude::*;
///
/// let fizzbuzz = c::include("foo/bar.h", "fizzbuzz");
///
/// let fizzbuzz_toks = quote! {
/// $fizzbuzz
/// };
///
/// assert_eq!(
/// vec![
/// "#include \"foo/bar.h\"",
/// "",
/// "fizzbuzz",
/// ],
/// fizzbuzz_toks.to_file_vec()?
/// );
/// # Ok::<_, genco::fmt::Error>(())
/// ```
pub fn include<M, N>(path: M, item: N) -> Import
where
M: Into<ItemStr>,
N: Into<ItemStr>,
{
Import {
path: path.into(),
item: item.into(),
system: false,
}
}

/// Include an item declared in a C system header such as `#include <stdio.h>`.
///
/// # Examples
///
/// ```
/// use genco::prelude::*;
///
/// let printf = c::include_system("stdio.h", "printf");
///
/// let printf_toks = quote! {
/// $printf
/// };
///
/// assert_eq!(
/// vec![
/// "#include <stdio.h>",
/// "",
/// "printf",
/// ],
/// printf_toks.to_file_vec()?
/// );
/// # Ok::<_, genco::fmt::Error>(())
/// ```
pub fn include_system<M, N>(path: M, item: N) -> Import
where
M: Into<ItemStr>,
N: Into<ItemStr>,
{
Import {
path: path.into(),
item: item.into(),
system: true,
}
}
2 changes: 2 additions & 0 deletions src/lang/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! # }
//! ```

pub mod c;
pub mod csharp;
pub mod dart;
pub mod go;
Expand All @@ -26,6 +27,7 @@ pub mod python;
pub mod rust;
pub mod swift;

pub use self::c::C;
pub use self::csharp::Csharp;
pub use self::dart::Dart;
pub use self::go::Go;
Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
//! * [🌐 <b>JavaScript</b>][js]<br>
//! <small>[Example][js-example]</small>
//!
//! * [🇨 <b>C</b>][c]<br>
//! <small>[Example][c-example]</small>
//!
//! * [🐍 <b>Python</b>][python]<br>
//! <small>[Example][python-example]</small><br>
//! **Requires a `nightly` compiler**
Expand Down Expand Up @@ -134,6 +137,8 @@
//! [dart-example]: https://github.com/udoprog/genco/blob/master/examples/dart.rs
//! [js]: https://docs.rs/genco/latest/genco/lang/js/index.html
//! [js-example]: https://github.com/udoprog/genco/blob/master/examples/js.rs
//! [c]: https://docs.rs/genco/latest/genco/lang/c/index.html
//! [c-example]: https://github.com/udoprog/genco/blob/master/examples/c.rs
//! [python]: https://docs.rs/genco/latest/genco/lang/python/index.html
//! [python-example]: https://github.com/udoprog/genco/blob/master/examples/python.rs
//! [solve namespace conflicts]: https://docs.rs/genco/latest/genco/lang/csharp/fn.import.html
Expand Down