slint/api/sixtyfps-rs/sixtyfps-rs-macro/lib.rs
Simon Hausmann 732a56259f Simplify compilation call sites further
Move run_passes into the library compilation function. That way the
FileDiagnostics are created by the parser, can be passed on to the library
compilation function and after that we don't need them anymore and can
replace them with future BuildDiagnostics for example.
2020-07-16 18:53:59 +02:00

195 lines
8.1 KiB
Rust

extern crate proc_macro;
use proc_macro::{Spacing, TokenStream};
use quote::ToTokens;
use sixtyfps_compilerlib::*;
fn fill_token_vec(stream: TokenStream, vec: &mut Vec<parser::Token>) {
let mut prev_spacing = Spacing::Alone;
for t in stream {
use parser::SyntaxKind;
use proc_macro::TokenTree;
match t {
TokenTree::Ident(i) => {
if let Some(last) = vec.last_mut() {
if last.kind == SyntaxKind::ColorLiteral && last.text.len() == 1 {
last.text = format!("#{}", i).into();
continue;
}
}
vec.push(parser::Token {
kind: SyntaxKind::Identifier,
text: i.to_string().into(),
span: Some(i.span()),
..Default::default()
});
}
TokenTree::Punct(p) => {
let kind = match p.as_char() {
':' => SyntaxKind::Colon,
'=' => {
if let Some(last) = vec.last_mut() {
let kt = match last.kind {
SyntaxKind::Star => Some((SyntaxKind::StarEqual, "*=")),
SyntaxKind::Colon => Some((SyntaxKind::ColonEqual, ":=")),
SyntaxKind::Plus => Some((SyntaxKind::PlusEqual, "+=")),
SyntaxKind::Minus => Some((SyntaxKind::MinusEqual, "-=")),
SyntaxKind::Div => Some((SyntaxKind::DivEqual, "/=")),
SyntaxKind::LAngle => Some((SyntaxKind::LessEqual, "<=")),
SyntaxKind::RAngle => Some((SyntaxKind::GreaterEqual, ">=")),
SyntaxKind::Equal => Some((SyntaxKind::EqualEqual, "==")),
SyntaxKind::Bang => Some((SyntaxKind::NotEqual, "!=")),
_ => None,
};
if let Some((k, t)) = kt {
if prev_spacing == Spacing::Joint {
last.kind = k;
last.text = t.into();
continue;
}
}
}
SyntaxKind::Equal
}
';' => SyntaxKind::Semicolon,
'!' => SyntaxKind::Bang,
'.' => SyntaxKind::Dot,
'+' => SyntaxKind::Plus,
'-' => SyntaxKind::Minus,
'*' => SyntaxKind::Star,
'/' => SyntaxKind::Div,
'<' => SyntaxKind::LAngle,
'>' => {
if let Some(last) = vec.last_mut() {
if last.kind == SyntaxKind::Equal && prev_spacing == Spacing::Joint {
last.kind = SyntaxKind::FatArrow;
last.text = "=>".into();
continue;
}
}
SyntaxKind::RAngle
}
'#' => SyntaxKind::ColorLiteral,
'?' => SyntaxKind::Question,
',' => SyntaxKind::Comma,
'&' => {
// Since the '&' alone does not exist or cannot be part of any other token that &&
// just consider it as '&&' and skip the joint ones. FIXME. do that properly
if let Some(last) = vec.last_mut() {
if last.kind == SyntaxKind::AndAnd && prev_spacing == Spacing::Joint {
continue;
}
}
SyntaxKind::AndAnd
}
'|' => {
// Since the '|' alone does not exist or cannot be part of any other token that ||
// just consider it as '||' and skip the joint ones. FIXME. do that properly
if let Some(last) = vec.last_mut() {
if last.kind == SyntaxKind::OrOr && prev_spacing == Spacing::Joint {
continue;
}
}
SyntaxKind::OrOr
}
'%' => {
// % can only exist after number literal
if let Some(last) = vec.last_mut() {
if last.kind == SyntaxKind::NumberLiteral {
last.text = format!("{}%", last.text).into();
continue;
}
}
SyntaxKind::Error
}
_ => SyntaxKind::Error,
};
prev_spacing = p.spacing();
vec.push(parser::Token {
kind,
text: p.to_string().into(),
span: Some(p.span()),
..Default::default()
});
}
TokenTree::Literal(l) => {
let s = l.to_string();
// Why can't the rust API give me the type of the literal
let f = s.chars().next().unwrap();
let kind = if f == '"' {
SyntaxKind::StringLiteral
} else if f.is_digit(10) {
if let Some(last) = vec.last_mut() {
if last.kind == SyntaxKind::ColorLiteral && last.text.len() == 1 {
last.text = format!("#{}", s).into();
continue;
}
}
SyntaxKind::NumberLiteral
} else {
SyntaxKind::Error
};
vec.push(parser::Token {
kind,
text: s.into(),
span: Some(l.span()),
..Default::default()
});
}
TokenTree::Group(g) => {
use proc_macro::Delimiter::*;
use SyntaxKind::*;
let (l, r, sl, sr) = match g.delimiter() {
Parenthesis => (LParent, RParent, "(", ")"),
Brace => (LBrace, RBrace, "{", "}"),
Bracket => (LBracket, RBracket, "[", "]"),
None => todo!(),
};
vec.push(parser::Token {
kind: l,
text: sl.into(),
span: Some(g.span()), // span_open is not stable
..Default::default()
});
fill_token_vec(g.stream(), vec);
vec.push(parser::Token {
kind: r,
text: sr.into(),
span: Some(g.span()), // span_clone is not stable
..Default::default()
});
}
}
}
}
#[proc_macro]
pub fn sixtyfps(stream: TokenStream) -> TokenStream {
let mut tokens = vec![];
fill_token_vec(stream, &mut tokens);
let (syntax_node, mut diag) = parser::parse_tokens(tokens.clone());
if let Some(cargo_manifest) = std::env::var_os("CARGO_MANIFEST_DIR") {
diag.current_path = cargo_manifest.into();
diag.current_path.push("Cargo.toml");
}
//println!("{:#?}", syntax_node);
let compiler_config = CompilerConfiguration::default();
let (tree, mut diag) = compile_syntax_node(syntax_node, diag, &compiler_config);
//println!("{:#?}", tree);
if diag.has_error() {
diag.map_offsets_to_span(&tokens);
return diag.into_token_stream().into();
}
let result = generator::rust::generate(&tree.root_component, &mut diag);
result
.unwrap_or_else(|| {
diag.map_offsets_to_span(&tokens);
diag.into_token_stream()
})
.into()
}