add template macro crate and parser

main
anna 1 year ago
parent b3a8f18e74
commit 35db908fcd
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

1
.gitignore vendored

@ -1,3 +1,4 @@
/target
/uwui_dsl/target
/.idea
/.vscode

7
Cargo.lock generated

@ -5,3 +5,10 @@ version = 3
[[package]]
name = "uwui"
version = "0.1.0"
dependencies = [
"uwui_dsl",
]
[[package]]
name = "uwui_dsl"
version = "0.1.0"

@ -2,3 +2,6 @@
name = "uwui"
version = "0.1.0"
edition = "2021"
[dependencies]
uwui_dsl = { path = "./uwui_dsl" }

@ -1,2 +1,13 @@
pub mod backend;
pub mod view;
pub use uwui_dsl::template;
fn asdf() {
let _ = template!(
VStack(gravity: Gravity::Center, homogay: true) {
Text(content: "hello, world")
Button(label: "don't click me")
}
);
}

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "uwui_dsl"
version = "0.1.0"

@ -0,0 +1,7 @@
[package]
name = "uwui_dsl"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true

@ -0,0 +1,28 @@
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_span)]
use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
/// Syntax tree parser.
mod parser;
use parser::Parser;
#[proc_macro]
pub fn template(input: TokenStream) -> TokenStream {
let mut stream = input.into_iter().peekable();
let parser = Parser::from(&mut stream);
for component in parser {
println!("{component:?}");
}
// stub, for now
TokenStream::from_iter(
[TokenTree::Group(Group::new(
Delimiter::Parenthesis,
TokenStream::new(),
))]
.into_iter(),
)
}

