Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 16 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "autofront"
version = "0.0.1"
edition = "2024"
homepage = "https://autolang.dev"
repository = "https://github.com/AutoLang-Dev/autofront"
license = "Apache-2.0"
version.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true

[profile.release]
opt-level = 3
Expand All @@ -19,7 +19,18 @@ unicode-normalization = "0.1.25"
unicode-width = "0.2.2"
annotate-snippets = "0.12.10"
anstream = "0.6.21"
num-bigint = "0.4.6"
macros = { path = "macros" }

[patch.crates-io]
annotate-snippets = { git = "https://github.com/KeqingMoe/annotate-snippets-rs.git", branch = "fix-unicode-patch" }

[workspace]
members = ["macros/"]

[workspace.package]
version = "0.0.1"
edition = "2024"
homepage = "https://autolang.dev"
repository = "https://github.com/AutoLang-Dev/autofront"
license = "Apache-2.0"
2 changes: 2 additions & 0 deletions locale/en-US/cli.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cli_help = Usage: autofront <COMMAND> [OPTIONS]
version Print version info
lex <FILE> Lexical analysis (unstable)
tt <FILE> Build TokenTree (unstable)
parse <FILE> Parse file (unstable)

Options:
-o <FILE> Write output to FILE
Expand All @@ -24,6 +25,7 @@ cli_help_help = help [<COMMAND>]: Display help message
cli_help_version = version: Print version info
cli_help_lex = lex <FILE>: Perform lexical analysis and print the results
cli_help_tt = tt <FILE>: Build a TokenTree
cli_help_parse = parse <FILE>: Parse file and print AST

cli_file = file
cli_option = option
9 changes: 9 additions & 0 deletions locale/en-US/parser.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mixed_and_or = cannot mix && and || operators
chained_range = cannot chain ~ or ~= operators
chained_assign = cannot chain assignment operators
unexpected_token = unexpected token {$token}
unexpected_group = unexpected group {$delim}
unexpected_end = expected more tokens, but reached end of file or closing bracket
bad_neq = != cannot be chained or mixed with other comparison operators
bad_3way = <=> cannot be chained or mixed with other comparison operators
mixed_greater_less = < and <= cannot be mixed with > and >=
4 changes: 3 additions & 1 deletion locale/zh-CN/cli.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cli_help = 用法:autofront <命令> [选项]
version 显示版本
lex <文件> 词法分析(不稳定)
tt <文件> 构建 TokenTree(不稳定)
parse <文件> 解析文件(不稳定)

选项:
-o <文件> 写入输出到文件
Expand All @@ -23,7 +24,8 @@ cli_help = 用法:autofront <命令> [选项]
cli_help_help = help [<命令>]:显示帮助信息
cli_help_version = version:显示版本信息
cli_help_lex = lex <文件>:对文件进行词法分析并打印结果
cli_help_tt = tt <文件>:构建 ToenTree
cli_help_tt = tt <文件>:构建 TokenTree
cli_help_parse = parse <文件>:解析文件并打印 AST

cli_file = 文件
cli_option = 选项
9 changes: 9 additions & 0 deletions locale/zh-CN/parser.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mixed_and_or = 不能混合使用 && 和 || 运算符
chained_range = 不能连用 ~ 或 ~= 运算符
chained_assign = 不能连用赋值运算符
unexpected_token = 意料之外的 Token {$token}
unexpected_group = 意料之外的括号 {$delim}
unexpected_end = 期待更多 Token,但已抵达文件尾或闭括号
bad_neq = != 不能连用或与其他比较运算符混用
bad_3way = <=> 不能连用或与其他比较运算符混用
mixed_greater_less = < 和 <= 不能与 > 和 >= 混用
14 changes: 14 additions & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "macros"
version.workspace = true
edition.workspace = true
license.workspace = true
publish = false

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0.104"
quote = "1.0.42"
syn = { version = "2.0.113", features = ["full", "extra-traits"] }
27 changes: 27 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mod names;
mod parse;
mod print;
mod recover;
mod span;

