swc/ecmascript/parser/src/lib.rs

456 lines
10 KiB
Rust
Raw Normal View History

//! 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() {
//! | ^^^^^
//! ```
//!
//! ## Error recovery
//!
//! The parser can recover from some parsing erros. For example, parser returns
//! `Ok(Module)` for the code below, while emitting error to handler.
//!
//! ```ts
//! const CONST = 9000 % 2;
//! const enum D {
//! // Comma is requied, but parser can recover because of the newline.
//! d = 10
//! g = CONST
//! }
//! ```
//!
//! # Example (lexer)
//!
//! See `lexer.rs` in examples directory.
//!
//! # Example (parser)
//!
//! ```
//! #[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::{lexer::Lexer, Parser, Session, SourceFileInput, Syntax};
//!
//! fn main() {
//! swc_common::GLOBALS.set(&swc_common::Globals::new(), || {
//! let cm: Arc<SourceMap> = Default::default();
//! 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 lexer = Lexer::new(
//! session,
//! Syntax::Es(Default::default()),
//! Default::default(),
//! SourceFileInput::from(&*fm),
//! None,
//! );
//!
//! let mut parser = Parser::new_from(session, lexer);
//!
//!
//! 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)]
pub use self::{
lexer::input::{Input, SourceFileInput},
parser::*,
};
2018-12-30 07:06:13 +03:00
use serde::{Deserialize, Serialize};
use swc_common::{errors::Handler, Span};
#[macro_use]
mod macros;
mod error;
pub mod lexer;
mod parser;
pub mod token;
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
#[serde(tag = "syntax")]
pub enum Syntax {
/// Standard
#[serde(rename = "ecmascript")]
Es(EsConfig),
#[serde(rename = "typescript")]
Typescript(TsConfig),
}
2018-12-30 07:06:13 +03:00
impl Default for Syntax {
fn default() -> Self {
Syntax::Es(Default::default())
2018-12-30 07:06:13 +03:00
}
}
impl Syntax {
/// Should we parse jsx?
pub fn jsx(self) -> bool {
match self {
Syntax::Es(EsConfig { jsx: true, .. })
| Syntax::Typescript(TsConfig { tsx: true, .. }) => true,
_ => false,
}
}
pub fn optional_chaining(self) -> bool {
match self {
Syntax::Es(EsConfig {
optional_chaining: true,
..
})
| Syntax::Typescript(TsConfig { .. }) => true,
_ => false,
}
}
2019-02-10 06:07:40 +03:00
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, .. }) | Syntax::Typescript(..) => 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,
..
})
| Syntax::Typescript(..) => 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,
..
})
| Syntax::Typescript(..) => 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,
}
}
pub fn export_namespace_from(self) -> bool {
match self {
Syntax::Es(EsConfig {
export_namespace_from: true,
..
})
| Syntax::Typescript(..) => true,
_ => false,
}
}
pub fn nullish_coalescing(self) -> bool {
match self {
Syntax::Es(EsConfig {
nullish_coalescing: true,
..
})
| Syntax::Typescript(..) => true,
_ => false,
}
}
pub fn import_meta(self) -> bool {
match self {
Syntax::Es(EsConfig {
import_meta: true, ..
})
| Syntax::Typescript(..) => true,
_ => false,
}
}
pub fn top_level_await(self) -> bool {
match self {
Syntax::Es(EsConfig {
top_level_await: true,
..
})
| Syntax::Typescript(..) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
2019-02-10 06:07:40 +03:00
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct TsConfig {
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub tsx: bool,
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub decorators: bool,
2019-02-10 06:07:40 +03:00
#[serde(default)]
pub dynamic_import: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq)]
pub enum JscTarget {
#[serde(rename = "es3")]
Es3,
#[serde(rename = "es5")]
Es5,
#[serde(rename = "es2015")]
Es2015,
#[serde(rename = "es2016")]
Es2016,
#[serde(rename = "es2017")]
Es2017,
#[serde(rename = "es2018")]
Es2018,
#[serde(rename = "es2019")]
Es2019,
}
impl Default for JscTarget {
fn default() -> Self {
JscTarget::Es3
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
2019-02-10 06:07:40 +03:00
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct EsConfig {
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub jsx: bool,
/// Support numeric separator.
/// Stage 3.
#[serde(rename = "numericSeparator")]
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub num_sep: bool,
#[serde(rename = "classPrivateProperty")]
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub class_private_props: bool,
#[serde(rename = "privateMethod")]
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub class_private_methods: bool,
#[serde(rename = "classProperty")]
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub class_props: bool,
/// Support function bind expression.
#[serde(rename = "functionBind")]
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub fn_bind: bool,
/// Enable decorators.
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub decorators: bool,
/// babel: `decorators.decoratorsBeforeExport`
///
/// Effective only if `decorator` is true.
#[serde(rename = "decoratorsBeforeExport")]
2019-01-08 12:27:38 +03:00
#[serde(default)]
pub decorators_before_export: bool,
2019-02-10 06:07:40 +03:00
#[serde(default)]
pub export_default_from: bool,
#[serde(default)]
pub export_namespace_from: bool,
2019-02-10 06:07:40 +03:00
#[serde(default)]
pub dynamic_import: bool,
/// Stage 3.
#[serde(default)]
pub nullish_coalescing: bool,
#[serde(default)]
pub optional_chaining: bool,
/// Stage 3.
#[serde(default)]
pub import_meta: bool,
/// Stage 3.
#[serde(default)]
pub top_level_await: bool,
}
/// Syntactic context.
#[derive(Debug, Clone, Copy, Default)]
pub 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,
is_continue_allowed: bool,
is_break_allowed: bool,
in_type: bool,
/// Typescript extension.
in_declare: bool,
span_of_fn_name: Option<Span>,
/// 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: &str, f: F) -> Result<Ret, ::testing::StdErr>
where
F: FnOnce(Session<'_>, SourceFileInput<'_>) -> Result<Ret, ()>,
{
use swc_common::FileName;
2018-01-23 15:38:48 +03:00
2019-01-20 05:08:26 +03:00
::testing::run_test(false, |cm, handler| {
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
2018-01-23 15:38:48 +03:00
f(Session { handler: &handler }, (&*fm).into())
})
}