@ -0,0 +1,295 @@
use proc_macro::{Delimiter, Diagnostic, Level, Spacing, TokenTree};
use std::{fmt, iter::Peekable};
/// A single component definition, including children.
pub struct Component {
/// The type name of this component (may be namespaced).
name: Name,
/// The parameter list as a dictionary, if `name` is followed by parentheses.
params: Option<Vec<Param>>,
/// All child component definitions.
children: Option<Vec<Component>>,
}
pub struct Name(Vec<TokenTree>);
pub struct Param(TokenTree, TokenTree, Expr);
pub struct Expr(Vec<TokenTree>);
/// The main syntax tree parser. This implements [`Iterator`], so it will
/// only parse (and consume the input iterator) when consumed itself.
pub struct Parser<'a, T: Iterator<Item = TokenTree>> {
stream: &'a mut Peekable<T>,
}
impl<'a, T: Iterator<Item = TokenTree>> From<&'a mut Peekable<T>> for Parser<'a, T> {
fn from(stream: &'a mut Peekable<T>) -> Self {
Self { stream }
}
}
impl<'a, T: Iterator<Item = TokenTree>> Iterator for Parser<'a, T> {
type Item = Component;
fn next(&mut self) -> Option<Component> {
self.parse_component()
}
}
impl<'a, T: Iterator<Item = TokenTree>> Parser<'a, T> {
/// ```notrust
/// component = name *SP [ param-list *SP ] [ child-list *SP ]
/// param-list = "(" *SP *( param *SP "," *SP ) [ param *SP ] ")"
/// child-list = "{" *SP *( component ) "}"
/// ```
fn parse_component(&mut self) -> Option<Component> {
self.stream.peek()?;
let name = Name::eat(self.stream)?;
let params = self
.stream
.next_if(Self::is_param_list)
.map(|tt| match tt {
TokenTree::Group(group) => group.stream().into_iter().peekable(),
_ => unreachable!("is_param_list already matched this"),
})
.map(|mut stream| {
let mut list = Vec::new();
while let Some(param) = Param::parse(&mut stream) {
list.push(param);
stream.next_if(|tt| match tt {
TokenTree::Punct(punct) => {
punct.as_char() == ',' && punct.spacing() == Spacing::Alone
}
_ => false,
});
}
if let Some(tt) = stream.peek() {
Diagnostic::spanned(tt.span(), Level::Error, "Unexpected token").emit();
}
list
});
let children = self
.stream
.next_if(Self::is_child_list)
.map(|tt| match tt {
TokenTree::Group(group) => group.stream().into_iter().peekable(),
_ => unreachable!("is_child_list already matched this"),
})
.map(|mut stream| Parser::from(&mut stream).collect());
Some(Component {
name,
params,
children,
})
}
fn is_param_list(tt: &TokenTree) -> bool {
match tt {
TokenTree::Group(group) => group.delimiter() == Delimiter::Parenthesis,
_ => false,
}
}
fn is_child_list(tt: &TokenTree) -> bool {
match tt {
TokenTree::Group(group) => group.delimiter() == Delimiter::Brace,
_ => false,
}
}
}
impl Name {
/// ```notrust
/// name = [ path-sep ] ident *( path-sep ident )
/// ```
fn eat(stream: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<Self> {
stream.peek()?;
let mut segments = Vec::new();
// [ path-sep ]
segments.extend(
Self::parse_path_sep(stream)
.into_iter()
.flat_map(|sep| sep.into_iter()),
);
// ident
segments.push(eat_ident(stream)?);
// *( path-sep ident )
while let Some(sep) = Self::parse_path_sep(stream) {
segments.extend(sep.into_iter());
segments.push(eat_ident(stream)?);
}
Some(Name(segments))
}
/// ```notrust
/// path-sep = 2*":"
/// ```
fn parse_path_sep(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Option<[TokenTree; 2]> {
let first = stream.next_if(|tt| match tt {
TokenTree::Punct(punct) => punct.as_char() == ':' && punct.spacing() == Spacing::Joint,
_ => false,
})?;
let second = match stream.next() {
Some(tt) => match &tt {
TokenTree::Punct(punct)
if punct.as_char() == ':' && punct.spacing() == Spacing::Alone =>
{
Some(tt)
}
_ => {
Diagnostic::spanned(tt.span(), Level::Error, "Unexpected token").emit();
None
}
},
None => {
Diagnostic::new(Level::Error, "Unexpected end of stream1").emit();
None
}
}?;
Some([first, second])
}
}
impl Param {
/// ```notrust
/// param = ident *SP ":" *SP expr
/// ```
fn parse(stream: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<Self> {
stream.peek()?;
let ident = eat_ident(stream)?;
let colon = match stream.next() {
Some(tt) => match &tt {
TokenTree::Punct(punct)
if punct.as_char() == ':' && punct.spacing() == Spacing::Alone =>
{
Some(tt)
}
_ => {
Diagnostic::spanned(tt.span(), Level::Error, "Expected a `:`").emit();
None
}
},
None => {
Diagnostic::new(Level::Error, "Unexpected end of stream2").emit();
None
}
}?;
let expr = Expr::parse(stream)?;
Some(Param(ident, colon, expr))
}
}
impl Expr {
/// ```notrust
/// expr = it's just rust code until we hit a comma, stop being a pedantic bitch about it
/// ```
fn parse(stream: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<Self> {
let mut tokens = Vec::new();
while let Some(tt) = stream.peek() {
match tt {
TokenTree::Punct(punct) if punct.as_char() == ',' => break,
_ => tokens.push(stream.next().expect("We already peeked this")),
}
}
if tokens.is_empty() {
None
} else {
Some(Expr(tokens))
}
}
}
fn eat_ident(stream: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<TokenTree> {
match stream.next() {
Some(tt) => match tt {
TokenTree::Ident(_) => Some(tt),
_ => {
Diagnostic::spanned(tt.span(), Level::Error, "Expected an identifier").emit();
None
}
},
None => {
Diagnostic::new(Level::Error, "Unexpected end of stream").emit();
None
}
}
}
///////////// debug stuff ///////////////
impl Component {
fn print(&self, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
let indent = " ".repeat(depth);
f.write_str(&indent)?;
fmt::Debug::fmt(&self.name, f)?;
if let Some(params) = &self.params {
f.write_str("(")?;
let mut first = true;
for param in params {
if first {
first = false;
} else {
f.write_str(", ")?;
}
fmt::Debug::fmt(param, f)?;
}
f.write_str(")")?;
}
f.write_str("\n")?;
if let Some(children) = &self.children {
for child in children {
child.print(f, depth + 1)?;
}
}
Ok(())
}
}
impl fmt::Debug for Component {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("\n")?;
self.print(f, 0)?;
f.write_str("\n")
}
}
impl fmt::Debug for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for segment in &self.0 {
f.write_str(&segment.to_string())?;
}
Ok(())
}
}
impl fmt::Debug for Param {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.0.to_string())?;
f.write_str(" => ")?;
fmt::Debug::fmt(&self.2, f)
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for tt in &self.0 {
f.write_str(&tt.to_string())?;
}
Ok(())
}
}
Loading…
Cancel
Save