Implement basic Typst -> Rust function transpilation

finchie
Nov 13, 2023, 2:34 PM
OVEFU3K2T36H3EMYOIF6U7AYWMJIV5IJYRL56F3PWRCG7EGEZMQAC

Dependencies

  • [2] GKBSBSDY Return `dyn Iterator` instead of `Vec` for `SupportedContent`
  • [3] I5IZPMTH Handle empty expressions
  • [4] YKL5NCLH Import items from `syn`
  • [5] I3NG5A4W Add support for content sequences
  • [6] BSJYWOYS Implement MVP Typst embedding
  • [7] BA5Y6VSE Output Rust code using `syn`
  • [8] Q3IYM4WF Add MVP table support
  • [9] GYTRFADR Support Typst subdirectories
  • [10] CQEA2ZDI Parse evaluated Typst code instead of AST
  • [11] ZRGSUUIC Add MVP list support
  • [12] HEIF2O2E Migrate from `pandoc` to `typst` for AST processing
  • [13] 2N3KOCP7 Create MVP Pandoc->Rust compiler

Change contents

  • file addition: scope.rs (----------)
    [3.1177]
    use std::vec;
    use syn::{punctuated::Punctuated, token, PathSegment};
    use typst::syntax::ast::Params;
    use crate::literal_string;
    pub fn typst_scope_to_rust(scope: &typst::eval::Scope) -> syn::Item {
    let functions = scope
    .iter()
    .map(|(name, value)| {
    if let Ok(value) = value.clone().cast::<typst::eval::Func>() {
    typst_func_to_rust(name.as_str(), value)
    } else {
    todo!()
    }
    })
    .collect::<Vec<_>>();
    syn::Item::Mod(syn::ItemMod {
    attrs: Vec::new(),
    vis: syn::Visibility::Public(token::Pub::default()),
    unsafety: None,
    mod_token: token::Mod::default(),
    ident: syn::Ident::new("test", proc_macro2::Span::call_site()),
    content: Some((token::Brace::default(), functions)),
    semi: None,
    })
    }
    fn typst_params_to_syn<'a>(params: Params<'a>) -> impl Iterator<Item = syn::FnArg> + 'a {
    dbg!(params.children().collect::<Vec<_>>());
    params.children().map(|child| {
    if let typst::syntax::ast::Param::Pos(typst::syntax::ast::Pattern::Normal(
    typst::syntax::ast::Expr::Ident(param),
    )) = child
    {
    syn::FnArg::Typed(syn::PatType {
    attrs: Vec::new(),
    pat: Box::new(syn::Pat::Ident(syn::PatIdent {
    attrs: Vec::new(),
    by_ref: None,
    mutability: None,
    ident: syn::Ident::new(param.as_str(), proc_macro2::Span::call_site()),
    subpat: None,
    })),
    colon_token: token::Colon::default(),
    ty: Box::new(syn::Type::Path(syn::TypePath {
    qself: None,
    path: syn::Path {
    leading_colon: None,
    segments: Punctuated::from_iter(
    vec![
    syn::PathSegment {
    ident: syn::Ident::new("typst", proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new("eval", proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new("Value", proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    },
    ]
    .into_iter(),
    ),
    },
    })),
    })
    } else {
    dbg!(child);
    todo!()
    }
    })
    }
    fn typst_op_to_syn(operation: typst::syntax::ast::BinOp) -> syn::BinOp {
    match operation {
    typst::syntax::ast::BinOp::Add => syn::BinOp::Add(token::Plus::default()),
    _ => todo!(),
    }
    }
    fn typst_expr_to_syn(expression: typst::syntax::ast::Expr) -> syn::Expr {
    match expression {
    typst::syntax::ast::Expr::Binary(binary) => syn::Expr::Call(syn::ExprCall {
    attrs: Vec::new(),
    func: Box::new(syn::Expr::Path(syn::ExprPath {
    attrs: Vec::new(),
    qself: None,
    path: syn::Path {
    leading_colon: None,
    segments: Punctuated::from_iter(
    vec![
    syn::PathSegment {
    ident: syn::Ident::new("typst", proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new("eval", proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new("ops", proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new(
    match binary.op() {
    typst::syntax::ast::BinOp::Add => "add",
    _ => todo!(),
    },
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::None,
    },
    ]
    .into_iter(),
    ),
    },
    })),
    paren_token: token::Paren::default(),
    args: Punctuated::from_iter(
    vec![
    typst_expr_to_syn(binary.lhs()),
    typst_expr_to_syn(binary.rhs()),
    ]
    .into_iter(),
    ),
    }),
    // syn::Expr::Binary(syn::ExprBinary {
    // attrs: Vec::new(),
    // left: Box::new(typst_expr_to_syn(binary.lhs())),
    // op: typst_op_to_syn(binary.op()),
    // right: Box::new(typst_expr_to_syn(binary.rhs())),
    // }),
    typst::syntax::ast::Expr::Ident(ident) => syn::Expr::Path(syn::ExprPath {
    attrs: Vec::new(),
    qself: None,
    path: syn::Path {
    leading_colon: None,
    segments: Punctuated::from_iter(
    Some(syn::PathSegment {
    ident: syn::Ident::new(ident.as_str(), proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    })
    .into_iter(),
    ),
    },
    }),
    _ => {
    dbg!(expression);
    todo!()
    }
    }
    }
    pub fn typst_func_to_rust(name: &str, func: typst::eval::Func) -> syn::Item {
    syn::Item::Fn(match func.repr {
    typst::eval::FuncRepr::Closure(closure) => {
    let closure_ast = closure.node.cast::<typst::syntax::ast::Closure>().unwrap();
    dbg!(typst_params_to_syn(closure_ast.params()).collect::<Vec<_>>());
    syn::ItemFn {
    attrs: Vec::new(),
    vis: syn::Visibility::Public(token::Pub::default()),
    sig: syn::Signature {
    constness: None,
    asyncness: None,
    unsafety: None,
    abi: None,
    fn_token: token::Fn::default(),
    ident: syn::Ident::new(name, proc_macro2::Span::call_site()),
    generics: syn::Generics::default(),
    paren_token: token::Paren::default(),
    inputs: Punctuated::from_iter(typst_params_to_syn(closure_ast.params())),
    variadic: None,
    output: syn::ReturnType::Type(
    token::RArrow::default(),
    Box::new(syn::Type::Path(syn::TypePath {
    qself: None,
    path: syn::Path {
    leading_colon: None,
    segments: Punctuated::from_iter(
    vec![
    syn::PathSegment {
    ident: syn::Ident::new(
    "typst",
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new(
    "diag",
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new(
    "StrResult",
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::AngleBracketed(
    syn::AngleBracketedGenericArguments {
    colon2_token: None,
    lt_token: token::Lt::default(),
    args: Punctuated::from_iter(
    Some(syn::GenericArgument::Type(
    syn::Type::Path(syn::TypePath {
    qself: None,
    path: syn::Path {
    leading_colon: None,
    segments: Punctuated::from_iter(
    vec![
    syn::PathSegment {
    ident: syn::Ident::new(
    "typst",
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::None,
    },
    syn::PathSegment {
    ident: syn::Ident::new(
    "eval",
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::None,
    },syn::PathSegment {
    ident: syn::Ident::new(
    "Value",
    proc_macro2::Span::call_site(),
    ),
    arguments: syn::PathArguments::None,
    }].into_iter(),
    ),
    },
    }),
    ))
    .into_iter(),
    ),
    gt_token: token::Gt::default(),
    },
    ),
    },
    ]
    .into_iter(),
    ),
    },
    })),
    ),
    },
    block: Box::new(syn::Block {
    brace_token: token::Brace::default(),
    stmts: vec![syn::Stmt::Expr(typst_expr_to_syn(closure_ast.body()), None)],
    }),
    }
    }
    _ => todo!(),
    })
    }
  • edit in crates/typser/src/lib.rs at line 3
    [3.23]
    [3.23]
    mod scope;
  • replacement in crates/typser/src/lib.rs at line 11
    [3.11][2.80:160](),[2.160][3.145:423](),[3.145][3.145:423]()
    token, AngleBracketedGenericArguments, ExprLit, ExprTuple, GenericArgument,
    GenericParam, Generics, Ident, Item, ItemFn, ItemMod, ItemUse, Lit, LitStr, PathArguments,
    PathSegment, ReturnType, Signature, Stmt, TraitBound, TraitBoundModifier, TypeImplTrait,
    TypeParam, TypeParamBound, TypePath, UseGroup, UseName, UsePath, UseTree, Visibility,
    [3.11]
    [3.399]
    token, AngleBracketedGenericArguments, ExprLit, ExprTuple, GenericArgument, GenericParam,
    Generics, Ident, Item, ItemFn, ItemMod, ItemUse, Lit, LitStr, PathArguments, PathSegment,
    ReturnType, Signature, Stmt, TraitBound, TraitBoundModifier, TypeImplTrait, TypeParam,
    TypeParamBound, TypePath, UseGroup, UseName, UsePath, UseTree, Visibility,
  • replacement in crates/typser/src/lib.rs at line 127
    [3.73][3.62:124](),[3.124][3.73:142](),[3.145][3.73:142](),[3.73][3.73:142](),[3.142][3.963:1057](),[3.963][3.963:1057]()
    dbg!(typst_ast.scope(), typst_ast.clone().content());
    let xilem_expressions = typst_to_xilem(typst_ast.content());
    let function = xilem_to_function(&fs_path_to_module_ident(&file), xilem_expressions);
    [3.73]
    [3.5171]
    // let xilem_expressions = typst_to_xilem(typst_ast.content());
    // let function = xilem_to_function(&fs_path_to_module_ident(&file), xilem_expressions);
    let function = scope::typst_scope_to_rust(typst_ast.scope());
  • replacement in crates/typser/Cargo.toml at line 12
    [3.5077][3.3376:3427](),[3.3427][3.4193:4252]()
    typst = { git = "https://github.com/typst/typst" }
    typst-library = { git = "https://github.com/typst/typst" }
    [3.5077]
    typst = { path = "../../../typst/crates/typst" }
    typst-library = { path = "../../../typst/crates/typst-library" }
  • replacement in docs/test.typ at line 1
    [3.242][2.0:44](),[2.44][3.66:67](),[3.66][3.66:67](),[3.67][2.45:79]()
    #show emph: it => {
    text(blue, it.body)
    }
    This is _emphasized_ differently.
    [3.242]
    #let add(x, y) = x + y