mirror of
https://github.com/swc-project/swc.git
synced 2024-11-09 12:57:40 +03:00
jsx support (#100)
swc_ecma_parser: - implement parser for jsx swc_ecma_transforms: - implement react::jsx transform swc_ecma_codegen: - implement code generator for jsx
This commit is contained in:
parent
cb59cd63c6
commit
603b83291d
@ -24,6 +24,11 @@ yarn add --dev swc
|
||||
|
||||
# Features
|
||||
|
||||
## Parsers
|
||||
- [x] es2019
|
||||
- [x] jsx
|
||||
- [ ] typescript
|
||||
|
||||
## Transforms
|
||||
New generation javascript to old-days javascript.
|
||||
|
||||
@ -72,6 +77,9 @@ New generation javascript to old-days javascript.
|
||||
- [ ] Using symbol as a key
|
||||
- [ ] optional-catch-binding
|
||||
- [ ] unicode-property-regex
|
||||
|
||||
- react
|
||||
- [x] jsx
|
||||
|
||||
# Performance
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
cat words.txt | sort > words_sorted.txt
|
||||
cat words.txt | uniq | sort > words_sorted.txt
|
||||
mv words_sorted.txt words.txt
|
@ -2,14 +2,15 @@ Infinity
|
||||
NODE_ENV
|
||||
NaN
|
||||
Object
|
||||
React
|
||||
RegExp
|
||||
_extends
|
||||
_toConsumableArray
|
||||
abstract
|
||||
apply
|
||||
as
|
||||
async
|
||||
await
|
||||
await
|
||||
break
|
||||
call
|
||||
case
|
||||
@ -19,6 +20,8 @@ concat
|
||||
const
|
||||
constructor
|
||||
continue
|
||||
createClass
|
||||
createReactClass
|
||||
debugger
|
||||
default
|
||||
delete
|
||||
@ -51,10 +54,10 @@ private
|
||||
process
|
||||
protected
|
||||
public
|
||||
readonly
|
||||
return
|
||||
set
|
||||
static
|
||||
static
|
||||
super
|
||||
switch
|
||||
target
|
||||
|
@ -57,6 +57,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Spanned for ::either::Either<A, B>
|
||||
where
|
||||
A: Spanned,
|
||||
B: Spanned,
|
||||
{
|
||||
fn span(&self) -> Span {
|
||||
match *self {
|
||||
::either::Either::Left(ref n) => n.span(),
|
||||
::either::Either::Right(ref n) => n.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> FoldWith<F> for Span {
|
||||
/// No op as span does not have any child.
|
||||
fn fold_children(self, _: &mut F) -> Span {
|
||||
|
@ -6,9 +6,10 @@ license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
documentation = "https://swc-project.github.io/rustdoc/swc_ecma_ast/"
|
||||
description = "Ecmascript ast."
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
swc_atoms = { version = "0.1", path ="../../atoms" }
|
||||
swc_common = { version = "0.1", path ="../../common" }
|
||||
enum_kind = { version = "0.1", path ="../../macros/enum_kind" }
|
||||
string_enum = { version = "0.1", path ="../../macros/string_enum" }
|
||||
string_enum = { version = "0.1", path ="../../macros/string_enum" }
|
@ -1,5 +1,6 @@
|
||||
use super::{
|
||||
AssignOp, BinaryOp, BlockStmt, Class, Function, Ident, Lit, Pat, Prop, Str, UnaryOp, UpdateOp,
|
||||
AssignOp, BinaryOp, BlockStmt, Class, Function, Ident, JSXElement, JSXEmptyExpr, JSXFragment,
|
||||
JSXMemberExpr, JSXNamespacedName, Lit, Pat, Prop, Str, UnaryOp, UpdateOp,
|
||||
};
|
||||
use swc_common::{ast_node, Fold, Span, Spanned};
|
||||
|
||||
@ -62,6 +63,12 @@ pub enum Expr {
|
||||
Await(AwaitExpr),
|
||||
|
||||
Paren(ParenExpr),
|
||||
|
||||
JSXMebmer(JSXMemberExpr),
|
||||
JSXNamespacedName(JSXNamespacedName),
|
||||
JSXEmpty(JSXEmptyExpr),
|
||||
JSXElement(JSXElement),
|
||||
JSXFragment(JSXFragment),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
@ -172,12 +179,14 @@ pub struct CallExpr {
|
||||
pub callee: ExprOrSuper,
|
||||
pub args: Vec<ExprOrSpread>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct NewExpr {
|
||||
pub span: Span,
|
||||
pub callee: Box<Expr>,
|
||||
pub args: Option<(Vec<ExprOrSpread>)>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct SeqExpr {
|
||||
/// TODO: Calculate
|
||||
|
147
ecmascript/ast/src/jsx.rs
Normal file
147
ecmascript/ast/src/jsx.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use super::{Expr, Ident, Lit, SpreadElement};
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{ast_node, Span};
|
||||
|
||||
/// Used for `obj` property of `JSXMemberExpr`.
|
||||
#[ast_node]
|
||||
pub enum JSXObject {
|
||||
JSXMemberExpr(Box<JSXMemberExpr>),
|
||||
Ident(Ident),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXMemberExpr {
|
||||
#[span(lo)]
|
||||
pub obj: JSXObject,
|
||||
#[span(hi)]
|
||||
pub prop: Ident,
|
||||
}
|
||||
|
||||
/// XML-based namespace syntax:
|
||||
#[ast_node]
|
||||
pub struct JSXNamespacedName {
|
||||
#[span(lo)]
|
||||
pub ns: Ident,
|
||||
#[span(hi)]
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
#[derive(Copy)]
|
||||
pub struct JSXEmptyExpr {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXExprContainer {
|
||||
#[span]
|
||||
pub expr: JSXExpr,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
#[allow(variant_size_differences)]
|
||||
pub enum JSXExpr {
|
||||
Expr(Box<Expr>),
|
||||
JSXEmptyExpr(JSXEmptyExpr),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXSpreadChild {
|
||||
#[span]
|
||||
pub expr: Box<Expr>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub enum JSXElementName {
|
||||
Ident(Ident),
|
||||
JSXMemberExpr(JSXMemberExpr),
|
||||
JSXNamespacedName(JSXNamespacedName),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXOpeningElement {
|
||||
pub name: JSXElementName,
|
||||
|
||||
pub span: Span,
|
||||
pub attrs: Vec<JSXAttrOrSpread>,
|
||||
pub self_closing: bool,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
#[allow(variant_size_differences)]
|
||||
pub enum JSXAttrOrSpread {
|
||||
JSXAttr(JSXAttr),
|
||||
SpreadElement(SpreadElement),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXClosingElement {
|
||||
pub span: Span,
|
||||
pub name: JSXElementName,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXAttr {
|
||||
pub span: Span,
|
||||
pub name: JSXAttrName,
|
||||
/// Babel uses Expr instead of JSXAttrValue
|
||||
pub value: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub enum JSXAttrName {
|
||||
Ident(Ident),
|
||||
JSXNamespacedName(JSXNamespacedName),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub enum JSXAttrValue {
|
||||
Lit(Lit),
|
||||
JSXExprContainer(JSXExprContainer),
|
||||
JSXElement(Box<JSXElement>),
|
||||
JSXFragment(JSXFragment),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXText {
|
||||
pub span: Span,
|
||||
pub value: JsWord,
|
||||
pub raw: JsWord,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXElement {
|
||||
pub span: Span,
|
||||
pub opening: JSXOpeningElement,
|
||||
pub children: Vec<JSXElementChild>,
|
||||
pub closing: Option<JSXClosingElement>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub enum JSXElementChild {
|
||||
JSXText(JSXText),
|
||||
JSXExprContainer(JSXExprContainer),
|
||||
JSXSpreadChild(JSXSpreadChild),
|
||||
JSXElement(Box<JSXElement>),
|
||||
JSXFragment(JSXFragment),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct JSXFragment {
|
||||
pub span: Span,
|
||||
pub opening: JSXOpeningFragment,
|
||||
pub children: Vec<JSXElementChild>,
|
||||
pub closing: JSXClosingFragment,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
#[derive(Copy)]
|
||||
pub struct JSXOpeningFragment {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
#[derive(Copy)]
|
||||
pub struct JSXClosingFragment {
|
||||
pub span: Span,
|
||||
}
|
@ -8,7 +8,6 @@
|
||||
#![deny(unreachable_pub)]
|
||||
#![deny(variant_size_differences)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate enum_kind;
|
||||
#[macro_use]
|
||||
extern crate string_enum;
|
||||
@ -25,6 +24,12 @@ pub use self::{
|
||||
TplElement, TplLit, UnaryExpr, UpdateExpr, YieldExpr,
|
||||
},
|
||||
function::Function,
|
||||
jsx::{
|
||||
JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXClosingElement, JSXClosingFragment,
|
||||
JSXElement, JSXElementChild, JSXElementName, JSXEmptyExpr, JSXExpr, JSXExprContainer,
|
||||
JSXFragment, JSXMemberExpr, JSXNamespacedName, JSXObject, JSXOpeningElement,
|
||||
JSXOpeningFragment, JSXSpreadChild, JSXText,
|
||||
},
|
||||
keywords::IdentExt,
|
||||
lit::{Bool, Lit, Null, Number, Regex, RegexFlags, Str},
|
||||
module::{Module, ModuleItem},
|
||||
@ -51,6 +56,7 @@ mod class;
|
||||
mod decl;
|
||||
mod expr;
|
||||
mod function;
|
||||
mod jsx;
|
||||
mod keywords;
|
||||
mod lit;
|
||||
mod macros;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::JSXText;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{ast_node, Span};
|
||||
@ -9,6 +10,7 @@ pub enum Lit {
|
||||
Null(Null),
|
||||
Num(Number),
|
||||
Regex(Regex),
|
||||
JSXText(JSXText),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
|
@ -11,7 +11,7 @@ extern crate testing;
|
||||
use sourcemap::SourceMapBuilder;
|
||||
use swc_common::FileName;
|
||||
use swc_ecma_codegen::Emitter;
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput};
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput, Syntax};
|
||||
use test::Bencher;
|
||||
|
||||
const SOURCE: &str = r#"
|
||||
@ -96,7 +96,7 @@ fn emit_colors(b: &mut Bencher) {
|
||||
cfg: Default::default(),
|
||||
};
|
||||
let fm = cm.new_source_file(FileName::Anon(0), SOURCE.into());
|
||||
let mut parser = Parser::new(session, SourceFileInput::from(&*fm));
|
||||
let mut parser = Parser::new(session, Syntax::Es2019, SourceFileInput::from(&*fm));
|
||||
let module = parser.parse_module().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
|
@ -80,6 +80,8 @@ fn (&mut self, node: Node) -> Result;
|
||||
block
|
||||
};
|
||||
|
||||
// Emitter methods return Result<_, _>
|
||||
// We inject this to avoid writing Ok(()) every time.
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
return Ok(());
|
||||
|
166
ecmascript/codegen/src/jsx.rs
Normal file
166
ecmascript/codegen/src/jsx.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use super::{Emitter, Result};
|
||||
use crate::list::ListFormat;
|
||||
use swc_common::Spanned;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen_macros::emitter;
|
||||
|
||||
impl<'a> Emitter<'a> {
|
||||
#[emitter]
|
||||
pub fn emit_jsx_element(&mut self, node: &JSXElement) -> Result {
|
||||
emit!(node.opening);
|
||||
self.emit_list(
|
||||
node.span(),
|
||||
Some(&node.children),
|
||||
ListFormat::JsxElementOrFragmentChildren,
|
||||
)?;
|
||||
if let Some(ref closing) = node.closing {
|
||||
emit!(closing)
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_opening_element(&mut self, node: &JSXOpeningElement) -> Result {
|
||||
punct!("<");
|
||||
emit!(node.name);
|
||||
|
||||
self.emit_list(
|
||||
node.span(),
|
||||
Some(&node.attrs),
|
||||
ListFormat::JsxElementAttributes,
|
||||
)?;
|
||||
|
||||
if node.self_closing {
|
||||
punct!("/");
|
||||
}
|
||||
punct!(">");
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_element_name(&mut self, node: &JSXElementName) -> Result {
|
||||
match *node {
|
||||
JSXElementName::Ident(ref n) => emit!(n),
|
||||
JSXElementName::JSXMemberExpr(ref n) => emit!(n),
|
||||
JSXElementName::JSXNamespacedName(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_attr(&mut self, node: &JSXAttr) -> Result {
|
||||
emit!(node.name);
|
||||
|
||||
if let Some(ref value) = node.value {
|
||||
punct!(" ");
|
||||
emit!(value);
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_attr_name(&mut self, node: &JSXAttrName) -> Result {
|
||||
match *node {
|
||||
JSXAttrName::Ident(ref n) => emit!(n),
|
||||
JSXAttrName::JSXNamespacedName(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_attr_or_spread(&mut self, node: &JSXAttrOrSpread) -> Result {
|
||||
match *node {
|
||||
JSXAttrOrSpread::JSXAttr(ref n) => emit!(n),
|
||||
JSXAttrOrSpread::SpreadElement(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_element_child(&mut self, node: &JSXElementChild) -> Result {
|
||||
match *node {
|
||||
JSXElementChild::JSXElement(ref n) => emit!(n),
|
||||
JSXElementChild::JSXExprContainer(ref n) => emit!(n),
|
||||
JSXElementChild::JSXFragment(ref n) => emit!(n),
|
||||
JSXElementChild::JSXSpreadChild(ref n) => emit!(n),
|
||||
JSXElementChild::JSXText(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_spread_child(&mut self, node: &JSXSpreadChild) -> Result {
|
||||
punct!("{");
|
||||
punct!("...");
|
||||
emit!(node.expr);
|
||||
punct!("}");
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_expr_container(&mut self, node: &JSXExprContainer) -> Result {
|
||||
punct!("{");
|
||||
emit!(node.expr);
|
||||
punct!("}");
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_expr(&mut self, node: &JSXExpr) -> Result {
|
||||
match *node {
|
||||
JSXExpr::Expr(ref n) => emit!(n),
|
||||
JSXExpr::JSXEmptyExpr(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_closing_element(&mut self, node: &JSXClosingElement) -> Result {
|
||||
punct!("</");
|
||||
emit!(node.name);
|
||||
punct!(">");
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_fragment(&mut self, node: &JSXFragment) -> Result {
|
||||
emit!(node.opening);
|
||||
|
||||
self.emit_list(
|
||||
node.span(),
|
||||
Some(&node.children),
|
||||
ListFormat::JsxElementOrFragmentChildren,
|
||||
)?;
|
||||
|
||||
emit!(node.closing);
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_opening_fragment(&mut self, node: &JSXOpeningFragment) -> Result {
|
||||
punct!("<>")
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_closing_fragment(&mut self, node: &JSXClosingFragment) -> Result {
|
||||
punct!("</>")
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_namespaced_name(&mut self, node: &JSXNamespacedName) -> Result {
|
||||
emit!(node.ns);
|
||||
punct!(":");
|
||||
emit!(node.name);
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_empty_expr(&mut self, node: &JSXEmptyExpr) -> Result {}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_text(&mut self, node: &JSXText) -> Result {
|
||||
self.emit_js_word(node.span(), &node.value)?;
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_member_expr(&mut self, node: &JSXMemberExpr) -> Result {
|
||||
emit!(node.obj);
|
||||
punct!(".");
|
||||
emit!(node.prop);
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_jsx_object(&mut self, node: &JSXObject) -> Result {
|
||||
match *node {
|
||||
JSXObject::Ident(ref n) => emit!(n),
|
||||
JSXObject::JSXMemberExpr(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ pub mod macros;
|
||||
mod comments;
|
||||
mod config;
|
||||
mod decl;
|
||||
mod jsx;
|
||||
pub mod list;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@ -259,6 +260,7 @@ impl<'a> Emitter<'a> {
|
||||
self.emit_js_word(flags.span, &flags.value)?;
|
||||
}
|
||||
}
|
||||
Lit::JSXText(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,6 +374,12 @@ impl<'a> Emitter<'a> {
|
||||
Expr::Unary(ref n) => emit!(n),
|
||||
Expr::Update(ref n) => emit!(n),
|
||||
Expr::Yield(ref n) => emit!(n),
|
||||
|
||||
Expr::JSXMebmer(ref n) => emit!(n),
|
||||
Expr::JSXNamespacedName(ref n) => emit!(n),
|
||||
Expr::JSXEmpty(ref n) => emit!(n),
|
||||
Expr::JSXElement(ref n) => emit!(n),
|
||||
Expr::JSXFragment(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
extern crate swc_ecma_parser;
|
||||
extern crate testing;
|
||||
use self::{
|
||||
swc_ecma_parser::{PResult, Parser, Session, SourceFileInput},
|
||||
swc_ecma_parser::{PResult, Parser, Session, SourceFileInput, Syntax},
|
||||
testing::NormalizedOutput,
|
||||
};
|
||||
use super::*;
|
||||
@ -83,6 +83,7 @@ fn test_from_to(from: &str, to: &str) {
|
||||
handler: &handler,
|
||||
cfg: Default::default(),
|
||||
},
|
||||
Syntax::Es2019,
|
||||
(&*src).into(),
|
||||
));
|
||||
|
||||
|
@ -183,12 +183,19 @@ impl StartsWithAlphaNum for Expr {
|
||||
_ => false,
|
||||
},
|
||||
|
||||
// TODO: Support `v => {}`
|
||||
// TODO(kdy1): Support `v => {}`
|
||||
Expr::Arrow(ArrowExpr { .. }) => false,
|
||||
|
||||
Expr::Tpl(_) | Expr::Update(_) | Expr::Array(_) | Expr::Object(_) | Expr::Paren(_) => {
|
||||
false
|
||||
}
|
||||
|
||||
// it's empty
|
||||
Expr::JSXEmpty(..) => false,
|
||||
// start with `<`
|
||||
Expr::JSXFragment(..) | Expr::JSXElement(..) => false,
|
||||
Expr::JSXNamespacedName(..) => true,
|
||||
Expr::JSXMebmer(..) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use std::{
|
||||
use swc_common::{sync::Lrc, Fold, FoldWith, Span};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::Emitter;
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput};
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput, Syntax};
|
||||
use test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName};
|
||||
use testing::NormalizedOutput;
|
||||
|
||||
@ -152,6 +152,7 @@ fn error_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
||||
handler: &handler,
|
||||
cfg: Default::default(),
|
||||
},
|
||||
Syntax::Es2019,
|
||||
(&*src).into(),
|
||||
);
|
||||
|
||||
|
@ -9,7 +9,7 @@ extern crate testing;
|
||||
extern crate walkdir;
|
||||
|
||||
use self::{
|
||||
parser::SourceFileInput,
|
||||
parser::{SourceFileInput, Syntax},
|
||||
test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName},
|
||||
};
|
||||
use std::{
|
||||
@ -95,7 +95,8 @@ fn add_golden_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
||||
cfg: Default::default(),
|
||||
handler: &handler,
|
||||
};
|
||||
parser::Parser::new(session, SourceFileInput::from(&*fm)).parse_module()?
|
||||
parser::Parser::new(session, Syntax::Es2019, SourceFileInput::from(&*fm))
|
||||
.parse_module()?
|
||||
};
|
||||
|
||||
lints::lint_all(&handler, &module);
|
||||
|
@ -17,6 +17,10 @@ unicode-xid = "0.1"
|
||||
log = { version = "0.4", features = ["release_max_level_debug"] }
|
||||
either = { version = "1.4" }
|
||||
smallvec = "0.6.7"
|
||||
lazy_static = "1"
|
||||
regex = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
testing = { version = "0.1", path ="../../testing" }
|
||||
testing = { version = "0.1", path ="../../testing" }
|
||||
env_logger = "0.6.0"
|
||||
walkdir = "2"
|
||||
|
@ -6,7 +6,7 @@ extern crate test;
|
||||
extern crate testing;
|
||||
|
||||
use swc_common::FileName;
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput};
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput, Syntax};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
@ -62,7 +62,7 @@ fn bench_module(b: &mut Bencher, src: &'static str) {
|
||||
|
||||
b.iter(|| {
|
||||
test::black_box({
|
||||
let mut parser = Parser::new(session, SourceFileInput::from(&*fm));
|
||||
let mut parser = Parser::new(session, Syntax::Es2019, SourceFileInput::from(&*fm));
|
||||
let _ = parser.parse_module();
|
||||
})
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
use self::SyntaxError::*;
|
||||
use crate::token::Token;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{self, Debug, Formatter},
|
||||
@ -8,7 +9,6 @@ use swc_common::{
|
||||
errors::{DiagnosticBuilder, Handler},
|
||||
Span,
|
||||
};
|
||||
use token::Token;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct Eof<'a> {
|
||||
@ -87,8 +87,12 @@ pub(crate) enum SyntaxError {
|
||||
LineBreakBeforeArrow,
|
||||
|
||||
/// Unexpected token
|
||||
Unexpected,
|
||||
Expected(&'static Token),
|
||||
Unexpected {
|
||||
got: String,
|
||||
},
|
||||
ReservedWordInImport,
|
||||
AssignProperty,
|
||||
Expected(&'static Token, String),
|
||||
ExpectedSemiForExprStmt {
|
||||
expr: Span,
|
||||
},
|
||||
@ -122,6 +126,14 @@ pub(crate) enum SyntaxError {
|
||||
YieldParamInGen,
|
||||
|
||||
AwaitForStmt,
|
||||
|
||||
UnterminatedJSXContents,
|
||||
EmptyJSXAttr,
|
||||
InvalidJSXValue,
|
||||
JSXExpectedClosingTagForLtGt,
|
||||
JSXExpectedClosingTag {
|
||||
tag: JsWord,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> From<ErrorToDiag<'a>> for Error {
|
||||
@ -180,8 +192,11 @@ impl<'a> From<ErrorToDiag<'a>> for DiagnosticBuilder<'a> {
|
||||
UnaryInExp { .. } => "** cannot be applied to unary expression".into(),
|
||||
LineBreakInThrow => "LineBreak cannot follow 'throw'".into(),
|
||||
LineBreakBeforeArrow => "Unexpected line break between arrow head and arrow".into(),
|
||||
Unexpected => "Unexpected token".into(),
|
||||
Expected(token) => format!("Expected {:?}", token).into(),
|
||||
Unexpected { ref got } => format!("Unexpected token {}", got).into(),
|
||||
|
||||
ReservedWordInImport => "cannot import as reserved word".into(),
|
||||
AssignProperty => "assignment property is invalid syntax".into(),
|
||||
Expected(token, ref got) => format!("Expected {:?}, got {}", token, got).into(),
|
||||
ExpectedSemiForExprStmt { .. } => "Expected ';', '}' or <eof>".into(),
|
||||
|
||||
AwaitStar => "await* has been removed from the async functions proposal. Use \
|
||||
@ -217,6 +232,16 @@ impl<'a> From<ErrorToDiag<'a>> for DiagnosticBuilder<'a> {
|
||||
LabelledGenerator => "Generator cannot be labelled".into(),
|
||||
YieldParamInGen => "'yield' cannot be used as a parameter within generator".into(),
|
||||
AwaitForStmt => "for await syntax is valid only for for-of statement".into(),
|
||||
|
||||
UnterminatedJSXContents => "Unterminated JSX contents".into(),
|
||||
EmptyJSXAttr => "JSX attributes must only be assigned a non-empty expression".into(),
|
||||
InvalidJSXValue => {
|
||||
"JSX value should be either an expression or a quoted JSX text".into()
|
||||
}
|
||||
JSXExpectedClosingTagForLtGt => "Expected corresponding JSX closing tag for <>".into(),
|
||||
JSXExpectedClosingTag { ref tag } => {
|
||||
format!("Expected corresponding JSX closing tag for <{}>", tag).into()
|
||||
}
|
||||
};
|
||||
|
||||
let d = e.handler.error(&msg).span(e.span);
|
||||
|
@ -6,6 +6,7 @@ pub struct SourceFileInput<'a> {
|
||||
fm: &'a SourceFile,
|
||||
start_pos: BytePos,
|
||||
last_pos: BytePos,
|
||||
orig: &'a str,
|
||||
iter: str::CharIndices<'a>,
|
||||
}
|
||||
|
||||
@ -19,6 +20,7 @@ impl<'a> From<&'a SourceFile> for SourceFileInput<'a> {
|
||||
SourceFileInput {
|
||||
start_pos: fm.start_pos,
|
||||
last_pos: fm.start_pos,
|
||||
orig: src,
|
||||
iter: src.char_indices(),
|
||||
fm,
|
||||
}
|
||||
@ -46,7 +48,7 @@ impl<'a> Input for SourceFileInput<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn current(&mut self) -> Option<char> {
|
||||
fn cur(&mut self) -> Option<char> {
|
||||
self.iter.clone().nth(0).map(|i| i.1)
|
||||
}
|
||||
|
||||
@ -58,6 +60,22 @@ impl<'a> Input for SourceFileInput<'a> {
|
||||
self.iter.clone().nth(2).map(|i| i.1)
|
||||
}
|
||||
|
||||
fn slice(&mut self, start: BytePos, end: BytePos) -> &str {
|
||||
assert!(start <= end, "Cannot slice {:?}..{:?}", start, end);
|
||||
let s = self.orig;
|
||||
|
||||
let start_idx = (start - self.fm.start_pos).0 as usize;
|
||||
let end_idx = (end - self.fm.start_pos).0 as usize;
|
||||
|
||||
let ret = &s[start_idx..end_idx];
|
||||
|
||||
self.iter = s[end_idx..].char_indices();
|
||||
self.last_pos = end;
|
||||
self.start_pos = end;
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn uncons_while<F>(&mut self, mut pred: F) -> &str
|
||||
where
|
||||
F: FnMut(char) -> bool,
|
||||
@ -80,10 +98,20 @@ impl<'a> Input for SourceFileInput<'a> {
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn reset_to(&mut self, to: BytePos) {
|
||||
let orig = self.orig;
|
||||
let idx = (to - self.fm.start_pos).0 as usize;
|
||||
|
||||
let s = &orig[idx..];
|
||||
self.iter = s.char_indices();
|
||||
self.start_pos = to;
|
||||
self.last_pos = to;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Input {
|
||||
fn current(&mut self) -> Option<char>;
|
||||
fn cur(&mut self) -> Option<char>;
|
||||
fn peek(&mut self) -> Option<char>;
|
||||
fn peek_ahead(&mut self) -> Option<char>;
|
||||
fn bump(&mut self);
|
||||
@ -92,11 +120,15 @@ pub trait Input {
|
||||
|
||||
fn last_pos(&self) -> BytePos;
|
||||
|
||||
fn slice(&mut self, start: BytePos, end: BytePos) -> &str;
|
||||
|
||||
/// Takes items from stream, testing each one with predicate. returns the
|
||||
/// range of items which passed predicate.
|
||||
fn uncons_while<F>(&mut self, f: F) -> &str
|
||||
where
|
||||
F: FnMut(char) -> bool;
|
||||
|
||||
fn reset_to(&mut self, to: BytePos);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -104,9 +136,44 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::lexer::util::CharExt;
|
||||
|
||||
#[test]
|
||||
fn src_input_slice_1() {
|
||||
let _ = crate::with_test_sess("foo/d", |_, mut i| {
|
||||
assert_eq!(i.slice(BytePos(0), BytePos(1)), "f");
|
||||
assert_eq!(i.last_pos, BytePos(1));
|
||||
assert_eq!(i.start_pos, BytePos(1));
|
||||
assert_eq!(i.cur(), Some('o'));
|
||||
|
||||
assert_eq!(i.slice(BytePos(1), BytePos(3)), "oo");
|
||||
assert_eq!(i.slice(BytePos(0), BytePos(3)), "foo");
|
||||
assert_eq!(i.last_pos, BytePos(3));
|
||||
assert_eq!(i.start_pos, BytePos(3));
|
||||
assert_eq!(i.cur(), Some('/'));
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn src_input_reset_to_1() {
|
||||
let _ = crate::with_test_sess("foad", |_, mut i| {
|
||||
assert_eq!(i.slice(BytePos(0), BytePos(2)), "fo");
|
||||
assert_eq!(i.last_pos, BytePos(2));
|
||||
assert_eq!(i.start_pos, BytePos(2));
|
||||
assert_eq!(i.cur(), Some('a'));
|
||||
i.reset_to(BytePos(0));
|
||||
|
||||
assert_eq!(i.cur(), Some('f'));
|
||||
assert_eq!(i.last_pos, BytePos(0));
|
||||
assert_eq!(i.start_pos, BytePos(0));
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn src_input_smoke_01() {
|
||||
let _ = ::with_test_sess("foo/d", |_, mut i| {
|
||||
let _ = crate::with_test_sess("foo/d", |_, mut i| {
|
||||
assert_eq!(i.cur_pos(), BytePos(0));
|
||||
assert_eq!(i.last_pos, BytePos(0));
|
||||
assert_eq!(i.start_pos, BytePos(0));
|
||||
@ -115,22 +182,22 @@ mod tests {
|
||||
// assert_eq!(i.cur_pos(), BytePos(4));
|
||||
assert_eq!(i.last_pos, BytePos(3));
|
||||
assert_eq!(i.start_pos, BytePos(3));
|
||||
assert_eq!(i.current(), Some('/'));
|
||||
assert_eq!(i.cur(), Some('/'));
|
||||
|
||||
i.bump();
|
||||
assert_eq!(i.last_pos, BytePos(4));
|
||||
assert_eq!(i.current(), Some('d'));
|
||||
assert_eq!(i.cur(), Some('d'));
|
||||
|
||||
i.bump();
|
||||
assert_eq!(i.last_pos, BytePos(5));
|
||||
assert_eq!(i.current(), None);
|
||||
assert_eq!(i.cur(), None);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn src_input_smoke_02() {
|
||||
let _ = ::with_test_sess("℘℘/℘℘", |_, mut i| {
|
||||
let _ = crate::with_test_sess("℘℘/℘℘", |_, mut i| {
|
||||
assert_eq!(i.iter.as_str(), "℘℘/℘℘");
|
||||
assert_eq!(i.cur_pos(), BytePos(0));
|
||||
assert_eq!(i.last_pos, BytePos(0));
|
||||
@ -140,7 +207,7 @@ mod tests {
|
||||
assert_eq!(i.iter.as_str(), "/℘℘");
|
||||
assert_eq!(i.last_pos, BytePos(6));
|
||||
assert_eq!(i.start_pos, BytePos(6));
|
||||
assert_eq!(i.current(), Some('/'));
|
||||
assert_eq!(i.cur(), Some('/'));
|
||||
i.bump();
|
||||
assert_eq!(i.last_pos, BytePos(7));
|
||||
assert_eq!(i.start_pos, BytePos(6));
|
||||
|
475
ecmascript/parser/src/lexer/jsx.rs
Normal file
475
ecmascript/parser/src/lexer/jsx.rs
Normal file
@ -0,0 +1,475 @@
|
||||
use super::*;
|
||||
use either::Either;
|
||||
use regex::Regex;
|
||||
|
||||
impl<'a, I: Input> Lexer<'a, I> {
|
||||
pub(super) fn read_jsx_token(&mut self) -> LexResult<Option<Token>> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
let mut chunk_start = self.input.cur_pos();
|
||||
let mut out = String::new();
|
||||
|
||||
loop {
|
||||
let cur = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
let start = self.state.start;
|
||||
self.error(start, SyntaxError::UnterminatedJSXContents)?
|
||||
}
|
||||
};
|
||||
let cur_pos = self.input.cur_pos();
|
||||
|
||||
match cur {
|
||||
'<' | '{' => {
|
||||
//
|
||||
if cur_pos == self.state.start {
|
||||
if cur == '<' && self.state.is_expr_allowed {
|
||||
self.input.bump();
|
||||
return Ok(Token::JSXTagStart).map(Some);
|
||||
}
|
||||
return self.read_token();
|
||||
}
|
||||
out.push_str(self.input.slice(chunk_start, cur_pos));
|
||||
|
||||
return Ok(Token::JSXText { raw: out.into() }).map(Some);
|
||||
}
|
||||
|
||||
'&' => {
|
||||
out.push_str(self.input.slice(chunk_start, cur_pos));
|
||||
out.push(self.read_jsx_entity()?);
|
||||
|
||||
chunk_start = self.input.cur_pos();
|
||||
}
|
||||
|
||||
_ => {
|
||||
if cur.is_line_break() {
|
||||
out.push_str(self.input.slice(chunk_start, cur_pos));
|
||||
match self.read_jsx_new_line(true)? {
|
||||
Either::Left(s) => out.push_str(s),
|
||||
Either::Right(c) => out.push(c),
|
||||
}
|
||||
chunk_start = cur_pos;
|
||||
} else {
|
||||
self.input.bump()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_jsx_entity(&mut self) -> LexResult<char> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
fn from_code(s: &str, radix: u32) -> LexResult<char> {
|
||||
// TODO(kdy1): unwrap -> Err
|
||||
let c = char::from_u32(
|
||||
u32::from_str_radix(s, radix).expect("failed to parse string as number"),
|
||||
)
|
||||
.expect("failed to parse number as char");
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref HEX_NUMBER: Regex = Regex::new(r"^[\da-fA-F]+$").unwrap();
|
||||
}
|
||||
lazy_static! {
|
||||
static ref DECIMAL_NUMBER: Regex = Regex::new(r"^\d+$").unwrap();
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
|
||||
let c = self.input.cur();
|
||||
debug_assert_eq!(c, Some('&'));
|
||||
self.input.bump();
|
||||
|
||||
let start_pos = self.input.cur_pos();
|
||||
|
||||
for _ in 0..10 {
|
||||
let c = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => break,
|
||||
};
|
||||
self.input.bump();
|
||||
|
||||
if c == ';' {
|
||||
if s.starts_with('#') {
|
||||
if s[1..].starts_with('x') {
|
||||
if HEX_NUMBER.is_match(&s[2..]) {
|
||||
return from_code(&s[2..], 16);
|
||||
}
|
||||
} else {
|
||||
if DECIMAL_NUMBER.is_match(&s[1..]) {
|
||||
return from_code(&s[1..], 10);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(entity) = xhtml(&s) {
|
||||
return Ok(entity);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
s.push(c)
|
||||
}
|
||||
|
||||
self.input.reset_to(start_pos);
|
||||
Ok('&')
|
||||
}
|
||||
|
||||
pub(super) fn read_jsx_new_line(
|
||||
&mut self,
|
||||
normalize_crlf: bool,
|
||||
) -> LexResult<Either<&'static str, char>> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
let ch = self.input.cur().unwrap();
|
||||
self.input.bump();
|
||||
|
||||
let out = if ch == '\r' && self.input.cur() == Some('\n') {
|
||||
self.input.bump();
|
||||
Either::Left(if normalize_crlf { "\n" } else { "\r\n" })
|
||||
} else {
|
||||
Either::Right(ch)
|
||||
};
|
||||
let cur_pos = self.input.cur_pos();
|
||||
self.state.cur_line += 1;
|
||||
self.state.line_start = cur_pos;
|
||||
|
||||
return Ok(out);
|
||||
}
|
||||
|
||||
pub(super) fn read_jsx_str(&mut self, quote: char) -> LexResult<Token> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
self.input.bump(); // `quote`
|
||||
|
||||
let mut out = String::new();
|
||||
let mut chunk_start = self.input.cur_pos();
|
||||
loop {
|
||||
let ch = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
let start = self.state.start;
|
||||
self.error(start, SyntaxError::UnterminatedStrLit)?
|
||||
}
|
||||
};
|
||||
|
||||
let cur_pos = self.input.cur_pos();
|
||||
|
||||
if ch == quote {
|
||||
break;
|
||||
}
|
||||
if ch == '&' {
|
||||
out.push_str(self.input.slice(chunk_start, cur_pos));
|
||||
out.push(self.read_jsx_entity()?);
|
||||
chunk_start = self.input.cur_pos();
|
||||
} else if ch.is_line_break() {
|
||||
out.push_str(self.input.slice(chunk_start, cur_pos));
|
||||
match self.read_jsx_new_line(false)? {
|
||||
Either::Left(s) => out.push_str(s),
|
||||
Either::Right(c) => out.push(c),
|
||||
}
|
||||
chunk_start = cur_pos;
|
||||
} else {
|
||||
self.input.bump();
|
||||
}
|
||||
}
|
||||
let cur_pos = self.input.cur_pos();
|
||||
out.push_str(self.input.slice(chunk_start, cur_pos));
|
||||
self.input.bump();
|
||||
return Ok(Token::Str {
|
||||
value: out.into(),
|
||||
has_escape: false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Read a JSX identifier (valid tag or attribute name).
|
||||
///
|
||||
/// Optimized version since JSX identifiers can"t contain
|
||||
/// escape characters and so can be read as single slice.
|
||||
/// Also assumes that first character was already checked
|
||||
/// by isIdentifierStart in readToken.
|
||||
pub(super) fn read_jsx_word(&mut self) -> LexResult<Token> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
debug_assert!(self.input.cur().is_some());
|
||||
debug_assert!(self.input.cur().unwrap().is_ident_start());
|
||||
|
||||
let cur_pos = self.input.cur_pos();
|
||||
let slice = self.input.uncons_while(|c| c.is_ident_part() || c == '-');
|
||||
|
||||
Ok(Token::JSXName { name: slice.into() })
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! xhtml {
|
||||
(
|
||||
$(
|
||||
$i:ident : $s:expr,
|
||||
)*
|
||||
) => {
|
||||
fn xhtml(s: &str) -> Option<char> {
|
||||
match s{
|
||||
$(stringify!($i) => Some($s),)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
xhtml!(
|
||||
quot: '\u{0022}',
|
||||
amp: '&',
|
||||
apos: '\u{0027}',
|
||||
lt: '<',
|
||||
gt: '>',
|
||||
nbsp: '\u{00A0}',
|
||||
iexcl: '\u{00A1}',
|
||||
cent: '\u{00A2}',
|
||||
pound: '\u{00A3}',
|
||||
curren: '\u{00A4}',
|
||||
yen: '\u{00A5}',
|
||||
brvbar: '\u{00A6}',
|
||||
sect: '\u{00A7}',
|
||||
uml: '\u{00A8}',
|
||||
copy: '\u{00A9}',
|
||||
ordf: '\u{00AA}',
|
||||
laquo: '\u{00AB}',
|
||||
not: '\u{00AC}',
|
||||
shy: '\u{00AD}',
|
||||
reg: '\u{00AE}',
|
||||
macr: '\u{00AF}',
|
||||
deg: '\u{00B0}',
|
||||
plusmn: '\u{00B1}',
|
||||
sup2: '\u{00B2}',
|
||||
sup3: '\u{00B3}',
|
||||
acute: '\u{00B4}',
|
||||
micro: '\u{00B5}',
|
||||
para: '\u{00B6}',
|
||||
middot: '\u{00B7}',
|
||||
cedil: '\u{00B8}',
|
||||
sup1: '\u{00B9}',
|
||||
ordm: '\u{00BA}',
|
||||
raquo: '\u{00BB}',
|
||||
frac14: '\u{00BC}',
|
||||
frac12: '\u{00BD}',
|
||||
frac34: '\u{00BE}',
|
||||
iquest: '\u{00BF}',
|
||||
Agrave: '\u{00C0}',
|
||||
Aacute: '\u{00C1}',
|
||||
Acirc: '\u{00C2}',
|
||||
Atilde: '\u{00C3}',
|
||||
Auml: '\u{00C4}',
|
||||
Aring: '\u{00C5}',
|
||||
AElig: '\u{00C6}',
|
||||
Ccedil: '\u{00C7}',
|
||||
Egrave: '\u{00C8}',
|
||||
Eacute: '\u{00C9}',
|
||||
Ecirc: '\u{00CA}',
|
||||
Euml: '\u{00CB}',
|
||||
Igrave: '\u{00CC}',
|
||||
Iacute: '\u{00CD}',
|
||||
Icirc: '\u{00CE}',
|
||||
Iuml: '\u{00CF}',
|
||||
ETH: '\u{00D0}',
|
||||
Ntilde: '\u{00D1}',
|
||||
Ograve: '\u{00D2}',
|
||||
Oacute: '\u{00D3}',
|
||||
Ocirc: '\u{00D4}',
|
||||
Otilde: '\u{00D5}',
|
||||
Ouml: '\u{00D6}',
|
||||
times: '\u{00D7}',
|
||||
Oslash: '\u{00D8}',
|
||||
Ugrave: '\u{00D9}',
|
||||
Uacute: '\u{00DA}',
|
||||
Ucirc: '\u{00DB}',
|
||||
Uuml: '\u{00DC}',
|
||||
Yacute: '\u{00DD}',
|
||||
THORN: '\u{00DE}',
|
||||
szlig: '\u{00DF}',
|
||||
agrave: '\u{00E0}',
|
||||
aacute: '\u{00E1}',
|
||||
acirc: '\u{00E2}',
|
||||
atilde: '\u{00E3}',
|
||||
auml: '\u{00E4}',
|
||||
aring: '\u{00E5}',
|
||||
aelig: '\u{00E6}',
|
||||
ccedil: '\u{00E7}',
|
||||
egrave: '\u{00E8}',
|
||||
eacute: '\u{00E9}',
|
||||
ecirc: '\u{00EA}',
|
||||
euml: '\u{00EB}',
|
||||
igrave: '\u{00EC}',
|
||||
iacute: '\u{00ED}',
|
||||
icirc: '\u{00EE}',
|
||||
iuml: '\u{00EF}',
|
||||
eth: '\u{00F0}',
|
||||
ntilde: '\u{00F1}',
|
||||
ograve: '\u{00F2}',
|
||||
oacute: '\u{00F3}',
|
||||
ocirc: '\u{00F4}',
|
||||
otilde: '\u{00F5}',
|
||||
ouml: '\u{00F6}',
|
||||
divide: '\u{00F7}',
|
||||
oslash: '\u{00F8}',
|
||||
ugrave: '\u{00F9}',
|
||||
uacute: '\u{00FA}',
|
||||
ucirc: '\u{00FB}',
|
||||
uuml: '\u{00FC}',
|
||||
yacute: '\u{00FD}',
|
||||
thorn: '\u{00FE}',
|
||||
yuml: '\u{00FF}',
|
||||
OElig: '\u{0152}',
|
||||
oelig: '\u{0153}',
|
||||
Scaron: '\u{0160}',
|
||||
scaron: '\u{0161}',
|
||||
Yuml: '\u{0178}',
|
||||
fnof: '\u{0192}',
|
||||
circ: '\u{02C6}',
|
||||
tilde: '\u{02DC}',
|
||||
Alpha: '\u{0391}',
|
||||
Beta: '\u{0392}',
|
||||
Gamma: '\u{0393}',
|
||||
Delta: '\u{0394}',
|
||||
Epsilon: '\u{0395}',
|
||||
Zeta: '\u{0396}',
|
||||
Eta: '\u{0397}',
|
||||
Theta: '\u{0398}',
|
||||
Iota: '\u{0399}',
|
||||
Kappa: '\u{039A}',
|
||||
Lambda: '\u{039B}',
|
||||
Mu: '\u{039C}',
|
||||
Nu: '\u{039D}',
|
||||
Xi: '\u{039E}',
|
||||
Omicron: '\u{039F}',
|
||||
Pi: '\u{03A0}',
|
||||
Rho: '\u{03A1}',
|
||||
Sigma: '\u{03A3}',
|
||||
Tau: '\u{03A4}',
|
||||
Upsilon: '\u{03A5}',
|
||||
Phi: '\u{03A6}',
|
||||
Chi: '\u{03A7}',
|
||||
Psi: '\u{03A8}',
|
||||
Omega: '\u{03A9}',
|
||||
alpha: '\u{03B1}',
|
||||
beta: '\u{03B2}',
|
||||
gamma: '\u{03B3}',
|
||||
delta: '\u{03B4}',
|
||||
epsilon: '\u{03B5}',
|
||||
zeta: '\u{03B6}',
|
||||
eta: '\u{03B7}',
|
||||
theta: '\u{03B8}',
|
||||
iota: '\u{03B9}',
|
||||
kappa: '\u{03BA}',
|
||||
lambda: '\u{03BB}',
|
||||
mu: '\u{03BC}',
|
||||
nu: '\u{03BD}',
|
||||
xi: '\u{03BE}',
|
||||
omicron: '\u{03BF}',
|
||||
pi: '\u{03C0}',
|
||||
rho: '\u{03C1}',
|
||||
sigmaf: '\u{03C2}',
|
||||
sigma: '\u{03C3}',
|
||||
tau: '\u{03C4}',
|
||||
upsilon: '\u{03C5}',
|
||||
phi: '\u{03C6}',
|
||||
chi: '\u{03C7}',
|
||||
psi: '\u{03C8}',
|
||||
omega: '\u{03C9}',
|
||||
thetasym: '\u{03D1}',
|
||||
upsih: '\u{03D2}',
|
||||
piv: '\u{03D6}',
|
||||
ensp: '\u{2002}',
|
||||
emsp: '\u{2003}',
|
||||
thinsp: '\u{2009}',
|
||||
zwnj: '\u{200C}',
|
||||
zwj: '\u{200D}',
|
||||
lrm: '\u{200E}',
|
||||
rlm: '\u{200F}',
|
||||
ndash: '\u{2013}',
|
||||
mdash: '\u{2014}',
|
||||
lsquo: '\u{2018}',
|
||||
rsquo: '\u{2019}',
|
||||
sbquo: '\u{201A}',
|
||||
ldquo: '\u{201C}',
|
||||
rdquo: '\u{201D}',
|
||||
bdquo: '\u{201E}',
|
||||
dagger: '\u{2020}',
|
||||
Dagger: '\u{2021}',
|
||||
bull: '\u{2022}',
|
||||
hellip: '\u{2026}',
|
||||
permil: '\u{2030}',
|
||||
prime: '\u{2032}',
|
||||
Prime: '\u{2033}',
|
||||
lsaquo: '\u{2039}',
|
||||
rsaquo: '\u{203A}',
|
||||
oline: '\u{203E}',
|
||||
frasl: '\u{2044}',
|
||||
euro: '\u{20AC}',
|
||||
image: '\u{2111}',
|
||||
weierp: '\u{2118}',
|
||||
real: '\u{211C}',
|
||||
trade: '\u{2122}',
|
||||
alefsym: '\u{2135}',
|
||||
larr: '\u{2190}',
|
||||
uarr: '\u{2191}',
|
||||
rarr: '\u{2192}',
|
||||
darr: '\u{2193}',
|
||||
harr: '\u{2194}',
|
||||
crarr: '\u{21B5}',
|
||||
lArr: '\u{21D0}',
|
||||
uArr: '\u{21D1}',
|
||||
rArr: '\u{21D2}',
|
||||
dArr: '\u{21D3}',
|
||||
hArr: '\u{21D4}',
|
||||
forall: '\u{2200}',
|
||||
part: '\u{2202}',
|
||||
exist: '\u{2203}',
|
||||
empty: '\u{2205}',
|
||||
nabla: '\u{2207}',
|
||||
isin: '\u{2208}',
|
||||
notin: '\u{2209}',
|
||||
ni: '\u{220B}',
|
||||
prod: '\u{220F}',
|
||||
sum: '\u{2211}',
|
||||
minus: '\u{2212}',
|
||||
lowast: '\u{2217}',
|
||||
radic: '\u{221A}',
|
||||
prop: '\u{221D}',
|
||||
infin: '\u{221E}',
|
||||
ang: '\u{2220}',
|
||||
and: '\u{2227}',
|
||||
or: '\u{2228}',
|
||||
cap: '\u{2229}',
|
||||
cup: '\u{222A}',
|
||||
int: '\u{222B}',
|
||||
there4: '\u{2234}',
|
||||
sim: '\u{223C}',
|
||||
cong: '\u{2245}',
|
||||
asymp: '\u{2248}',
|
||||
ne: '\u{2260}',
|
||||
equiv: '\u{2261}',
|
||||
le: '\u{2264}',
|
||||
ge: '\u{2265}',
|
||||
sub: '\u{2282}',
|
||||
sup: '\u{2283}',
|
||||
nsub: '\u{2284}',
|
||||
sube: '\u{2286}',
|
||||
supe: '\u{2287}',
|
||||
oplus: '\u{2295}',
|
||||
otimes: '\u{2297}',
|
||||
perp: '\u{22A5}',
|
||||
sdot: '\u{22C5}',
|
||||
lceil: '\u{2308}',
|
||||
rceil: '\u{2309}',
|
||||
lfloor: '\u{230A}',
|
||||
rfloor: '\u{230B}',
|
||||
lang: '\u{2329}',
|
||||
rang: '\u{232A}',
|
||||
loz: '\u{25CA}',
|
||||
spades: '\u{2660}',
|
||||
clubs: '\u{2663}',
|
||||
hearts: '\u{2665}',
|
||||
diams: '\u{2666}',
|
||||
);
|
@ -6,43 +6,48 @@
|
||||
#![allow(unused_variables)]
|
||||
pub use self::input::Input;
|
||||
use self::{state::State, util::*};
|
||||
use crate::{
|
||||
error::{Error, SyntaxError},
|
||||
token::*,
|
||||
Context, Session, Syntax,
|
||||
};
|
||||
use ast::Str;
|
||||
use error::SyntaxError;
|
||||
use std::char;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{BytePos, Span};
|
||||
use token::*;
|
||||
use Context;
|
||||
use Session;
|
||||
|
||||
pub mod input;
|
||||
mod jsx;
|
||||
mod number;
|
||||
mod state;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod util;
|
||||
|
||||
pub(crate) type LexResult<T> = Result<T, ::error::Error>;
|
||||
pub(crate) type LexResult<T> = Result<T, Error>;
|
||||
|
||||
pub(crate) struct Lexer<'a, I: Input> {
|
||||
session: Session<'a>,
|
||||
pub ctx: Context,
|
||||
input: I,
|
||||
state: State,
|
||||
pub syntax: Syntax,
|
||||
}
|
||||
|
||||
impl<'a, I: Input> Lexer<'a, I> {
|
||||
pub fn new(session: Session<'a>, input: I) -> Self {
|
||||
pub fn new(session: Session<'a>, syntax: Syntax, input: I) -> Self {
|
||||
Lexer {
|
||||
session,
|
||||
input,
|
||||
state: Default::default(),
|
||||
state: State::new(syntax),
|
||||
ctx: Default::default(),
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
|
||||
/// babel: `getTokenFromCode`
|
||||
fn read_token(&mut self) -> LexResult<Option<Token>> {
|
||||
let c = match self.input.current() {
|
||||
let c = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => return Ok(None),
|
||||
};
|
||||
@ -105,7 +110,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
':' => {
|
||||
self.input.bump();
|
||||
|
||||
if self.session.cfg.fn_bind && self.input.current() == Some(':') {
|
||||
if self.session.cfg.fn_bind && self.input.cur() == Some(':') {
|
||||
self.input.bump();
|
||||
return Ok(Some(tok!("::")));
|
||||
}
|
||||
@ -138,13 +143,13 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
|
||||
// check for **
|
||||
if is_mul {
|
||||
if self.input.current() == Some('*') {
|
||||
if self.input.cur() == Some('*') {
|
||||
self.input.bump();
|
||||
token = BinOp(Exp)
|
||||
}
|
||||
}
|
||||
|
||||
if self.input.current() == Some('=') {
|
||||
if self.input.cur() == Some('=') {
|
||||
self.input.bump();
|
||||
token = match token {
|
||||
BinOp(Mul) => AssignOp(MulAssign),
|
||||
@ -163,7 +168,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
let token = if c == '&' { BitAnd } else { BitOr };
|
||||
|
||||
// '|=', '&='
|
||||
if self.input.current() == Some('=') {
|
||||
if self.input.cur() == Some('=') {
|
||||
self.input.bump();
|
||||
return Ok(Some(AssignOp(match token {
|
||||
BitAnd => BitAndAssign,
|
||||
@ -173,7 +178,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
}
|
||||
|
||||
// '||', '&&'
|
||||
if self.input.current() == Some(c) {
|
||||
if self.input.cur() == Some(c) {
|
||||
self.input.bump();
|
||||
return Ok(Some(BinOp(match token {
|
||||
BitAnd => LogicalAnd,
|
||||
@ -187,7 +192,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
'^' => {
|
||||
// Bitwise xor
|
||||
self.input.bump();
|
||||
if self.input.current() == Some('=') {
|
||||
if self.input.cur() == Some('=') {
|
||||
self.input.bump();
|
||||
AssignOp(BitXorAssign)
|
||||
} else {
|
||||
@ -199,7 +204,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
self.input.bump();
|
||||
|
||||
// '++', '--'
|
||||
if self.input.current() == Some(c) {
|
||||
if self.input.cur() == Some(c) {
|
||||
self.input.bump();
|
||||
|
||||
// Handle -->
|
||||
@ -217,7 +222,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
} else {
|
||||
MinusMinus
|
||||
}
|
||||
} else if self.input.current() == Some('=') {
|
||||
} else if self.input.cur() == Some('=') {
|
||||
self.input.bump();
|
||||
AssignOp(if c == '+' { AddAssign } else { SubAssign })
|
||||
} else {
|
||||
@ -230,11 +235,11 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
'!' | '=' => {
|
||||
self.input.bump();
|
||||
|
||||
if self.input.current() == Some('=') {
|
||||
if self.input.cur() == Some('=') {
|
||||
// "=="
|
||||
self.input.bump();
|
||||
|
||||
if self.input.current() == Some('=') {
|
||||
if self.input.cur() == Some('=') {
|
||||
self.input.bump();
|
||||
if c == '!' {
|
||||
BinOp(NotEqEq)
|
||||
@ -248,7 +253,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
BinOp(EqEq)
|
||||
}
|
||||
}
|
||||
} else if c == '=' && self.input.current() == Some('>') {
|
||||
} else if c == '=' && self.input.cur() == Some('>') {
|
||||
// "=>"
|
||||
self.input.bump();
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
//! See https://tc39.github.io/ecma262/#sec-literals-numeric-literals
|
||||
|
||||
use super::*;
|
||||
use error::SyntaxError;
|
||||
use crate::error::SyntaxError;
|
||||
use std::fmt::Display;
|
||||
|
||||
impl<'a, I: Input> Lexer<'a, I> {
|
||||
@ -287,8 +287,8 @@ mod tests {
|
||||
where
|
||||
F: FnOnce(&mut Lexer<SourceFileInput>) -> Ret,
|
||||
{
|
||||
::with_test_sess(s, |sess, fm| {
|
||||
let mut l = Lexer::new(sess, fm.into());
|
||||
crate::with_test_sess(s, |sess, fm| {
|
||||
let mut l = Lexer::new(sess, Syntax::Es2019, fm.into());
|
||||
Ok(f(&mut l))
|
||||
})
|
||||
.unwrap()
|
||||
@ -375,8 +375,8 @@ mod tests {
|
||||
});
|
||||
|
||||
let vec = panic::catch_unwind(|| {
|
||||
::with_test_sess(case, |mut sess, input| {
|
||||
let mut l = Lexer::new(sess, input);
|
||||
crate::with_test_sess(case, |mut sess, input| {
|
||||
let mut l = Lexer::new(sess, Syntax::Es2019, input);
|
||||
l.ctx.strict = strict;
|
||||
Ok(l.map(|ts| ts.token).collect::<Vec<_>>())
|
||||
})
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::{Input, Lexer};
|
||||
use crate::{lexer::util::CharExt, token::*, Syntax};
|
||||
use enum_kind::Kind;
|
||||
use smallvec::SmallVec;
|
||||
use swc_common::BytePos;
|
||||
use token::*;
|
||||
|
||||
/// State of lexer.
|
||||
///
|
||||
@ -15,8 +15,12 @@ pub(super) struct State {
|
||||
pub had_line_break: bool,
|
||||
/// TODO: Remove this field.
|
||||
is_first: bool,
|
||||
pub start: BytePos,
|
||||
pub cur_line: usize,
|
||||
pub line_start: BytePos,
|
||||
|
||||
context: Context,
|
||||
syntax: Syntax,
|
||||
|
||||
token_type: Option<TokenType>,
|
||||
}
|
||||
@ -31,13 +35,23 @@ enum TokenType {
|
||||
Semi,
|
||||
BinOp(BinOpToken),
|
||||
Keyword(Keyword),
|
||||
JSXName,
|
||||
JSXText,
|
||||
JSXTagStart,
|
||||
JSXTagEnd,
|
||||
Other { before_expr: bool },
|
||||
}
|
||||
impl TokenType {
|
||||
fn before_expr(self) -> bool {
|
||||
match self {
|
||||
TokenType::Template | TokenType::Dot | TokenType::RParen => false,
|
||||
TokenType::Colon | TokenType::LBrace | TokenType::Semi => true,
|
||||
TokenType::JSXName
|
||||
| TokenType::JSXTagStart
|
||||
| TokenType::JSXTagEnd
|
||||
| TokenType::Template
|
||||
| TokenType::Dot
|
||||
| TokenType::RParen => false,
|
||||
|
||||
TokenType::JSXText | TokenType::Colon | TokenType::LBrace | TokenType::Semi => true,
|
||||
TokenType::BinOp(b) => b.before_expr(),
|
||||
TokenType::Keyword(k) => k.before_expr(),
|
||||
TokenType::Other { before_expr } => before_expr,
|
||||
@ -54,7 +68,12 @@ impl<'a> From<&'a Token> for TokenType {
|
||||
Token::LBrace => TokenType::LBrace,
|
||||
Token::RParen => TokenType::RParen,
|
||||
Token::Semi => TokenType::Semi,
|
||||
Token::JSXTagEnd => TokenType::JSXTagEnd,
|
||||
Token::JSXTagStart => TokenType::JSXTagStart,
|
||||
Token::JSXText { .. } => TokenType::JSXText,
|
||||
Token::JSXName { .. } => TokenType::JSXName,
|
||||
Token::BinOp(op) => TokenType::BinOp(op),
|
||||
|
||||
Token::Word(Keyword(k)) => TokenType::Keyword(k),
|
||||
_ => TokenType::Other {
|
||||
before_expr: t.before_expr(),
|
||||
@ -88,17 +107,67 @@ impl<'a, I: Input> Iterator for Lexer<'a, I> {
|
||||
}
|
||||
};
|
||||
|
||||
let start = self.cur_pos();
|
||||
|
||||
let res = if let Some(Type::Tpl {
|
||||
start: start_pos_of_tpl,
|
||||
}) = self.state.context.current()
|
||||
{
|
||||
self.read_tmpl_token(start_pos_of_tpl).map(Some)
|
||||
} else {
|
||||
self.read_token()
|
||||
match self.input.cur() {
|
||||
Some(..) => {}
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// println!(
|
||||
// "\tContext: ({:?}) {:?}",
|
||||
// self.input.cur().unwrap(),
|
||||
// self.state.context.0
|
||||
// );
|
||||
|
||||
let start = self.cur_pos();
|
||||
self.state.start = start;
|
||||
|
||||
let res = (|| -> Result<Option<_>, _> {
|
||||
if self.syntax.jsx() {
|
||||
//jsx
|
||||
if self.state.context.current() == Some(Type::JSXExpr) {
|
||||
let start = self.cur_pos();
|
||||
|
||||
return self.read_jsx_token();
|
||||
}
|
||||
|
||||
let c = self.cur();
|
||||
if let Some(c) = c {
|
||||
if self.state.context.current() == Some(Type::JSXOpeningTag)
|
||||
|| self.state.context.current() == Some(Type::JSXClosingTag)
|
||||
{
|
||||
if c.is_ident_start() {
|
||||
return self.read_jsx_word().map(Some);
|
||||
}
|
||||
|
||||
if c == '>' {
|
||||
self.input.bump();
|
||||
return Ok(Some(Token::JSXTagEnd));
|
||||
}
|
||||
|
||||
if (c == '\'' || c == '"')
|
||||
&& self.state.context.current() == Some(Type::JSXOpeningTag)
|
||||
{
|
||||
return self.read_jsx_str(c).map(Some);
|
||||
}
|
||||
}
|
||||
|
||||
if c == '<' && self.state.is_expr_allowed && self.input.peek() != Some('!') {
|
||||
self.input.bump();
|
||||
return Ok(Some(Token::JSXTagStart));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Type::Tpl {
|
||||
start: start_pos_of_tpl,
|
||||
}) = self.state.context.current()
|
||||
{
|
||||
self.read_tmpl_token(start_pos_of_tpl).map(Some)
|
||||
} else {
|
||||
self.read_token()
|
||||
}
|
||||
})();
|
||||
|
||||
let token = match res.map_err(Token::Error).map_err(Some) {
|
||||
Ok(t) => t,
|
||||
Err(e) => e,
|
||||
@ -119,8 +188,8 @@ impl<'a, I: Input> Iterator for Lexer<'a, I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
impl State {
|
||||
pub fn new(syntax: Syntax) -> Self {
|
||||
State {
|
||||
is_expr_allowed: true,
|
||||
octal_pos: None,
|
||||
@ -128,6 +197,10 @@ impl Default for State {
|
||||
had_line_break: false,
|
||||
context: Context(smallvec![Type::BraceStmt]),
|
||||
token_type: None,
|
||||
start: BytePos(0),
|
||||
line_start: BytePos(0),
|
||||
cur_line: 1,
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,6 +232,7 @@ impl State {
|
||||
|
||||
self.is_expr_allowed = Self::is_expr_allowed_on_next(
|
||||
&mut self.context,
|
||||
self.syntax,
|
||||
prev,
|
||||
start,
|
||||
next,
|
||||
@ -171,6 +245,7 @@ impl State {
|
||||
/// `start`: start of newly produced token.
|
||||
fn is_expr_allowed_on_next(
|
||||
context: &mut Context,
|
||||
syntax: Syntax,
|
||||
prev: Option<TokenType>,
|
||||
start: BytePos,
|
||||
next: &Token,
|
||||
@ -248,16 +323,30 @@ impl State {
|
||||
}
|
||||
|
||||
tok!('{') => {
|
||||
let next_ctxt = if context.is_brace_block(prev, had_line_break, is_expr_allowed)
|
||||
{
|
||||
Type::BraceStmt
|
||||
let cur = context.current();
|
||||
if syntax.jsx() && cur == Some(Type::JSXOpeningTag) {
|
||||
context.push(Type::BraceExpr)
|
||||
} else if syntax.jsx() && cur == Some(Type::JSXExpr) {
|
||||
context.push(Type::TplQuasi);
|
||||
} else {
|
||||
Type::BraceExpr
|
||||
};
|
||||
context.push(next_ctxt);
|
||||
let next_ctxt =
|
||||
if context.is_brace_block(prev, had_line_break, is_expr_allowed) {
|
||||
Type::BraceStmt
|
||||
} else {
|
||||
Type::BraceExpr
|
||||
};
|
||||
context.push(next_ctxt);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
tok!('/') if syntax.jsx() && prev == Some(TokenType::JSXTagStart) => {
|
||||
context.pop();
|
||||
context.pop(); // do not consider JSX expr -> JSX open tag -> ... anymore
|
||||
context.push(Type::JSXClosingTag); // reconsider as closing tag context
|
||||
false
|
||||
}
|
||||
|
||||
tok!("${") => {
|
||||
context.push(Type::TplQuasi);
|
||||
return true;
|
||||
@ -290,6 +379,27 @@ impl State {
|
||||
return false;
|
||||
}
|
||||
|
||||
// tt.jsxTagStart.updateContext
|
||||
Token::JSXTagStart => {
|
||||
context.push(Type::JSXExpr); // treat as beginning of JSX expression
|
||||
context.push(Type::JSXOpeningTag); // start opening tag context
|
||||
return false;
|
||||
}
|
||||
|
||||
// tt.jsxTagEnd.updateContext
|
||||
Token::JSXTagEnd => {
|
||||
let out = context.pop();
|
||||
if (out == Some(Type::JSXOpeningTag)
|
||||
&& prev == Some(TokenType::BinOp(BinOpToken::Div)))
|
||||
|| out == Some(Type::JSXClosingTag)
|
||||
{
|
||||
context.pop();
|
||||
return context.current() == Some(Type::JSXExpr);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
return next.before_expr();
|
||||
}
|
||||
@ -393,4 +503,50 @@ enum Type {
|
||||
},
|
||||
#[kind(is_expr)]
|
||||
FnExpr,
|
||||
JSXOpeningTag,
|
||||
JSXClosingTag,
|
||||
#[kind(is_expr, preserve_space)]
|
||||
JSXExpr,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn with_lexer<F, Ret>(
|
||||
syntax: crate::Syntax,
|
||||
s: &'static str,
|
||||
f: F,
|
||||
) -> Result<Ret, ::testing::StdErr>
|
||||
where
|
||||
F: FnOnce(&mut Lexer<crate::lexer::input::SourceFileInput>) -> Result<Ret, ()>,
|
||||
{
|
||||
crate::with_test_sess(s, |sess, fm| {
|
||||
let mut l = Lexer::new(sess, syntax, fm);
|
||||
let res = f(&mut l);
|
||||
|
||||
let c: SmallVec<[Type; 32]> = smallvec![Type::BraceStmt];
|
||||
assert_eq!(l.state.context.0, c);
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex(syntax: Syntax, s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(syntax, s, |l| Ok(l.collect())).unwrap()
|
||||
}
|
||||
|
||||
/// lex `s` within module context.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex_module(syntax: Syntax, s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(syntax, s, |l| {
|
||||
l.ctx.strict = true;
|
||||
l.ctx.module = true;
|
||||
|
||||
Ok(l.collect())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex_tokens(syntax: Syntax, s: &'static str) -> Vec<Token> {
|
||||
with_lexer(syntax, s, |l| Ok(l.map(|ts| ts.token).collect())).unwrap()
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
use super::{input::SourceFileInput, *};
|
||||
use super::{
|
||||
state::{lex, lex_module, lex_tokens, with_lexer},
|
||||
*,
|
||||
};
|
||||
use error::{Error, SyntaxError};
|
||||
use std::{ops::Range, str};
|
||||
use test::Bencher;
|
||||
@ -11,31 +14,6 @@ fn sp(r: Range<usize>) -> Span {
|
||||
)
|
||||
}
|
||||
|
||||
fn with_lexer<F, Ret>(s: &'static str, f: F) -> Result<Ret, ::testing::StdErr>
|
||||
where
|
||||
F: FnOnce(&mut Lexer<SourceFileInput>) -> Result<Ret, ()>,
|
||||
{
|
||||
::with_test_sess(s, |sess, fm| {
|
||||
let mut l = Lexer::new(sess, fm);
|
||||
f(&mut l)
|
||||
})
|
||||
}
|
||||
|
||||
fn lex(s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(s, |l| Ok(l.collect())).unwrap()
|
||||
}
|
||||
fn lex_module(s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(s, |l| {
|
||||
l.ctx.strict = true;
|
||||
l.ctx.module = true;
|
||||
Ok(l.collect())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
fn lex_tokens(s: &'static str) -> Vec<Token> {
|
||||
with_lexer(s, |l| Ok(l.map(|ts| ts.token).collect())).unwrap()
|
||||
}
|
||||
|
||||
trait LineBreak: Into<TokenAndSpan> {
|
||||
fn lb(self) -> TokenAndSpan {
|
||||
TokenAndSpan {
|
||||
@ -125,7 +103,7 @@ impl WithSpan for AssignOpToken {
|
||||
#[test]
|
||||
fn module_legacy_octal() {
|
||||
assert_eq!(
|
||||
lex_module("01"),
|
||||
lex_module(Syntax::Es2019, "01"),
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..2),
|
||||
error: SyntaxError::LegacyOctal,
|
||||
@ -137,7 +115,7 @@ fn module_legacy_octal() {
|
||||
#[test]
|
||||
fn module_legacy_decimal() {
|
||||
assert_eq!(
|
||||
lex_module("08"),
|
||||
lex_module(Syntax::Es2019, "08"),
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..2),
|
||||
error: SyntaxError::LegacyDecimal,
|
||||
@ -150,7 +128,7 @@ fn module_legacy_decimal() {
|
||||
#[test]
|
||||
fn module_legacy_comment_1() {
|
||||
assert_eq!(
|
||||
lex_module("<!-- foo oo"),
|
||||
lex_module(Syntax::Es2019, "<!-- foo oo"),
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..11),
|
||||
error: SyntaxError::LegacyCommentInModule,
|
||||
@ -163,7 +141,7 @@ fn module_legacy_comment_1() {
|
||||
#[test]
|
||||
fn module_legacy_comment_2() {
|
||||
assert_eq!(
|
||||
lex_module("-->"),
|
||||
lex_module(Syntax::Es2019, "-->"),
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..3),
|
||||
error: SyntaxError::LegacyCommentInModule,
|
||||
@ -184,7 +162,7 @@ fn test262_lexer_error_0001() {
|
||||
1.span(7..8),
|
||||
RParen.span(8..9),
|
||||
],
|
||||
lex("123..a(1)")
|
||||
lex(Syntax::Es2019, "123..a(1)")
|
||||
)
|
||||
}
|
||||
|
||||
@ -200,39 +178,49 @@ fn test262_lexer_error_0002() {
|
||||
.lb(),
|
||||
Semi.span(15..16),
|
||||
],
|
||||
lex(r#"'use\x20strict';"#)
|
||||
lex(Syntax::Es2019, r#"'use\x20strict';"#)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test262_lexer_error_0003() {
|
||||
assert_eq!(vec!["a".span(0..6).lb()], lex(r#"\u0061"#));
|
||||
assert_eq!(vec!["a".span(0..6).lb()], lex(Syntax::Es2019, r#"\u0061"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test262_lexer_error_0004() {
|
||||
assert_eq!(
|
||||
vec![tok!('+'), tok!('{'), tok!('}'), tok!('/'), 1.into_token()],
|
||||
lex_tokens("+{} / 1")
|
||||
lex_tokens(Syntax::Es2019, "+{} / 1")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ident_escape_unicode() {
|
||||
assert_eq!(vec!["aa".span(0..7).lb()], lex(r#"a\u0061"#));
|
||||
assert_eq!(
|
||||
vec!["aa".span(0..7).lb()],
|
||||
lex(Syntax::Es2019, r#"a\u0061"#)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ident_escape_unicode_2() {
|
||||
assert_eq!(vec!["℘℘".span(0..6).lb()], lex("℘℘"));
|
||||
assert_eq!(
|
||||
vec!["℘℘".span(0..6).lb()],
|
||||
lex(Syntax::Es2019, "℘℘")
|
||||
);
|
||||
|
||||
assert_eq!(vec!["℘℘".span(0..9).lb()], lex(r#"℘\u2118"#));
|
||||
assert_eq!(
|
||||
vec!["℘℘".span(0..9).lb()],
|
||||
lex(Syntax::Es2019, r#"℘\u2118"#)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tpl_multiline() {
|
||||
assert_eq!(
|
||||
lex_tokens(
|
||||
Syntax::Es2019,
|
||||
"`this
|
||||
is
|
||||
multiline`"
|
||||
@ -252,7 +240,7 @@ multiline`"
|
||||
#[test]
|
||||
fn tpl_raw_unicode_escape() {
|
||||
assert_eq!(
|
||||
lex_tokens(r"`\u{0010}`"),
|
||||
lex_tokens(Syntax::Es2019, r"`\u{0010}`"),
|
||||
vec![
|
||||
tok!('`'),
|
||||
Token::Template {
|
||||
@ -268,7 +256,7 @@ fn tpl_raw_unicode_escape() {
|
||||
#[test]
|
||||
fn str_escape() {
|
||||
assert_eq!(
|
||||
lex_tokens(r#"'\n'"#),
|
||||
lex_tokens(Syntax::Es2019, r#"'\n'"#),
|
||||
vec![Token::Str {
|
||||
value: "\n".into(),
|
||||
has_escape: true
|
||||
@ -279,7 +267,7 @@ fn str_escape() {
|
||||
#[test]
|
||||
fn str_escape_2() {
|
||||
assert_eq!(
|
||||
lex_tokens(r#"'\\n'"#),
|
||||
lex_tokens(Syntax::Es2019, r#"'\\n'"#),
|
||||
vec![Token::Str {
|
||||
value: "\\n".into(),
|
||||
has_escape: true
|
||||
@ -290,7 +278,7 @@ fn str_escape_2() {
|
||||
#[test]
|
||||
fn str_escape_hex() {
|
||||
assert_eq!(
|
||||
lex(r#"'\x61'"#),
|
||||
lex(Syntax::Es2019, r#"'\x61'"#),
|
||||
vec![Token::Str {
|
||||
value: "a".into(),
|
||||
has_escape: true,
|
||||
@ -303,7 +291,7 @@ fn str_escape_hex() {
|
||||
#[test]
|
||||
fn str_escape_octal() {
|
||||
assert_eq!(
|
||||
lex(r#"'Hello\012World'"#),
|
||||
lex(Syntax::Es2019, r#"'Hello\012World'"#),
|
||||
vec![Token::Str {
|
||||
value: "Hello\nWorld".into(),
|
||||
has_escape: true,
|
||||
@ -316,7 +304,7 @@ fn str_escape_octal() {
|
||||
#[test]
|
||||
fn str_escape_unicode_long() {
|
||||
assert_eq!(
|
||||
lex(r#"'\u{00000000034}'"#),
|
||||
lex(Syntax::Es2019, r#"'\u{00000000034}'"#),
|
||||
vec![Token::Str {
|
||||
value: "4".into(),
|
||||
has_escape: true,
|
||||
@ -329,7 +317,7 @@ fn str_escape_unicode_long() {
|
||||
#[test]
|
||||
fn regexp_unary_void() {
|
||||
assert_eq!(
|
||||
lex("void /test/"),
|
||||
lex(Syntax::Es2019, "void /test/"),
|
||||
vec![
|
||||
Void.span(0..4).lb(),
|
||||
Regex(
|
||||
@ -344,7 +332,7 @@ fn regexp_unary_void() {
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
lex("void (/test/)"),
|
||||
lex(Syntax::Es2019, "void (/test/)"),
|
||||
vec![
|
||||
Void.span(0..4).lb(),
|
||||
LParen.span(5..6),
|
||||
@ -365,7 +353,7 @@ fn regexp_unary_void() {
|
||||
#[test]
|
||||
fn non_regexp_unary_plus() {
|
||||
assert_eq!(
|
||||
lex("+{} / 1"),
|
||||
lex(Syntax::Es2019, "+{} / 1"),
|
||||
vec![
|
||||
tok!('+').span(0..1).lb(),
|
||||
tok!('{').span(1..2),
|
||||
@ -378,19 +366,11 @@ fn non_regexp_unary_plus() {
|
||||
|
||||
// ----------
|
||||
|
||||
#[test]
|
||||
fn invalid_but_lexable() {
|
||||
assert_eq!(
|
||||
vec![LParen.span(0).lb(), LBrace.span(1), Semi.span(2)],
|
||||
lex("({;")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paren_semi() {
|
||||
assert_eq!(
|
||||
vec![LParen.span(0).lb(), RParen.span(1), Semi.span(2)],
|
||||
lex("();")
|
||||
lex(Syntax::Es2019, "();")
|
||||
);
|
||||
}
|
||||
|
||||
@ -404,7 +384,7 @@ fn ident_paren() {
|
||||
RParen.span(4),
|
||||
Semi.span(5),
|
||||
],
|
||||
lex("a(bc);")
|
||||
lex(Syntax::Es2019, "a(bc);")
|
||||
);
|
||||
}
|
||||
|
||||
@ -412,14 +392,14 @@ fn ident_paren() {
|
||||
fn read_word() {
|
||||
assert_eq!(
|
||||
vec!["a".span(0).lb(), "b".span(2), "c".span(4)],
|
||||
lex("a b c"),
|
||||
lex(Syntax::Es2019, "a b c"),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_regex() {
|
||||
assert_eq!(
|
||||
lex("x = /42/i"),
|
||||
lex(Syntax::Es2019, "x = /42/i"),
|
||||
vec![
|
||||
"x".span(0).lb(),
|
||||
Assign.span(2),
|
||||
@ -440,7 +420,7 @@ fn simple_regex() {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
lex("/42/"),
|
||||
lex(Syntax::Es2019, "/42/"),
|
||||
vec![Regex(
|
||||
Str {
|
||||
span: sp(1..3),
|
||||
@ -481,7 +461,7 @@ fn complex_regex() {
|
||||
}),
|
||||
),
|
||||
],
|
||||
lex_tokens("f(); function foo() {} /42/i")
|
||||
lex_tokens(Syntax::Es2019, "f(); function foo() {} /42/i")
|
||||
)
|
||||
}
|
||||
|
||||
@ -489,7 +469,7 @@ fn complex_regex() {
|
||||
fn simple_div() {
|
||||
assert_eq!(
|
||||
vec!["a".span(0).lb(), Div.span(2), "b".span(4)],
|
||||
lex("a / b")
|
||||
lex(Syntax::Es2019, "a / b")
|
||||
);
|
||||
}
|
||||
|
||||
@ -510,7 +490,7 @@ fn complex_divide() {
|
||||
BinOp(Div),
|
||||
Word(Ident("i".into())),
|
||||
],
|
||||
lex_tokens("x = function foo() {} /a/i"),
|
||||
lex_tokens(Syntax::Es2019, "x = function foo() {} /a/i"),
|
||||
"/ should be parsed as div operator"
|
||||
)
|
||||
}
|
||||
@ -543,11 +523,15 @@ fn spec_001() {
|
||||
assert_eq!(
|
||||
expected,
|
||||
lex_tokens(
|
||||
Syntax::Es2019,
|
||||
"a = b
|
||||
/hi/g.exec(c).map(d);"
|
||||
)
|
||||
);
|
||||
assert_eq!(expected, lex_tokens("a = b / hi / g.exec(c).map(d);"));
|
||||
assert_eq!(
|
||||
expected,
|
||||
lex_tokens(Syntax::Es2019, "a = b / hi / g.exec(c).map(d);")
|
||||
);
|
||||
}
|
||||
|
||||
// ---------- Tests ported from esprima
|
||||
@ -555,7 +539,7 @@ fn spec_001() {
|
||||
#[test]
|
||||
fn after_if() {
|
||||
assert_eq!(
|
||||
lex("if(x){} /y/.test(z)"),
|
||||
lex(Syntax::Es2019, "if(x){} /y/.test(z)"),
|
||||
vec![
|
||||
Keyword::If.span(0..2).lb(),
|
||||
LParen.span(2),
|
||||
@ -583,7 +567,7 @@ fn after_if() {
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
assert_eq!(lex(""), vec![]);
|
||||
assert_eq!(lex(Syntax::Es2019, ""), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -600,7 +584,7 @@ fn invalid_number_failure() {
|
||||
// BlockComment(" hello world ".into()).span(0..17),
|
||||
// Regex("42".into(), "".into()).span(17..21),
|
||||
// ],
|
||||
// lex("/* hello world */ /42/")
|
||||
// lex(Syntax::Es2019, "/* hello world */ /42/")
|
||||
// )
|
||||
// }
|
||||
|
||||
@ -615,7 +599,7 @@ fn invalid_number_failure() {
|
||||
// 42.span(13..15),
|
||||
// LineComment(" the Ultimate".into()).span(17..32),
|
||||
// ],
|
||||
// lex("var answer = 42 // the Ultimate"),
|
||||
// lex(Syntax::Es2019, "var answer = 42 // the Ultimate"),
|
||||
// )
|
||||
// }
|
||||
|
||||
@ -636,7 +620,7 @@ fn migrated_0002() {
|
||||
.span(9..13),
|
||||
RParen.span(13),
|
||||
],
|
||||
lex("tokenize(/42/)")
|
||||
lex(Syntax::Es2019, "tokenize(/42/)")
|
||||
)
|
||||
}
|
||||
|
||||
@ -651,7 +635,7 @@ fn migrated_0003() {
|
||||
42.span(9..11),
|
||||
Div.span(11),
|
||||
],
|
||||
lex("(false) /42/"),
|
||||
lex(Syntax::Es2019, "(false) /42/"),
|
||||
)
|
||||
}
|
||||
|
||||
@ -675,7 +659,7 @@ fn migrated_0004() {
|
||||
)
|
||||
.span(15..19),
|
||||
],
|
||||
lex("function f(){} /42/")
|
||||
lex(Syntax::Es2019, "function f(){} /42/")
|
||||
);
|
||||
}
|
||||
|
||||
@ -693,7 +677,7 @@ fn migrated_0004() {
|
||||
// Div.span(13),
|
||||
// 42.span(14..16),
|
||||
// ],
|
||||
// lex("function (){} /42")
|
||||
// lex(Syntax::Es2019, "function (){} /42")
|
||||
// );
|
||||
// }
|
||||
|
||||
@ -702,7 +686,7 @@ fn migrated_0006() {
|
||||
// This test seems wrong.
|
||||
// assert_eq!(
|
||||
// vec![LBrace.span(0).lb(), RBrace.span(1), Div.span(3), 42.span(4..6)],
|
||||
// lex("{} /42")
|
||||
// lex(Syntax::Es2019, "{} /42")
|
||||
// )
|
||||
|
||||
assert_eq!(
|
||||
@ -719,7 +703,7 @@ fn migrated_0006() {
|
||||
)
|
||||
.span(3..7),
|
||||
],
|
||||
lex("{} /42/")
|
||||
lex(Syntax::Es2019, "{} /42/")
|
||||
)
|
||||
}
|
||||
|
||||
@ -730,21 +714,21 @@ fn str_lit() {
|
||||
value: "abcde".into(),
|
||||
has_escape: false,
|
||||
}],
|
||||
lex_tokens("'abcde'")
|
||||
lex_tokens(Syntax::Es2019, "'abcde'")
|
||||
);
|
||||
assert_eq_ignore_span!(
|
||||
vec![Token::Str {
|
||||
value: "abc".into(),
|
||||
has_escape: true,
|
||||
}],
|
||||
lex_tokens("'\\\nabc'")
|
||||
lex_tokens(Syntax::Es2019, "'\\\nabc'")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tpl_empty() {
|
||||
assert_eq!(
|
||||
lex_tokens(r#"``"#),
|
||||
lex_tokens(Syntax::Es2019, r#"``"#),
|
||||
vec![
|
||||
tok!('`'),
|
||||
Template {
|
||||
@ -760,7 +744,7 @@ fn tpl_empty() {
|
||||
#[test]
|
||||
fn tpl() {
|
||||
assert_eq!(
|
||||
lex_tokens(r#"`${a}`"#),
|
||||
lex_tokens(Syntax::Es2019, r#"`${a}`"#),
|
||||
vec![
|
||||
tok!('`'),
|
||||
Template {
|
||||
@ -784,8 +768,11 @@ fn tpl() {
|
||||
#[test]
|
||||
fn comment() {
|
||||
assert_eq!(
|
||||
lex("// foo
|
||||
a"),
|
||||
lex(
|
||||
Syntax::Es2019,
|
||||
"// foo
|
||||
a"
|
||||
),
|
||||
vec![TokenAndSpan {
|
||||
token: Word(Ident("a".into())),
|
||||
span: sp(7..8),
|
||||
@ -798,9 +785,12 @@ a"),
|
||||
#[test]
|
||||
fn comment_2() {
|
||||
assert_eq!(
|
||||
lex("// foo
|
||||
lex(
|
||||
Syntax::Es2019,
|
||||
"// foo
|
||||
// bar
|
||||
a"),
|
||||
a"
|
||||
),
|
||||
vec![TokenAndSpan {
|
||||
token: Word(Ident("a".into())),
|
||||
span: sp(14..15),
|
||||
@ -810,12 +800,82 @@ a"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jsx_01() {
|
||||
assert_eq!(
|
||||
lex_tokens(Syntax::Jsx, "<a />"),
|
||||
vec![
|
||||
Token::JSXTagStart,
|
||||
Token::JSXName { name: "a".into() },
|
||||
tok!('/'),
|
||||
Token::JSXTagEnd,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jsx_02() {
|
||||
assert_eq!(
|
||||
lex_tokens(Syntax::Jsx, "<a>foo</a>"),
|
||||
vec![
|
||||
Token::JSXTagStart,
|
||||
Token::JSXName { name: "a".into() },
|
||||
Token::JSXTagEnd,
|
||||
Token::JSXText { raw: "foo".into() },
|
||||
Token::JSXTagStart,
|
||||
tok!('/'),
|
||||
Token::JSXName { name: "a".into() },
|
||||
Token::JSXTagEnd,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jsx_03() {
|
||||
assert_eq!(
|
||||
lex_tokens(Syntax::Jsx, "<a><br /></a>"),
|
||||
vec![
|
||||
Token::JSXTagStart,
|
||||
Token::JSXName { name: "a".into() },
|
||||
Token::JSXTagEnd,
|
||||
//
|
||||
Token::JSXTagStart,
|
||||
Token::JSXName { name: "br".into() },
|
||||
tok!('/'),
|
||||
Token::JSXTagEnd,
|
||||
//
|
||||
Token::JSXTagStart,
|
||||
tok!('/'),
|
||||
Token::JSXName { name: "a".into() },
|
||||
Token::JSXTagEnd,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jsx_04() {
|
||||
assert_eq!(
|
||||
lex_tokens(Syntax::Jsx, "yield <a></a>"),
|
||||
vec![
|
||||
Token::Word(Word::Keyword(Yield)),
|
||||
Token::JSXTagStart,
|
||||
Token::JSXName { name: "a".into() },
|
||||
Token::JSXTagEnd,
|
||||
//
|
||||
Token::JSXTagStart,
|
||||
tok!('/'),
|
||||
Token::JSXName { name: "a".into() },
|
||||
Token::JSXTagEnd,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn lex_colors_js(b: &mut Bencher) {
|
||||
b.bytes = include_str!("../../colors.js").len() as _;
|
||||
|
||||
b.iter(|| {
|
||||
let _ = with_lexer(include_str!("../../colors.js"), |lexer| {
|
||||
let _ = with_lexer(Syntax::Es2019, include_str!("../../colors.js"), |lexer| {
|
||||
for _ in lexer {}
|
||||
Ok(())
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
//!
|
||||
//! [babylon/util/identifier.js]:https://github.com/babel/babel/blob/master/packages/babylon/src/util/identifier.js
|
||||
use super::{input::Input, LexResult, Lexer};
|
||||
use error::{ErrorToDiag, SyntaxError};
|
||||
use crate::error::{ErrorToDiag, SyntaxError};
|
||||
use swc_common::{BytePos, Span};
|
||||
use unicode_xid::UnicodeXID;
|
||||
|
||||
@ -67,7 +67,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
}
|
||||
|
||||
pub(super) fn cur(&mut self) -> Option<char> {
|
||||
self.input.current()
|
||||
self.input.cur()
|
||||
}
|
||||
pub(super) fn peek(&mut self) -> Option<char> {
|
||||
self.input.peek()
|
||||
@ -84,11 +84,13 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
}
|
||||
|
||||
/// Shorthand for `let span = self.span(start); self.error_span(span)`
|
||||
#[cold]
|
||||
pub(super) fn error(&mut self, start: BytePos, kind: SyntaxError) -> LexResult<!> {
|
||||
let span = self.span(start);
|
||||
self.error_span(span, kind)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub(super) fn error_span(&mut self, span: Span, kind: SyntaxError) -> LexResult<!> {
|
||||
let err = ErrorToDiag {
|
||||
handler: self.session.handler,
|
||||
|
@ -27,7 +27,7 @@
|
||||
//! sync::Lrc,
|
||||
//! FileName, FilePathMapping, SourceMap,
|
||||
//! };
|
||||
//! use swc_ecma_parser::{Parser, Session, SourceFileInput};
|
||||
//! use swc_ecma_parser::{Parser, Session, SourceFileInput, Syntax};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! swc_common::GLOBALS.set(&swc_common::Globals::new(), || {
|
||||
@ -50,7 +50,7 @@
|
||||
//! "function foo() {}".into(),
|
||||
//! );
|
||||
//!
|
||||
//! let mut parser = Parser::new(session, SourceFileInput::from(&*fm));
|
||||
//! let mut parser = Parser::new(session, Syntax::Es2019, SourceFileInput::from(&*fm));
|
||||
//!
|
||||
//! let _module = parser.parse_module().expect("failed to parser module");
|
||||
//! });
|
||||
@ -65,7 +65,6 @@
|
||||
#![feature(const_fn)]
|
||||
#![feature(specialization)]
|
||||
#![feature(never_type)]
|
||||
// #![feature(nll)]
|
||||
#![feature(try_from)]
|
||||
#![feature(try_trait)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
@ -81,12 +80,17 @@ extern crate log;
|
||||
#[macro_use(js_word)]
|
||||
extern crate swc_atoms;
|
||||
extern crate enum_kind;
|
||||
extern crate regex;
|
||||
extern crate swc_common;
|
||||
extern crate swc_ecma_ast as ast;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate swc_ecma_ast as ast;
|
||||
#[macro_use]
|
||||
#[cfg(test)]
|
||||
extern crate testing;
|
||||
#[cfg(test)]
|
||||
extern crate env_logger;
|
||||
#[cfg(test)]
|
||||
extern crate test;
|
||||
extern crate unicode_xid;
|
||||
pub use self::{
|
||||
@ -102,6 +106,23 @@ mod lexer;
|
||||
mod parser;
|
||||
mod token;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Syntax {
|
||||
Es2019,
|
||||
Jsx,
|
||||
Typescript,
|
||||
Tsx,
|
||||
}
|
||||
impl Syntax {
|
||||
/// Should we pare jsx?
|
||||
pub fn jsx(self) -> bool {
|
||||
match self {
|
||||
Syntax::Jsx | Syntax::Tsx => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Config {
|
||||
/// Support numeric separator.
|
||||
@ -128,6 +149,8 @@ struct Context {
|
||||
in_function: bool,
|
||||
|
||||
in_parameters: bool,
|
||||
|
||||
in_forced_jsx_context: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -218,6 +218,10 @@ macro_rules! tok {
|
||||
("yield") => {
|
||||
Token::Word(Keyword(Yield))
|
||||
};
|
||||
|
||||
(JSXTagEnd) => {
|
||||
Token::JSXTagEnd
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! token_including_semi {
|
||||
|
@ -423,11 +423,11 @@ mod tests {
|
||||
use swc_common::DUMMY_SP;
|
||||
|
||||
fn lhs(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_lhs_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_lhs_expr())
|
||||
}
|
||||
|
||||
fn expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_expr())
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::{pat::PatType, util::ExprExt, *};
|
||||
use either::Either;
|
||||
use swc_common::Spanned;
|
||||
|
||||
mod ops;
|
||||
@ -622,6 +623,39 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
pub(super) fn parse_lhs_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
|
||||
let start = cur_pos!();
|
||||
|
||||
// parse jsx
|
||||
if self.input.syntax().jsx() {
|
||||
fn into_expr(e: Either<JSXFragment, JSXElement>) -> Box<Expr> {
|
||||
match e {
|
||||
Either::Left(l) => box l.into(),
|
||||
Either::Right(r) => box r.into(),
|
||||
}
|
||||
}
|
||||
match *cur!(true)? {
|
||||
Token::JSXText { .. } => {
|
||||
return self
|
||||
.parse_jsx_text()
|
||||
.map(Lit::JSXText)
|
||||
.map(Expr::Lit)
|
||||
.map(Box::new);
|
||||
}
|
||||
Token::JSXTagStart => {
|
||||
return self.parse_jsx_element().map(into_expr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if is!('<') && !peeked_is!('!') {
|
||||
// In case we encounter an lt token here it will always be the start of
|
||||
// jsx as the lt sign is not allowed in places that expect an expression
|
||||
|
||||
// FIXME:
|
||||
// this.finishToken(tt.jsxTagStart);
|
||||
|
||||
return self.parse_jsx_element().map(into_expr);
|
||||
}
|
||||
}
|
||||
|
||||
// `super()` can't be handled from parse_new_expr()
|
||||
if eat!("super") {
|
||||
let obj = ExprOrSuper::Super(span!(start));
|
||||
|
@ -203,7 +203,7 @@ mod tests {
|
||||
use swc_common::DUMMY_SP;
|
||||
|
||||
fn bin(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_bin_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_bin_expr())
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
@ -2,19 +2,19 @@ use super::*;
|
||||
use swc_common::DUMMY_SP as span;
|
||||
|
||||
fn lhs(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_lhs_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_lhs_expr())
|
||||
}
|
||||
|
||||
fn new_expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_new_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_new_expr())
|
||||
}
|
||||
|
||||
fn member_expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_member_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_member_expr())
|
||||
}
|
||||
|
||||
fn expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| {
|
||||
test_parser(s, Syntax::Es2019, |p| {
|
||||
p.parse_stmt(true).map(|stmt| match stmt {
|
||||
Stmt::Expr(expr) => expr,
|
||||
_ => unreachable!(),
|
||||
|
@ -2,7 +2,7 @@ use super::*;
|
||||
use swc_common::{Span, Spanned, Visit, VisitWith};
|
||||
|
||||
impl<'a, I: Input> Parser<'a, I> {
|
||||
pub(in parser) fn verify_expr(&self, expr: Box<Expr>) -> PResult<'a, Box<Expr>> {
|
||||
pub(in crate::parser) fn verify_expr(&self, expr: Box<Expr>) -> PResult<'a, Box<Expr>> {
|
||||
let mut v = Verifier { errors: vec![] };
|
||||
|
||||
v.visit(&expr);
|
||||
@ -34,7 +34,7 @@ impl Visit<Prop> for Verifier {
|
||||
fn visit(&mut self, p: &Prop) {
|
||||
match *p {
|
||||
Prop::Assign { .. } => {
|
||||
self.errors.push((p.span(), SyntaxError::Unexpected));
|
||||
self.errors.push((p.span(), SyntaxError::AssignProperty));
|
||||
}
|
||||
_ => p.visit_children(self),
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use lexer::{Input, Lexer};
|
||||
use crate::{
|
||||
lexer::{Input, Lexer},
|
||||
token::*,
|
||||
Context, Syntax,
|
||||
};
|
||||
use swc_common::{BytePos, Span, DUMMY_SP};
|
||||
use token::*;
|
||||
use Context;
|
||||
|
||||
/// This struct is responsible for managing current token and peeked token.
|
||||
pub(super) struct ParserInput<'a, I: Input> {
|
||||
@ -164,4 +166,8 @@ impl<'a, I: Input> ParserInput<'a, I> {
|
||||
pub fn set_ctx(&mut self, ctx: Context) {
|
||||
self.iter.ctx = ctx;
|
||||
}
|
||||
|
||||
pub const fn syntax(&self) -> Syntax {
|
||||
self.iter.syntax
|
||||
}
|
||||
}
|
||||
|
429
ecmascript/parser/src/parser/jsx/mod.rs
Normal file
429
ecmascript/parser/src/parser/jsx/mod.rs
Normal file
@ -0,0 +1,429 @@
|
||||
use super::*;
|
||||
use either::Either;
|
||||
use swc_common::{Span, Spanned, SyntaxContext};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[parser]
|
||||
impl<'a, I: Input> Parser<'a, I> {
|
||||
/// Parse next token as JSX identifier
|
||||
pub(super) fn parse_jsx_ident(&mut self) -> PResult<'a, Ident> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
match *cur!(true)? {
|
||||
Token::JSXName { .. } => match bump!() {
|
||||
Token::JSXName { name } => {
|
||||
let span = self.input.prev_span();
|
||||
Ok(Ident::new(name, span))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ if self.ctx().in_forced_jsx_context => self.parse_ident_ref(),
|
||||
_ => unexpected!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse namespaced identifier.
|
||||
pub(super) fn parse_jsx_namespaced_name(&mut self) -> PResult<'a, JSXAttrName> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let ns = self.parse_jsx_ident()?;
|
||||
if !eat!(':') {
|
||||
return Ok(JSXAttrName::Ident(ns));
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_ident()?;
|
||||
Ok(JSXAttrName::JSXNamespacedName(JSXNamespacedName {
|
||||
ns,
|
||||
name,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parses element name in any form - namespaced, member or single
|
||||
/// identifier.
|
||||
pub(super) fn parse_jsx_element_name(&mut self) -> PResult<'a, JSXElementName> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start_pos = cur_pos!();
|
||||
let mut node = match self.parse_jsx_namespaced_name()? {
|
||||
JSXAttrName::Ident(i) => JSXElementName::Ident(i),
|
||||
JSXAttrName::JSXNamespacedName(i) => JSXElementName::JSXNamespacedName(i),
|
||||
};
|
||||
while eat!('.') {
|
||||
let prop = self.parse_jsx_ident()?;
|
||||
let new_node = JSXElementName::JSXMemberExpr(JSXMemberExpr {
|
||||
obj: match node {
|
||||
JSXElementName::Ident(i) => JSXObject::Ident(i),
|
||||
JSXElementName::JSXMemberExpr(i) => JSXObject::JSXMemberExpr(box i),
|
||||
_ => unimplemented!("JSXNamespacedName -> JSXObject"),
|
||||
},
|
||||
prop,
|
||||
});
|
||||
node = new_node;
|
||||
}
|
||||
return Ok(node);
|
||||
}
|
||||
|
||||
/// Parses any type of JSX attribute value.
|
||||
///
|
||||
/// TODO(kdy1): Change return type to JSXAttrValue
|
||||
pub(super) fn parse_jsx_attr_value(&mut self) -> PResult<'a, Box<Expr>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = cur_pos!();
|
||||
|
||||
match *cur!(true)? {
|
||||
tok!('{') => {
|
||||
let node = self.parse_jsx_expr_container()?;
|
||||
match node.expr {
|
||||
JSXExpr::JSXEmptyExpr(..) => {
|
||||
syntax_error!(span!(start), SyntaxError::EmptyJSXAttr)
|
||||
}
|
||||
JSXExpr::Expr(expr) => Ok(expr),
|
||||
}
|
||||
}
|
||||
Token::Str { .. } | Token::JSXTagStart => self.parse_lhs_expr(),
|
||||
|
||||
_ => syntax_error!(span!(start), SyntaxError::InvalidJSXValue),
|
||||
}
|
||||
}
|
||||
|
||||
/// JSXEmptyExpression is unique type since it doesn't actually parse
|
||||
/// anything, and so it should start at the end of last read token (left
|
||||
/// brace) and finish at the beginning of the next one (right brace).
|
||||
pub(super) fn parse_jsx_empty_expr(&mut self) -> PResult<'a, JSXEmptyExpr> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
let start = cur_pos!();
|
||||
|
||||
Ok(JSXEmptyExpr {
|
||||
span: Span::new(start, start, SyntaxContext::empty()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse JSX spread child
|
||||
pub(super) fn parse_jsx_spread_child(&mut self) -> PResult<'a, JSXSpreadChild> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
let start = cur_pos!();
|
||||
expect!('{');
|
||||
expect!("...");
|
||||
let expr = self.parse_expr()?;
|
||||
expect!('}');
|
||||
|
||||
Ok(JSXSpreadChild { expr })
|
||||
}
|
||||
|
||||
/// Parses JSX expression enclosed into curly brackets.
|
||||
pub(super) fn parse_jsx_expr_container(&mut self) -> PResult<'a, JSXExprContainer> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = cur_pos!();
|
||||
bump!();
|
||||
let expr = if is!('}') {
|
||||
self.parse_jsx_empty_expr().map(JSXExpr::JSXEmptyExpr)?
|
||||
} else {
|
||||
self.parse_expr().map(JSXExpr::Expr)?
|
||||
};
|
||||
expect!('}');
|
||||
return Ok(JSXExprContainer { expr });
|
||||
}
|
||||
|
||||
/// Parses following JSX attribute name-value pair.
|
||||
pub(super) fn parse_jsx_attr(&mut self) -> PResult<'a, JSXAttrOrSpread> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
let start = cur_pos!();
|
||||
|
||||
if eat!('{') {
|
||||
let dot3_start = cur_pos!();
|
||||
expect!("...");
|
||||
let dot3_token = span!(dot3_start);
|
||||
let expr = self.parse_assignment_expr()?;
|
||||
expect!('}');
|
||||
return Ok(SpreadElement { dot3_token, expr }.into());
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_namespaced_name()?;
|
||||
let value = if eat!('=') {
|
||||
self.parse_jsx_attr_value().map(Some)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(JSXAttr {
|
||||
span: span!(start),
|
||||
name,
|
||||
value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Parses JSX opening tag starting after "<".
|
||||
pub(super) fn parse_jsx_opening_element_at(
|
||||
&mut self,
|
||||
start_pos: BytePos,
|
||||
) -> PResult<'a, Either<JSXOpeningFragment, JSXOpeningElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = cur_pos!();
|
||||
|
||||
if eat!(JSXTagEnd) {
|
||||
return Ok(Either::Left(JSXOpeningFragment { span: span!(start) }));
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_element_name()?;
|
||||
self.parse_jsx_opening_element_after_name(name)
|
||||
.map(Either::Right)
|
||||
}
|
||||
|
||||
pub(super) fn parse_jsx_opening_element_after_name(
|
||||
&mut self,
|
||||
name: JSXElementName,
|
||||
) -> PResult<'a, JSXOpeningElement> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = name.span().lo();
|
||||
|
||||
let mut attrs = vec![];
|
||||
while let Ok(..) = cur!(false) {
|
||||
if is!('/') || is!(JSXTagEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
let attr = self.parse_jsx_attr()?;
|
||||
attrs.push(attr);
|
||||
}
|
||||
let self_closing = eat!('/');
|
||||
if !eat!(JSXTagEnd) & !(self.ctx().in_forced_jsx_context && eat!('>')) {
|
||||
unexpected!()
|
||||
}
|
||||
Ok(JSXOpeningElement {
|
||||
span: span!(start),
|
||||
name,
|
||||
attrs,
|
||||
self_closing,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses JSX closing tag starting after "</".
|
||||
fn parse_jsx_closing_element_at(
|
||||
&mut self,
|
||||
start_pos: BytePos,
|
||||
) -> PResult<'a, Either<JSXClosingFragment, JSXClosingElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = cur_pos!();
|
||||
|
||||
if eat!(JSXTagEnd) {
|
||||
return Ok(Either::Left(JSXClosingFragment { span: span!(start) }));
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_element_name()?;
|
||||
expect!(JSXTagEnd);
|
||||
Ok(Either::Right(JSXClosingElement {
|
||||
span: span!(start),
|
||||
name,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parses entire JSX element, including it"s opening tag
|
||||
/// (starting after "<"), attributes, contents and closing tag.
|
||||
///
|
||||
/// babel: `jsxParseElementAt`
|
||||
pub(super) fn parse_jsx_element_at(
|
||||
&mut self,
|
||||
start_pos: BytePos,
|
||||
) -> PResult<'a, Either<JSXFragment, JSXElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = cur_pos!();
|
||||
let forced_jsx_context = match bump!() {
|
||||
tok!('<') => true,
|
||||
Token::JSXTagStart => false,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ctx = Context {
|
||||
in_forced_jsx_context: forced_jsx_context,
|
||||
..self.ctx()
|
||||
};
|
||||
self.with_ctx(ctx).parse_with(|p| {
|
||||
let opening_element = p.parse_jsx_opening_element_at(start_pos)?;
|
||||
let mut children = vec![];
|
||||
let mut closing_element = None;
|
||||
|
||||
let self_closing = match opening_element {
|
||||
Either::Right(ref el) => el.self_closing,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !self_closing {
|
||||
'contents: loop {
|
||||
match *cur!(true)? {
|
||||
Token::JSXTagStart => {
|
||||
let start = cur_pos!();
|
||||
|
||||
if peeked_is!('/') {
|
||||
bump!(); // JSXTagStart
|
||||
assert_and_bump!('/');
|
||||
|
||||
closing_element =
|
||||
p.parse_jsx_closing_element_at(start_pos).map(Some)?;
|
||||
break 'contents;
|
||||
}
|
||||
|
||||
children.push(p.parse_jsx_element_at(start).map(|e| match e {
|
||||
Either::Left(e) => JSXElementChild::from(e),
|
||||
Either::Right(e) => JSXElementChild::from(box e),
|
||||
})?);
|
||||
}
|
||||
Token::JSXText { .. } => {
|
||||
children.push(p.parse_jsx_text().map(JSXElementChild::from)?)
|
||||
}
|
||||
tok!('{') => {
|
||||
if peeked_is!("...") {
|
||||
children
|
||||
.push(p.parse_jsx_spread_child().map(JSXElementChild::from)?);
|
||||
} else {
|
||||
children
|
||||
.push(p.parse_jsx_expr_container().map(JSXElementChild::from)?);
|
||||
}
|
||||
}
|
||||
_ => unexpected!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
let span = span!(start);
|
||||
|
||||
Ok(match (opening_element, closing_element) {
|
||||
(Either::Left(opening), Some(Either::Right(closing))) => {
|
||||
syntax_error!(closing.span(), SyntaxError::JSXExpectedClosingTagForLtGt);
|
||||
}
|
||||
(Either::Right(opening), Some(Either::Left(closing))) => {
|
||||
syntax_error!(
|
||||
closing.span(),
|
||||
SyntaxError::JSXExpectedClosingTag {
|
||||
tag: get_qualified_jsx_name(&opening.name)
|
||||
}
|
||||
);
|
||||
}
|
||||
(Either::Left(opening), Some(Either::Left(closing))) => Either::Left(JSXFragment {
|
||||
span,
|
||||
opening,
|
||||
children,
|
||||
closing,
|
||||
}),
|
||||
(Either::Right(opening), None) => Either::Right(JSXElement {
|
||||
span,
|
||||
opening,
|
||||
children,
|
||||
closing: None,
|
||||
}),
|
||||
(Either::Right(opening), Some(Either::Right(closing))) => {
|
||||
if get_qualified_jsx_name(&closing.name)
|
||||
!= get_qualified_jsx_name(&opening.name)
|
||||
{
|
||||
syntax_error!(
|
||||
closing.span(),
|
||||
SyntaxError::JSXExpectedClosingTag {
|
||||
tag: get_qualified_jsx_name(&opening.name)
|
||||
}
|
||||
);
|
||||
}
|
||||
Either::Right(JSXElement {
|
||||
span,
|
||||
opening,
|
||||
children,
|
||||
closing: Some(closing),
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses entire JSX element from current position.
|
||||
///
|
||||
/// babel: `jsxParseElement`
|
||||
pub(super) fn parse_jsx_element(&mut self) -> PResult<'a, Either<JSXFragment, JSXElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
debug_assert!({
|
||||
match *cur!(true)? {
|
||||
Token::JSXTagStart | tok!('<') => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
let start_pos = cur_pos!();
|
||||
|
||||
let element = self.parse_jsx_element_at(start_pos);
|
||||
element
|
||||
}
|
||||
|
||||
pub(super) fn parse_jsx_text(&mut self) -> PResult<'a, JSXText> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
assert!({
|
||||
match *cur!(false)? {
|
||||
Token::JSXText { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
let token = bump!();
|
||||
let span = self.input.prev_span();
|
||||
match token {
|
||||
Token::JSXText { raw } => Ok(JSXText {
|
||||
span,
|
||||
// TODO
|
||||
value: raw.clone(),
|
||||
raw,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait IsFragment {
|
||||
fn is_fragment(&self) -> bool;
|
||||
}
|
||||
|
||||
impl IsFragment for Either<JSXOpeningFragment, JSXOpeningElement> {
|
||||
fn is_fragment(&self) -> bool {
|
||||
match *self {
|
||||
Either::Left(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsFragment for Either<JSXClosingFragment, JSXClosingElement> {
|
||||
fn is_fragment(&self) -> bool {
|
||||
match *self {
|
||||
Either::Left(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: IsFragment> IsFragment for Option<T> {
|
||||
fn is_fragment(&self) -> bool {
|
||||
self.as_ref().map(|s| s.is_fragment()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_qualified_jsx_name(name: &JSXElementName) -> JsWord {
|
||||
fn get_qualified_obj_name(obj: &JSXObject) -> JsWord {
|
||||
match *obj {
|
||||
JSXObject::Ident(ref i) => i.sym.clone(),
|
||||
JSXObject::JSXMemberExpr(box JSXMemberExpr { ref obj, ref prop }) => {
|
||||
format!("{}.{}", get_qualified_obj_name(obj), prop.sym).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
match *name {
|
||||
JSXElementName::Ident(ref i) => i.sym.clone(),
|
||||
JSXElementName::JSXNamespacedName(JSXNamespacedName { ref ns, ref name }) => {
|
||||
format!("{}:{}", ns.sym, name.sym).into()
|
||||
}
|
||||
JSXElementName::JSXMemberExpr(JSXMemberExpr { ref obj, ref prop }) => {
|
||||
format!("{}.{}", get_qualified_obj_name(obj), prop.sym).into()
|
||||
}
|
||||
}
|
||||
}
|
76
ecmascript/parser/src/parser/jsx/tests.rs
Normal file
76
ecmascript/parser/src/parser/jsx/tests.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use super::*;
|
||||
use crate::parser::test_parser;
|
||||
use swc_common::DUMMY_SP as span;
|
||||
|
||||
fn jsx(src: &'static str) -> Box<Expr> {
|
||||
test_parser(src, Syntax::Jsx, |p| p.parse_expr())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn self_closing_01() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx("<a />"),
|
||||
box Expr::JSXElement(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("a".into(), span)),
|
||||
self_closing: true,
|
||||
attrs: vec![]
|
||||
},
|
||||
children: vec![],
|
||||
closing: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_01() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx("<a>foo</a>"),
|
||||
box Expr::JSXElement(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("a".into(), span)),
|
||||
self_closing: false,
|
||||
attrs: vec![]
|
||||
},
|
||||
children: vec![JSXElementChild::JSXText(JSXText {
|
||||
span,
|
||||
raw: "foo".into(),
|
||||
value: "foo".into(),
|
||||
})],
|
||||
closing: Some(JSXClosingElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("a".into(), span)),
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_in_attr() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx(r#"<div id="w < w" />;"#),
|
||||
box Expr::JSXElement(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
attrs: vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
|
||||
span,
|
||||
name: JSXAttrName::Ident(Ident::new("id".into(), span)),
|
||||
value: Some(box Expr::Lit(Lit::Str(Str {
|
||||
span,
|
||||
value: "w < w".into(),
|
||||
has_escape: false,
|
||||
}))),
|
||||
})],
|
||||
name: JSXElementName::Ident(Ident::new("div".into(), span)),
|
||||
self_closing: true,
|
||||
},
|
||||
children: vec![],
|
||||
closing: None
|
||||
})
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
macro_rules! unexpected {
|
||||
($p:expr) => {{
|
||||
// unimplemented!("Unexpected token")
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Unexpected)
|
||||
let got = format!("{:?}", cur!($p, false).ok());
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Unexpected { got })
|
||||
}};
|
||||
}
|
||||
|
||||
@ -122,7 +122,8 @@ macro_rules! expect {
|
||||
($p:expr, $t:tt) => {{
|
||||
const TOKEN: &Token = &token_including_semi!($t);
|
||||
if !eat!($p, $t) {
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Expected(TOKEN))
|
||||
let cur = format!("{:?}", cur!($p, false).ok());
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Expected(TOKEN, cur))
|
||||
}
|
||||
}};
|
||||
}
|
||||
@ -131,7 +132,8 @@ macro_rules! expect_exact {
|
||||
($p:expr, $t:tt) => {{
|
||||
const TOKEN: &Token = &token_including_semi!($t);
|
||||
if !eat_exact!($p, $t) {
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Expected(TOKEN))
|
||||
let cur = format!("{:?}", cur!($p, false).ok());
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Expected(TOKEN, cur))
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
#![deny(non_snake_case)]
|
||||
use self::{input::ParserInput, util::ParseObject};
|
||||
use crate::{
|
||||
error::SyntaxError,
|
||||
lexer::{Input, Lexer},
|
||||
parser_macros::parser,
|
||||
token::*,
|
||||
Context, Session, Syntax,
|
||||
};
|
||||
use ast::*;
|
||||
use error::SyntaxError;
|
||||
use lexer::{Input, Lexer};
|
||||
use parser_macros::parser;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{BytePos, Span};
|
||||
use token::*;
|
||||
use Context;
|
||||
use Session;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
@ -18,6 +19,7 @@ mod class_and_fn;
|
||||
mod expr;
|
||||
mod ident;
|
||||
pub mod input;
|
||||
mod jsx;
|
||||
mod object;
|
||||
mod pat;
|
||||
mod stmt;
|
||||
@ -42,10 +44,10 @@ struct State {
|
||||
|
||||
#[parser]
|
||||
impl<'a, I: Input> Parser<'a, I> {
|
||||
pub fn new(session: Session<'a>, input: I) -> Self {
|
||||
pub fn new(session: Session<'a>, syntax: Syntax, input: I) -> Self {
|
||||
Parser {
|
||||
session,
|
||||
input: ParserInput::new(Lexer::new(session, input)),
|
||||
input: ParserInput::new(Lexer::new(session, syntax, input)),
|
||||
state: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -83,16 +85,17 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_parser<F, Ret>(s: &'static str, f: F) -> Ret
|
||||
pub fn test_parser<F, Ret>(s: &'static str, syntax: Syntax, f: F) -> Ret
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut Parser<'a, ::SourceFileInput>) -> Result<Ret, ()>,
|
||||
{
|
||||
::with_test_sess(s, |sess, input| f(&mut Parser::new(sess, input))).unwrap()
|
||||
crate::with_test_sess(s, |sess, input| f(&mut Parser::new(sess, syntax, input)))
|
||||
.unwrap_or_else(|output| panic!("test_parser(): failed to parse \n{}\n{}", s, output))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_legacy() {
|
||||
test_parser("<!--", |f| {
|
||||
test_parser("<!--", Syntax::Es2019, |f| {
|
||||
let res = f.parse_module();
|
||||
assert!(f.ctx().module);
|
||||
assert!(f.ctx().strict);
|
||||
|
@ -460,7 +460,7 @@ mod tests {
|
||||
use swc_common::DUMMY_SP as span;
|
||||
|
||||
fn array_pat(s: &'static str) -> Pat {
|
||||
test_parser(s, |p| p.parse_array_binding_pat())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_array_binding_pat())
|
||||
}
|
||||
|
||||
fn ident(s: &str) -> Ident {
|
||||
|
@ -726,10 +726,10 @@ mod tests {
|
||||
use swc_common::DUMMY_SP as span;
|
||||
|
||||
fn stmt(s: &'static str) -> Stmt {
|
||||
test_parser(s, |p| p.parse_stmt(true))
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_stmt(true))
|
||||
}
|
||||
fn expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| p.parse_expr())
|
||||
test_parser(s, Syntax::Es2019, |p| p.parse_expr())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -99,7 +99,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
// 'ImportedBinding'
|
||||
// 'IdentifierName' as 'ImportedBinding'
|
||||
if self.ctx().is_reserved_word(&orig_name.sym) {
|
||||
syntax_error!(orig_name.span, SyntaxError::Unexpected)
|
||||
syntax_error!(orig_name.span, SyntaxError::ReservedWordInImport)
|
||||
}
|
||||
|
||||
let local = orig_name;
|
||||
|
@ -102,6 +102,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
f(self)
|
||||
}
|
||||
|
||||
/// Creates a span from `start` to current pos.
|
||||
pub(super) fn span(&mut self, start: BytePos) -> Span {
|
||||
let end = last_pos!(self);
|
||||
if cfg!(debug_assertions) && start > end {
|
||||
@ -113,6 +114,10 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
}
|
||||
Span::new(start, end, Default::default())
|
||||
}
|
||||
|
||||
pub(super) const fn syntax(&self) -> Syntax {
|
||||
self.input.syntax()
|
||||
}
|
||||
}
|
||||
pub trait ParseObject<'a, Obj> {
|
||||
type Prop;
|
||||
@ -187,6 +192,13 @@ pub(super) trait ExprExt {
|
||||
Expr::Yield(..) | Expr::Arrow(..) | Expr::Assign(..) => false,
|
||||
|
||||
Expr::Seq(..) => false,
|
||||
|
||||
// jsx
|
||||
Expr::JSXMebmer(..)
|
||||
| Expr::JSXNamespacedName(..)
|
||||
| Expr::JSXEmpty(..)
|
||||
| Expr::JSXElement(..)
|
||||
| Expr::JSXFragment(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! [babel/bablyon]:https://github.com/babel/babel/blob/2d378d076eb0c5fe63234a8b509886005c01d7ee/packages/babylon/src/tokenizer/types.js
|
||||
pub(crate) use self::{AssignOpToken::*, BinOpToken::*, Keyword::*, Token::*, Word::*};
|
||||
use crate::error::Error;
|
||||
pub(crate) use ast::AssignOp as AssignOpToken;
|
||||
use ast::{BinaryOp, Str};
|
||||
use enum_kind::Kind;
|
||||
@ -113,7 +114,18 @@ pub(crate) enum Token {
|
||||
#[kind(starts_expr)]
|
||||
Num(f64),
|
||||
|
||||
Error(#[fold(ignore)] ::error::Error),
|
||||
JSXName {
|
||||
name: JsWord,
|
||||
},
|
||||
#[kind(before_expr)]
|
||||
JSXText {
|
||||
raw: JsWord,
|
||||
},
|
||||
#[kind(starts_expr)]
|
||||
JSXTagStart,
|
||||
JSXTagEnd,
|
||||
|
||||
Error(#[fold(ignore)] Error),
|
||||
}
|
||||
|
||||
#[derive(Kind, Debug, Clone, Copy, Eq, PartialEq, Hash, Fold)]
|
||||
|
281
ecmascript/parser/tests/jsx.rs
Normal file
281
ecmascript/parser/tests/jsx.rs
Normal file
@ -0,0 +1,281 @@
|
||||
#![feature(box_syntax)]
|
||||
#![feature(specialization)]
|
||||
#![feature(test)]
|
||||
|
||||
extern crate swc_common;
|
||||
extern crate swc_ecma_ast;
|
||||
extern crate swc_ecma_parser;
|
||||
extern crate test;
|
||||
extern crate testing;
|
||||
extern crate walkdir;
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
path::Path,
|
||||
};
|
||||
use swc_common::{Fold, FoldWith, Span};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_parser::{PResult, Parser, Session, SourceFileInput, Syntax};
|
||||
use test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName};
|
||||
use testing::{NormalizedOutput, StdErr};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn add_test<F: FnOnce() + Send + 'static>(
|
||||
tests: &mut Vec<TestDescAndFn>,
|
||||
name: String,
|
||||
ignore: bool,
|
||||
f: F,
|
||||
) {
|
||||
tests.push(TestDescAndFn {
|
||||
desc: TestDesc {
|
||||
name: TestName::DynTestName(name),
|
||||
ignore,
|
||||
should_panic: No,
|
||||
allow_fail: false,
|
||||
},
|
||||
testfn: TestFn::DynTestFn(box f),
|
||||
});
|
||||
}
|
||||
|
||||
fn error_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
||||
let root = {
|
||||
let mut root = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf();
|
||||
root.push("tests");
|
||||
root.push("jsx");
|
||||
root.push("errors");
|
||||
root
|
||||
};
|
||||
|
||||
eprintln!("Loading tests from {}", root.display());
|
||||
|
||||
let dir = root;
|
||||
|
||||
for entry in WalkDir::new(&dir).into_iter() {
|
||||
let entry = entry?;
|
||||
if entry.file_type().is_dir() || !entry.file_name().to_string_lossy().ends_with(".js") {
|
||||
continue;
|
||||
}
|
||||
let file_name = entry
|
||||
.path()
|
||||
.strip_prefix(&dir)
|
||||
.expect("failed to strip prefix")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let input = {
|
||||
let mut buf = String::new();
|
||||
File::open(entry.path())?.read_to_string(&mut buf)?;
|
||||
buf
|
||||
};
|
||||
|
||||
let ignore = false;
|
||||
|
||||
let dir = dir.clone();
|
||||
let name = format!("jsx::error::{}", file_name);
|
||||
add_test(tests, name, ignore, move || {
|
||||
eprintln!(
|
||||
"\n\n========== Running error reporting test {}\nSource:\n{}\n",
|
||||
file_name, input
|
||||
);
|
||||
|
||||
let path = dir.join(&file_name);
|
||||
// Parse source
|
||||
let err = parse_module(&path).expect_err("should fail, but parsed as");
|
||||
|
||||
if err
|
||||
.compare_to_file(format!("{}.stderr", path.display()))
|
||||
.is_err()
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reference_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
||||
let root = {
|
||||
let mut root = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf();
|
||||
root.push("tests");
|
||||
root.push("jsx");
|
||||
root.push("basic");
|
||||
root
|
||||
};
|
||||
|
||||
eprintln!("Loading tests from {}", root.display());
|
||||
|
||||
let dir = root;
|
||||
|
||||
for entry in WalkDir::new(&dir).into_iter() {
|
||||
let entry = entry?;
|
||||
if entry.file_type().is_dir() || !entry.file_name().to_string_lossy().ends_with(".js") {
|
||||
continue;
|
||||
}
|
||||
let file_name = entry
|
||||
.path()
|
||||
.strip_prefix(&dir)
|
||||
.expect("failed to strip prefix")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let input = {
|
||||
let mut buf = String::new();
|
||||
File::open(entry.path())?.read_to_string(&mut buf)?;
|
||||
buf
|
||||
};
|
||||
|
||||
let ignore = false;
|
||||
|
||||
let dir = dir.clone();
|
||||
let name = format!("jsx::reference::{}", file_name);
|
||||
add_test(tests, name, ignore, move || {
|
||||
eprintln!(
|
||||
"\n\n========== Running reference test {}\nSource:\n{}\n",
|
||||
file_name, input
|
||||
);
|
||||
|
||||
let path = dir.join(&file_name);
|
||||
// Parse source
|
||||
let module = parse_module(&path).expect("should be parsed");
|
||||
|
||||
if StdErr::from(format!("{:#?}", module))
|
||||
.compare_to_file(format!("{}.stdout", path.display()))
|
||||
.is_err()
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_module<'a>(file_name: &Path) -> Result<Module, NormalizedOutput> {
|
||||
with_parser(file_name, |p| p.parse_module())
|
||||
}
|
||||
|
||||
fn with_parser<F, Ret>(file_name: &Path, f: F) -> Result<Ret, StdErr>
|
||||
where
|
||||
F: for<'a> FnOnce(&mut Parser<'a, SourceFileInput>) -> PResult<'a, Ret>,
|
||||
{
|
||||
let output = ::testing::run_test(|cm, handler| {
|
||||
let fm = cm
|
||||
.load_file(file_name)
|
||||
.unwrap_or_else(|e| panic!("failed to load {}: {}", file_name.display(), e));
|
||||
|
||||
let res = f(&mut Parser::new(
|
||||
Session {
|
||||
handler: &handler,
|
||||
cfg: Default::default(),
|
||||
},
|
||||
Syntax::Jsx,
|
||||
(&*fm).into(),
|
||||
));
|
||||
|
||||
res
|
||||
});
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references() {
|
||||
let args: Vec<_> = env::args().collect();
|
||||
let mut tests = Vec::new();
|
||||
reference_tests(&mut tests).unwrap();
|
||||
test_main(&args, tests, Options::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error() {
|
||||
let args: Vec<_> = env::args().collect();
|
||||
let mut tests = Vec::new();
|
||||
error_tests(&mut tests).unwrap();
|
||||
test_main(&args, tests, Options::new());
|
||||
}
|
||||
|
||||
pub fn normalize<T>(t: T) -> T
|
||||
where
|
||||
Normalizer: Fold<T>,
|
||||
{
|
||||
let mut n = Normalizer;
|
||||
n.fold(t)
|
||||
}
|
||||
|
||||
pub struct Normalizer;
|
||||
impl Fold<Span> for Normalizer {
|
||||
fn fold(&mut self, _: Span) -> Span {
|
||||
Span::default()
|
||||
}
|
||||
}
|
||||
impl Fold<Str> for Normalizer {
|
||||
fn fold(&mut self, s: Str) -> Str {
|
||||
Str {
|
||||
span: Default::default(),
|
||||
has_escape: false,
|
||||
..s
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Fold<Expr> for Normalizer {
|
||||
fn fold(&mut self, e: Expr) -> Expr {
|
||||
let e = e.fold_children(self);
|
||||
|
||||
match e {
|
||||
Expr::Paren(ParenExpr { expr, .. }) => *expr,
|
||||
Expr::New(NewExpr {
|
||||
callee,
|
||||
args: None,
|
||||
span,
|
||||
}) => Expr::New(NewExpr {
|
||||
span,
|
||||
callee,
|
||||
args: Some(vec![]),
|
||||
}),
|
||||
// Flatten comma expressions.
|
||||
Expr::Seq(SeqExpr { mut exprs, span }) => {
|
||||
let need_work = exprs.iter().any(|n| match **n {
|
||||
Expr::Seq(..) => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if need_work {
|
||||
exprs = exprs.into_iter().fold(vec![], |mut v, e| {
|
||||
match *e {
|
||||
Expr::Seq(SeqExpr { exprs, .. }) => v.extend(exprs),
|
||||
_ => v.push(e),
|
||||
}
|
||||
v
|
||||
});
|
||||
}
|
||||
Expr::Seq(SeqExpr { exprs, span })
|
||||
}
|
||||
_ => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold<PropName> for Normalizer {
|
||||
fn fold(&mut self, n: PropName) -> PropName {
|
||||
let n = n.fold_children(self);
|
||||
|
||||
match n {
|
||||
PropName::Ident(Ident { sym, .. }) => PropName::Str(Str {
|
||||
span: Default::default(),
|
||||
value: sym,
|
||||
has_escape: false,
|
||||
}),
|
||||
PropName::Num(num) => PropName::Str(Str {
|
||||
span: Default::default(),
|
||||
value: num.to_string().into(),
|
||||
has_escape: false,
|
||||
}),
|
||||
_ => n,
|
||||
}
|
||||
}
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/1/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/1/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a />
|
59
ecmascript/parser/tests/jsx/basic/1/input.js.stdout
Normal file
59
ecmascript/parser/tests/jsx/basic/1/input.js.stdout
Normal file
@ -0,0 +1,59 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/10/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/10/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a>{/* this is a comment */}</a>
|
103
ecmascript/parser/tests/jsx/basic/10/input.js.stdout
Normal file
103
ecmascript/parser/tests/jsx/basic/10/input.js.stdout
Normal file
@ -0,0 +1,103 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
3
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXExprContainer(
|
||||
JSXExprContainer {
|
||||
expr: JSXEmptyExpr(
|
||||
JSXEmptyExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
27
|
||||
),
|
||||
hi: BytePos(
|
||||
27
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
30
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
30
|
||||
),
|
||||
hi: BytePos(
|
||||
31
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/11/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/11/input.js
Normal file
@ -0,0 +1 @@
|
||||
<div>@test content</div>
|
101
ecmascript/parser/tests/jsx/basic/11/input.js.stdout
Normal file
101
ecmascript/parser/tests/jsx/basic/11/input.js.stdout
Normal file
@ -0,0 +1,101 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
24
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
24
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('@test content' type=dynamic),
|
||||
raw: Atom('@test content' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
20
|
||||
),
|
||||
hi: BytePos(
|
||||
24
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
20
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/12/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/12/input.js
Normal file
@ -0,0 +1 @@
|
||||
<div><br />7x invalid-js-identifier</div>
|
143
ecmascript/parser/tests/jsx/basic/12/input.js.stdout
Normal file
143
ecmascript/parser/tests/jsx/basic/12/input.js.stdout
Normal file
@ -0,0 +1,143 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
br,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
),
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('7x invalid-js-identifier' type=dynamic),
|
||||
raw: Atom('7x invalid-js-identifier' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/13/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/13/input.js
Normal file
@ -0,0 +1 @@
|
||||
<LeftRight left=<a /> right=<b>monkeys /> gorillas</b> />
|
244
ecmascript/parser/tests/jsx/basic/13/input.js.stdout
Normal file
244
ecmascript/parser/tests/jsx/basic/13/input.js.stdout
Normal file
@ -0,0 +1,244 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
57
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
57
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
LeftRight,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
57
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
21
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
left,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
16
|
||||
),
|
||||
hi: BytePos(
|
||||
21
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
21
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
22
|
||||
),
|
||||
hi: BytePos(
|
||||
54
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
right,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
22
|
||||
),
|
||||
hi: BytePos(
|
||||
27
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
28
|
||||
),
|
||||
hi: BytePos(
|
||||
54
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
29
|
||||
),
|
||||
hi: BytePos(
|
||||
30
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
29
|
||||
),
|
||||
hi: BytePos(
|
||||
31
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
31
|
||||
),
|
||||
hi: BytePos(
|
||||
50
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('monkeys /> gorillas' type=dynamic),
|
||||
raw: Atom('monkeys /> gorillas' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
52
|
||||
),
|
||||
hi: BytePos(
|
||||
54
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
52
|
||||
),
|
||||
hi: BytePos(
|
||||
53
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/14/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/14/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a.b></a.b>
|
117
ecmascript/parser/tests/jsx/basic/14/input.js.stdout
Normal file
117
ecmascript/parser/tests/jsx/basic/14/input.js.stdout
Normal file
@ -0,0 +1,117 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: JSXMemberExpr(
|
||||
JSXMemberExpr {
|
||||
obj: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: JSXMemberExpr(
|
||||
JSXMemberExpr {
|
||||
obj: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/15/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/15/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a.b.c></a.b.c>
|
149
ecmascript/parser/tests/jsx/basic/15/input.js.stdout
Normal file
149
ecmascript/parser/tests/jsx/basic/15/input.js.stdout
Normal file
@ -0,0 +1,149 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: JSXMemberExpr(
|
||||
JSXMemberExpr {
|
||||
obj: JSXMemberExpr(
|
||||
JSXMemberExpr {
|
||||
obj: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
prop: Ident(
|
||||
c,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
6
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
7
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: JSXMemberExpr(
|
||||
JSXMemberExpr {
|
||||
obj: JSXMemberExpr(
|
||||
JSXMemberExpr {
|
||||
obj: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
prop: Ident(
|
||||
c,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/16/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/16/input.js
Normal file
@ -0,0 +1 @@
|
||||
(<div />) < x;
|
100
ecmascript/parser/tests/jsx/basic/16/input.js.stdout
Normal file
100
ecmascript/parser/tests/jsx/basic/16/input.js.stdout
Normal file
@ -0,0 +1,100 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
Bin(
|
||||
BinExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
13
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
op: "<",
|
||||
left: Paren(
|
||||
ParenExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
expr: JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
2
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
2
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
right: Ident(
|
||||
Ident(
|
||||
x,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
12
|
||||
),
|
||||
hi: BytePos(
|
||||
13
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/17/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/17/input.js
Normal file
@ -0,0 +1 @@
|
||||
<div {...props} />
|
87
ecmascript/parser/tests/jsx/basic/17/input.js.stdout
Normal file
87
ecmascript/parser/tests/jsx/basic/17/input.js.stdout
Normal file
@ -0,0 +1,87 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
SpreadElement(
|
||||
SpreadElement {
|
||||
dot3_token: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
expr: Ident(
|
||||
Ident(
|
||||
props,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/18/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/18/input.js
Normal file
@ -0,0 +1 @@
|
||||
<div {...props} post="attribute" />
|
133
ecmascript/parser/tests/jsx/basic/18/input.js.stdout
Normal file
133
ecmascript/parser/tests/jsx/basic/18/input.js.stdout
Normal file
@ -0,0 +1,133 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
SpreadElement(
|
||||
SpreadElement {
|
||||
dot3_token: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
expr: Ident(
|
||||
Ident(
|
||||
props,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
16
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
post,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
16
|
||||
),
|
||||
hi: BytePos(
|
||||
20
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
21
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('attribute' type=dynamic),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/19/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/19/input.js
Normal file
@ -0,0 +1 @@
|
||||
<div pre="leading" pre2="attribute" {...props}></div>
|
205
ecmascript/parser/tests/jsx/basic/19/input.js.stdout
Normal file
205
ecmascript/parser/tests/jsx/basic/19/input.js.stdout
Normal file
@ -0,0 +1,205 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
53
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
53
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
47
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
pre,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('leading' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
19
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
pre2,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
19
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
24
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('attribute' type=dynamic),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
SpreadElement(
|
||||
SpreadElement {
|
||||
dot3_token: Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
expr: Ident(
|
||||
Ident(
|
||||
props,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
40
|
||||
),
|
||||
hi: BytePos(
|
||||
45
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: false
|
||||
},
|
||||
children: [],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
49
|
||||
),
|
||||
hi: BytePos(
|
||||
53
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
49
|
||||
),
|
||||
hi: BytePos(
|
||||
52
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/2/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/2/input.js
Normal file
@ -0,0 +1 @@
|
||||
<n:a n:v />
|
116
ecmascript/parser/tests/jsx/basic/2/input.js.stdout
Normal file
116
ecmascript/parser/tests/jsx/basic/2/input.js.stdout
Normal file
@ -0,0 +1,116 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: JSXNamespacedName(
|
||||
JSXNamespacedName {
|
||||
ns: Ident(
|
||||
n,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
),
|
||||
name: Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: JSXNamespacedName(
|
||||
JSXNamespacedName {
|
||||
ns: Ident(
|
||||
n,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
6
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
),
|
||||
name: Ident(
|
||||
v,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
value: None
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/20/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/20/input.js
Normal file
@ -0,0 +1 @@
|
||||
<A aa={aa.bb.cc} bb={bb.cc.dd}><div>{aa.b}</div></A>
|
412
ecmascript/parser/tests/jsx/basic/20/input.js.stdout
Normal file
412
ecmascript/parser/tests/jsx/basic/20/input.js.stdout
Normal file
@ -0,0 +1,412 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
52
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
52
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
A,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
31
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
aa,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Member(
|
||||
MemberExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
12
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
obj: Expr(
|
||||
Member(
|
||||
MemberExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
obj: Expr(
|
||||
Ident(
|
||||
Ident(
|
||||
aa,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
Ident(
|
||||
bb,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
10
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
computed: false
|
||||
}
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
Ident(
|
||||
cc,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
computed: false
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
30
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
bb,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
19
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Member(
|
||||
MemberExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
26
|
||||
),
|
||||
hi: BytePos(
|
||||
29
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
obj: Expr(
|
||||
Member(
|
||||
MemberExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
23
|
||||
),
|
||||
hi: BytePos(
|
||||
26
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
obj: Expr(
|
||||
Ident(
|
||||
Ident(
|
||||
bb,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
21
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
Ident(
|
||||
cc,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
24
|
||||
),
|
||||
hi: BytePos(
|
||||
26
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
computed: false
|
||||
}
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
Ident(
|
||||
dd,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
27
|
||||
),
|
||||
hi: BytePos(
|
||||
29
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
computed: false
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
31
|
||||
),
|
||||
hi: BytePos(
|
||||
48
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
32
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
32
|
||||
),
|
||||
hi: BytePos(
|
||||
36
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXExprContainer(
|
||||
JSXExprContainer {
|
||||
expr: Expr(
|
||||
Member(
|
||||
MemberExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
39
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
obj: Expr(
|
||||
Ident(
|
||||
Ident(
|
||||
aa,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
39
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
prop: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
40
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
computed: false
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
44
|
||||
),
|
||||
hi: BytePos(
|
||||
48
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
44
|
||||
),
|
||||
hi: BytePos(
|
||||
47
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
50
|
||||
),
|
||||
hi: BytePos(
|
||||
52
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
A,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
50
|
||||
),
|
||||
hi: BytePos(
|
||||
51
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/21/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/21/input.js
Normal file
@ -0,0 +1 @@
|
||||
<div {...c}> {...children}{a}{...b}</div>
|
185
ecmascript/parser/tests/jsx/basic/21/input.js.stdout
Normal file
185
ecmascript/parser/tests/jsx/basic/21/input.js.stdout
Normal file
@ -0,0 +1,185 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
SpreadElement(
|
||||
SpreadElement {
|
||||
dot3_token: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
expr: Ident(
|
||||
Ident(
|
||||
c,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
12
|
||||
),
|
||||
hi: BytePos(
|
||||
13
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom(' ' type=inline),
|
||||
raw: Atom(' ' type=inline)
|
||||
}
|
||||
),
|
||||
JSXSpreadChild(
|
||||
JSXSpreadChild {
|
||||
expr: Ident(
|
||||
Ident(
|
||||
children,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
25
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXExprContainer(
|
||||
JSXExprContainer {
|
||||
expr: Expr(
|
||||
Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
27
|
||||
),
|
||||
hi: BytePos(
|
||||
28
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXSpreadChild(
|
||||
JSXSpreadChild {
|
||||
expr: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
33
|
||||
),
|
||||
hi: BytePos(
|
||||
34
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/3/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/3/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a n:foo="bar"> {value} <b><c /></b></a>
|
308
ecmascript/parser/tests/jsx/basic/3/input.js.stdout
Normal file
308
ecmascript/parser/tests/jsx/basic/3/input.js.stdout
Normal file
@ -0,0 +1,308 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: JSXNamespacedName(
|
||||
JSXNamespacedName {
|
||||
ns: Ident(
|
||||
n,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
),
|
||||
name: Ident(
|
||||
foo,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('bar' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
15
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom(' ' type=inline),
|
||||
raw: Atom(' ' type=inline)
|
||||
}
|
||||
),
|
||||
JSXExprContainer(
|
||||
JSXExprContainer {
|
||||
expr: Expr(
|
||||
Ident(
|
||||
Ident(
|
||||
value,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
23
|
||||
),
|
||||
hi: BytePos(
|
||||
24
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom(' ' type=inline),
|
||||
raw: Atom(' ' type=inline)
|
||||
}
|
||||
),
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
24
|
||||
),
|
||||
hi: BytePos(
|
||||
36
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
25
|
||||
),
|
||||
hi: BytePos(
|
||||
26
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
25
|
||||
),
|
||||
hi: BytePos(
|
||||
27
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
27
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
c,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
28
|
||||
),
|
||||
hi: BytePos(
|
||||
29
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
28
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
34
|
||||
),
|
||||
hi: BytePos(
|
||||
36
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
34
|
||||
),
|
||||
hi: BytePos(
|
||||
35
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
38
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
38
|
||||
),
|
||||
hi: BytePos(
|
||||
39
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/4/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/4/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a b={" "} c=" " d="&" e="&r;" />
|
244
ecmascript/parser/tests/jsx/basic/4/input.js.stdout
Normal file
244
ecmascript/parser/tests/jsx/basic/4/input.js.stdout
Normal file
@ -0,0 +1,244 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom(' ' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
c,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom(' ' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
26
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
d,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
18
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
19
|
||||
),
|
||||
hi: BytePos(
|
||||
26
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('&' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
27
|
||||
),
|
||||
hi: BytePos(
|
||||
37
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
e,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
27
|
||||
),
|
||||
hi: BytePos(
|
||||
28
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
29
|
||||
),
|
||||
hi: BytePos(
|
||||
37
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('&r;' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
2
ecmascript/parser/tests/jsx/basic/5/input.js
Normal file
2
ecmascript/parser/tests/jsx/basic/5/input.js
Normal file
@ -0,0 +1,2 @@
|
||||
<a
|
||||
/>
|
59
ecmascript/parser/tests/jsx/basic/5/input.js.stdout
Normal file
59
ecmascript/parser/tests/jsx/basic/5/input.js.stdout
Normal file
@ -0,0 +1,59 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/6/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/6/input.js
Normal file
@ -0,0 +1 @@
|
||||
<日本語></日本語>
|
85
ecmascript/parser/tests/jsx/basic/6/input.js.stdout
Normal file
85
ecmascript/parser/tests/jsx/basic/6/input.js.stdout
Normal file
@ -0,0 +1,85 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
日本語,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
日本語,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
5
ecmascript/parser/tests/jsx/basic/7/input.js
Normal file
5
ecmascript/parser/tests/jsx/basic/7/input.js
Normal file
@ -0,0 +1,5 @@
|
||||
<AbC-def
|
||||
test="&&">
|
||||
bar
|
||||
baz
|
||||
</AbC-def>
|
160
ecmascript/parser/tests/jsx/basic/7/input.js.stdout
Normal file
160
ecmascript/parser/tests/jsx/basic/7/input.js.stdout
Normal file
@ -0,0 +1,160 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
51
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
51
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
AbC-def,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
31
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
test,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Lit(
|
||||
Str(
|
||||
Str {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
16
|
||||
),
|
||||
hi: BytePos(
|
||||
31
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('&&' type=inline),
|
||||
has_escape: false
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
32
|
||||
),
|
||||
hi: BytePos(
|
||||
41
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
bar
|
||||
|
||||
baz
|
||||
|
||||
' type=dynamic),
|
||||
raw: Atom('
|
||||
|
||||
bar
|
||||
|
||||
baz
|
||||
|
||||
' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
43
|
||||
),
|
||||
hi: BytePos(
|
||||
51
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
AbC-def,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
43
|
||||
),
|
||||
hi: BytePos(
|
||||
50
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/8/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/8/input.js
Normal file
@ -0,0 +1 @@
|
||||
<a b={x ? <c /> : <d />} />
|
200
ecmascript/parser/tests/jsx/basic/8/input.js.stdout
Normal file
200
ecmascript/parser/tests/jsx/basic/8/input.js.stdout
Normal file
@ -0,0 +1,200 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
27
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
27
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
27
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [
|
||||
JSXAttr(
|
||||
JSXAttr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
24
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
b,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
value: Some(
|
||||
Cond(
|
||||
CondExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
test: Ident(
|
||||
Ident(
|
||||
x,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
7
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
cons: JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
10
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
c,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
11
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
),
|
||||
alt: JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
18
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
d,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
19
|
||||
),
|
||||
hi: BytePos(
|
||||
20
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
19
|
||||
),
|
||||
hi: BytePos(
|
||||
23
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
4
ecmascript/parser/tests/jsx/basic/asi/input.js
Normal file
4
ecmascript/parser/tests/jsx/basic/asi/input.js
Normal file
@ -0,0 +1,4 @@
|
||||
function x() {
|
||||
let x
|
||||
<div />
|
||||
}
|
148
ecmascript/parser/tests/jsx/basic/asi/input.js.stdout
Normal file
148
ecmascript/parser/tests/jsx/basic/asi/input.js.stdout
Normal file
@ -0,0 +1,148 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
34
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Decl(
|
||||
Fn(
|
||||
FnDecl {
|
||||
ident: Ident(
|
||||
x,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
9
|
||||
),
|
||||
hi: BytePos(
|
||||
10
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
),
|
||||
function: Function {
|
||||
params: [],
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
34
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: BlockStmt {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
34
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
stmts: [
|
||||
Decl(
|
||||
Var(
|
||||
VarDecl {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
17
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
kind: "let",
|
||||
decls: [
|
||||
VarDeclarator {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
21
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
x,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
21
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
init: None
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
),
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
25
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
26
|
||||
),
|
||||
hi: BytePos(
|
||||
29
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
26
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: true
|
||||
},
|
||||
children: [],
|
||||
closing: None
|
||||
}
|
||||
)
|
||||
)
|
||||
]
|
||||
},
|
||||
is_generator: false,
|
||||
is_async: false
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
<a>{}</a>
|
@ -0,0 +1,103 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
3
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXExprContainer(
|
||||
JSXExprContainer {
|
||||
expr: JSXEmptyExpr(
|
||||
JSXEmptyExpr {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
4
|
||||
),
|
||||
hi: BytePos(
|
||||
4
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
9
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
a,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
8
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/entity/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/entity/input.js
Normal file
@ -0,0 +1 @@
|
||||
<A>💩</A>
|
101
ecmascript/parser/tests/jsx/basic/entity/input.js.stdout
Normal file
101
ecmascript/parser/tests/jsx/basic/entity/input.js.stdout
Normal file
@ -0,0 +1,101 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
A,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
3
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('💩' type=inline),
|
||||
raw: Atom('💩' type=inline)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
14
|
||||
),
|
||||
hi: BytePos(
|
||||
16
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
A,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
14
|
||||
),
|
||||
hi: BytePos(
|
||||
15
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/fragment-1/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/fragment-1/input.js
Normal file
@ -0,0 +1 @@
|
||||
<></>
|
53
ecmascript/parser/tests/jsx/basic/fragment-1/input.js.stdout
Normal file
53
ecmascript/parser/tests/jsx/basic/fragment-1/input.js.stdout
Normal file
@ -0,0 +1,53 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXFragment(
|
||||
JSXFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
},
|
||||
children: [],
|
||||
closing: JSXClosingFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
4
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
1
ecmascript/parser/tests/jsx/basic/fragment-2/input.js
Normal file
1
ecmascript/parser/tests/jsx/basic/fragment-2/input.js
Normal file
@ -0,0 +1 @@
|
||||
<>Hi, I'm a string!</>
|
69
ecmascript/parser/tests/jsx/basic/fragment-2/input.js.stdout
Normal file
69
ecmascript/parser/tests/jsx/basic/fragment-2/input.js.stdout
Normal file
@ -0,0 +1,69 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXFragment(
|
||||
JSXFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
2
|
||||
),
|
||||
hi: BytePos(
|
||||
19
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('Hi, I'm a string!' type=dynamic),
|
||||
raw: Atom('Hi, I'm a string!' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: JSXClosingFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
21
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
6
ecmascript/parser/tests/jsx/basic/fragment-3/input.js
Normal file
6
ecmascript/parser/tests/jsx/basic/fragment-3/input.js
Normal file
@ -0,0 +1,6 @@
|
||||
< >
|
||||
<span>
|
||||
hi
|
||||
</span>
|
||||
<div>bye</div>
|
||||
</>
|
287
ecmascript/parser/tests/jsx/basic/fragment-3/input.js.stdout
Normal file
287
ecmascript/parser/tests/jsx/basic/fragment-3/input.js.stdout
Normal file
@ -0,0 +1,287 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
50
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXFragment(
|
||||
JSXFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
50
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
2
|
||||
),
|
||||
hi: BytePos(
|
||||
3
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
3
|
||||
),
|
||||
hi: BytePos(
|
||||
6
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
),
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
29
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
span,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
11
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
12
|
||||
),
|
||||
hi: BytePos(
|
||||
22
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
hi
|
||||
|
||||
' type=dynamic),
|
||||
raw: Atom('
|
||||
|
||||
hi
|
||||
|
||||
' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
24
|
||||
),
|
||||
hi: BytePos(
|
||||
29
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
span,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
24
|
||||
),
|
||||
hi: BytePos(
|
||||
28
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
29
|
||||
),
|
||||
hi: BytePos(
|
||||
32
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
),
|
||||
JSXElement(
|
||||
JSXElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
32
|
||||
),
|
||||
hi: BytePos(
|
||||
46
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningElement {
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
33
|
||||
),
|
||||
hi: BytePos(
|
||||
36
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
),
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
33
|
||||
),
|
||||
hi: BytePos(
|
||||
37
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
attrs: [],
|
||||
self_closing: false
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
37
|
||||
),
|
||||
hi: BytePos(
|
||||
40
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('bye' type=inline),
|
||||
raw: Atom('bye' type=inline)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: Some(
|
||||
JSXClosingElement {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
42
|
||||
),
|
||||
hi: BytePos(
|
||||
46
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
name: Ident(
|
||||
Ident(
|
||||
div,
|
||||
Span {
|
||||
lo: BytePos(
|
||||
42
|
||||
),
|
||||
hi: BytePos(
|
||||
45
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
46
|
||||
),
|
||||
hi: BytePos(
|
||||
47
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: JSXClosingFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
49
|
||||
),
|
||||
hi: BytePos(
|
||||
50
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
7
ecmascript/parser/tests/jsx/basic/fragment-4/input.js
Normal file
7
ecmascript/parser/tests/jsx/basic/fragment-4/input.js
Normal file
@ -0,0 +1,7 @@
|
||||
<>
|
||||
<>
|
||||
<>
|
||||
super deep
|
||||
</>
|
||||
</>
|
||||
</>
|
227
ecmascript/parser/tests/jsx/basic/fragment-4/input.js.stdout
Normal file
227
ecmascript/parser/tests/jsx/basic/fragment-4/input.js.stdout
Normal file
@ -0,0 +1,227 @@
|
||||
Module {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
49
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
body: [
|
||||
Stmt(
|
||||
Expr(
|
||||
JSXFragment(
|
||||
JSXFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
0
|
||||
),
|
||||
hi: BytePos(
|
||||
49
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
1
|
||||
),
|
||||
hi: BytePos(
|
||||
2
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
2
|
||||
),
|
||||
hi: BytePos(
|
||||
5
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
),
|
||||
JSXFragment(
|
||||
JSXFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
5
|
||||
),
|
||||
hi: BytePos(
|
||||
45
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
6
|
||||
),
|
||||
hi: BytePos(
|
||||
7
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
7
|
||||
),
|
||||
hi: BytePos(
|
||||
12
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
),
|
||||
JSXFragment(
|
||||
JSXFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
12
|
||||
),
|
||||
hi: BytePos(
|
||||
39
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
opening: JSXOpeningFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
13
|
||||
),
|
||||
hi: BytePos(
|
||||
14
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
},
|
||||
children: [
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
14
|
||||
),
|
||||
hi: BytePos(
|
||||
36
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
super deep
|
||||
|
||||
' type=dynamic),
|
||||
raw: Atom('
|
||||
|
||||
super deep
|
||||
|
||||
' type=dynamic)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: JSXClosingFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
38
|
||||
),
|
||||
hi: BytePos(
|
||||
39
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
39
|
||||
),
|
||||
hi: BytePos(
|
||||
42
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: JSXClosingFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
44
|
||||
),
|
||||
hi: BytePos(
|
||||
45
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
JSXText(
|
||||
JSXText {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
45
|
||||
),
|
||||
hi: BytePos(
|
||||
46
|
||||
),
|
||||
ctxt: #0
|
||||
},
|
||||
value: Atom('
|
||||
|
||||
' type=inline),
|
||||
raw: Atom('
|
||||
|
||||
' type=inline)
|
||||
}
|
||||
)
|
||||
],
|
||||
closing: JSXClosingFragment {
|
||||
span: Span {
|
||||
lo: BytePos(
|
||||
48
|
||||
),
|
||||
hi: BytePos(
|
||||
49
|
||||
),
|
||||
ctxt: #0
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
7
ecmascript/parser/tests/jsx/basic/fragment-5/input.js
Normal file
7
ecmascript/parser/tests/jsx/basic/fragment-5/input.js
Normal file
@ -0,0 +1,7 @@
|
||||
<
|
||||
// comment1
|
||||
/* comment2 */
|
||||
>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user