Skip to content

victorporof/rsx-parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Under heavy research and development, please don't use this yet!

rsx-parser

License: MPL 2.0 Build Status

JSX-like parser combinator for Rust

Purpose

This is an experimental parser for JSX-like code in Rust. The long term goal might be to build "something like React" in Rust, but this can mean a number of things, from a direct port with 100% API compatibility to a completely different product. A JSX-like parser is a good and simple place to start experimenting from.

How to use

Documentation

This crate concerns itself strictly with parsing the RSX syntax. If you're just looking to write RSX in your project, take a look at the RSX compiler plugin instead.

Otherwise, add this to your Cargo.toml file:

[dependencies]
rsx-parser = { git = "https://github.com/victorporof/rsx-parser.git" }

Then, simply import the library into your code to parse RSX. The parser generates an rsx_parser::RSXElement abstract syntax tree representing the RSX code, returning it together with the remaining unparsed input (if any). Other parser combinators can thus use this output.

extern crate rsx_parser;
use rsx_parser::parse;
use rsx_parser::types::RSXElement;

let source = "<div>Hello world!</div>";
let (ast, remaining): (RSXElement, _) = parse(source).unwrap();

All data structures (including the AST) are self tokenizing values, meaning that they can serialize themselves to generate a quote::Tokens which can be then directly translated into a proc_macro::TokenStream, used for creating Rust compiler plugins as procedural macros. See the syn and quote crates for more information.

Note that procedural macros are not fully standardized as of September 2017, but sufficient features are available in the current Rust nightly version (1.22). See the RFC and the tracking issue for more information.

Use the rust-self-tokenize or quote crates for the quote::Tokens type.

extern crate self_tokenize_trait;
use self_tokenize_trait::{ToCustomTokens, Tokens};

let source = "<div>{ external_rust_code() }</div>";
let (ast, remaining) = parse(source).unwrap();
let mut tokens = Tokens::new();
ast.to_custom_tokens(&mut tokens);

This library should work on the stable Rust channel, but if you want to use the RSX compiler plugin, then you need Nightly:

rustup default nightly

RSX vs. JSX

The JSX spec is, although a draft, presumably stable. Syntax extension equivalents can be found for Rust, which is the main scope of this experiment.

Example, inspired from the JSX spec website linked above:

const FunDropdown = (props) =>
  <Dropdown show={props.visible}>
    A dropdown list
    <Menu
      icon={props.menu.icon}
      onHide={(e) => console.log(e)}
      onShow={(e) => console.log(e)}
    >
      <MenuItem>Do Something</MenuItem>
      {
        shouldDoSomethingFun()
          ? <MenuItem>Do Something Fun!</MenuItem>
          : <MenuItem>Do Something Else</MenuItem>
      }
    </Menu>
  </Dropdown>;

An equivalent interpretation of JSX in Rust, using compiler plugins, looks this:

fn fun_dropdown(props: Props) -> RSXElement {
  rsx! {
    <Dropdown show={props.visible}>
      A dropdown list
      <Menu
        icon={props.menu.icon}
        onHide={|e: Event| println!("{:?}", e)}
        onShow={|e: Event| println!("{:?}", e)}
      >
        <MenuItem>Do Something</MenuItem>
        {
          if should_do_something_fun() {
            <MenuItem>Do Something Fun!</MenuItem>
          } else {
            <MenuItem>Do Something Else</MenuItem>
          }
        }
      </Menu>
    </Dropdown>
  }
}

Supported grammar

All of the JSX official grammar is supported. In the case of handling arbitrary Rust code inside RSX, the treatment is similar: JSX can contain JavaScript "code blocks" delimited by curly braces (specifically "assignment expressions"), in clearly defined locations such as attribute values, children contents etc. Rust expressions provide sufficient equivalence.

PrimaryExpression

  • JSXElement

Elements

JSXElement

  • JSXSelfClosingElement
  • JSXOpeningElement JSXChildren? JSXClosingElement

JSXSelfClosingElement

  • < JSXElementName JSXAttributes? / >

JSXOpeningElement

  • < JSXElementName JSXAttributes? >

JSXClosingElement

  • < / JSXElementName >

JSXElementName

  • JSXIdentifier
  • JSXNamedspacedName
  • JSXMemberExpression

JSXIdentifier

  • IdentifierStart
  • JSXIdentifier IdentifierPart
  • JSXIdentifier -

JSXNamespacedName

  • JSXIdentifier : JSXIdentifier

JSXMemberExpression

  • JSXIdentifier . JSXIdentifier
  • JSXMemberExpression . JSXIdentifier

Attributes

JSXAttributes

  • JSXSpreadAttribute JSXAttributes?
  • JSXAttribute JSXAttributes?

JSXSpreadAttribute

  • { ... AssignmentExpression }

JSXAttribute

  • JSXAttributeName = JSXAttributeValue

JSXAttributeName

  • JSXIdentifier
  • JSXNamespacedName

JSXAttributeValue

  • " JSXDoubleStringCharacters? "
  • ' JSXSingleStringCharacters? '
  • { AssignmentExpression }
  • JSXElement

JSXDoubleStringCharacters

  • JSXDoubleStringCharacter JSXDoubleStringCharacters?

JSXDoubleStringCharacter

  • SourceCharacter but not "

JSXSingleStringCharacters

  • JSXSingleStringCharacter JSXSingleStringCharacters?

JSXSingleStringCharacter

  • SourceCharacter but not '

Children

JSXChildren

  • JSXChild JSXChildren?

JSXChild

  • JSXText
  • JSXElement
  • { AssignmentExpression? }

JSXText

  • JSXTextCharacter JSXText?

JSXTextCharacter

  • SourceCharacter but not one of {, <, > or }

About

JSX-like parser combinator for Rust

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages