Skip to content

Commit

Permalink
Add C support (#38)
Browse files Browse the repository at this point in the history
Co-authored-by: John-John Tedro <[email protected]>
  • Loading branch information
believeinlain and udoprog authored May 8, 2023
1 parent cf2a987 commit f9b4f73
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 0 deletions.
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();

tokens.format_file(&mut w.as_formatter(&fmt), &config)?;
Ok(())
}
162 changes: 162 additions & 0 deletions src/lang/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//! 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();

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 {}

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

0 comments on commit f9b4f73

Please sign in to comment.