mirror of
https://github.com/swc-project/swc.git
synced 2024-08-16 11:50:22 +03:00
initial commit
This commit is contained in:
commit
0f9532dd5d
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
/target/
|
||||
**/*.bk
|
||||
Cargo.lock
|
5
.rustfmt.toml
Normal file
5
.rustfmt.toml
Normal file
@ -0,0 +1,5 @@
|
||||
reorder_imports = true
|
||||
reorder_imports_in_group = true
|
||||
reorder_imported_names = true
|
||||
wrap_comments = true
|
||||
write_mode = "replace"
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[workspace]
|
||||
|
||||
|
||||
[package]
|
||||
name = "swc"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[dependencies]
|
||||
swc_atoms = { path = "./atoms" }
|
||||
swc_common = { path = "./common" }
|
||||
swc_macros = { path = "./macros" }
|
201
LICENSE-APACHE
Normal file
201
LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
25
LICENSE-MIT
Normal file
25
LICENSE-MIT
Normal file
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2017 The swc Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
11
atoms/Cargo.toml
Normal file
11
atoms/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "swc_atoms"
|
||||
build = "build.rs"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[dependencies]
|
||||
string_cache = "0.6"
|
||||
|
||||
[build-dependencies]
|
||||
string_cache_codegen = "0.4"
|
57
atoms/build.rs
Normal file
57
atoms/build.rs
Normal file
@ -0,0 +1,57 @@
|
||||
extern crate string_cache_codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
gen(
|
||||
"js_ident",
|
||||
"JsIdent",
|
||||
&[
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"do",
|
||||
"else",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"return",
|
||||
"switch",
|
||||
"throw",
|
||||
"try",
|
||||
"var",
|
||||
"let",
|
||||
"const",
|
||||
"while",
|
||||
"with",
|
||||
"new",
|
||||
"this",
|
||||
"super",
|
||||
"class",
|
||||
"extends",
|
||||
"export",
|
||||
"import",
|
||||
"yield",
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"in",
|
||||
"instanceof",
|
||||
"typeof",
|
||||
"void",
|
||||
"delete",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn gen(mac_name: &str, type_name: &str, atoms: &[&str]) {
|
||||
string_cache_codegen::AtomType::new(type_name, &format!("{}!", mac_name))
|
||||
.atoms(atoms)
|
||||
.write_to_file(&Path::new(&env::var("OUT_DIR").unwrap()).join(format!("{}.rs", mac_name)))
|
||||
.unwrap();
|
||||
}
|
3
atoms/src/lib.rs
Normal file
3
atoms/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
extern crate string_cache;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/js_ident.rs"));
|
6
common/Cargo.toml
Normal file
6
common/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "swc_common"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[dependencies]
|
7
common/src/ast_node.rs
Normal file
7
common/src/ast_node.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use EqIgnoreSpan;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Currently just a marker trait.
|
||||
///
|
||||
pub trait AstNode: Debug + EqIgnoreSpan + Hash + Clone {}
|
88
common/src/eq_ignore_span.rs
Normal file
88
common/src/eq_ignore_span.rs
Normal file
@ -0,0 +1,88 @@
|
||||
pub trait EqIgnoreSpan: Eq {
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Eq> EqIgnoreSpan for T {
|
||||
default fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
*self == *other
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_for_eq_ty {
|
||||
($Type:ty) => {
|
||||
impl $crate::EqIgnoreSpan for $Type {
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool { *self == *other }
|
||||
}
|
||||
};
|
||||
($Type:ty,) => {
|
||||
impl_for_eq_ty!($Type);
|
||||
};
|
||||
($Type:ty, $($rest:tt)+) => {
|
||||
impl_for_eq_ty!($Type);
|
||||
impl_for_eq_ty!($($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
impl_for_eq_ty!(
|
||||
bool,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
usize,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
isize,
|
||||
String,
|
||||
char,
|
||||
);
|
||||
|
||||
impl<T: EqIgnoreSpan> EqIgnoreSpan for Option<T> {
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
match (self.as_ref(), other.as_ref()) {
|
||||
(Some(l), Some(r)) if l.eq_ignore_span(r) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + EqIgnoreSpan> EqIgnoreSpan for &'a T {
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
<T as EqIgnoreSpan>::eq_ignore_span(*self, *other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EqIgnoreSpan> EqIgnoreSpan for [T] {
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
if self.len() != other.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..self.len() {
|
||||
if !self[i].eq_ignore_span(&other[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EqIgnoreSpan for Box<T>
|
||||
where
|
||||
T: ?Sized + EqIgnoreSpan,
|
||||
{
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
<T as EqIgnoreSpan>::eq_ignore_span(&self, &other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EqIgnoreSpan for Vec<T>
|
||||
where
|
||||
T: EqIgnoreSpan,
|
||||
{
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
(&self[..]).eq_ignore_span(&other[..])
|
||||
}
|
||||
}
|
9
common/src/lib.rs
Normal file
9
common/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![feature(specialization)]
|
||||
|
||||
pub use self::ast_node::AstNode;
|
||||
pub use self::eq_ignore_span::EqIgnoreSpan;
|
||||
pub use self::span::{BytePos, Span};
|
||||
|
||||
mod ast_node;
|
||||
mod eq_ignore_span;
|
||||
mod span;
|
37
common/src/span.rs
Normal file
37
common/src/span.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct BytePos(pub u32);
|
||||
impl Display for BytePos {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Span {
|
||||
pub start: BytePos,
|
||||
pub end: BytePos,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub const DUMMY: Span = Span {
|
||||
start: BytePos(0),
|
||||
end: BytePos(0),
|
||||
};
|
||||
}
|
||||
|
||||
impl Default for Span {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Span::DUMMY
|
||||
}
|
||||
}
|
||||
|
||||
impl ::EqIgnoreSpan for Span {
|
||||
/// always returns true
|
||||
#[inline]
|
||||
fn eq_ignore_span(&self, _: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
11
macros/Cargo.toml
Normal file
11
macros/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "swc_macros"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
ast_node = { path = "./ast_node" }
|
||||
enum_kind = { path = "./enum_kind" }
|
||||
eq_ignore_span = { path = "./eq_ignore_span" }
|
21
macros/ast_node/Cargo.toml
Normal file
21
macros/ast_node/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "ast_node"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
swc_macros_common = { path = "../common" }
|
||||
pmutil = { git = "https://github.com/kdy1/rust-pmutil" }
|
||||
proc-macro2 = { version = "0.1", features = ["unstable"] }
|
||||
|
||||
[dependencies.syn]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
features = ["full", "parsing", "printing", "extra-traits"]
|
||||
|
||||
|
||||
[dependencies.synom]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
|
58
macros/ast_node/src/lib.rs
Normal file
58
macros/ast_node/src/lib.rs
Normal file
@ -0,0 +1,58 @@
|
||||
#![feature(box_syntax, proc_macro)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate pmutil;
|
||||
extern crate proc_macro2;
|
||||
extern crate proc_macro;
|
||||
#[macro_use]
|
||||
extern crate swc_macros_common as common;
|
||||
extern crate syn;
|
||||
|
||||
use common::prelude::*;
|
||||
|
||||
#[proc_macro_derive(AstNode)]
|
||||
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
|
||||
let type_name = &input.ident;
|
||||
|
||||
let item: Item = Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
CONST_NAME: type_name.new_ident_with(|n| format!("_IMPL_AST_NODE_FOR_{}", n)),
|
||||
Type: type_name,
|
||||
},
|
||||
{
|
||||
#[allow(non_upper_case_globals)]
|
||||
const CONST_NAME: () = {
|
||||
extern crate swc_common as _swc_common;
|
||||
impl _swc_common::AstNode for Type {}
|
||||
()
|
||||
};
|
||||
}
|
||||
))
|
||||
.parse();
|
||||
|
||||
print("derive(AstNode)", item.into_tokens())
|
||||
}
|
||||
|
||||
/// Alias for
|
||||
///
|
||||
/// `#[derive(Clone, Debug, Eq, PartialEq, Hash, EqIgnoreSpan, AstNode)]`
|
||||
#[proc_macro_attribute]
|
||||
pub fn ast_node(
|
||||
_attr: proc_macro::TokenStream,
|
||||
s: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item: Item = syn::parse(s).expect("failed to parse tokens as an item");
|
||||
|
||||
let tokens = pmutil::Quote::new_call_site().quote_with(smart_quote!(Vars { item }, {
|
||||
#[derive(
|
||||
Clone, Debug, Eq, PartialEq, Hash,
|
||||
::swc_macros::EqIgnoreSpan,
|
||||
::swc_macros::AstNode
|
||||
)]
|
||||
item
|
||||
}));
|
||||
|
||||
print("ast_node", tokens)
|
||||
}
|
19
macros/common/Cargo.toml
Normal file
19
macros/common/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "swc_macros_common"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[dependencies]
|
||||
pmutil = { git = "https://github.com/kdy1/rust-pmutil" }
|
||||
proc-macro2 = { version = "0.1", features = ["unstable"] }
|
||||
|
||||
[dependencies.syn]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
features = ["full", "parsing", "printing", "extra-traits"]
|
||||
|
||||
|
||||
[dependencies.synom]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
|
||||
[dependencies.quote]
|
||||
git = "https://github.com/dtolnay/quote"
|
56
macros/common/src/lib.rs
Normal file
56
macros/common/src/lib.rs
Normal file
@ -0,0 +1,56 @@
|
||||
extern crate pmutil;
|
||||
extern crate proc_macro2;
|
||||
extern crate proc_macro;
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
use pmutil::synom_ext::FromSpan;
|
||||
use proc_macro2::Span;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub fn call_site<T: FromSpan>() -> T {
|
||||
FromSpan::from_span(Span::call_site())
|
||||
}
|
||||
|
||||
/// `attr` - tokens inside `#[]`. e.g. `derive(EqIgnoreSpan)`, ast_node
|
||||
pub fn print<T: Into<proc_macro2::TokenStream>>(
|
||||
attr: &'static str,
|
||||
t: T,
|
||||
) -> proc_macro::TokenStream {
|
||||
use std::env;
|
||||
|
||||
let tokens = t.into();
|
||||
|
||||
match env::var("PRINT_GENERATED") {
|
||||
Ok(ref s) if s == "1" || attr == s => {}
|
||||
_ => return tokens.into(),
|
||||
}
|
||||
|
||||
println!("\n\tOutput of #[{}]:\n\t {}", attr, tokens);
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
/// fail! is a panic! with location reporting.
|
||||
#[macro_export]
|
||||
macro_rules! fail {
|
||||
($($args:tt)+) => {{
|
||||
panic!("{}\n --> {}:{}:{}", format_args!($($args)*), file!(), line!(), column!());
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unimplemented {
|
||||
($($args:tt)+) => {{
|
||||
fail!("not yet implemented: {}", format_args!($($args)*));
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unreachable {
|
||||
() => {{
|
||||
fail!("internal error: unreacable");
|
||||
}};
|
||||
($($args:tt)+) => {{
|
||||
fail!("internal error: unreacable\n{}", format_args!($($args)*));
|
||||
}};
|
||||
}
|
8
macros/common/src/prelude.rs
Normal file
8
macros/common/src/prelude.rs
Normal file
@ -0,0 +1,8 @@
|
||||
pub use pmutil::prelude::*;
|
||||
pub use quote::{ToTokens, Tokens};
|
||||
|
||||
pub use super::{call_site, print};
|
||||
pub use proc_macro2::{Delimiter, Span, TokenNode, TokenStream, TokenTree};
|
||||
pub use syn::*;
|
||||
pub use syn::Span as SynSpan;
|
||||
pub use syn::delimited::{Delimited, Element};
|
21
macros/enum_kind/Cargo.toml
Normal file
21
macros/enum_kind/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "enum_kind"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
swc_macros_common = { path = "../common" }
|
||||
pmutil = { git = "https://github.com/kdy1/rust-pmutil" }
|
||||
proc-macro2 = { version = "0.1", features = ["unstable"] }
|
||||
|
||||
[dependencies.syn]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
features = ["full", "parsing", "printing", "extra-traits"]
|
||||
|
||||
|
||||
[dependencies.synom]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
|
189
macros/enum_kind/src/expand.rs
Normal file
189
macros/enum_kind/src/expand.rs
Normal file
@ -0,0 +1,189 @@
|
||||
use common::prelude::*;
|
||||
use input::*;
|
||||
use util::{is_attr_name, is_bool};
|
||||
|
||||
pub fn expand(
|
||||
Input {
|
||||
attrs,
|
||||
name,
|
||||
variants,
|
||||
generics,
|
||||
vis,
|
||||
}: Input,
|
||||
) -> Item {
|
||||
let items = attrs
|
||||
.fns
|
||||
.into_iter()
|
||||
.map(|f| f.expand(&name, vis.clone(), &variants))
|
||||
.map(ImplItem::Method)
|
||||
.fold(Tokens::new(), |mut t, i| {
|
||||
i.to_tokens(&mut t);
|
||||
t
|
||||
});
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
impl_generics,
|
||||
name,
|
||||
ty_generics,
|
||||
where_clause,
|
||||
items,
|
||||
},
|
||||
{
|
||||
impl impl_generics name ty_generics where_clause {
|
||||
items
|
||||
}
|
||||
}
|
||||
))
|
||||
.parse()
|
||||
}
|
||||
|
||||
impl FnDef {
|
||||
fn expand(self, enum_name: &Ident, vis: Visibility, variants: &[EnumVar]) -> ImplItemMethod {
|
||||
let FnDef {
|
||||
name,
|
||||
return_type,
|
||||
default_value,
|
||||
} = self;
|
||||
|
||||
let name_span = name.span;
|
||||
|
||||
let arms = variants
|
||||
.iter()
|
||||
.map(|v| -> Arm {
|
||||
// Pattern for this variant.
|
||||
let pat = match v.data {
|
||||
VariantData::Struct(ref _fields, _) => Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
EnumName: enum_name,
|
||||
VariantName: v.name,
|
||||
},
|
||||
{ &EnumName::VariantName {..} }
|
||||
))
|
||||
.parse::<Pat>(),
|
||||
VariantData::Tuple(ref _fields, _) => Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
EnumName: enum_name,
|
||||
VariantName: v.name,
|
||||
},
|
||||
{ &EnumName::VariantName(..) }
|
||||
))
|
||||
.parse::<Pat>(),
|
||||
VariantData::Unit => Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
EnumName: enum_name,
|
||||
VariantName: v.name,
|
||||
},
|
||||
{ &EnumName::VariantName }
|
||||
))
|
||||
.parse::<Pat>(),
|
||||
};
|
||||
|
||||
let body = {
|
||||
let value = match v.attrs
|
||||
.fn_values
|
||||
.iter()
|
||||
.find(|fn_val| fn_val.fn_name == name)
|
||||
.map(|attr| attr.value.clone())
|
||||
{
|
||||
Some(Some(value)) => Some(value),
|
||||
|
||||
// if return type is bool and attribute is specified, it return true.
|
||||
Some(None) if is_bool(&return_type) => Some(
|
||||
ExprKind::Lit(Lit {
|
||||
value: LitKind::Bool(true),
|
||||
span: SynSpan(Span::call_site()),
|
||||
}).into(),
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
value
|
||||
.or_else(|| default_value.clone())
|
||||
.map(Box::new)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"value of {fn_name} for {variant} is not specified.",
|
||||
fn_name = name,
|
||||
variant = v.name
|
||||
);
|
||||
})
|
||||
};
|
||||
|
||||
Arm {
|
||||
pats: vec![pat].into(),
|
||||
body,
|
||||
|
||||
// Forward cfg attributes.
|
||||
attrs: v.attrs
|
||||
.extras
|
||||
.iter()
|
||||
.filter(|attr| is_attr_name(attr, "cfg"))
|
||||
.cloned()
|
||||
.collect(),
|
||||
rocket_token: call_site(),
|
||||
comma: Some(call_site()),
|
||||
guard: None,
|
||||
if_token: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// match self {}
|
||||
let match_expr = ExprKind::Match(ExprMatch {
|
||||
match_token: call_site(),
|
||||
brace_token: call_site(),
|
||||
|
||||
expr: Quote::new_call_site()
|
||||
.quote_with(smart_quote!(Vars {}, { self }))
|
||||
.parse::<Expr>()
|
||||
.into(),
|
||||
|
||||
arms,
|
||||
}).into();
|
||||
|
||||
ImplItemMethod {
|
||||
sig: MethodSig {
|
||||
constness: Constness::NotConst,
|
||||
unsafety: Unsafety::Normal,
|
||||
abi: None,
|
||||
ident: name,
|
||||
// fn (&self) -> ReturnTpe
|
||||
decl: FnDecl {
|
||||
fn_token: name.span.as_token(),
|
||||
paren_token: name.span.as_token(),
|
||||
inputs: vec![
|
||||
// TODO
|
||||
FnArg::SelfRef(ArgSelfRef {
|
||||
and_token: name_span.as_token(),
|
||||
self_token: name_span.as_token(),
|
||||
lifetime: None,
|
||||
mutbl: Mutability::Immutable,
|
||||
}),
|
||||
].into(),
|
||||
output: ReturnType::Type(return_type, name_span.as_token()),
|
||||
generics: Default::default(),
|
||||
variadic: false,
|
||||
dot_tokens: None,
|
||||
},
|
||||
},
|
||||
|
||||
block: Block {
|
||||
brace_token: call_site(),
|
||||
stmts: vec![Stmt::Expr(Box::new(match_expr))],
|
||||
},
|
||||
|
||||
// TODO
|
||||
vis,
|
||||
|
||||
attrs: Default::default(),
|
||||
defaultness: Defaultness::Final,
|
||||
}
|
||||
}
|
||||
}
|
54
macros/enum_kind/src/input.rs
Normal file
54
macros/enum_kind/src/input.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use syn::*;
|
||||
|
||||
/// Parsed input.
|
||||
#[derive(Debug)]
|
||||
pub struct Input {
|
||||
pub attrs: EnumAttrs,
|
||||
/// Name of enum.
|
||||
pub name: Ident,
|
||||
pub vis: Visibility,
|
||||
pub generics: Generics,
|
||||
|
||||
pub variants: Vec<EnumVar>,
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EnumAttrs {
|
||||
pub fns: Vec<FnDef>,
|
||||
pub extras: Vec<Attribute>,
|
||||
}
|
||||
|
||||
/// Function to generate.
|
||||
///
|
||||
/// `#[kind(function(name = "bool"))]`
|
||||
#[derive(Debug)]
|
||||
pub struct FnDef {
|
||||
/// Name of function.
|
||||
pub name: Ident,
|
||||
pub return_type: Type,
|
||||
|
||||
pub default_value: Option<Expr>,
|
||||
}
|
||||
|
||||
/// Variant of enum.
|
||||
#[derive(Debug)]
|
||||
pub struct EnumVar {
|
||||
/// Name of variant.
|
||||
pub name: Ident,
|
||||
pub attrs: VariantAttrs,
|
||||
pub data: VariantData,
|
||||
}
|
||||
|
||||
/// Parsed attributes.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VariantAttrs {
|
||||
pub fn_values: Vec<VariantAttr>,
|
||||
pub extras: Vec<Attribute>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VariantAttr {
|
||||
pub fn_name: Ident,
|
||||
pub value: Option<Expr>,
|
||||
}
|
167
macros/enum_kind/src/lib.rs
Normal file
167
macros/enum_kind/src/lib.rs
Normal file
@ -0,0 +1,167 @@
|
||||
//! # Atributes on enum
|
||||
//! ## functions
|
||||
//! `#[kind(functions(name = "return_type"))]`
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use]
|
||||
//! extern crate enum_kind;
|
||||
//!
|
||||
//! /// You can split attributes if you want.
|
||||
//! #[derive(Kind)]
|
||||
//! #[kind(functions(is_a = "bool", is_b = "bool"))]
|
||||
//! #[kind(functions(is_a_or_b = "bool", num = "u8"))]
|
||||
//! pub enum E {
|
||||
//! #[kind(is_a, is_a_or_b, num = "1")]
|
||||
//! A,
|
||||
//! /// You can split attributes if you want.
|
||||
//! #[kind(is_b)]
|
||||
//! #[kind(is_a_or_b)]
|
||||
//! #[kind(num = "2")]
|
||||
//! B(u8),
|
||||
//! /// Default value of bool is false if not specified and true if specified.
|
||||
//! ///
|
||||
//! /// Both struct like variant and tuple like variant are supported.
|
||||
//! #[kind(num = "3")]
|
||||
//! C {},
|
||||
//! }
|
||||
//! # fn main() {
|
||||
//! assert!(E::A.is_a() && E::A.is_a_or_b() && !E::A.is_b());
|
||||
//! assert_eq!(E::A.num(), 1);
|
||||
//!
|
||||
//! assert!(!E::B(0).is_a() && E::B(0).is_a_or_b() && E::B(0).is_b());
|
||||
//! assert_eq!(E::B(0).num(), 2);
|
||||
//!
|
||||
//! assert!(!E::C{}.is_a() && !E::C{}.is_a_or_b() && !E::C{}.is_b());
|
||||
//! assert_eq!(E::C{}.num(), 3);
|
||||
//!
|
||||
//!
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! -----
|
||||
//!
|
||||
//! # Real usecase
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use]
|
||||
//! extern crate enum_kind;
|
||||
//!
|
||||
//! #[derive(Kind, Debug, Clone, Eq, PartialEq, Hash)]
|
||||
//! #[kind(function(precedence = "u8"))]
|
||||
//! pub enum BinOpToken {
|
||||
//! /// `==`
|
||||
//! #[kind(precedence = "6")]
|
||||
//! EqEq,
|
||||
//! /// `!=`
|
||||
//! #[kind(precedence = "6")]
|
||||
//! NotEq,
|
||||
//! /// `===`
|
||||
//! #[kind(precedence = "6")]
|
||||
//! EqEqEq,
|
||||
//! /// `!==`
|
||||
//! #[kind(precedence = "6")]
|
||||
//! NotEqEq,
|
||||
//! /// `<`
|
||||
//! #[kind(precedence = "7")]
|
||||
//! Lt,
|
||||
//! /// `<=`
|
||||
//! #[kind(precedence = "7")]
|
||||
//! LtEq,
|
||||
//! /// `>`
|
||||
//! #[kind(precedence = "7")]
|
||||
//! Gt,
|
||||
//! #[kind(precedence = "7")]
|
||||
//! /// `>=`
|
||||
//! #[kind(precedence = "7")]
|
||||
//! GtEq,
|
||||
//! /// `<<`
|
||||
//! #[kind(precedence = "8")]
|
||||
//! LShift,
|
||||
//! /// `>>`
|
||||
//! #[kind(precedence = "8")]
|
||||
//! RShift,
|
||||
//! /// `>>>`
|
||||
//! #[kind(precedence = "8")]
|
||||
//! ZeroFillRShift,
|
||||
//! /// `+`
|
||||
//! #[kind(precedence = "9")]
|
||||
//! Plus,
|
||||
//! /// `-`
|
||||
//! #[kind(precedence = "9")]
|
||||
//! Minus,
|
||||
//! /// `*`
|
||||
//! #[kind(precedence = "10")]
|
||||
//! Mul,
|
||||
//! /// `/`
|
||||
//! #[kind(precedence = "10")]
|
||||
//! Div,
|
||||
//! /// `%`
|
||||
//! #[kind(precedence = "10")]
|
||||
//! Mod,
|
||||
//! /// `|`
|
||||
//! #[kind(precedence = "3")]
|
||||
//! BitOr,
|
||||
//! /// `^`
|
||||
//! #[kind(precedence = "4")]
|
||||
//! BitXor,
|
||||
//! /// `&`
|
||||
//! #[kind(precedence = "5")]
|
||||
//! BitAnd,
|
||||
//! /// `in`
|
||||
//! #[kind(precedence = "7")]
|
||||
//! In,
|
||||
//! /// `instanceof`
|
||||
//! #[kind(precedence = "7")]
|
||||
//! InstanceOf,
|
||||
//! /// `**`
|
||||
//! #[kind(precedence = "10")]
|
||||
//! Exp,
|
||||
//! /// `||`
|
||||
//! #[kind(precedence = "1")]
|
||||
//! LogicalOr,
|
||||
//! /// `&&`
|
||||
//! #[kind(precedence = "2")]
|
||||
//! LogicalAnd,
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
#[macro_use]
|
||||
extern crate pmutil;
|
||||
extern crate proc_macro2;
|
||||
extern crate proc_macro;
|
||||
#[macro_use]
|
||||
extern crate swc_macros_common as common;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate synom;
|
||||
|
||||
use common::prelude::*;
|
||||
|
||||
mod expand;
|
||||
mod input;
|
||||
mod parse;
|
||||
mod util;
|
||||
|
||||
#[proc_macro_derive(Kind, attributes(kind))]
|
||||
pub fn derive_kind(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = syn::parse::<syn::DeriveInput>(input)
|
||||
.map(From::from)
|
||||
.expect("failed to parse derive input");
|
||||
let item = expand::expand(input);
|
||||
let tokens = item.into_tokens();
|
||||
|
||||
// println!("Expanded:{}", tokens);
|
||||
|
||||
tokens.into()
|
||||
}
|
237
macros/enum_kind/src/parse.rs
Normal file
237
macros/enum_kind/src/parse.rs
Normal file
@ -0,0 +1,237 @@
|
||||
use common::prelude::*;
|
||||
use input::*;
|
||||
use std::fmt::Display;
|
||||
use std::ops::AddAssign;
|
||||
use synom::Synom;
|
||||
use util::{is_attr_name, is_bool};
|
||||
|
||||
impl From<DeriveInput> for Input {
|
||||
fn from(
|
||||
DeriveInput {
|
||||
ident: name,
|
||||
vis,
|
||||
attrs,
|
||||
generics,
|
||||
body,
|
||||
}: DeriveInput,
|
||||
) -> Self {
|
||||
let variants = match body {
|
||||
Body::Enum(body) => body.variants
|
||||
.into_iter()
|
||||
.map(Element::into_item)
|
||||
.map(From::from)
|
||||
.collect(),
|
||||
_ => panic!("#[derive(Kind)] only works for enums"),
|
||||
};
|
||||
|
||||
Input {
|
||||
name,
|
||||
vis,
|
||||
generics,
|
||||
attrs: parse_attrs(attrs),
|
||||
variants,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Synom for EnumAttrs {
|
||||
named!(parse -> Self, do_parse!(
|
||||
_function: syn!(Ident) >>
|
||||
fns: parens!(
|
||||
call!(Delimited::parse_terminated)
|
||||
) >>
|
||||
({
|
||||
let fns: Delimited<_, tokens::Comma> = fns.0;
|
||||
// TODO: Verify `functions`.
|
||||
EnumAttrs {
|
||||
fns: fns.into_vec(),
|
||||
extras: Default::default(),
|
||||
}
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
impl AddAssign<Result<Self, Attribute>> for EnumAttrs {
|
||||
fn add_assign(&mut self, rhs: Result<Self, Attribute>) {
|
||||
match rhs {
|
||||
Ok(attr) => {
|
||||
self.fns.extend(attr.fns);
|
||||
self.extras.extend(attr.extras);
|
||||
}
|
||||
Err(attr) => self.extras.push(attr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FnDef {
|
||||
fn def_value_for_type(ty: &Type) -> Option<Expr> {
|
||||
if is_bool(ty) {
|
||||
return Some(
|
||||
ExprKind::Lit(Lit {
|
||||
value: LitKind::Bool(false),
|
||||
span: SynSpan(Span::call_site()),
|
||||
}).into(),
|
||||
);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Synom for FnDef {
|
||||
named!(parse -> Self, do_parse!(
|
||||
name: syn!(Ident) >>
|
||||
syn!(tokens::Eq) >>
|
||||
return_type: syn!(Lit) >>
|
||||
({
|
||||
let return_type = parse_str_as_tokens(return_type);
|
||||
FnDef {
|
||||
default_value: FnDef::def_value_for_type(&return_type),
|
||||
name,
|
||||
return_type,
|
||||
}
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
impl From<Variant> for EnumVar {
|
||||
fn from(
|
||||
Variant {
|
||||
attrs,
|
||||
data,
|
||||
ident: name,
|
||||
..
|
||||
}: Variant,
|
||||
) -> Self {
|
||||
EnumVar {
|
||||
name,
|
||||
data,
|
||||
attrs: parse_attrs(attrs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Synom for VariantAttrs {
|
||||
named!(parse -> Self, do_parse!(
|
||||
fn_values: call!(Delimited::parse_terminated)
|
||||
>>
|
||||
({
|
||||
let fn_values: Delimited<_, tokens::Comma> = fn_values;
|
||||
VariantAttrs {
|
||||
fn_values: fn_values.into_vec(),
|
||||
extras: Default::default(),
|
||||
}
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
impl AddAssign<Result<Self, Attribute>> for VariantAttrs {
|
||||
fn add_assign(&mut self, rhs: Result<Self, Attribute>) {
|
||||
match rhs {
|
||||
Ok(attr) => {
|
||||
self.fn_values.extend(attr.fn_values);
|
||||
self.extras.extend(attr.extras);
|
||||
}
|
||||
Err(attr) => self.extras.push(attr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Synom for VariantAttr {
|
||||
named!(parse -> Self, do_parse!(
|
||||
fn_name: syn!(Ident) >>
|
||||
value: option!(
|
||||
do_parse!(
|
||||
syn!(tokens::Eq) >>
|
||||
p: syn!(Lit) >>
|
||||
({
|
||||
parse_str_as_tokens(p)
|
||||
})
|
||||
)
|
||||
) >>
|
||||
(VariantAttr{ fn_name, value, })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse kind attr as MetaItem.
|
||||
fn parse_attrs<T>(attrs: Vec<Attribute>) -> T
|
||||
where
|
||||
T: Default + Synom + AddAssign<Result<T, Attribute>>,
|
||||
{
|
||||
/// returns `tokens` where `tts` = `vec![Group(Paren, tokens)]`
|
||||
fn unwrap_paren<I>(tts: I) -> TokenStream
|
||||
where
|
||||
I: IntoIterator<Item = TokenTree>,
|
||||
{
|
||||
let mut tts = tts.into_iter();
|
||||
let mut tt = tts.next();
|
||||
|
||||
match tt {
|
||||
Some(TokenTree {
|
||||
kind: TokenNode::Group(Delimiter::Parenthesis, tokens),
|
||||
span,
|
||||
}) => {
|
||||
if tts.next().is_none() {
|
||||
return tokens;
|
||||
}
|
||||
tt = Some(TokenTree {
|
||||
kind: TokenNode::Group(Delimiter::Parenthesis, tokens),
|
||||
span,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
panic!(
|
||||
"expected tokens to be wrpped in a paren like #[kind(tokens)]\ngot {}",
|
||||
match tt {
|
||||
Some(ref tt) => tt as &Display,
|
||||
None => &"None" as &Display,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let mut res = Default::default();
|
||||
for attr in attrs {
|
||||
if attr.is_sugared_doc {
|
||||
continue;
|
||||
}
|
||||
|
||||
if is_attr_name(&attr, "kind") {
|
||||
let tts = attr.tts.into_iter().map(|t| {
|
||||
// syn::parse doesn't like Vec<syn::TokenTree>.
|
||||
t.0
|
||||
});
|
||||
let tts = unwrap_paren(tts);
|
||||
let parsed: T = parse(tts.into())
|
||||
.unwrap_or_else(|err| panic!("failed to parse attribute: {}", err));
|
||||
|
||||
res += Ok(parsed);
|
||||
} else {
|
||||
res += Err(attr)
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Parse content of string literal as if it's tts.
|
||||
fn parse_str_as_tokens<T>(lit: Lit) -> T
|
||||
where
|
||||
T: Synom,
|
||||
{
|
||||
let span = lit.span.0;
|
||||
// WTF? Literal does not provide a way to get string...
|
||||
let tt = lit.value.to_string();
|
||||
|
||||
// TODO:Remove '"' only for first and last.
|
||||
let tts = tt.replace("\"", "")
|
||||
.parse::<TokenStream>()
|
||||
.expect("failed to create TokenStream for return type")
|
||||
.into_iter()
|
||||
.map(|tt| TokenTree { span, ..tt })
|
||||
.collect::<TokenStream>();
|
||||
|
||||
parse(tts.into()).expect("failed to parse string literal")
|
||||
}
|
41
macros/enum_kind/src/util.rs
Normal file
41
macros/enum_kind/src/util.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use syn::*;
|
||||
|
||||
pub fn is_bool(ty: &Type) -> bool {
|
||||
match *ty {
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path:
|
||||
Path {
|
||||
leading_colon: None,
|
||||
ref segments,
|
||||
},
|
||||
}) => {
|
||||
// check for bool
|
||||
if segments.len() == 1 && segments.first().unwrap().item().ident.sym.as_str() == "bool"
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_attr_name(attr: &Attribute, name: &str) -> bool {
|
||||
match *attr {
|
||||
Attribute {
|
||||
path:
|
||||
Path {
|
||||
leading_colon: None,
|
||||
ref segments,
|
||||
},
|
||||
is_sugared_doc: false,
|
||||
..
|
||||
} if segments.len() == 1 =>
|
||||
{
|
||||
segments.first().unwrap().into_item().ident == name
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
23
macros/enum_kind/tests/usage.rs
Normal file
23
macros/enum_kind/tests/usage.rs
Normal file
@ -0,0 +1,23 @@
|
||||
#[macro_use]
|
||||
extern crate enum_kind;
|
||||
|
||||
#[derive(Debug, Kind)]
|
||||
#[kind(functions(is_a = "bool", prec = "u8"))]
|
||||
pub enum Tokens {
|
||||
#[kind(is_a)]
|
||||
#[kind(prec = "7")]
|
||||
A,
|
||||
#[kind(prec = "6")] StructLike {},
|
||||
#[kind(prec = "5")] TupleLike(u8),
|
||||
|
||||
#[kind(prec = "6")]
|
||||
#[cfg(feature = "not-used")]
|
||||
Unused,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_bool() {
|
||||
assert!(Tokens::A.is_a());
|
||||
assert!(!Tokens::StructLike {}.is_a());
|
||||
assert!(!Tokens::TupleLike(5).is_a());
|
||||
}
|
20
macros/eq_ignore_span/Cargo.toml
Normal file
20
macros/eq_ignore_span/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "eq_ignore_span"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1@outlook.kr>"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
swc_macros_common = { path = "../common" }
|
||||
pmutil = { git = "https://github.com/kdy1/rust-pmutil" }
|
||||
proc-macro2 = { version = "0.1", features = ["unstable"] }
|
||||
|
||||
[dependencies.syn]
|
||||
git = "https://github.com/dtolnay/syn"
|
||||
features = ["full", "parsing", "printing", "extra-traits"]
|
||||
|
||||
|
||||
[dependencies.synom]
|
||||
git = "https://github.com/dtolnay/syn"
|
299
macros/eq_ignore_span/src/lib.rs
Normal file
299
macros/eq_ignore_span/src/lib.rs
Normal file
@ -0,0 +1,299 @@
|
||||
#![feature(box_syntax)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate pmutil;
|
||||
extern crate proc_macro2;
|
||||
extern crate proc_macro;
|
||||
#[macro_use]
|
||||
extern crate swc_macros_common as common;
|
||||
extern crate syn;
|
||||
|
||||
use common::prelude::*;
|
||||
use std::iter;
|
||||
|
||||
#[proc_macro_derive(EqIgnoreSpan)]
|
||||
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = syn::parse(input).expect("failed to parse derive input");
|
||||
|
||||
print("derive(EqIgnoreSpan)", expand(input).into_tokens())
|
||||
}
|
||||
|
||||
fn expand(input: DeriveInput) -> Item {
|
||||
let type_name = &input.ident;
|
||||
let body = expand_method_body(&input.ident, input.body);
|
||||
|
||||
Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
CONST_NAME: type_name.new_ident_with(|n| format!("_IMPL_EQ_IGNORE_SPAN_FOR_{}", n)),
|
||||
Type: type_name,
|
||||
body,
|
||||
},
|
||||
{
|
||||
#[allow(non_upper_case_globals)]
|
||||
const CONST_NAME: () = {
|
||||
extern crate swc_common as _swc_common;
|
||||
impl _swc_common::EqIgnoreSpan for Type {
|
||||
fn eq_ignore_span(&self, __rhs: &Self) -> bool {
|
||||
body
|
||||
}
|
||||
}
|
||||
()
|
||||
};
|
||||
}
|
||||
))
|
||||
.parse()
|
||||
}
|
||||
|
||||
/// Bind variants.
|
||||
/// name is Some(EnumName) for enum, and none for struct.
|
||||
fn bind_variant(
|
||||
qual_name: Path,
|
||||
v: &VariantData,
|
||||
prefix: &str,
|
||||
field_binding_mode: BindingMode,
|
||||
) -> (Pat, Vec<Ident>) {
|
||||
match *v {
|
||||
VariantData::Unit => {
|
||||
// EnumName::VariantName
|
||||
let pat = Pat::Path(PatPath {
|
||||
qself: None,
|
||||
path: qual_name,
|
||||
});
|
||||
let pat = Pat::Ref(PatRef {
|
||||
pat: box pat,
|
||||
and_token: Span::call_site().as_token(),
|
||||
mutbl: Mutability::Immutable,
|
||||
});
|
||||
|
||||
// Unit tuple does not have field bindings
|
||||
(pat, vec![])
|
||||
}
|
||||
VariantData::Struct(ref fields, brace_token) => {
|
||||
let mut bindings = vec![];
|
||||
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(Element::into_item)
|
||||
.map(|f| f.ident.expect("struct field must have ident"))
|
||||
.map(|ident| {
|
||||
let binded_ident = ident.new_ident_with(|s| format!("{}{}", prefix, s));
|
||||
bindings.push(binded_ident.clone());
|
||||
FieldPat {
|
||||
ident,
|
||||
pat: box PatIdent {
|
||||
mode: field_binding_mode,
|
||||
ident: binded_ident,
|
||||
subpat: None,
|
||||
at_token: None,
|
||||
}.into(),
|
||||
is_shorthand: false,
|
||||
colon_token: Some(ident.span.as_token()),
|
||||
attrs: Default::default(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// EnumName::VariantName { fields }
|
||||
let pat = Pat::Struct(PatStruct {
|
||||
path: qual_name,
|
||||
fields,
|
||||
brace_token,
|
||||
dot2_token: None,
|
||||
});
|
||||
let pat = Pat::Ref(PatRef {
|
||||
pat: box pat,
|
||||
and_token: Span::call_site().as_token(),
|
||||
mutbl: Mutability::Immutable,
|
||||
});
|
||||
(pat, bindings)
|
||||
}
|
||||
VariantData::Tuple(ref fields, paren_token) => {
|
||||
// TODO
|
||||
let mut bindings = vec![];
|
||||
|
||||
let pats = fields
|
||||
.iter()
|
||||
.map(Element::into_item)
|
||||
.enumerate()
|
||||
.map(|(i, _)| {
|
||||
let binded_ident = Span::call_site().new_ident(format!("{}{}", prefix, i));
|
||||
bindings.push(binded_ident.clone());
|
||||
Pat::Ident(PatIdent {
|
||||
mode: field_binding_mode,
|
||||
ident: binded_ident,
|
||||
subpat: None,
|
||||
at_token: None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
// EnumName::VariantName { fields }
|
||||
let pat = Pat::TupleStruct(PatTupleStruct {
|
||||
path: qual_name,
|
||||
pat: PatTuple {
|
||||
pats,
|
||||
paren_token,
|
||||
dots_pos: None,
|
||||
dot2_token: None,
|
||||
comma_token: None,
|
||||
},
|
||||
});
|
||||
let pat = Pat::Ref(PatRef {
|
||||
pat: box pat,
|
||||
and_token: Span::call_site().as_token(),
|
||||
mutbl: Mutability::Immutable,
|
||||
});
|
||||
(pat, bindings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates method "eq_ignore_span"
|
||||
fn expand_method_body(name: &Ident, body: Body) -> Expr {
|
||||
/// qual_name: EnumName::VariantName for enum,
|
||||
/// StructName for struct
|
||||
fn arm_for_variant(qual_name: Path, data: &VariantData) -> Arm {
|
||||
let span = Span::call_site();
|
||||
|
||||
let binding_mode = BindingMode::ByRef(span.as_token(), Mutability::Immutable);
|
||||
let (lhs_pat, lhs_bindings) = bind_variant(qual_name.clone(), data, "lhs_", binding_mode);
|
||||
let (rhs_pat, rhs_bindings) = bind_variant(qual_name, data, "rhs_", binding_mode);
|
||||
|
||||
let guard = (lhs_bindings.into_iter().zip(rhs_bindings))
|
||||
.map(|(lhs, rhs)| -> Box<Expr> {
|
||||
box Quote::from_tokens(&lhs)
|
||||
.quote_with(smart_quote!(Vars { lhs, rhs }, {
|
||||
_swc_ast_common::EqIgnoreSpan::eq_ignore_span(lhs, rhs)
|
||||
}))
|
||||
.parse()
|
||||
})
|
||||
.fold(None, |orig, additional_guard| match orig {
|
||||
Some(orig) => Some(box Quote::new_call_site()
|
||||
.quote_with(smart_quote!(
|
||||
Vars {
|
||||
orig,
|
||||
additional_guard,
|
||||
},
|
||||
{ orig && additional_guard }
|
||||
))
|
||||
.parse()),
|
||||
None => Some(additional_guard),
|
||||
});
|
||||
|
||||
// (lhs_pat, rhs_pat) if guard => true
|
||||
Arm {
|
||||
attrs: Default::default(),
|
||||
pats: vec![
|
||||
Pat::Tuple(PatTuple {
|
||||
pats: vec![lhs_pat, rhs_pat].into(),
|
||||
comma_token: None,
|
||||
dots_pos: None,
|
||||
dot2_token: None,
|
||||
paren_token: span.as_token(),
|
||||
}),
|
||||
].into(),
|
||||
if_token: if guard.is_some() {
|
||||
Some(span.as_token())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
guard,
|
||||
rocket_token: span.as_token(),
|
||||
body: box ExprKind::Lit(Lit {
|
||||
span: span.as_syn_span(),
|
||||
value: LitKind::Bool(true),
|
||||
}).into(),
|
||||
comma: Some(span.as_token()),
|
||||
}
|
||||
}
|
||||
|
||||
/// match *self + delegate to variants
|
||||
fn body_for_enum(name: &Ident, BodyEnum { variants, .. }: BodyEnum) -> Expr {
|
||||
let span = Span::call_site();
|
||||
|
||||
let arms = variants
|
||||
.into_iter()
|
||||
.map(syn::delimited::Element::into_item)
|
||||
.map(|v| {
|
||||
arm_for_variant(
|
||||
// EnumName::VariantName
|
||||
Path {
|
||||
leading_colon: None,
|
||||
segments: vec![name.clone(), v.ident]
|
||||
.into_iter()
|
||||
.map(PathSegment::from)
|
||||
.collect(),
|
||||
},
|
||||
&v.data,
|
||||
)
|
||||
})
|
||||
.chain(iter::once({
|
||||
// _ => false,
|
||||
Arm {
|
||||
attrs: Default::default(),
|
||||
pats: vec![
|
||||
Pat::Wild(PatWild {
|
||||
underscore_token: span.as_token(),
|
||||
}),
|
||||
].into(),
|
||||
if_token: None,
|
||||
guard: None,
|
||||
rocket_token: span.as_token(),
|
||||
body: box ExprKind::Lit(Lit {
|
||||
span: span.as_syn_span(),
|
||||
value: LitKind::Bool(false),
|
||||
}).into(),
|
||||
comma: Some(span.as_token()),
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
|
||||
ExprKind::Match(ExprMatch {
|
||||
match_token: span.as_token(),
|
||||
expr: box Quote::new(span)
|
||||
.quote_with(smart_quote!(Vars {}, { (&*self, &*__rhs) }))
|
||||
.parse(),
|
||||
brace_token: span.as_token(),
|
||||
arms,
|
||||
}).into()
|
||||
}
|
||||
|
||||
fn body_for_struct(name: &Ident, BodyStruct { data, .. }: BodyStruct) -> Expr {
|
||||
let span = Span::call_site();
|
||||
|
||||
let arms = iter::once(arm_for_variant(name.clone().into(), &data))
|
||||
.chain(iter::once({
|
||||
// _ => false,
|
||||
Arm {
|
||||
attrs: Default::default(),
|
||||
pats: vec![
|
||||
Pat::Wild(PatWild {
|
||||
underscore_token: span.as_token(),
|
||||
}),
|
||||
].into(),
|
||||
if_token: None,
|
||||
guard: None,
|
||||
rocket_token: span.as_token(),
|
||||
body: box ExprKind::Lit(Lit {
|
||||
span: span.as_syn_span(),
|
||||
value: LitKind::Bool(false),
|
||||
}).into(),
|
||||
comma: Some(span.as_token()),
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
|
||||
ExprKind::Match(ExprMatch {
|
||||
match_token: span.as_token(),
|
||||
expr: box Quote::new(span)
|
||||
.quote_with(smart_quote!(Vars {}, { (&*self, &*__rhs) }))
|
||||
.parse(),
|
||||
brace_token: span.as_token(),
|
||||
arms,
|
||||
}).into()
|
||||
}
|
||||
|
||||
match body {
|
||||
Body::Enum(e) => body_for_enum(name, e),
|
||||
Body::Struct(s) => body_for_struct(name, s),
|
||||
}
|
||||
}
|
13
macros/src/lib.rs
Normal file
13
macros/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Macros used by swc project.
|
||||
#![allow(unused_import)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate ast_node;
|
||||
#[macro_use]
|
||||
extern crate enum_kind;
|
||||
#[macro_use]
|
||||
extern crate eq_ignore_span;
|
||||
|
||||
pub use ast_node::*;
|
||||
pub use enum_kind::*;
|
||||
pub use eq_ignore_span::*;
|
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub extern crate swc_common;
|
||||
pub extern crate swc_macros;
|
Loading…
Reference in New Issue
Block a user