mirror of
https://github.com/swc-project/swc.git
synced 2024-12-26 15:12:08 +03:00
bd219688ac
swc_ecma_parser: - fix parsing of cond expression (Closes #158) - fix parsing of function signature (Closes #159)
350 lines
8.1 KiB
Rust
350 lines
8.1 KiB
Rust
//! es2019 parser
|
|
//!
|
|
//! # Features
|
|
//!
|
|
//! ## Heavily tested
|
|
//!
|
|
//! Passes almost all tests from [tc39/test262][].
|
|
//!
|
|
//! ## Error reporting
|
|
//!
|
|
//! ```sh
|
|
//! error: 'implements', 'interface', 'let', 'package', 'private', 'protected', 'public', 'static', or 'yield' cannot be used as an identifier in strict mode
|
|
//! --> invalid.js:3:10
|
|
//! |
|
|
//! 3 | function yield() {
|
|
//! | ^^^^^
|
|
//! ```
|
|
//!
|
|
//! # Example
|
|
//!
|
|
//! ```
|
|
//! #[macro_use]
|
|
//! extern crate swc_common;
|
|
//! extern crate swc_ecma_parser;
|
|
//! use std::sync::Arc;
|
|
//! use swc_common::{
|
|
//! errors::{ColorConfig, Handler},
|
|
//! FileName, FilePathMapping, SourceMap,
|
|
//! };
|
|
//! use swc_ecma_parser::{Parser, Session, SourceFileInput, Syntax};
|
|
//!
|
|
//! fn main() {
|
|
//! swc_common::GLOBALS.set(&swc_common::Globals::new(), || {
|
|
//! let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
|
|
//! let handler =
|
|
//! Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));
|
|
//!
|
|
//! let session = Session { handler: &handler };
|
|
//!
|
|
//! // Real usage
|
|
//! // let fm = cm
|
|
//! // .load_file(Path::new("test.js"))
|
|
//! // .expect("failed to load test.js");
|
|
//!
|
|
//! let fm = cm.new_source_file(
|
|
//! FileName::Custom("test.js".into()),
|
|
//! "function foo() {}".into(),
|
|
//! );
|
|
//!
|
|
//! let mut parser = Parser::new(
|
|
//! session,
|
|
//! Syntax::Es(Default::default()),
|
|
//! SourceFileInput::from(&*fm),
|
|
//! None, // Disable comments
|
|
//! );
|
|
//!
|
|
//! let _module = parser
|
|
//! .parse_module()
|
|
//! .map_err(|mut e| {
|
|
//! e.emit();
|
|
//! ()
|
|
//! })
|
|
//! .expect("failed to parser module");
|
|
//! });
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//!
|
|
//! [tc39/test262]:https://github.com/tc39/test262
|
|
|
|
#![cfg_attr(any(test, feature = "fold"), feature(specialization))]
|
|
#![cfg_attr(test, feature(box_syntax))]
|
|
#![cfg_attr(test, feature(test))]
|
|
#![deny(unreachable_patterns)]
|
|
#![deny(unsafe_code)]
|
|
|
|
extern crate either;
|
|
#[macro_use]
|
|
extern crate smallvec;
|
|
extern crate swc_ecma_parser_macros as parser_macros;
|
|
#[macro_use]
|
|
extern crate log;
|
|
#[macro_use(js_word)]
|
|
extern crate swc_atoms;
|
|
extern crate enum_kind;
|
|
extern crate regex;
|
|
extern crate serde;
|
|
extern crate swc_common;
|
|
#[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::{
|
|
lexer::input::{Input, SourceFileInput},
|
|
parser::*,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use swc_common::errors::Handler;
|
|
|
|
#[macro_use]
|
|
mod macros;
|
|
mod error;
|
|
mod lexer;
|
|
mod parser;
|
|
mod token;
|
|
|
|
#[derive(Clone, Copy, Deserialize, Serialize)]
|
|
#[serde(tag = "syntax")]
|
|
pub enum Syntax {
|
|
/// Standard
|
|
#[serde(rename = "ecmascript")]
|
|
Es(EsConfig),
|
|
#[serde(rename = "typescript")]
|
|
Typescript(TsConfig),
|
|
}
|
|
|
|
impl Default for Syntax {
|
|
fn default() -> Self {
|
|
Syntax::Es(Default::default())
|
|
}
|
|
}
|
|
|
|
impl Syntax {
|
|
/// Should we pare jsx?
|
|
pub fn jsx(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig { jsx: true, .. })
|
|
| Syntax::Typescript(TsConfig { tsx: true, .. }) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn dynamic_import(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
dynamic_import: true,
|
|
..
|
|
})
|
|
| Syntax::Typescript(TsConfig {
|
|
dynamic_import: true,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn fn_bind(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig { fn_bind: true, .. }) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn num_sep(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig { num_sep: true, .. }) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn decorators(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
decorators: true, ..
|
|
})
|
|
| Syntax::Typescript(TsConfig {
|
|
decorators: true, ..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn class_private_methods(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
class_private_methods: true,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn class_private_props(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
class_private_props: true,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn class_props(self) -> bool {
|
|
if self.typescript() {
|
|
return true;
|
|
}
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
class_props: true, ..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn decorators_before_export(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
decorators_before_export: true,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Should we pare typescript?
|
|
pub fn typescript(self) -> bool {
|
|
match self {
|
|
Syntax::Typescript(..) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn export_default_from(self) -> bool {
|
|
match self {
|
|
Syntax::Es(EsConfig {
|
|
export_default_from: true,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
|
pub struct TsConfig {
|
|
#[serde(default)]
|
|
pub tsx: bool,
|
|
|
|
#[serde(default)]
|
|
pub decorators: bool,
|
|
|
|
#[serde(default)]
|
|
pub dynamic_import: bool,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
|
pub struct EsConfig {
|
|
#[serde(default)]
|
|
pub jsx: bool,
|
|
/// Support numeric separator.
|
|
#[serde(rename = "numericSeparator")]
|
|
#[serde(default)]
|
|
pub num_sep: bool,
|
|
|
|
#[serde(rename = "classPrivateProperty")]
|
|
#[serde(default)]
|
|
pub class_private_props: bool,
|
|
|
|
#[serde(rename = "privateMethod")]
|
|
#[serde(default)]
|
|
pub class_private_methods: bool,
|
|
|
|
#[serde(rename = "classProperty")]
|
|
#[serde(default)]
|
|
pub class_props: bool,
|
|
|
|
/// Support function bind expression.
|
|
#[serde(rename = "functionBind")]
|
|
#[serde(default)]
|
|
pub fn_bind: bool,
|
|
|
|
/// Enable decorators.
|
|
#[serde(default)]
|
|
pub decorators: bool,
|
|
|
|
/// babel: `decorators.decoratorsBeforeExport`
|
|
///
|
|
/// Effective only if `decorator` is true.
|
|
#[serde(rename = "decoratorsBeforeExport")]
|
|
#[serde(default)]
|
|
pub decorators_before_export: bool,
|
|
|
|
#[serde(default)]
|
|
pub export_default_from: bool,
|
|
|
|
#[serde(default)]
|
|
pub dynamic_import: bool,
|
|
}
|
|
|
|
/// Syntatic context.
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub(crate) struct Context {
|
|
/// Is in module code?
|
|
module: bool,
|
|
strict: bool,
|
|
include_in_expr: bool,
|
|
/// If true, await expression is parsed, and "await" is treated as a
|
|
/// keyword.
|
|
in_async: bool,
|
|
/// If true, yield expression is parsed, and "yield" is treated as a
|
|
/// keyword.
|
|
in_generator: bool,
|
|
|
|
in_type: bool,
|
|
/// Typescript extension.
|
|
in_declare: bool,
|
|
|
|
/// If true, `:` should not be treated as a type annotation.
|
|
in_cond_expr: bool,
|
|
|
|
in_function: bool,
|
|
|
|
in_parameters: bool,
|
|
|
|
in_method: bool,
|
|
in_class_prop: bool,
|
|
|
|
in_property_name: bool,
|
|
|
|
in_forced_jsx_context: bool,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct Session<'a> {
|
|
pub handler: &'a Handler,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn with_test_sess<F, Ret>(src: &'static str, f: F) -> Result<Ret, ::testing::StdErr>
|
|
where
|
|
F: FnOnce(Session, SourceFileInput) -> Result<Ret, ()>,
|
|
{
|
|
use swc_common::FileName;
|
|
|
|
::testing::run_test(false, |cm, handler| {
|
|
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
|
|
|
|
f(Session { handler: &handler }, (&*fm).into())
|
|
})
|
|
}
|