diff --git a/README.md b/README.md index 888c9b6..f485898 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,9 @@ The following are languages which have built-in support in genco. * [🌐 JavaScript][js]
[Example][js-example] +* [🇨 C][c]
+ [Example][c-example] + * [🐍 Python][python]
[Example][python-example]
**Requires a `nightly` compiler** @@ -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 diff --git a/examples/c.rs b/examples/c.rs new file mode 100644 index 0000000..8eddecb --- /dev/null +++ b/examples/c.rs @@ -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::(); + let config = c::Config::default(); + + tokens.format_file(&mut w.as_formatter(&fmt), &config)?; + Ok(()) +} diff --git a/src/lang/c.rs b/src/lang/c.rs new file mode 100644 index 0000000..be9b763 --- /dev/null +++ b/src/lang/c.rs @@ -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; + +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 `. +/// +/// 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(path: M, item: N) -> Import +where + M: Into, + N: Into, +{ + Import { + path: path.into(), + item: item.into(), + system: false, + } +} + +/// Include an item declared in a C system header such as `#include `. +/// +/// # Examples +/// +/// ``` +/// use genco::prelude::*; +/// +/// let printf = c::include_system("stdio.h", "printf"); +/// +/// let printf_toks = quote! { +/// $printf +/// }; +/// +/// assert_eq!( +/// vec![ +/// "#include ", +/// "", +/// "printf", +/// ], +/// printf_toks.to_file_vec()? +/// ); +/// # Ok::<_, genco::fmt::Error>(()) +/// ``` +pub fn include_system(path: M, item: N) -> Import +where + M: Into, + N: Into, +{ + Import { + path: path.into(), + item: item.into(), + system: true, + } +} diff --git a/src/lang/mod.rs b/src/lang/mod.rs index b98721f..a8205ea 100644 --- a/src/lang/mod.rs +++ b/src/lang/mod.rs @@ -17,6 +17,7 @@ //! # } //! ``` +pub mod c; pub mod csharp; pub mod dart; pub mod go; @@ -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; diff --git a/src/lib.rs b/src/lib.rs index 0b96cb4..a0d7b6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,9 @@ //! * [🌐 JavaScript][js]
//! [Example][js-example] //! +//! * [🇨 C][c]
+//! [Example][c-example] +//! //! * [🐍 Python][python]
//! [Example][python-example]
//! **Requires a `nightly` compiler** @@ -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