diff --git a/Cargo.toml b/Cargo.toml index 92064c25..63dfee7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ wasm-bindgen-cli-support = { path = "crates/cli-support", version = '=0.2.11' } [workspace] members = [ "crates/cli", + "crates/webidl", "examples/hello_world", "examples/smorgasboard", "examples/console_log", diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml new file mode 100644 index 00000000..2a53980f --- /dev/null +++ b/crates/webidl/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "wasm-bindgen-webidl" +version = "0.1.0" +authors = ["Nick Fitzgerald "] + +[[test]] +name = "webidl-all" +path = "tests/all/lib.rs" + +[dev-dependencies] +wasm-bindgen-backend = { version = "=0.2.11", path = "../backend", features = ["extra-traits"] } + +[dependencies] +failure = "0.1" +proc-macro2 = "0.4" +syn = { version = '0.14', features = ['full'] } +wasm-bindgen-backend = { version = "=0.2.11", path = "../backend" } +webidl = "0.6.0" diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs new file mode 100755 index 00000000..4458d43d --- /dev/null +++ b/crates/webidl/src/lib.rs @@ -0,0 +1,102 @@ +/*! +# `wasm_bindgen_webidl` + +Converts WebIDL into wasm-bindgen's internal AST form, so that bindings can be +emitted for the types and methods described in the WebIDL. + */ + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] + +extern crate failure; +extern crate proc_macro2; +extern crate syn; +extern crate wasm_bindgen_backend as backend; +extern crate webidl; + +use failure::ResultExt; +use proc_macro2::Ident; +use std::fs; +use std::io::{self, Read}; +use std::path::Path; + +/// Either `Ok(t)` or `Err(failure::Error)`. +pub type Result = ::std::result::Result; + +/// Parse the WebIDL at the given path into a wasm-bindgen AST. +pub fn parse_file(webidl_path: &Path) -> Result { + let file = fs::File::open(webidl_path).context("opening WebIDL file")?; + let mut file = io::BufReader::new(file); + let mut source = String::new(); + file.read_to_string(&mut source) + .context("reading WebIDL file")?; + parse(&source) +} + +/// Parse a string of WebIDL source text into a wasm-bindgen AST. +pub fn parse(webidl_source: &str) -> Result { + let definitions = webidl::parse_string(webidl_source).context("parsing WebIDL source text")?; + + let mut program = backend::ast::Program::default(); + definitions.webidl_parse(&mut program)?; + + Ok(program) +} + +trait WebidlParse { + fn webidl_parse(&self, program: &mut backend::ast::Program) -> Result<()>; +} + +impl WebidlParse for Vec { + fn webidl_parse(&self, program: &mut backend::ast::Program) -> Result<()> { + for def in self { + def.webidl_parse(program)?; + } + Ok(()) + } +} + +impl WebidlParse for webidl::ast::Definition { + fn webidl_parse(&self, program: &mut backend::ast::Program) -> Result<()> { + match *self { + webidl::ast::Definition::Interface(ref interface) => interface.webidl_parse(program), + // TODO + webidl::ast::Definition::Callback(..) + | webidl::ast::Definition::Dictionary(..) + | webidl::ast::Definition::Enum(..) + | webidl::ast::Definition::Implements(..) + | webidl::ast::Definition::Includes(..) + | webidl::ast::Definition::Mixin(..) + | webidl::ast::Definition::Namespace(..) + | webidl::ast::Definition::Typedef(..) => Ok(()), + } + } +} + +impl WebidlParse for webidl::ast::Interface { + fn webidl_parse(&self, program: &mut backend::ast::Program) -> Result<()> { + match *self { + webidl::ast::Interface::NonPartial(ref interface) => interface.webidl_parse(program), + // TODO + webidl::ast::Interface::Callback(..) | webidl::ast::Interface::Partial(..) => Ok(()), + } + } +} + +impl WebidlParse for webidl::ast::NonPartialInterface { + fn webidl_parse(&self, program: &mut backend::ast::Program) -> Result<()> { + program.imports.push(backend::ast::Import { + module: None, + version: None, + js_namespace: None, + kind: backend::ast::ImportKind::Type(backend::ast::ImportType { + vis: syn::Visibility::Public(syn::VisPublic { + pub_token: syn::token::Pub(proc_macro2::Span::call_site()), + }), + name: Ident::new(&self.name, proc_macro2::Span::call_site()), + }), + }); + println!("{:#?}", self); + Ok(()) + } +} diff --git a/crates/webidl/tests/all/Event.webidl b/crates/webidl/tests/all/Event.webidl new file mode 100644 index 00000000..972961e9 --- /dev/null +++ b/crates/webidl/tests/all/Event.webidl @@ -0,0 +1,59 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://www.w3.org/TR/2012/WD-dom-20120105/ + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +[Constructor(DOMString type, optional EventInit eventInitDict), + Exposed=(Window,Worker,System), ProbablyShortLivingWrapper] +interface Event { + [Pure] + readonly attribute DOMString type; + [Pure] + readonly attribute EventTarget? target; + [Pure] + readonly attribute EventTarget? currentTarget; + + sequence composedPath(); + + const unsigned short NONE = 0; + const unsigned short CAPTURING_PHASE = 1; + const unsigned short AT_TARGET = 2; + const unsigned short BUBBLING_PHASE = 3; + [Pure] + readonly attribute unsigned short eventPhase; + + void stopPropagation(); + void stopImmediatePropagation(); + + [Pure] + readonly attribute boolean bubbles; + [Pure] + readonly attribute boolean cancelable; + [NeedsCallerType] + void preventDefault(); + [Pure, NeedsCallerType] + readonly attribute boolean defaultPrevented; + [ChromeOnly, Pure] + readonly attribute boolean defaultPreventedByChrome; + [ChromeOnly, Pure] + readonly attribute boolean defaultPreventedByContent; + [Pure] + readonly attribute boolean composed; + + [Unforgeable, Pure] + readonly attribute boolean isTrusted; + [Pure] + readonly attribute DOMHighResTimeStamp timeStamp; + + void initEvent(DOMString type, + optional boolean bubbles = false, + optional boolean cancelable = false); + attribute boolean cancelBubble; +}; diff --git a/crates/webidl/tests/all/event.rs b/crates/webidl/tests/all/event.rs new file mode 100644 index 00000000..1a55cc29 --- /dev/null +++ b/crates/webidl/tests/all/event.rs @@ -0,0 +1,24 @@ +use super::backend; +use proc_macro2; +use syn; + +assert_parse!( + event, + include_str!("./Event.webidl"), + backend::ast::Program { + exports: vec![], + imports: vec![backend::ast::Import { + module: None, + version: None, + js_namespace: None, + kind: backend::ast::ImportKind::Type(backend::ast::ImportType { + vis: syn::Visibility::Public(syn::VisPublic { + pub_token: syn::token::Pub(proc_macro2::Span::call_site()), + }), + name: syn::Ident::new("Event", proc_macro2::Span::call_site()), + }), + }], + enums: vec![], + structs: vec![], + } +); diff --git a/crates/webidl/tests/all/lib.rs b/crates/webidl/tests/all/lib.rs new file mode 100644 index 00000000..bcb50c28 --- /dev/null +++ b/crates/webidl/tests/all/lib.rs @@ -0,0 +1,21 @@ +extern crate proc_macro2; +extern crate syn; +extern crate wasm_bindgen_backend as backend; +extern crate wasm_bindgen_webidl as wb_webidl; + +pub fn assert_parse(webidl: &str, expected: backend::ast::Program) { + let actual = wb_webidl::parse(webidl).expect("should parse the webidl source OK"); + assert_eq!(expected, actual); +} + +macro_rules! assert_parse { + ($test_name:ident, $webidl_source:expr, $expected_ast:expr) => { + #[test] + fn $test_name() { + $crate::assert_parse($webidl_source, $expected_ast); + } + }; +} + +mod event; +mod simple; diff --git a/crates/webidl/tests/all/simple.rs b/crates/webidl/tests/all/simple.rs new file mode 100644 index 00000000..dd2118ef --- /dev/null +++ b/crates/webidl/tests/all/simple.rs @@ -0,0 +1,3 @@ +use super::backend; + +assert_parse!(empty, "", backend::ast::Program::default());