use proc_macro::TokenStream;

#[proc_macro_derive(AstPrint)]
pub fn derive_ast_print(input: TokenStream) -> TokenStream {
print::derive_ast_print(input)
}

#[proc_macro_derive(Parse, attributes(option, boxed, optbox))]
pub fn derive_parse(input: TokenStream) -> TokenStream {
parse::derive_parse(input)
}

#[proc_macro_derive(Recover, attributes(recover))]
pub fn derive_recover(input: TokenStream) -> TokenStream {
recover::derive_recover(input)
}

#[proc_macro_derive(Span, attributes(span))]
pub fn derive_span(input: TokenStream) -> TokenStream {
span::derive_span(input)
}
34 changes: 34 additions & 0 deletions macros/src/names.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#![allow(non_snake_case)]

use proc_macro2::TokenStream;
use quote::quote;

pub struct Names {
pub Parse: TokenStream,
pub ParseBuffer: TokenStream,
pub Result: TokenStream,
pub ParseError: TokenStream,
pub DiagSink: TokenStream,
pub AstPrint: TokenStream,
pub Spanned: TokenStream,
pub Span: TokenStream,
pub Error: TokenStream,
}

impl Names {
pub fn new() -> Self {
let ns = quote! { crate::pipelines::parser };

Self {
Parse: quote! { #ns::syntax::parse::Parse },
ParseBuffer: quote! { #ns::buffer::ParseBuffer },
Result: quote! { #ns::syntax::parse::Result },
ParseError: quote! { #ns::syntax::parse::ParseError },
DiagSink: quote! { crate::utils::DiagSink },
AstPrint: quote! { #ns::print::AstPrint },
Spanned: quote! { #ns::span::Spanned },
Span: quote! { crate::utils::Span },
Error: quote! { #ns::syntax::token::Error },
}
}
}
89 changes: 89 additions & 0 deletions macros/src/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Fields, parse_macro_input};

use crate::names::Names;

pub fn derive_parse(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand_derive_parse(&input)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}

fn expand_derive_parse(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let Names {
Parse,
ParseBuffer,
Result,
DiagSink,
..
} = Names::new();

let name = &input.ident;
let generics = &input.generics;

let Data::Struct(data_struct) = &input.data else {
return Err(syn::Error::new(
input.span(),
"Parse can only be derived for structs",
));
};

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let parse = quote! { parse };
let parse_required = quote! { parse_required };
let get_parser = |i| {
if i == 0 { &parse } else { &parse_required }
};

let (parse_stmts, construction) = match &data_struct.fields {
Fields::Named(fields) => {
let field_names = fields.named.iter().map(|f| &f.ident);
let parse_stmts = field_names.clone().enumerate().map(|(i, ident)| {
let parser = get_parser(i);
quote! { let #ident = input.#parser(sink)?; }
});
let construction = quote! { #name { #( #field_names, )* } };
(quote! { #(#parse_stmts)* }, construction)
}

Fields::Unnamed(fields) => {
let vars: Vec<_> = (0..fields.unnamed.len())
.map(|i| format_ident!("_{i}"))
.collect();

let parse_stmts = vars.iter().enumerate().map(|(i, var)| {
let parser = get_parser(i);
quote! { let #var = input.#parser(sink)?; }
});

(
quote! { #(#parse_stmts)* },
quote! { #name ( #( #vars, )* ) },
)
}

Fields::Unit => (quote! {}, quote! { #name }),
};

Ok(quote! {
impl #impl_generics #Parse for #name #ty_generics #where_clause {
fn parse(
input: &#ParseBuffer,
sink: &mut #DiagSink
) -> #Result<Self> {
#parse_stmts
Ok(#construction)
}
}

impl #impl_generics #Parse for std::option::Option<#name> {
fn parse(input: &#ParseBuffer, sink: &mut #DiagSink) -> #Result<Self> {
input.try_parse(sink)
}
}
})
}
Loading