...
 
Commits (12)
......@@ -114,11 +114,18 @@ gnome_class! {
// should we list SomeInterface in the "class" line above?
// Pros: makes it obvious at a glance what interfaces are implemented
// Cons: a little duplication
impl SomeInterface for Foo {
//
// We have "impl interface" because if we just have the user's SomeInterface,
// we don't know if it corresponds to a class whose methods we are overriding,
// or to an interface that we want to implement. So, with "impl interface"
// we make this unambiguous.
impl interface SomeInterface for Foo {
virtual fn blah(&self, ...) {
}
}
// FIXME: we need syntax to define new GTypeInterfaces
interface Blah: RequiredClass + RequiredIface + RequiredIface {
virtual fn blah(&self, ...);
}
}
```
// use lalrpop_intern::InternedString;
// use quote::Tokens;
use proc_macro2::Term;
use quote::{ToTokens, Tokens};
use syn::punctuated::Punctuated;
use syn::{Attribute, Lit};
use syn::{Block, FnArg, Ident, Path, ReturnType, Type};
......@@ -23,11 +22,19 @@ impl Program {
_ => None,
})
}
pub fn interfaces<'a>(&'a self) -> impl Iterator<Item = &'a Interface> + 'a {
self.items.iter().filter_map(|item| match *item {
Item::Interface(ref i) => Some(i),
_ => None,
})
}
}
pub enum Item {
Class(Class),
Impl(Impl),
Interface(Interface),
}
pub fn get_program_classes<'a>(program: &'a Program) -> Vec<&'a Class> {
......@@ -52,13 +59,20 @@ pub struct Class {
// similar to syn::ItemImpl
pub struct Impl {
pub is_interface: bool,
pub trait_: Option<Ident>,
pub self_path: Ident,
pub items: Vec<ImplItem>,
}
pub struct Interface {
pub name: Ident,
// FIXME: required class and interfaces
pub items: Vec<ImplItem>,
}
pub enum ClassItem {
PrivateField(Field)
PrivateField(Field),
}
pub struct ImplItem {
......@@ -87,3 +101,11 @@ pub struct Field {
pub ty: Type,
pub semi: Token!(;),
}
impl ToTokens for Field {
fn to_tokens(&self, tokens: &mut Tokens) {
let name = &self.name;
let ty = &self.ty;
(quote_cs! { #name: #ty }).to_tokens(tokens);
}
}
use quote::Tokens;
use self::cstringident::*;
use super::*;
use glib_utils::*;
use super::class::ClassContext;
use super::cstringident::CStringIdent;
// This has all the one-time boilerplate for a GObject implementation:
// the instance and class structs, the get_type(), instance_init(),
......
// We give `ClassName` variables an identifier that uses upper-case.
#![allow(non_snake_case)]
use proc_macro2::Span;
use quote::{ToTokens, Tokens};
use syn::Ident;
use errors::*;
use glib_utils::*;
use hir::{Class, Program};
pub struct ClassContext<'ast> {
pub program: &'ast Program<'ast>,
pub class: &'ast Class<'ast>,
pub PrivateStructName: Ident,
pub ModuleName: Ident,
pub InstanceName: &'ast Ident,
pub InstanceNameFfi: Ident,
pub ClassName: Ident,
pub PrivateClassName: Ident,
pub ParentInstance: &'ast ToTokens,
pub ParentInstanceFfi: &'ast Tokens,
pub ParentClassFfi: &'ast Tokens,
pub GObject: Tokens,
pub GObjectFfi: Tokens,
pub GObjectClassFfi: Tokens,
pub InstanceExt: Ident,
}
impl<'ast> ClassContext<'ast> {
#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn new(program: &'ast Program, class: &'ast Class) -> Self {
// This function creates a ClassContext by generating the
// commonly-used symbol names for the class in question, for
// example, "FooClass" out of "Foo".
let InstanceName = &class.name;
// If our instance name is "Foo" and we have a suffix "Bar", generates a
// "FooBar" Ident. These are used for the generated module name,
// instance/class struct names, etc.
//
// Note that we switch the spans of all identifiers to be
// `Span::call_site` which differs from what `syn` does upstream which
// is to use `Span::default` (currently). This is sort of a...
//
// FIXME(rust-lang/rust#45934) we should be able to use vanilla upstream
// `syn` ideally, but it's not clear how that would change, if at all
let container_name = |suffix: &str| {
Ident::new(
&format!("{}{}", InstanceName.as_ref(), suffix),
Span::call_site(),
)
};
let InstanceNameFfi = container_name("Ffi");
let PrivateStructName = container_name("Priv");
let ModuleName = container_name("Mod"); // toplevel "InstanceMod" module name
let ClassName = container_name("Class");
let PrivateClassName = container_name("ClassPrivate");
let InstanceExt = container_name("Ext"); // public trait with all the class's methods
let GObject = tokens_GObject();
let GObjectFfi = tokens_GObjectFfi();
let GObjectClassFfi = tokens_GObjectClassFfi();
let ParentInstance = &class.parent;
let ParentInstanceFfi = &class.parent_ffi;
let ParentClassFfi = &class.parent_class_ffi;
ClassContext {
program,
class,
PrivateStructName,
ModuleName,
InstanceName,
ClassName,
PrivateClassName,
ParentInstance,
ParentInstanceFfi,
ParentClassFfi,
GObject,
GObjectFfi,
GObjectClassFfi,
InstanceExt,
InstanceNameFfi,
}
}
pub fn gen_class(&self) -> Result<Tokens> {
Ok(self.gen_boilerplate())
}
pub fn exported_fn_name(&self, method_name: &str) -> Ident {
Ident::new(
&format!(
"{}_{}",
lower_case_instance_name(self.InstanceName.as_ref()),
method_name
),
Span::call_site(),
)
}
pub fn instance_get_type_fn_name(&self) -> Ident {
self.exported_fn_name("get_type")
}
}
use syn::Block;
use quote::Tokens;
use syn::{Block, Ident};
use super::*;
use glib_utils::*;
use hir::{FnSig, Method, Signal, Slot, VirtualMethod};
use super::class::ClassContext;
impl<'ast> ClassContext<'ast> {
pub fn slots(&self) -> Vec<Tokens> {
......
use quote::Tokens;
use syn::Ident;
use hir::{Method, Slot, VirtualMethod};
use super::class::ClassContext;
use super::signals;
use super::*;
impl<'ast> ClassContext<'ast> {
/// Returns, for each method, something like
......
// We give `ClassName` variables an identifier that uses upper-case.
#![allow(non_snake_case)]
use proc_macro::{Diagnostic, Level};
use proc_macro2::{Delimiter, Group, Span, TokenTree};
use quote::{ToTokens, Tokens};
use syn::spanned::Spanned;
use syn::{Ident, Path};
use errors::*;
use hir::*;
use ast;
use quote::Tokens;
mod boilerplate;
mod class;
mod cstringident;
mod imp;
mod instance_ext;
mod signals;
mod signatures;
use glib_utils::*;
use self::class::ClassContext;
use errors::*;
use hir::Program;
pub fn classes(program: &Program) -> Result<Tokens> {
pub fn codegen(program: &Program) -> Result<Tokens> {
let class_tokens = program
.classes
.iter()
......@@ -31,440 +26,3 @@ pub fn classes(program: &Program) -> Result<Tokens> {
.collect::<Result<Vec<_>>>()?;
Ok(quote_cs! { #(#class_tokens)* })
}
struct ClassContext<'ast> {
program: &'ast Program<'ast>,
class: &'ast Class<'ast>,
PrivateStructName: Ident,
ModuleName: Ident,
InstanceName: &'ast Ident,
InstanceNameFfi: Ident,
ClassName: Ident,
PrivateClassName: Ident,
ParentInstance: &'ast ToTokens,
ParentInstanceFfi: &'ast Tokens,
ParentClassFfi: &'ast Tokens,
GObject: Tokens,
GObjectFfi: Tokens,
GObjectClassFfi: Tokens,
InstanceExt: Ident,
}
impl<'ast> ClassContext<'ast> {
#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn new(program: &'ast Program, class: &'ast Class) -> Self {
// This function creates a ClassContext by generating the
// commonly-used symbol names for the class in question, for
// example, "FooClass" out of "Foo".
let InstanceName = &class.name;
// If our instance name is "Foo" and we have a suffix "Bar", generates a
// "FooBar" Ident. These are used for the generated module name,
// instance/class struct names, etc.
//
// Note that we switch the spans of all identifiers to be
// `Span::call_site` which differs from what `syn` does upstream which
// is to use `Span::default` (currently). This is sort of a...
//
// FIXME(rust-lang/rust#45934) we should be able to use vanilla upstream
// `syn` ideally, but it's not clear how that would change, if at all
let container_name = |suffix: &str| {
Ident::new(
&format!("{}{}", InstanceName.as_ref(), suffix),
Span::call_site(),
)
};
let InstanceNameFfi = container_name("Ffi");
let PrivateStructName = container_name("Priv");
let ModuleName = container_name("Mod"); // toplevel "InstanceMod" module name
let ClassName = container_name("Class");
let PrivateClassName = container_name("ClassPrivate");
let InstanceExt = container_name("Ext"); // public trait with all the class's methods
let GObject = tokens_GObject();
let GObjectFfi = tokens_GObjectFfi();
let GObjectClassFfi = tokens_GObjectClassFfi();
let ParentInstance = &class.parent;
let ParentInstanceFfi = &class.parent_ffi;
let ParentClassFfi = &class.parent_class_ffi;
ClassContext {
program,
class,
PrivateStructName,
ModuleName,
InstanceName,
ClassName,
PrivateClassName,
ParentInstance,
ParentInstanceFfi,
ParentClassFfi,
GObject,
GObjectFfi,
GObjectClassFfi,
InstanceExt,
InstanceNameFfi,
}
}
pub fn gen_class(&self) -> Result<Tokens> {
Ok(self.gen_boilerplate())
}
fn exported_fn_name(&self, method_name: &str) -> Ident {
Ident::new(
&format!(
"{}_{}",
lower_case_instance_name(self.InstanceName.as_ref()),
method_name
),
Span::call_site(),
)
}
fn instance_get_type_fn_name(&self) -> Ident {
self.exported_fn_name("get_type")
}
}
impl<'ast> FnSig<'ast> {
/// Generates the Glib type name of the function's return value
///
/// For example, if the `FnSig` represents a `fn foo(...) ->
/// bool`, then this function will generate something that
/// resolves to `glib_sys::gboolean`.
fn output_glib_type<'a>(&'a self) -> impl ToTokens + 'a {
ToGlibType(&self.output, self)
}
/// Generates an argument list just with Rust types, suitable for `Fn` signatures, without
/// `&Self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a: bool, b: i32)`, then this
/// function will generate tokens for `bool, i32`. This is useful when generating
/// an `Fn(&Self, bool, i32)` signature.
///
/// Note that the first parameter `&Self` is omitted. This is so that callers can
/// emit it themselves.
fn input_arg_types<'a>(&'a self) -> impl ToTokens + 'a {
ArgTypes(self)
}
/// Generates an argument list with Glib types suitable for function prototypes, without the
/// `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a: bool, b: i32)`, then this
/// function will generate tokens for `a: glib_sys::boolean, b: i32,`. This is useful when
/// generating a prototype for an `unsafe extern "C" fn` callable from C.
///
/// Note that the first parameter `&self` is omitted. This is so that callers
/// can emit a suitable C pointer instead of a Rust `&self`.
fn input_args_with_glib_types<'a>(&'a self) -> impl ToTokens + 'a {
FnArgsWithGlibTypes(self)
}
/// Generates an argument list with values converted from Glib types, without the `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `<bool as FromGlib<_>>::from_glib(a), b,`. Presumably the
/// generated tokens are being used in a function call from C to
/// Rust.
///
/// Note that the first parameter `&self` is omitted. This is so that the caller
/// can emit the tokens for the first argument as appropriate.
fn input_args_from_glib_types<'a>(&'a self) -> impl ToTokens + 'a {
ArgNamesFromGlib(&self.inputs[1..])
}
/// Generates an argument list with values converted to Glib types, without the `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `<bool as ToGlib>::to_glib(&a), b,`. Presumably the generated
/// tokens are being used in a function call from Rust to C.
///
/// Note that the first parameter `&self` is omitted. This is so that the caller
/// can emit the tokens for the first argument as appropriate.
fn input_args_to_glib_types<'a>(&'a self) -> impl ToTokens + 'a {
ArgNamesToGlib(&self.inputs[1..])
}
/// Generates an argument list with values converted to Glib values
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `<self as &glib::ToValue>.to_value(), <&a as &glib::ToValue>::to_value(), <&b as
/// &glib::ToValue>::to_value(),`. The generated tokens are suitable for
/// generating a GValue argument list to be passed to `g_signal_emitv()`.
fn input_args_to_glib_values<'a>(&'a self) -> impl ToTokens + 'a {
ArgNamesToGlibValues(&self.inputs)
}
/// Generates a list of argument names with no type conversions, without the `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `a, b,`. This is just to pass through arguments from inside a
/// wrapper function.
///
/// Note that the first parameter `&self` is omitted. This is so that the caller
/// can emit the tokens for the first argument as appropriate.
fn input_arg_names<'a>(&'a self) -> impl ToTokens + 'a {
ArgNames(&self.inputs[1..])
}
/// Generates the conversion from a Rust return value into a Glib value
///
/// For example, if the `FnSig` has an `output` type of `bool`,
/// and the `tokens` correspond to `true`, this function will
/// generate `<bool as ToGlib>::to_glib(&true)`. This can be used
/// by code which generates a function callable from C that wraps
/// Rust code.
fn ret_to_glib<'a, T: ToTokens + 'a>(&'a self, tokens: T) -> impl ToTokens + 'a {
ToGlib(&self.output, tokens)
}
fn ret_from_glib_fn<'a, V: ToTokens>(&'a self, v: &'a V) -> impl ToTokens + 'a {
let mut tokens = Tokens::new();
v.to_tokens(&mut tokens);
FromGlib(&self.output, tokens)
}
}
struct ToGlibType<'ast>(&'ast Ty<'ast>, &'ast FnSig<'ast>);
impl<'ast> ToTokens for ToGlibType<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self.0 {
Ty::Unit => self.0.to_tokens(tokens),
Ty::Char(i) | Ty::Bool(i) => {
(quote_cs! {
<#i as ToGlib>::GlibType
}).to_tokens(tokens);
}
Ty::Borrowed(ref t) => {
(quote_cs! {
<#t as GlibPtrDefault>::GlibType
}).to_tokens(tokens);
}
Ty::Integer(i) => i.to_tokens(tokens),
Ty::Owned(_) => {
Diagnostic::spanned(
self.0.span().unstable(),
Level::Error,
"unimplemented glib type for owned types",
).emit();
(quote! {
()
}).to_tokens(tokens);
}
}
}
}
struct ToGlib<'ast, T>(&'ast Ty<'ast>, T);
impl<'ast, T: ToTokens> ToTokens for ToGlib<'ast, T> {
fn to_tokens(&self, tokens: &mut Tokens) {
let expr = &self.1;
match *self.0 {
// no conversion necessary
Ty::Unit | Ty::Integer(_) => self.1.to_tokens(tokens),
Ty::Char(i) | Ty::Bool(i) => {
(quote_cs! {
<#i as ToGlib>::to_glib(&#expr)
}).to_tokens(tokens);
}
Ty::Borrowed(ref t) => {
(quote_cs! {
<#t as ToGlibPtr<_>>::to_glib_none(#expr).0
}).to_tokens(tokens);
}
Ty::Owned(_) => {
Diagnostic::spanned(
self.0.span().unstable(),
Level::Error,
"unimplemented glib type for owned types",
).emit();
(quote! {
()
}).to_tokens(tokens);
}
}
}
}
struct FromGlib<'ast>(&'ast Ty<'ast>, Tokens);
impl<'ast> ToTokens for FromGlib<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
let needs_conversion = match *self.0 {
Ty::Unit => false, // no conversion necessary
Ty::Char(i) | Ty::Bool(i) => {
(quote_cs! {
<#i as FromGlib<_>>::from_glib
}).to_tokens(tokens);
true
}
Ty::Borrowed(ref t) => {
(quote_cs! {
&<#t as FromGlibPtrBorrow<_>>::from_glib_borrow
}).to_tokens(tokens);
true
}
Ty::Integer(_) => false, // no conversion necessary
Ty::Owned(_) => {
Diagnostic::spanned(
self.0.span().unstable(),
Level::Error,
"unimplemented glib type for owned types",
).emit();
false
}
};
if needs_conversion {
tokens.append(TokenTree::Group(Group::new(
Delimiter::Parenthesis,
self.1.clone().into_tokens().into(),
)));
} else {
self.1.to_tokens(tokens);
}
}
}
struct ArgTypes<'ast>(&'ast FnSig<'ast>);
impl<'ast> ToTokens for ArgTypes<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0.inputs[1..].iter() {
match *arg {
FnArg::Arg { ref ty, .. } => {
ty.to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct FnArgsWithGlibTypes<'ast>(&'ast FnSig<'ast>);
impl<'ast> ToTokens for FnArgsWithGlibTypes<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0.inputs[1..].iter() {
match *arg {
FnArg::Arg {
name,
ref ty,
mutbl: _,
} => {
name.to_tokens(tokens);
Token!(:)([Span::call_site()]).to_tokens(tokens);
ToGlibType(ty, self.0).to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct ArgNamesFromGlib<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNamesFromGlib<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::Arg {
ref name,
ref ty,
mutbl: _,
} => {
let mut name_tokens = Tokens::new();
name.to_tokens(&mut name_tokens);
FromGlib(ty, name_tokens).to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct ArgNamesToGlib<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNamesToGlib<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::Arg {
ref ty,
name,
mutbl: _,
} => {
ToGlib(ty, name).to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct ArgNamesToGlibValues<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNamesToGlibValues<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::SelfRef(..) => {
let code = quote_cs! {
(self as &glib::ToValue).to_value(),
};
code.to_tokens(tokens);
}
FnArg::Arg { name, .. } => {
let code = quote_cs! {
(&#name as &glib::ToValue).to_value(),
};
code.to_tokens(tokens);
}
}
}
}
}
struct ArgNames<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNames<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::Arg { name, .. } => {
name.to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
impl ToTokens for ast::Field {
fn to_tokens(&self, tokens: &mut Tokens) {
let name = &self.name;
let ty = &self.ty;
(quote_cs! { #name: #ty }).to_tokens(tokens);
}
}
\ No newline at end of file
use self::cstringident::CStringIdent;
use super::*;
use quote::Tokens;
use syn::{Ident, Path};
use glib_utils::*;
use hir::{FnArg, Signal, Slot, Ty};
use super::class::ClassContext;
use super::cstringident::CStringIdent;
impl<'ast> ClassContext<'ast> {
pub fn signal_trampolines(&self) -> Vec<Tokens> {
......
use proc_macro::{Diagnostic, Level};
use proc_macro2::{Delimiter, Group, Span, TokenTree};
use quote::{ToTokens, Tokens};
use syn::spanned::Spanned;
use hir::{FnArg, FnSig, Ty};
impl<'ast> FnSig<'ast> {
/// Generates the Glib type name of the function's return value
///
/// For example, if the `FnSig` represents a `fn foo(...) ->
/// bool`, then this function will generate something that
/// resolves to `glib_sys::gboolean`.
pub fn output_glib_type<'a>(&'a self) -> impl ToTokens + 'a {
ToGlibType(&self.output, self)
}
/// Generates an argument list just with Rust types, suitable for `Fn` signatures, without
/// `&Self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a: bool, b: i32)`, then this
/// function will generate tokens for `bool, i32`. This is useful when generating
/// an `Fn(&Self, bool, i32)` signature.
///
/// Note that the first parameter `&Self` is omitted. This is so that callers can
/// emit it themselves.
pub fn input_arg_types<'a>(&'a self) -> impl ToTokens + 'a {
ArgTypes(self)
}
/// Generates an argument list with Glib types suitable for function prototypes, without the
/// `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a: bool, b: i32)`, then this
/// function will generate tokens for `a: glib_sys::boolean, b: i32,`. This is useful when
/// generating a prototype for an `unsafe extern "C" fn` callable from C.
///
/// Note that the first parameter `&self` is omitted. This is so that callers
/// can emit a suitable C pointer instead of a Rust `&self`.
pub fn input_args_with_glib_types<'a>(&'a self) -> impl ToTokens + 'a {
FnArgsWithGlibTypes(self)
}
/// Generates an argument list with values converted from Glib types, without the `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `<bool as FromGlib<_>>::from_glib(a), b,`. Presumably the
/// generated tokens are being used in a function call from C to
/// Rust.
///
/// Note that the first parameter `&self` is omitted. This is so that the caller
/// can emit the tokens for the first argument as appropriate.
pub fn input_args_from_glib_types<'a>(&'a self) -> impl ToTokens + 'a {
ArgNamesFromGlib(&self.inputs[1..])
}
/// Generates an argument list with values converted to Glib types, without the `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `<bool as ToGlib>::to_glib(&a), b,`. Presumably the generated
/// tokens are being used in a function call from Rust to C.
///
/// Note that the first parameter `&self` is omitted. This is so that the caller
/// can emit the tokens for the first argument as appropriate.
pub fn input_args_to_glib_types<'a>(&'a self) -> impl ToTokens + 'a {
ArgNamesToGlib(&self.inputs[1..])
}
/// Generates an argument list with values converted to Glib values
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `<self as &glib::ToValue>.to_value(), <&a as &glib::ToValue>::to_value(), <&b as
/// &glib::ToValue>::to_value(),`. The generated tokens are suitable for
/// generating a GValue argument list to be passed to `g_signal_emitv()`.
pub fn input_args_to_glib_values<'a>(&'a self) -> impl ToTokens + 'a {
ArgNamesToGlibValues(&self.inputs)
}
/// Generates a list of argument names with no type conversions, without the `&self`
///
/// For example, if the `FnSig` represents a `fn foo(&self, a:
/// bool, b: i32)`, then this function will generate tokens for
/// `a, b,`. This is just to pass through arguments from inside a
/// wrapper function.
///
/// Note that the first parameter `&self` is omitted. This is so that the caller
/// can emit the tokens for the first argument as appropriate.
pub fn input_arg_names<'a>(&'a self) -> impl ToTokens + 'a {
ArgNames(&self.inputs[1..])
}
/// Generates the conversion from a Rust return value into a Glib value
///
/// For example, if the `FnSig` has an `output` type of `bool`,
/// and the `tokens` correspond to `true`, this function will
/// generate `<bool as ToGlib>::to_glib(&true)`. This can be used
/// by code which generates a function callable from C that wraps
/// Rust code.
pub fn ret_to_glib<'a, T: ToTokens + 'a>(&'a self, tokens: T) -> impl ToTokens + 'a {
ToGlib(&self.output, tokens)
}
pub fn ret_from_glib_fn<'a, V: ToTokens>(&'a self, v: &'a V) -> impl ToTokens + 'a {
let mut tokens = Tokens::new();
v.to_tokens(&mut tokens);
FromGlib(&self.output, tokens)
}
}
struct ToGlibType<'ast>(&'ast Ty<'ast>, &'ast FnSig<'ast>);
impl<'ast> ToTokens for ToGlibType<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self.0 {
Ty::Unit => self.0.to_tokens(tokens),
Ty::Char(i) | Ty::Bool(i) => {
(quote_cs! {
<#i as ToGlib>::GlibType
}).to_tokens(tokens);
}
Ty::Borrowed(ref t) => {
(quote_cs! {
<#t as GlibPtrDefault>::GlibType
}).to_tokens(tokens);
}
Ty::Integer(i) => i.to_tokens(tokens),
Ty::Owned(_) => {
Diagnostic::spanned(
self.0.span().unstable(),
Level::Error,
"unimplemented glib type for owned types",
).emit();
(quote! {
()
}).to_tokens(tokens);
}
}
}
}
struct ToGlib<'ast, T>(&'ast Ty<'ast>, T);
impl<'ast, T: ToTokens> ToTokens for ToGlib<'ast, T> {
fn to_tokens(&self, tokens: &mut Tokens) {
let expr = &self.1;
match *self.0 {
// no conversion necessary
Ty::Unit | Ty::Integer(_) => self.1.to_tokens(tokens),
Ty::Char(i) | Ty::Bool(i) => {
(quote_cs! {
<#i as ToGlib>::to_glib(&#expr)
}).to_tokens(tokens);
}
Ty::Borrowed(ref t) => {
(quote_cs! {
<#t as ToGlibPtr<_>>::to_glib_none(#expr).0
}).to_tokens(tokens);
}
Ty::Owned(_) => {
Diagnostic::spanned(
self.0.span().unstable(),
Level::Error,
"unimplemented glib type for owned types",
).emit();
(quote! {
()
}).to_tokens(tokens);
}
}
}
}
struct FromGlib<'ast>(&'ast Ty<'ast>, Tokens);
impl<'ast> ToTokens for FromGlib<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
let needs_conversion = match *self.0 {
Ty::Unit => false, // no conversion necessary
Ty::Char(i) | Ty::Bool(i) => {
(quote_cs! {
<#i as FromGlib<_>>::from_glib
}).to_tokens(tokens);
true
}
Ty::Borrowed(ref t) => {
(quote_cs! {
&<#t as FromGlibPtrBorrow<_>>::from_glib_borrow
}).to_tokens(tokens);
true
}
Ty::Integer(_) => false, // no conversion necessary
Ty::Owned(_) => {
Diagnostic::spanned(
self.0.span().unstable(),
Level::Error,
"unimplemented glib type for owned types",
).emit();
false
}
};
if needs_conversion {
tokens.append(TokenTree::Group(Group::new(
Delimiter::Parenthesis,
self.1.clone().into_tokens().into(),
)));
} else {
self.1.to_tokens(tokens);
}
}
}
struct ArgTypes<'ast>(&'ast FnSig<'ast>);
impl<'ast> ToTokens for ArgTypes<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0.inputs[1..].iter() {
match *arg {
FnArg::Arg { ref ty, .. } => {
ty.to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct FnArgsWithGlibTypes<'ast>(&'ast FnSig<'ast>);
impl<'ast> ToTokens for FnArgsWithGlibTypes<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0.inputs[1..].iter() {
match *arg {
FnArg::Arg {
name,
ref ty,
mutbl: _,
} => {
name.to_tokens(tokens);
Token!(:)([Span::call_site()]).to_tokens(tokens);
ToGlibType(ty, self.0).to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct ArgNamesFromGlib<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNamesFromGlib<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::Arg {
ref name,
ref ty,
mutbl: _,
} => {
let mut name_tokens = Tokens::new();
name.to_tokens(&mut name_tokens);
FromGlib(ty, name_tokens).to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct ArgNamesToGlib<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNamesToGlib<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::Arg {
ref ty,
name,
mutbl: _,
} => {
ToGlib(ty, name).to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
struct ArgNamesToGlibValues<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNamesToGlibValues<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::SelfRef(..) => {
let code = quote_cs! {
(self as &glib::ToValue).to_value(),
};
code.to_tokens(tokens);
}
FnArg::Arg { name, .. } => {
let code = quote_cs! {
(&#name as &glib::ToValue).to_value(),
};
code.to_tokens(tokens);
}
}
}
}
}
struct ArgNames<'ast>(&'ast [FnArg<'ast>]);
impl<'ast> ToTokens for ArgNames<'ast> {
fn to_tokens(&self, tokens: &mut Tokens) {
for arg in self.0 {
match *arg {
FnArg::Arg { name, .. } => {
name.to_tokens(tokens);
Token!(,)([Span::call_site()]).to_tokens(tokens);
}
FnArg::SelfRef(..) => unreachable!(),
}
}
}
}
......@@ -23,11 +23,17 @@ use super::glib_utils::*;
pub struct Program<'ast> {
pub classes: Classes<'ast>,
pub interfaces: Interfaces<'ast>,
}
pub struct Classes<'ast> {
items: HashMap<Ident, Class<'ast>>,
}
pub struct Interfaces<'ast> {
items: HashMap<Ident, Interface<'ast>>,
}
#[cfg_attr(rustfmt, rustfmt_skip)]
pub struct Class<'ast> {
pub name: Ident, // Foo
......@@ -49,6 +55,14 @@ pub struct Class<'ast> {
pub overrides: HashMap<Ident, Vec<Method<'ast>>>,
}
pub struct Interface<'ast> {
pub name: Ident, // Foo
// The order of these is important; it's the order of the slots in FooIface
pub slots: Vec<Slot<'ast>>,
// pub n_reserved_slots: usize,
}
pub enum Slot<'ast> {
Method(Method<'ast>),
VirtualMethod(VirtualMethod<'ast>),
......@@ -156,7 +170,15 @@ impl<'ast> Program<'ast> {
classes.add_impl(impl_)?;
}
Ok(Program { classes })
let mut interfaces = Interfaces::new();
for iface in ast.interfaces() {
interfaces.add(iface)?;
}
Ok(Program {
classes,
interfaces,
})
}
}
......@@ -189,8 +211,9 @@ impl<'ast> Classes<'ast> {
.items
.iter()
.filter_map(|i| match *i {
ast::ClassItem::PrivateField(ref field) => Some(field)
}).collect(),
ast::ClassItem::PrivateField(ref field) => Some(field),
})
.collect(),
slots: Vec::new(),
overrides: HashMap::new(),
},
......@@ -206,8 +229,12 @@ impl<'ast> Classes<'ast> {
Some(class) => class,
None => bail!("impl for class that doesn't exist: {}", impl_.self_path),
};
match impl_.trait_ {
Some(parent_class) => {
match *impl_ {
ast::Impl {
is_interface: false,
trait_: Some(parent_class),
..
} => {
for item in impl_.items.iter() {
let item = match item.node {
ast::ImplItemKind::Method(ref m) => m,
......@@ -245,12 +272,25 @@ impl<'ast> Classes<'ast> {
.push(method);
}
}
None => {
ast::Impl {
is_interface: false,
trait_: None,
..
} => {
for item in impl_.items.iter() {
let slot = class.translate_slot(item)?;
class.slots.push(slot);
}
}
ast::Impl {
is_interface: true,
trait_: Some(_parent_class),
..
} => unimplemented!(),
_ => unreachable!(),
}
Ok(())
......@@ -457,6 +497,36 @@ impl<'ast> Class<'ast> {
}
}
impl<'ast> Interfaces<'ast> {
fn new() -> Interfaces<'ast> {
Interfaces {
items: HashMap::new(),
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn get(&self, name: &str) -> &Interface {
self.items.iter().find(|c| c.1.name == name).unwrap().1
}
fn add(&mut self, ast_iface: &'ast ast::Interface) -> Result<()> {
let prev = self.items.insert(
ast_iface.name,
Interface {
name: ast_iface.name,
slots: Vec::new(),
},
);
if prev.is_some() {
bail!("redefinition of interface `{}`", ast_iface.name);
}
Ok(())
}
}
fn make_path_glib_object() -> Path {
let tokens = quote_cs! { glib::Object };
let token_stream = TokenStream::from(tokens);
......
......@@ -63,7 +63,7 @@ mod parser;
/// impl Foo {
/// pub fn a_static_method(&self) {
/// // self.get_priv() gives us access to the private
// fields declared in class Foo
// fields declared in class Foo
/// do_something_with_u32(self.get_priv().private_field.get());
/// }
///
......@@ -150,7 +150,7 @@ pub fn gobject_gen(input: TokenStream) -> TokenStream {
let result: Result<quote::Tokens> = (|| {
let program = hir::Program::from_ast_program(&ast_program)?;
gen::classes(&program)
gen::codegen(&program)
})();
match result {
......
......@@ -69,6 +69,22 @@ impl Synom for ast::Class {
}
}
impl Synom for ast::Interface {
named!(parse -> Self, do_parse!(
call!(keyword("interface")) >>
name: syn!(Ident) >>
items_and_braces: braces!(many0!(syn!(ast::ImplItem))) >>
(ast::Interface {
name: name,
items: items_and_braces.1,
})
));
fn description() -> Option<&'static str> {
Some("interface item")
}
}
impl Synom for ast::ClassItem {
named!(parse -> Self, alt!(
syn!(ast::Field) => { |x| ast::ClassItem::PrivateField(x) }
......@@ -97,6 +113,7 @@ impl Synom for ast::Field {
impl Synom for ast::Impl {
named!(parse -> Self, do_parse!(
keyword!(impl) >>
interface: option!(call!(keyword("interface"))) >>
trait_: option!(do_parse!(
path: syn!(Ident) >>
keyword!(for) >>
......@@ -105,6 +122,7 @@ impl Synom for ast::Impl {
self_path: syn!(Ident) >>
body: braces!(many0!(syn!(ast::ImplItem))) >>
(ast::Impl {
is_interface: interface.is_some(),
trait_: trait_,
self_path: self_path,
items: body.1
......@@ -238,6 +256,8 @@ pub mod tests {
parses_plain_impl_item();
parses_impl_item_with_trait();
parses_class_with_private_field();
parses_impl_interface();
parses_interface();
}
fn assert_tokens_equal<T: ToTokens>(x: &T, s: &str) {
......@@ -259,7 +279,8 @@ pub mod tests {
foo : u32;
bar : u32;
baz : u32;
}";
\
}";
let class = parse_str::<ast::Class>(raw).unwrap();
assert_eq!(class.items.len(), 3);
......@@ -285,10 +306,17 @@ pub mod tests {
}
}
fn test_parsing_impl_item(raw: &str, trait_name: Option<&str>, self_name: &str) {
fn test_parsing_impl_item(
raw: &str,
trait_name: Option<&str>,
self_name: &str,
is_interface: bool,
) {
let item = parse_str::<ast::Item>(raw).unwrap();
if let ast::Item::Impl(ref impl_) = item {
assert_eq!(impl_.is_interface, is_interface);
if let Some(trait_path) = impl_.trait_ {
assert_tokens_equal(&trait_path, trait_name.as_ref().unwrap());
} else {
......@@ -302,10 +330,21 @@ pub mod tests {
}
fn parses_plain_impl_item() {
test_parsing_impl_item("impl Foo {}", None, "Foo");
test_parsing_impl_item("impl Foo {}", None, "Foo", false);
}
fn parses_impl_item_with_trait() {
test_parsing_impl_item("impl Foo for Bar {}", Some("Foo"), "Bar");
test_parsing_impl_item("impl Foo for Bar {}", Some("Foo"), "Bar", false);
}
fn parses_impl_interface() {
test_parsing_impl_item("impl interface Foo for Bar {}", Some("Foo"), "Bar", true);
}
fn parses_interface() {
let raw = "interface Foo { virtual fn bar(&self); }";
let iface = parse_str::<ast::Interface>(raw).unwrap();
assert_eq!(iface.name.as_ref(), "Foo");
}
}
#![feature(proc_macro)]
extern crate gobject_gen;
extern crate gobject_sys;
#[macro_use]
extern crate glib;
extern crate glib_sys;
extern crate libc;
use gobject_gen::gobject_gen;
gobject_gen! {
interface Foo {
virtual fn foo(&self);
}
}