diff --git a/Cargo.toml b/Cargo.toml index 633f9c6..da1c5e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "resast" -version = "0.4.0" +version = "0.5.0-alpha.3" authors = ["rfm "] edition = "2018" description = "Rusty-ECMAScript Abstract Syntax Tree" @@ -21,4 +21,4 @@ pretty_env_logger = "0.3" [features] default = [] serialization = ["serde", "serde_derive"] -esprima = ["serialization"] \ No newline at end of file +esprima = ["serialization"] diff --git a/package.json b/package.json new file mode 100644 index 0000000..64eb887 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "esprima": "^4.0.1" + } +} diff --git a/run_es_parse.js b/run_es_parse.js deleted file mode 100644 index c653d0c..0000000 --- a/run_es_parse.js +++ /dev/null @@ -1,26 +0,0 @@ -const esp = require('esprima'); -const fs = require('fs'); -async function main() { - let js = process.argv[2]; - console.error(js); - return new Promise((r, j) => { - fs.readFile(js, 'utf8', (err, content) => { - if (err) return j(err); - let parsed; - if (js.endsWith('module.js')) { - parsed = esp.parseModule(content); - } else { - parsed = esp.parseScript(content); - } - let json = JSON.stringify(parsed, (key, value) => { - if (key === 'value' && value instanceof RegExp) { - value = value.toString(); - } - return value; - }, 4); - return r(json); - }); - }) -} - -main().then((json) => console.log(json)).catch(e => console.error(e)); \ No newline at end of file diff --git a/src/decl.rs b/src/decl.rs index 3b14b98..d9f4dd0 100644 --- a/src/decl.rs +++ b/src/decl.rs @@ -91,7 +91,7 @@ pub enum ImportSpecifier<'a> { /// import {Thing} from './stuff.js'; /// import {People as Persons} from './places.js'; /// ``` - Normal(NormalImportSpec<'a>), + Normal(Vec>), /// A specifier that has been exported with the /// default keyword, this should not be wrapped in /// curly braces. diff --git a/src/lib.rs b/src/lib.rs index 26df553..4c77dbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,449 +1,387 @@ -#[cfg(feature = "serde")] -#[macro_use] -extern crate serde_derive; - -use std::borrow::Cow; - -pub mod decl; -pub mod expr; -pub mod pat; -pub mod stmt; -#[cfg(feature = "esprima")] -pub mod serde; - -use decl::Decl; -use expr::{Expr, Lit, Prop}; -use pat::Pat; -use stmt::Stmt; - -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct Ident<'a> { - pub name: Cow<'a, str>, -} - -impl<'a> Ident<'a> { - pub fn new(s: String) -> Self { - Ident { - name: Cow::Owned(s) - } - } - pub fn from(s: &'a str) -> Self { - Ident { - name: Cow::Borrowed(s) - } - } -} - -/// A fully parsed javascript program. -/// -/// It is essentially a collection of `ProgramPart`s -/// with a flag denoting if the representation is -/// a ES6 Mod or a Script. -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum Program<'a> { - /// An ES6 Mod - Mod(Vec>), - /// Not an ES6 Mod - Script(Vec>), -} - -impl<'a> Program<'a> { - pub fn module(parts: Vec>) -> Self { - Program::Mod(parts) - } - pub fn script(parts: Vec>) -> Self { - Program::Script(parts) - } -} - -/// A single part of a Javascript program. -/// This will be either a Directive, Decl or a Stmt -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] -pub enum ProgramPart<'a> { - /// A Directive like `'use strict';` - Dir(Dir<'a>), - /// A variable, function or module declaration - Decl(Decl<'a>), - /// Any other kind of statement - Stmt(Stmt<'a>), -} - -impl<'a> ProgramPart<'a> { - pub fn decl(inner: Decl<'a>) -> Self { - ProgramPart::Decl(inner) - } - pub fn stmt(inner: Stmt<'a>) -> Self { - ProgramPart::Stmt(inner) - } -} - -/// pretty much always `'use strict'`, this can appear at the -/// top of a file or function -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct Dir<'a> { - pub expr: Lit<'a>, - pub dir: Cow<'a, str>, -} - -/// A function, this will be part of either a function -/// declaration (ID is required) or a function expression -/// (ID is optional) -/// ```js -/// //function declaration -/// function thing() {} -/// //function expressions -/// var x = function() {} -/// let y = function q() {} -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct Func<'a> { - pub id: Option>, - pub params: Vec>, - pub body: FuncBody<'a>, - pub generator: bool, - pub is_async: bool, -} - -impl<'a> Func<'a> { - pub fn new( - id: Option>, - params: Vec>, - body: FuncBody<'a>, - generator: bool, - is_async: bool - ) -> Self { - Func { - id, - params, - body, - generator, - is_async, - } - } -} - -/// A single function argument from a function signature -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] -pub enum FuncArg<'a> { - Expr(Expr<'a>), - Pat(Pat<'a>), -} - -impl<'a> FuncArg<'a> { - pub fn expr(expr: Expr) -> FuncArg { - FuncArg::Expr(expr) - } - pub fn pat(pat: Pat) -> FuncArg { - FuncArg::Pat(pat) - } -} - -/// The block statement that makes up the function's body -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct FuncBody<'a>(pub Vec>); -/// A way to declare object templates -/// ```js -/// class Thing { -/// constructor() { -/// this._a = 0; -/// } -/// stuff() { -/// return 'stuff' -/// } -/// set a(value) { -/// if (value > 100) { -/// this._a = 0; -/// } else { -/// this._a = value; -/// } -/// } -/// get a() { -/// return this._a; -/// } -/// } -/// let y = class { -/// constructor() { -/// this.a = 100; -/// } -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct Class<'a> { - pub id: Option>, - pub super_class: Option>>, - pub body: ClassBody<'a>, -} -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct ClassBody<'a>(pub Vec>); - -impl<'a> Class<'a> { - pub fn new(id: Option>, super_class: Option>, body: Vec>) -> Class<'a> { - Class { - id, - super_class: super_class.map(Box::new), - body: ClassBody(body), - } - } -} - -/// The kind of variable being defined (`var`/`let`/`const`) -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(rename_all = "camelCase", untagged))] -pub enum VarKind { - Var, - Let, - Const, -} - - -/// The available operators for assignment Exprs -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum AssignOp { - Equal, - PlusEqual, - MinusEqual, - TimesEqual, - DivEqual, - ModEqual, - LeftShiftEqual, - RightShiftEqual, - UnsignedRightShiftEqual, - OrEqual, - XOrEqual, - AndEqual, - PowerOfEqual, -} - - -/// The available logical operators -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum LogicalOp { - Or, - And, -} - - -/// The available operations for `Binary` Exprs -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum BinaryOp { - Equal, - NotEqual, - StrictEqual, - StrictNotEqual, - LessThan, - GreaterThan, - LessThanEqual, - GreaterThanEqual, - LeftShift, - RightShift, - UnsignedRightShift, - Plus, - Minus, - Times, - Over, - Mod, - Or, - XOr, - And, - In, - InstanceOf, - PowerOf, -} - - -/// `++` or `--` -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum UpdateOp { - Increment, - Decrement, -} - -/// The allowed operators for an Expr -/// to be `Unary` -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum UnaryOp { - Minus, - Plus, - Not, - Tilde, - TypeOf, - Void, - Delete, -} - - -/// A flag for determining what kind of property -#[derive(Debug, Clone, PartialEq, Copy)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum PropKind { - /// A property with a value - Init, - /// A method with the get keyword - Get, - /// A method with the set keyword - Set, - /// A constructor - Ctor, - /// A standard method - Method, -} - -pub mod prelude { - pub use crate::{ - AssignOp, - BinaryOp, - Class, - ClassBody, - Dir, - Func, - FuncArg, - FuncBody, - Ident, - LogicalOp, - Program, - ProgramPart, - PropKind, - UnaryOp, - UpdateOp, - VarKind, - }; - pub use crate::expr::{ - ArrayExpr, - ArrowFuncBody, - ArrowFuncExpr, - AssignExpr, - AssignLeft, - BinaryExpr, - CallExpr, - ConditionalExpr, - Expr, - Lit, - LogicalExpr, - MemberExpr, - MetaProp, - NewExpr, - ObjExpr, - ObjProp, - Prop, - PropKey, - PropValue, - RegEx, - StringLit, - TaggedTemplateExpr, - TemplateElement, - TemplateLit, - UnaryExpr, - UpdateExpr, - YieldExpr, - }; - pub use crate::decl::{ - Decl, - VarDecl, - ModDecl, - ModImport, - ImportSpecifier, - ModExport, - NamedExportDecl, - DefaultExportDecl, - ExportSpecifier, - NormalImportSpec - }; - pub use crate::stmt::{ - Stmt, - WithStmt, - LabeledStmt, - IfStmt, - SwitchStmt, - SwitchCase, - BlockStmt, - TryStmt, - CatchClause, - WhileStmt, - DoWhileStmt, - ForStmt, - LoopInit, - ForInStmt, - ForOfStmt, - LoopLeft, - }; - pub use crate::pat::{ - ArrayPatPart, - AssignPat, - ObjPat, - ObjPatPart, - Pat, - }; -} \ No newline at end of file +#[cfg(feature = "serde")] +#[macro_use] +extern crate serde_derive; + +use std::borrow::Cow; + +pub mod decl; +pub mod expr; +pub mod pat; +#[cfg(feature = "esprima")] +pub mod serde; +pub mod spanned; +pub mod stmt; + +use decl::Decl; +use expr::{Expr, Lit, Prop}; +use pat::Pat; +use stmt::Stmt; + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct Ident<'a> { + pub name: Cow<'a, str>, +} + +impl<'a> Ident<'a> { + pub fn new(s: String) -> Self { + Ident { + name: Cow::Owned(s), + } + } + pub fn from(s: &'a str) -> Self { + Ident { + name: Cow::Borrowed(s), + } + } +} + +/// A fully parsed javascript program. +/// +/// It is essentially a collection of `ProgramPart`s +/// with a flag denoting if the representation is +/// a ES6 Mod or a Script. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum Program<'a> { + /// An ES6 Mod + Mod(Vec>), + /// Not an ES6 Mod + Script(Vec>), +} + +impl<'a> Program<'a> { + pub fn module(parts: Vec>) -> Self { + Program::Mod(parts) + } + pub fn script(parts: Vec>) -> Self { + Program::Script(parts) + } +} + +/// A single part of a Javascript program. +/// This will be either a Directive, Decl or a Stmt +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] +pub enum ProgramPart<'a> { + /// A Directive like `'use strict';` + Dir(Dir<'a>), + /// A variable, function or module declaration + Decl(Decl<'a>), + /// Any other kind of statement + Stmt(Stmt<'a>), +} + +impl<'a> ProgramPart<'a> { + pub fn decl(inner: Decl<'a>) -> Self { + ProgramPart::Decl(inner) + } + pub fn stmt(inner: Stmt<'a>) -> Self { + ProgramPart::Stmt(inner) + } +} + +/// pretty much always `'use strict'`, this can appear at the +/// top of a file or function +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct Dir<'a> { + pub expr: Lit<'a>, + pub dir: Cow<'a, str>, +} + +/// A function, this will be part of either a function +/// declaration (ID is required) or a function expression +/// (ID is optional) +/// ```js +/// //function declaration +/// function thing() {} +/// //function expressions +/// var x = function() {} +/// let y = function q() {} +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct Func<'a> { + pub id: Option>, + pub params: Vec>, + pub body: FuncBody<'a>, + pub generator: bool, + pub is_async: bool, +} + +impl<'a> Func<'a> { + pub fn new( + id: Option>, + params: Vec>, + body: FuncBody<'a>, + generator: bool, + is_async: bool, + ) -> Self { + Func { + id, + params, + body, + generator, + is_async, + } + } +} + +/// A single function argument from a function signature +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] +pub enum FuncArg<'a> { + Expr(Expr<'a>), + Pat(Pat<'a>), +} + +impl<'a> FuncArg<'a> { + pub fn expr(expr: Expr) -> FuncArg { + FuncArg::Expr(expr) + } + pub fn pat(pat: Pat) -> FuncArg { + FuncArg::Pat(pat) + } +} + +/// The block statement that makes up the function's body +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct FuncBody<'a>(pub Vec>); +/// A way to declare object templates +/// ```js +/// class Thing { +/// constructor() { +/// this._a = 0; +/// } +/// stuff() { +/// return 'stuff' +/// } +/// set a(value) { +/// if (value > 100) { +/// this._a = 0; +/// } else { +/// this._a = value; +/// } +/// } +/// get a() { +/// return this._a; +/// } +/// } +/// let y = class { +/// constructor() { +/// this.a = 100; +/// } +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct Class<'a> { + pub id: Option>, + pub super_class: Option>>, + pub body: ClassBody<'a>, +} +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct ClassBody<'a>(pub Vec>); + +impl<'a> Class<'a> { + pub fn new( + id: Option>, + super_class: Option>, + body: Vec>, + ) -> Class<'a> { + Class { + id, + super_class: super_class.map(Box::new), + body: ClassBody(body), + } + } +} + +/// The kind of variable being defined (`var`/`let`/`const`) +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +#[cfg_attr( + all(feature = "serde", feature = "esprima"), + serde(rename_all = "camelCase", untagged) +)] +pub enum VarKind { + Var, + Let, + Const, +} + +/// The available operators for assignment Exprs +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum AssignOp { + Equal, + PlusEqual, + MinusEqual, + TimesEqual, + DivEqual, + ModEqual, + LeftShiftEqual, + RightShiftEqual, + UnsignedRightShiftEqual, + OrEqual, + XOrEqual, + AndEqual, + PowerOfEqual, +} + +/// The available logical operators +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum LogicalOp { + Or, + And, +} + +/// The available operations for `Binary` Exprs +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum BinaryOp { + Equal, + NotEqual, + StrictEqual, + StrictNotEqual, + LessThan, + GreaterThan, + LessThanEqual, + GreaterThanEqual, + LeftShift, + RightShift, + UnsignedRightShift, + Plus, + Minus, + Times, + Over, + Mod, + Or, + XOr, + And, + In, + InstanceOf, + PowerOf, +} + +/// `++` or `--` +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum UpdateOp { + Increment, + Decrement, +} + +/// The allowed operators for an Expr +/// to be `Unary` +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum UnaryOp { + Minus, + Plus, + Not, + Tilde, + TypeOf, + Void, + Delete, +} + +/// A flag for determining what kind of property +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum PropKind { + /// A property with a value + Init, + /// A method with the get keyword + Get, + /// A method with the set keyword + Set, + /// A constructor + Ctor, + /// A standard method + Method, +} + +pub mod prelude { + pub use crate::decl::{ + Decl, DefaultExportDecl, ExportSpecifier, ImportSpecifier, ModDecl, ModExport, ModImport, + NamedExportDecl, NormalImportSpec, VarDecl, + }; + pub use crate::expr::{ + ArrayExpr, ArrowFuncBody, ArrowFuncExpr, AssignExpr, AssignLeft, BinaryExpr, CallExpr, + ConditionalExpr, Expr, Lit, LogicalExpr, MemberExpr, MetaProp, NewExpr, ObjExpr, ObjProp, + Prop, PropKey, PropValue, RegEx, StringLit, TaggedTemplateExpr, TemplateElement, + TemplateLit, UnaryExpr, UpdateExpr, YieldExpr, + }; + pub use crate::pat::{ArrayPatPart, AssignPat, ObjPat, ObjPatPart, Pat}; + pub use crate::stmt::{ + BlockStmt, CatchClause, DoWhileStmt, ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, + LoopInit, LoopLeft, Stmt, SwitchCase, SwitchStmt, TryStmt, WhileStmt, WithStmt, + }; + pub use crate::{ + AssignOp, BinaryOp, Class, ClassBody, Dir, Func, FuncArg, FuncBody, Ident, LogicalOp, + Program, ProgramPart, PropKind, UnaryOp, UpdateOp, VarKind, + }; +} diff --git a/src/pat.rs b/src/pat.rs index 9ece1d8..1b702ad 100644 --- a/src/pat.rs +++ b/src/pat.rs @@ -1,57 +1,55 @@ -use crate::expr::{Expr, Prop}; -use crate::{Ident}; -/// All of the different ways you can declare an identifier -/// and/or value -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] -pub enum Pat<'a> { - Ident(Ident<'a>), - Obj(ObjPat<'a>), - Array(Vec>>), - RestElement(Box>), - Assign(AssignPat<'a>), -} - -impl<'a> Pat<'a> { - pub fn ident_from(s: &'a str) -> Self { - Pat::Ident( - Ident::from(s) - ) - } -} - -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] -pub enum ArrayPatPart<'a> { - Pat(Pat<'a>), - Expr(Expr<'a>), -} - -/// similar to an `ObjectExpr` -pub type ObjPat<'a> = Vec>; -/// A single part of an ObjectPat -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] -pub enum ObjPatPart<'a> { - Assign(Prop<'a>), - Rest(Box>), -} - -/// An assignment as a pattern -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct AssignPat<'a> { - pub left: Box>, - pub right: Box>, -} +use crate::expr::{Expr, Prop}; +use crate::Ident; +/// All of the different ways you can declare an identifier +/// and/or value +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] +pub enum Pat<'a> { + Ident(Ident<'a>), + Obj(ObjPat<'a>), + Array(Vec>>), + RestElement(Box>), + Assign(AssignPat<'a>), +} + +impl<'a> Pat<'a> { + pub fn ident_from(s: &'a str) -> Self { + Pat::Ident(Ident::from(s)) + } +} + +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] +pub enum ArrayPatPart<'a> { + Pat(Pat<'a>), + Expr(Expr<'a>), +} + +/// similar to an `ObjectExpr` +pub type ObjPat<'a> = Vec>; +/// A single part of an ObjectPat +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +#[cfg_attr(all(feature = "serde", feature = "esprima"), serde(untagged))] +pub enum ObjPatPart<'a> { + Assign(Prop<'a>), + Rest(Box>), +} + +/// An assignment as a pattern +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct AssignPat<'a> { + pub left: Box>, + pub right: Box>, +} diff --git a/src/serde/ser.rs b/src/serde/ser.rs index f4bee53..7f54632 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -1,1245 +1,1244 @@ -use crate::prelude::*; -use serde::ser::{Serialize, SerializeStruct, Serializer}; - -impl<'a> Serialize for Program<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "Program")?; - match self { - Program::Script(ref body) => { - state.serialize_field("sourceType", "script")?; - state.serialize_field("body", &body)?; - } - Program::Mod(ref body) => { - state.serialize_field("sourceType", "module")?; - state.serialize_field("body", body)?; - } - } - state.end() - } -} - -impl<'a> Serialize for ProgramPart<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - ProgramPart::Decl(ref d) => d.serialize(serializer), - ProgramPart::Dir(ref d) => d.serialize(serializer), - ProgramPart::Stmt(ref s) => s.serialize(serializer), - } - } -} -impl<'a> Serialize for Dir<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "ExpressionStatement")?; - state.serialize_field("expression", &self.expr)?; - if let Lit::String(ref sl) = self.expr { - match sl { - StringLit::Double(ref s) => { - if !s.is_empty() { - state.serialize_field("directive", &self.dir)?; - } - } - StringLit::Single(ref s) => { - if !s.is_empty() { - state.serialize_field("directive", &self.dir)?; - } - } - } - } - state.end() - } -} -impl<'a> Serialize for Decl<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Decl::Func(ref f) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "FunctionDeclaration")?; - state.serialize_field("id", &f.id)?; - state.serialize_field("body", &f.body)?; - state.serialize_field("generator", &f.generator)?; - state.serialize_field("async", &f.is_async)?; - state.serialize_field("expression", &false)?; - state.serialize_field("params", &f.params)?; - state.end() - } - Decl::Class(ref c) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "ClassDeclaration")?; - state.serialize_field("body", &c.body)?; - state.serialize_field("id", &c.id)?; - state.serialize_field("superClass", &c.super_class)?; - state.end() - } - Decl::Var(ref kind, ref vs) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "VariableDeclaration")?; - state.serialize_field("kind", kind)?; - state.serialize_field("declarations", vs)?; - state.end() - } - Decl::Import(ref imp) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ImportDeclaration")?; - state.serialize_field("specifiers", &imp.specifiers)?; - state.serialize_field("source", &imp.source)?; - state.end() - } - Decl::Export(ref exp) => exp.serialize(serializer), - } - } -} - -impl<'a> Serialize for ModExport<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - ModExport::All(ref source) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ExportAllDeclaration")?; - state.serialize_field("source", source)?; - state.end() - } - ModExport::Default(ref def) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ExportDefaultDeclaration")?; - state.serialize_field("declaration", def)?; - state.end() - } - ModExport::Named(ref named) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ExportNamedDeclaration")?; - match named { - NamedExportDecl::Decl(ref d) => { - let specs: Vec<()> = Vec::new(); - let source: Option<()> = None; - state.serialize_field("declaration", d)?; - state.serialize_field("specifiers", &specs)?; - state.serialize_field("source", &source)?; - } - NamedExportDecl::Specifier(ref specs, source) => { - let decl: Option<()> = None; - state.serialize_field("declaration", &decl)?; - state.serialize_field("specifiers", specs)?; - state.serialize_field("source", source)?; - } - } - state.end() - } - } - } -} - -impl<'a> Serialize for DefaultExportDecl<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - DefaultExportDecl::Decl(ref d) => d.serialize(serializer), - DefaultExportDecl::Expr(ref e) => e.serialize(serializer), - } - } -} - -impl<'a> Serialize for ImportSpecifier<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - ImportSpecifier::Default(ref d) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ImportDefaultSpecifier")?; - state.serialize_field("local", d)?; - state.end() - } - ImportSpecifier::Namespace(ref n) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ImportNamespaceSpecifier")?; - state.serialize_field("local", n)?; - state.end() - } - ImportSpecifier::Normal(ref n) => { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ImportSpecifier")?; - state.serialize_field("local", &n.local)?; - state.serialize_field("imported", &n.imported)?; - state.end() - } - } - } -} -impl<'a> Serialize for ExportSpecifier<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ExportSpecifier")?; - state.serialize_field("exported", &self.exported)?; - state.serialize_field("local", &self.local)?; - state.end() - } -} -impl<'a> Serialize for FuncArg<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - FuncArg::Expr(ref ex) => ex.serialize(serializer), - FuncArg::Pat(ref pat) => pat.serialize(serializer), - } - } -} - -impl<'a> Serialize for ClassBody<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 7)?; - state.serialize_field("type", "ClassBody")?; - let body: Vec = self.0.iter().map(MethodDef).collect(); - state.serialize_field("body", &body)?; - state.end() - } -} - -impl Serialize for VarKind { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = match self { - VarKind::Const => "const", - VarKind::Let => "let", - VarKind::Var => "var", - }; - serializer.serialize_str(s) - } -} -impl<'a> Serialize for Ident<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "Identifier")?; - let unescaped = unescaper(&self.name).unwrap_or_else(|| self.name.to_string()); - state.serialize_field("name", &unescaped)?; - state.end() - } -} - -impl<'a> Serialize for VarDecl<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "VariableDeclarator")?; - state.serialize_field("id", &self.id)?; - state.serialize_field("init", &self.init)?; - state.end() - } -} - -impl<'a> Serialize for FuncBody<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "BlockStatement")?; - state.serialize_field("body", &self.0)?; - state.end() - } -} - -impl<'a> Serialize for Stmt<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Stmt::Labeled(ref l) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "LabeledStatement")?; - state.serialize_field("label", &l.label)?; - state.serialize_field("body", &l.body)?; - state.end() - } - Stmt::Block(ref b) => b.serialize(serializer), - Stmt::Break(ref b) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "BreakStatement")?; - state.serialize_field("label", &b)?; - state.end() - } - Stmt::Continue(ref c) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "ContinueStatement")?; - state.serialize_field("label", &c)?; - state.end() - } - Stmt::Debugger => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "DebuggerStatement")?; - state.end() - } - Stmt::DoWhile(ref d) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "DoWhileStatement")?; - state.serialize_field("test", &d.test)?; - state.serialize_field("body", &d.body)?; - state.end() - } - Stmt::Empty => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "EmptyStatement")?; - state.end() - } - Stmt::Expr(ref e) => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "ExpressionStatement")?; - state.serialize_field("expression", e)?; - state.end() - } - Stmt::For(ref f) => { - let mut state = serializer.serialize_struct("Node", 5)?; - state.serialize_field("type", "ForStatement")?; - state.serialize_field("init", &f.init)?; - state.serialize_field("test", &f.test)?; - state.serialize_field("update", &f.update)?; - state.serialize_field("body", &f.body)?; - state.end() - } - Stmt::ForIn(ref f) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "ForInStatement")?; - state.serialize_field("left", &f.left)?; - state.serialize_field("right", &f.right)?; - state.serialize_field("body", &f.body)?; - state.serialize_field("each", &false)?; - state.end() - } - Stmt::ForOf(ref f) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "ForOfStatement")?; - state.serialize_field("left", &f.left)?; - state.serialize_field("right", &f.right)?; - state.serialize_field("body", &f.body)?; - state.end() - } - Stmt::If(ref f) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "IfStatement")?; - state.serialize_field("test", &f.test)?; - state.serialize_field("consequent", &f.consequent)?; - state.serialize_field("alternate", &f.alternate)?; - state.end() - } - Stmt::Return(ref r) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "ReturnStatement")?; - state.serialize_field("argument", r)?; - state.end() - } - Stmt::Switch(ref s) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "SwitchStatement")?; - state.serialize_field("discriminant", &s.discriminant)?; - state.serialize_field("cases", &s.cases)?; - state.end() - } - Stmt::Throw(ref t) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "ThrowStatement")?; - state.serialize_field("argument", t)?; - state.end() - } - Stmt::Try(ref t) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "TryStatement")?; - state.serialize_field("block", &t.block)?; - state.serialize_field("handler", &t.handler)?; - state.serialize_field("finalizer", &t.finalizer)?; - state.end() - } - Stmt::Var(ref v) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "VariableStatement")?; - state.serialize_field("decls", &v)?; - state.end() - } - Stmt::While(ref w) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "WhileStatement")?; - state.serialize_field("test", &w.test)?; - state.serialize_field("body", &w.body)?; - state.end() - } - Stmt::With(ref w) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "WithStatement")?; - state.serialize_field("object", &w.object)?; - state.serialize_field("body", &w.body)?; - state.end() - } - } - } -} - -impl<'a> Serialize for Lit<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Lit::Number(ref n) => serialize_number(serializer, n), - Lit::String(ref sl) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "Literal")?; - let (quote, value) = match sl { - StringLit::Double(ref s) => ('"', s), - StringLit::Single(ref s) => ('\'', s), - }; - let quoted = format!("{0}{1}{0}", quote, value); - let inner = if let Some(esc) = unescaper("ed) { - if esc.trim_matches(quote) != "\n" { - esc - } else { - format!("{0}{0}", quote) - } - } else { - value.to_string() - }; - state.serialize_field("value", &inner[1..inner.len() - 1])?; - state.serialize_field("raw", "ed)?; - state.end() - } - Lit::RegEx(ref r) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "Literal")?; - state.serialize_field("raw", &format!("/{}/{}", r.pattern, r.flags))?; - state.serialize_field("value", &format_regex_value(r))?; - state.serialize_field("regex", r)?; - state.end() - } - Lit::Null => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "Literal")?; - state.serialize_field("raw", "null")?; - let value: Option<()> = None; - state.serialize_field("value", &value)?; - state.end() - } - Lit::Boolean(ref b) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "Literal")?; - let raw = if *b { "true" } else { "false" }; - state.serialize_field("raw", raw)?; - state.serialize_field("value", b)?; - state.end() - } - Lit::Template(ref t) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "TemplateLiteral")?; - state.serialize_field("quasis", &t.quasis)?; - state.serialize_field("expressions", &t.expressions)?; - state.end() - } - } - } -} - -fn format_regex_value(r: &RegEx) -> String { - let mut ret = String::from("/"); - let mut escaped = false; - for c in r.pattern.chars() { - if c == '\\' { - escaped = true; - ret.push(c); - } else if !escaped && c == '/' { - ret.push_str(r"\/"); - } else { - ret.push(c); - } - } - ret.push('/'); - if r.flags.is_empty() { - return ret; - } - if r.flags.contains('g') { - ret.push('g'); - } - if r.flags.contains('i') { - ret.push('i'); - } - if r.flags.contains('m') { - ret.push('m'); - } - if r.flags.contains('u') { - ret.push('u') - } - if r.flags.contains('y') { - ret.push('y') - } - ret.push_str(&r.flags.replace( - |c| c == 'g' || c == 'i' || c == 'm' || c == 'u' || c == 'y', - "", - )); - ret -} - -fn serialize_number(s: S, n: &str) -> Result -where - S: Serializer, -{ - let mut state = s.serialize_struct("Node", 3)?; - state.serialize_field("type", "Literal")?; - state.serialize_field("raw", &n)?; - if n.starts_with("0") { - if n.len() == 1 { - serialize_int(&mut state, 10, n)?; - } else if n[1..2].eq_ignore_ascii_case("x") { - serialize_int(&mut state, 16, &n[2..])?; - } else if n[1..2].eq_ignore_ascii_case("o") { - serialize_int(&mut state, 8, &n[2..])?; - } else if n[1..2].eq_ignore_ascii_case("b") { - serialize_int(&mut state, 2, &n[2..])?; - } else if n.chars().all(|c| c.is_digit(8)) { - serialize_int(&mut state, 8, n)?; - } else if n.contains('E') || n.contains('e') || n.contains('.') { - serialize_float(&mut state, n)?; - } else { - serialize_int(&mut state, 10, n)?; - } - } else if n.contains('E') || n.contains('e') || n.contains('.') { - serialize_float(&mut state, n)?; - } else { - serialize_int(&mut state, 10, n)?; - } - state.end() -} - -fn serialize_int(state: &mut T, radix: u32, n: &str) -> Result<(), T::Error> -where - T: SerializeStruct, -{ - if let Ok(value) = i128::from_str_radix(n, radix) { - if value < ::std::i32::MAX as i128 { - state.serialize_field("value", &(value as i32)) - } else { - state.serialize_field("value", &(value as f64)) - } - } else { - state.serialize_field("value", &::std::f32::NAN) - } -} -fn serialize_float(state: &mut T, n: &str) -> Result<(), T::Error> -where - T: SerializeStruct, -{ - if let Ok(value) = n.parse::() { - if value % 1.0 == 0.0 { - state.serialize_field("value", &(value as i32)) - } else { - state.serialize_field("value", &value) - } - } else { - state.serialize_field("value", &::std::f32::NAN) - } -} - -impl<'a> Serialize for Expr<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Expr::Array(ref a) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "ArrayExpression")?; - state.serialize_field("elements", a)?; - state.end() - } - Expr::ArrowFunc(ref a) => { - let mut state = serializer.serialize_struct("Node", 6)?; - state.serialize_field("type", "ArrowFunctionExpression")?; - state.serialize_field("id", &a.id)?; - state.serialize_field("expression", &a.expression)?; - state.serialize_field("generator", &a.generator)?; - state.serialize_field("params", &a.params)?; - state.serialize_field("async", &a.is_async)?; - match a.body { - ArrowFuncBody::Expr(ref e) => { - state.serialize_field("body", e)?; - } - ArrowFuncBody::FuncBody(ref b) => { - state.serialize_field("body", b)?; - } - } - state.end() - } - Expr::ArrowParamPlaceHolder(_, _) => { - unreachable!("ArrowParamPlaceHolder Expression should never be returned by the parsing process"); - } - Expr::Assign(ref a) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "AssignmentExpression")?; - state.serialize_field("left", &a.left)?; - state.serialize_field("operator", &a.operator)?; - state.serialize_field("right", &a.right)?; - state.end() - } - Expr::Await(ref a) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "AwaitExpression")?; - state.serialize_field("expression", a)?; - state.end() - } - Expr::Binary(ref b) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "BinaryExpression")?; - state.serialize_field("left", &b.left)?; - state.serialize_field("operator", &b.operator)?; - state.serialize_field("right", &b.right)?; - state.end() - } - Expr::Call(ref c) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "CallExpression")?; - state.serialize_field("callee", &c.callee)?; - state.serialize_field("arguments", &c.arguments)?; - state.end() - } - Expr::Class(ref c) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "ClassExpression")?; - state.serialize_field("id", &c.id)?; - state.serialize_field("superClass", &c.super_class)?; - state.serialize_field("body", &c.body)?; - state.end() - } - Expr::Conditional(ref c) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "ConditionalExpression")?; - state.serialize_field("test", &c.test)?; - state.serialize_field("consequent", &c.consequent)?; - state.serialize_field("alternate", &c.alternate)?; - state.end() - } - Expr::Func(ref f) => { - let mut state = serializer.serialize_struct("Node", 6)?; - state.serialize_field("type", "FunctionExpression")?; - state.serialize_field("id", &f.id)?; - state.serialize_field("params", &f.params)?; - state.serialize_field("body", &f.body)?; - state.serialize_field("generator", &f.generator)?; - state.serialize_field("async", &f.is_async)?; - state.serialize_field("expression", &false)?; - state.end() - } - Expr::Ident(ref i) => i.serialize(serializer), - Expr::Lit(ref l) => l.serialize(serializer), - Expr::Logical(ref l) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "LogicalExpression")?; - state.serialize_field("left", &l.left)?; - state.serialize_field("operator", &l.operator)?; - state.serialize_field("right", &l.right)?; - state.end() - } - Expr::Member(ref m) => { - let mut state = serializer.serialize_struct("Node", 4)?; - state.serialize_field("type", "MemberExpression")?; - state.serialize_field("object", &m.object)?; - state.serialize_field("property", &m.property)?; - state.serialize_field("computed", &m.computed)?; - state.end() - } - Expr::MetaProp(ref m) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "MetaProperty")?; - state.serialize_field("meta", &m.meta)?; - state.serialize_field("property", &m.property)?; - state.end() - } - Expr::New(ref n) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "NewExpression")?; - state.serialize_field("callee", &n.callee)?; - state.serialize_field("arguments", &n.arguments)?; - state.end() - } - Expr::Obj(ref o) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "ObjectExpression")?; - state.serialize_field("properties", o)?; - state.end() - } - Expr::Sequence(ref s) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "SequenceExpression")?; - state.serialize_field("expressions", s)?; - state.end() - } - Expr::Spread(ref s) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "SpreadElement")?; - state.serialize_field("argument", s)?; - state.end() - } - Expr::Super => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "Super")?; - state.end() - } - Expr::TaggedTemplate(ref t) => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "TaggedTemplateExpression")?; - state.serialize_field("tag", &t.tag)?; - state.serialize_field("quasi", &t.quasi)?; - state.end() - } - Expr::This => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "ThisExpression")?; - state.end() - } - Expr::Unary(ref u) => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "UnaryExpression")?; - state.serialize_field("argument", &u.argument)?; - state.serialize_field("operator", &u.operator)?; - state.serialize_field("prefix", &u.prefix)?; - state.end() - } - Expr::Update(ref u) => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "UpdateExpression")?; - state.serialize_field("argument", &u.argument)?; - state.serialize_field("operator", &u.operator)?; - state.serialize_field("prefix", &u.prefix)?; - state.end() - } - Expr::Yield(ref y) => { - let mut state = serializer.serialize_struct("Node", 1)?; - state.serialize_field("type", "YieldExpression")?; - state.serialize_field("argument", &y.argument)?; - state.serialize_field("delegate", &y.delegate)?; - state.end() - } - } - } -} - -impl<'a> Serialize for Pat<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Pat::Array(ref a) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "ArrayPattern")?; - state.serialize_field("elements", a)?; - state.end() - } - Pat::Assign(ref a) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "AssignmentPattern")?; - state.serialize_field("left", &a.left)?; - state.serialize_field("right", &a.right)?; - state.end() - } - Pat::Ident(ref i) => i.serialize(serializer), - Pat::Obj(ref o) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "ObjectPattern")?; - state.serialize_field("properties", o)?; - state.end() - } - Pat::RestElement(ref r) => { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "RestElement")?; - state.serialize_field("argument", r)?; - state.end() - } - } - } -} - -impl Serialize for PropKind { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - PropKind::Ctor => serializer.serialize_str("constructor"), - PropKind::Get => serializer.serialize_str("get"), - PropKind::Init => serializer.serialize_str("init"), - PropKind::Method => serializer.serialize_str("method"), - PropKind::Set => serializer.serialize_str("set"), - } - } -} - -impl<'a> Serialize for Prop<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "Property")?; - state.serialize_field("key", &self.key)?; - state.serialize_field("computed", &self.computed)?; - state.serialize_field("kind", &self.kind)?; - state.serialize_field("method", &self.method)?; - state.serialize_field("shorthand", &self.short_hand)?; - if self.short_hand && self.value == PropValue::None { - state.serialize_field("value", &self.key)?; - } else { - state.serialize_field("value", &self.value)?; - } - if self.is_static { - state.serialize_field("static", &self.is_static)?; - } - state.end() - } -} - -struct MethodDef<'a>(pub &'a Prop<'a>); - -impl<'a> Serialize for MethodDef<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "MethodDefinition")?; - state.serialize_field("static", &self.0.is_static)?; - state.serialize_field("static", &self.0.is_static)?; - state.serialize_field("value", &self.0.value)?; - state.serialize_field("computed", &self.0.computed)?; - state.serialize_field("kind", &self.0.kind)?; - state.serialize_field("key", &self.0.key)?; - state.end() - } -} - -impl<'a> Serialize for TemplateLit<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "TemplateLiteral")?; - state.serialize_field("expressions", &self.expressions)?; - state.serialize_field("quasis", &self.quasis)?; - state.end() - } -} - -impl<'a> Serialize for TemplateElement<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "TemplateElement")?; - state.serialize_field("tail", &self.tail)?; - let mut value = ::std::collections::HashMap::new(); - let cooked = if let Some(s) = unescaper(&self.cooked) { - s - } else { - self.cooked.to_string() - }; - value.insert("cooked", cooked.as_str()); - let end_len = if self.raw.ends_with("${") { 2 } else { 1 }; - value.insert("raw", &self.raw[1..self.raw.len() - end_len]); - state.serialize_field("value", &value)?; - state.end() - } -} -impl<'a> Serialize for BlockStmt<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "BlockStatement")?; - state.serialize_field("body", &self.0)?; - state.end() - } -} - -impl<'a> Serialize for CatchClause<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 2)?; - state.serialize_field("type", "CatchClause")?; - state.serialize_field("param", &self.param)?; - state.serialize_field("body", &self.body)?; - state.end() - } -} -impl<'a> Serialize for SwitchCase<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "SwitchCase")?; - state.serialize_field("test", &self.test)?; - state.serialize_field("consequent", &self.consequent)?; - state.end() - } -} - -impl Serialize for AssignOp { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = match self { - AssignOp::Equal => "=", - AssignOp::PlusEqual => "+=", - AssignOp::MinusEqual => "-=", - AssignOp::TimesEqual => "*=", - AssignOp::DivEqual => "/=", - AssignOp::ModEqual => "%=", - AssignOp::LeftShiftEqual => "<<=", - AssignOp::RightShiftEqual => ">>=", - AssignOp::UnsignedRightShiftEqual => ">>>=", - AssignOp::OrEqual => "|=", - AssignOp::XOrEqual => "^=", - AssignOp::AndEqual => "&=", - AssignOp::PowerOfEqual => "**=", - }; - serializer.serialize_str(s) - } -} -impl Serialize for BinaryOp { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = match self { - BinaryOp::Equal => "==", - BinaryOp::NotEqual => "!=", - BinaryOp::StrictEqual => "===", - BinaryOp::StrictNotEqual => "!==", - BinaryOp::LessThan => "<", - BinaryOp::GreaterThan => ">", - BinaryOp::LessThanEqual => "<=", - BinaryOp::GreaterThanEqual => ">=", - BinaryOp::LeftShift => "<<", - BinaryOp::RightShift => ">>", - BinaryOp::UnsignedRightShift => ">>>", - BinaryOp::Plus => "+", - BinaryOp::Minus => "-", - BinaryOp::Times => "*", - BinaryOp::Over => "/", - BinaryOp::Mod => "%", - BinaryOp::Or => "|", - BinaryOp::XOr => "^", - BinaryOp::And => "&", - BinaryOp::In => "in", - BinaryOp::InstanceOf => "instanceof", - BinaryOp::PowerOf => "**", - }; - serializer.serialize_str(s) - } -} -impl Serialize for LogicalOp { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = match self { - LogicalOp::And => "&&", - LogicalOp::Or => "||", - }; - serializer.serialize_str(s) - } -} -impl Serialize for UnaryOp { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = match self { - UnaryOp::Minus => "-", - UnaryOp::Plus => "+", - UnaryOp::Not => "!", - UnaryOp::Tilde => "~", - UnaryOp::TypeOf => "typeof", - UnaryOp::Void => "void", - UnaryOp::Delete => "delete", - }; - serializer.serialize_str(s) - } -} -impl Serialize for UpdateOp { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = match self { - UpdateOp::Increment => "++", - UpdateOp::Decrement => "--", - }; - serializer.serialize_str(s) - } -} -impl<'a> Serialize for LoopLeft<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - LoopLeft::Expr(ref e) => e.serialize(serializer), - LoopLeft::Pat(ref p) => p.serialize(serializer), - LoopLeft::Variable(ref kind, ref v) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "VariableDeclaration")?; - state.serialize_field("kind", kind)?; - state.serialize_field("declarations", &[v])?; - state.end() - } - } - } -} -impl<'a> Serialize for LoopInit<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - LoopInit::Expr(ref e) => e.serialize(serializer), - LoopInit::Variable(ref kind, ref v) => { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "VariableDeclaration")?; - state.serialize_field("kind", kind)?; - state.serialize_field("declarations", v)?; - state.end() - } - } - } -} -impl<'a> Serialize for AssignPat<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Node", 3)?; - state.serialize_field("type", "AssignmentPattern")?; - state.serialize_field("left", &self.left)?; - state.serialize_field("right", &self.right)?; - state.end() - } -} - -use std::collections::VecDeque; -fn unescaper(s: &str) -> Option { - let mut queue: VecDeque<_> = String::from(s).chars().collect(); - let mut s = String::new(); - - while let Some(c) = queue.pop_front() { - if c != '\\' { - s.push(c); - continue; - } - - match queue.pop_front() { - Some('b') => s.push('\u{0008}'), - Some('f') => s.push('\u{000C}'), - Some('n') => s.push('\n'), - Some('r') => s.push('\r'), - Some('t') => s.push('\t'), - Some('v') => s.push('\u{000b}'), - Some('\'') => s.push('\''), - Some('\"') => s.push('\"'), - Some('\\') => s.push('\\'), - Some('u') => { - if let Some(x) = unescape_unicode(&mut queue) { - s.push(x); - } else { - return None; - } - } - Some('x') => { - if let Some(x) = unescape_byte(&mut queue) { - s.push(x) - } else { - return None; - } - } - Some('\0') => s.push('\0'), - Some(c) => { - if c.is_digit(8) { - if let Some(x) = unescape_octal(c, &mut queue) { - s.push(x); - } else { - return None; - } - } else { - s.push(c) - } - } - _ => return None, - }; - } - - Some(s) -} - -fn unescape_unicode(queue: &mut VecDeque) -> Option { - let ret = hex_char_code(queue)?; - ::std::char::from_u32(ret) -} - -fn hex_char_code(queue: &mut VecDeque) -> Option { - if let Some(c) = queue.pop_front() { - if c == '{' { - let mut x = 0; - while let Some(c) = queue.pop_front() { - if c == '}' { - break; - } - x = x * 16 + c.to_digit(16)?; - } - Some(x) - } else { - let mut x = c.to_digit(16)?; - for _ in 0..3 { - if let Some(u) = queue.pop_front() { - x = x * 16 + u.to_digit(16)?; - } - } - if x >= 0xD800 && x <= 0xDBFF { - debug_assert!(queue.pop_front() == Some('\\')); - debug_assert!(queue.pop_front() == Some('u')); - let high = (x - 0xD800) * 0x400; - let low = hex_char_code(queue)? - 0xDC00; - x = 0x10000 + high + low; - } - Some(x) - } - } else { - None - } -} - -fn unescape_byte(queue: &mut VecDeque) -> Option { - let mut s = String::new(); - - for _ in 0..2 { - if let Some(c) = queue.pop_front() { - s.push(c) - } else { - return None; - } - } - match u32::from_str_radix(&s, 16) { - Ok(u) => ::std::char::from_u32(u), - Err(e) => { - panic!("{}", e); - } - } -} - -fn unescape_octal(c: char, queue: &mut VecDeque) -> Option { - let (ret, ct) = if let Some(next) = queue.get(0) { - if !next.is_digit(8) { - let d = c.to_digit(8)?; - return std::char::from_u32(d); - } else if c >= '0' && c < '4' { - let s = if let Some(third) = queue.get(1) { - if !third.is_digit(8) { - format!("{}{}", c, next) - } else { - format!("{}{}{}", c, next, third) - } - } else { - format!("{}{}", c, next) - }; - let ct = s.len().saturating_sub(1); - match u32::from_str_radix(&s, 8) { - Ok(r) => (::std::char::from_u32(r), ct), - Err(e) => panic!("{}", e), - } - } else { - match u32::from_str_radix(&format!("{}{}", c, next), 8) { - Ok(r) => (::std::char::from_u32(r), 1), - Err(e) => panic!("{}", e), - } - } - } else { - (Some(c), 0) - }; - for _ in 0..ct { - let _ = queue.pop_front(); - } - ret -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn four_hundred() { - let js = r#""\1\00\400\000\""#; - let expectation = "\"\u{1}\u{0} 0\u{0}\""; - assert_eq!(unescaper(js).unwrap(), expectation.to_string()); - } - #[test] - fn escape_lots() { - let js = "\"\\'\\\"\\\\\\b\\f\\n\\r\\t\\v\\0\""; - let expectation = "\"'\"\\\u{8}\u{c}\n\r\t\u{b}\u{0}\""; - assert_eq!(unescaper(js).unwrap(), expectation.to_string()); - } - - #[test] - fn escaped_new_line() { - let js = r#""\\\n""#; - let expectation = "\"\\\n\""; - assert_eq!(unescaper(js).unwrap(), expectation.to_string()); - } - - #[test] - fn unicode_ident() { - let js = "φ"; - let expectation = "φ"; - assert_eq!(unescaper(js).unwrap(), expectation.to_string()); - } - - #[test] - fn unicode_string() { - let js = r#""\uD834\uDF06\u2603\u03C6 \u{0000001F4a9}\u{1D306}\u{2603}\u{3c6} 𝌆☃φ""#; - let expectation = "\"𝌆☃φ 💩𝌆☃φ 𝌆☃φ\""; - assert_eq!(unescaper(js).unwrap(), expectation.to_string()); - } - -} +use crate::prelude::*; +use serde::ser::{Serialize, SerializeStruct, Serializer}; + +impl<'a> Serialize for Program<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "Program")?; + match self { + Program::Script(ref body) => { + state.serialize_field("sourceType", "script")?; + state.serialize_field("body", &body)?; + } + Program::Mod(ref body) => { + state.serialize_field("sourceType", "module")?; + state.serialize_field("body", body)?; + } + } + state.end() + } +} + +impl<'a> Serialize for ProgramPart<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + ProgramPart::Decl(ref d) => d.serialize(serializer), + ProgramPart::Dir(ref d) => d.serialize(serializer), + ProgramPart::Stmt(ref s) => s.serialize(serializer), + } + } +} +impl<'a> Serialize for Dir<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "ExpressionStatement")?; + state.serialize_field("expression", &self.expr)?; + if let Lit::String(ref sl) = self.expr { + match sl { + StringLit::Double(ref s) => { + if !s.is_empty() { + state.serialize_field("directive", &self.dir)?; + } + } + StringLit::Single(ref s) => { + if !s.is_empty() { + state.serialize_field("directive", &self.dir)?; + } + } + } + } + state.end() + } +} +impl<'a> Serialize for Decl<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Decl::Func(ref f) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "FunctionDeclaration")?; + state.serialize_field("id", &f.id)?; + state.serialize_field("body", &f.body)?; + state.serialize_field("generator", &f.generator)?; + state.serialize_field("async", &f.is_async)?; + state.serialize_field("expression", &false)?; + state.serialize_field("params", &f.params)?; + state.end() + } + Decl::Class(ref c) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "ClassDeclaration")?; + state.serialize_field("body", &c.body)?; + state.serialize_field("id", &c.id)?; + state.serialize_field("superClass", &c.super_class)?; + state.end() + } + Decl::Var(ref kind, ref vs) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "VariableDeclaration")?; + state.serialize_field("kind", kind)?; + state.serialize_field("declarations", vs)?; + state.end() + } + Decl::Import(ref imp) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ImportDeclaration")?; + state.serialize_field("specifiers", &imp.specifiers)?; + state.serialize_field("source", &imp.source)?; + state.end() + } + Decl::Export(ref exp) => exp.serialize(serializer), + } + } +} + +impl<'a> Serialize for ModExport<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + ModExport::All(ref source) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ExportAllDeclaration")?; + state.serialize_field("source", source)?; + state.end() + } + ModExport::Default(ref def) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ExportDefaultDeclaration")?; + state.serialize_field("declaration", def)?; + state.end() + } + ModExport::Named(ref named) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ExportNamedDeclaration")?; + match named { + NamedExportDecl::Decl(ref d) => { + let specs: Vec<()> = Vec::new(); + let source: Option<()> = None; + state.serialize_field("declaration", d)?; + state.serialize_field("specifiers", &specs)?; + state.serialize_field("source", &source)?; + } + NamedExportDecl::Specifier(ref specs, source) => { + let decl: Option<()> = None; + state.serialize_field("declaration", &decl)?; + state.serialize_field("specifiers", specs)?; + state.serialize_field("source", source)?; + } + } + state.end() + } + } + } +} + +impl<'a> Serialize for DefaultExportDecl<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + DefaultExportDecl::Decl(ref d) => d.serialize(serializer), + DefaultExportDecl::Expr(ref e) => e.serialize(serializer), + } + } +} + +impl<'a> Serialize for ImportSpecifier<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + ImportSpecifier::Default(ref d) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ImportDefaultSpecifier")?; + state.serialize_field("local", d)?; + state.end() + } + ImportSpecifier::Namespace(ref n) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ImportNamespaceSpecifier")?; + state.serialize_field("local", n)?; + state.end() + } + ImportSpecifier::Normal(ref n) => { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ImportSpecifier")?; + // state.serialize_field("local", &n.)?; + // state.serialize_field("imported", &n.imported)?; + state.end() + } + } + } +} +impl<'a> Serialize for ExportSpecifier<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ExportSpecifier")?; + state.serialize_field("exported", &self.exported)?; + state.serialize_field("local", &self.local)?; + state.end() + } +} +impl<'a> Serialize for FuncArg<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + FuncArg::Expr(ref ex) => ex.serialize(serializer), + FuncArg::Pat(ref pat) => pat.serialize(serializer), + } + } +} + +impl<'a> Serialize for ClassBody<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 7)?; + state.serialize_field("type", "ClassBody")?; + let body: Vec = self.0.iter().map(MethodDef).collect(); + state.serialize_field("body", &body)?; + state.end() + } +} + +impl Serialize for VarKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + VarKind::Const => "const", + VarKind::Let => "let", + VarKind::Var => "var", + }; + serializer.serialize_str(s) + } +} +impl<'a> Serialize for Ident<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "Identifier")?; + let unescaped = unescaper(&self.name).unwrap_or_else(|| self.name.to_string()); + state.serialize_field("name", &unescaped)?; + state.end() + } +} + +impl<'a> Serialize for VarDecl<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "VariableDeclarator")?; + state.serialize_field("id", &self.id)?; + state.serialize_field("init", &self.init)?; + state.end() + } +} + +impl<'a> Serialize for FuncBody<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "BlockStatement")?; + state.serialize_field("body", &self.0)?; + state.end() + } +} + +impl<'a> Serialize for Stmt<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Stmt::Labeled(ref l) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "LabeledStatement")?; + state.serialize_field("label", &l.label)?; + state.serialize_field("body", &l.body)?; + state.end() + } + Stmt::Block(ref b) => b.serialize(serializer), + Stmt::Break(ref b) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "BreakStatement")?; + state.serialize_field("label", &b)?; + state.end() + } + Stmt::Continue(ref c) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "ContinueStatement")?; + state.serialize_field("label", &c)?; + state.end() + } + Stmt::Debugger => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "DebuggerStatement")?; + state.end() + } + Stmt::DoWhile(ref d) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "DoWhileStatement")?; + state.serialize_field("test", &d.test)?; + state.serialize_field("body", &d.body)?; + state.end() + } + Stmt::Empty => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "EmptyStatement")?; + state.end() + } + Stmt::Expr(ref e) => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "ExpressionStatement")?; + state.serialize_field("expression", e)?; + state.end() + } + Stmt::For(ref f) => { + let mut state = serializer.serialize_struct("Node", 5)?; + state.serialize_field("type", "ForStatement")?; + state.serialize_field("init", &f.init)?; + state.serialize_field("test", &f.test)?; + state.serialize_field("update", &f.update)?; + state.serialize_field("body", &f.body)?; + state.end() + } + Stmt::ForIn(ref f) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "ForInStatement")?; + state.serialize_field("left", &f.left)?; + state.serialize_field("right", &f.right)?; + state.serialize_field("body", &f.body)?; + state.serialize_field("each", &false)?; + state.end() + } + Stmt::ForOf(ref f) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "ForOfStatement")?; + state.serialize_field("left", &f.left)?; + state.serialize_field("right", &f.right)?; + state.serialize_field("body", &f.body)?; + state.end() + } + Stmt::If(ref f) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "IfStatement")?; + state.serialize_field("test", &f.test)?; + state.serialize_field("consequent", &f.consequent)?; + state.serialize_field("alternate", &f.alternate)?; + state.end() + } + Stmt::Return(ref r) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "ReturnStatement")?; + state.serialize_field("argument", r)?; + state.end() + } + Stmt::Switch(ref s) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "SwitchStatement")?; + state.serialize_field("discriminant", &s.discriminant)?; + state.serialize_field("cases", &s.cases)?; + state.end() + } + Stmt::Throw(ref t) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "ThrowStatement")?; + state.serialize_field("argument", t)?; + state.end() + } + Stmt::Try(ref t) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "TryStatement")?; + state.serialize_field("block", &t.block)?; + state.serialize_field("handler", &t.handler)?; + state.serialize_field("finalizer", &t.finalizer)?; + state.end() + } + Stmt::Var(ref v) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "VariableStatement")?; + state.serialize_field("decls", &v)?; + state.end() + } + Stmt::While(ref w) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "WhileStatement")?; + state.serialize_field("test", &w.test)?; + state.serialize_field("body", &w.body)?; + state.end() + } + Stmt::With(ref w) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "WithStatement")?; + state.serialize_field("object", &w.object)?; + state.serialize_field("body", &w.body)?; + state.end() + } + } + } +} + +impl<'a> Serialize for Lit<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Lit::Number(ref n) => serialize_number(serializer, n), + Lit::String(ref sl) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "Literal")?; + let (quote, value) = match sl { + StringLit::Double(ref s) => ('"', s), + StringLit::Single(ref s) => ('\'', s), + }; + let quoted = format!("{0}{1}{0}", quote, value); + let inner = if let Some(esc) = unescaper("ed) { + if esc.trim_matches(quote) != "\n" { + esc + } else { + format!("{0}{0}", quote) + } + } else { + value.to_string() + }; + state.serialize_field("value", &inner[1..inner.len() - 1])?; + state.serialize_field("raw", "ed)?; + state.end() + } + Lit::RegEx(ref r) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "Literal")?; + state.serialize_field("raw", &format!("/{}/{}", r.pattern, r.flags))?; + state.serialize_field("value", &format_regex_value(r))?; + state.serialize_field("regex", r)?; + state.end() + } + Lit::Null => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "Literal")?; + state.serialize_field("raw", "null")?; + let value: Option<()> = None; + state.serialize_field("value", &value)?; + state.end() + } + Lit::Boolean(ref b) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "Literal")?; + let raw = if *b { "true" } else { "false" }; + state.serialize_field("raw", raw)?; + state.serialize_field("value", b)?; + state.end() + } + Lit::Template(ref t) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "TemplateLiteral")?; + state.serialize_field("quasis", &t.quasis)?; + state.serialize_field("expressions", &t.expressions)?; + state.end() + } + } + } +} + +fn format_regex_value(r: &RegEx) -> String { + let mut ret = String::from("/"); + let mut escaped = false; + for c in r.pattern.chars() { + if c == '\\' { + escaped = true; + ret.push(c); + } else if !escaped && c == '/' { + ret.push_str(r"\/"); + } else { + ret.push(c); + } + } + ret.push('/'); + if r.flags.is_empty() { + return ret; + } + if r.flags.contains('g') { + ret.push('g'); + } + if r.flags.contains('i') { + ret.push('i'); + } + if r.flags.contains('m') { + ret.push('m'); + } + if r.flags.contains('u') { + ret.push('u') + } + if r.flags.contains('y') { + ret.push('y') + } + ret.push_str(&r.flags.replace( + |c| c == 'g' || c == 'i' || c == 'm' || c == 'u' || c == 'y', + "", + )); + ret +} + +fn serialize_number(s: S, n: &str) -> Result +where + S: Serializer, +{ + let mut state = s.serialize_struct("Node", 3)?; + state.serialize_field("type", "Literal")?; + state.serialize_field("raw", &n)?; + if n.starts_with("0") { + if n.len() == 1 { + serialize_int(&mut state, 10, n)?; + } else if n[1..2].eq_ignore_ascii_case("x") { + serialize_int(&mut state, 16, &n[2..])?; + } else if n[1..2].eq_ignore_ascii_case("o") { + serialize_int(&mut state, 8, &n[2..])?; + } else if n[1..2].eq_ignore_ascii_case("b") { + serialize_int(&mut state, 2, &n[2..])?; + } else if n.chars().all(|c| c.is_digit(8)) { + serialize_int(&mut state, 8, n)?; + } else if n.contains('E') || n.contains('e') || n.contains('.') { + serialize_float(&mut state, n)?; + } else { + serialize_int(&mut state, 10, n)?; + } + } else if n.contains('E') || n.contains('e') || n.contains('.') { + serialize_float(&mut state, n)?; + } else { + serialize_int(&mut state, 10, n)?; + } + state.end() +} + +fn serialize_int(state: &mut T, radix: u32, n: &str) -> Result<(), T::Error> +where + T: SerializeStruct, +{ + if let Ok(value) = i128::from_str_radix(n, radix) { + if value < ::std::i32::MAX as i128 { + state.serialize_field("value", &(value as i32)) + } else { + state.serialize_field("value", &(value as f64)) + } + } else { + state.serialize_field("value", &::std::f32::NAN) + } +} +fn serialize_float(state: &mut T, n: &str) -> Result<(), T::Error> +where + T: SerializeStruct, +{ + if let Ok(value) = n.parse::() { + if value % 1.0 == 0.0 { + state.serialize_field("value", &(value as i32)) + } else { + state.serialize_field("value", &value) + } + } else { + state.serialize_field("value", &::std::f32::NAN) + } +} + +impl<'a> Serialize for Expr<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Expr::Array(ref a) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "ArrayExpression")?; + state.serialize_field("elements", a)?; + state.end() + } + Expr::ArrowFunc(ref a) => { + let mut state = serializer.serialize_struct("Node", 6)?; + state.serialize_field("type", "ArrowFunctionExpression")?; + state.serialize_field("id", &a.id)?; + state.serialize_field("expression", &a.expression)?; + state.serialize_field("generator", &a.generator)?; + state.serialize_field("params", &a.params)?; + state.serialize_field("async", &a.is_async)?; + match a.body { + ArrowFuncBody::Expr(ref e) => { + state.serialize_field("body", e)?; + } + ArrowFuncBody::FuncBody(ref b) => { + state.serialize_field("body", b)?; + } + } + state.end() + } + Expr::ArrowParamPlaceHolder(_, _) => { + unreachable!("ArrowParamPlaceHolder Expression should never be returned by the parsing process"); + } + Expr::Assign(ref a) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "AssignmentExpression")?; + state.serialize_field("left", &a.left)?; + state.serialize_field("operator", &a.operator)?; + state.serialize_field("right", &a.right)?; + state.end() + } + Expr::Await(ref a) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "AwaitExpression")?; + state.serialize_field("expression", a)?; + state.end() + } + Expr::Binary(ref b) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "BinaryExpression")?; + state.serialize_field("left", &b.left)?; + state.serialize_field("operator", &b.operator)?; + state.serialize_field("right", &b.right)?; + state.end() + } + Expr::Call(ref c) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "CallExpression")?; + state.serialize_field("callee", &c.callee)?; + state.serialize_field("arguments", &c.arguments)?; + state.end() + } + Expr::Class(ref c) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "ClassExpression")?; + state.serialize_field("id", &c.id)?; + state.serialize_field("superClass", &c.super_class)?; + state.serialize_field("body", &c.body)?; + state.end() + } + Expr::Conditional(ref c) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "ConditionalExpression")?; + state.serialize_field("test", &c.test)?; + state.serialize_field("consequent", &c.consequent)?; + state.serialize_field("alternate", &c.alternate)?; + state.end() + } + Expr::Func(ref f) => { + let mut state = serializer.serialize_struct("Node", 6)?; + state.serialize_field("type", "FunctionExpression")?; + state.serialize_field("id", &f.id)?; + state.serialize_field("params", &f.params)?; + state.serialize_field("body", &f.body)?; + state.serialize_field("generator", &f.generator)?; + state.serialize_field("async", &f.is_async)?; + state.serialize_field("expression", &false)?; + state.end() + } + Expr::Ident(ref i) => i.serialize(serializer), + Expr::Lit(ref l) => l.serialize(serializer), + Expr::Logical(ref l) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "LogicalExpression")?; + state.serialize_field("left", &l.left)?; + state.serialize_field("operator", &l.operator)?; + state.serialize_field("right", &l.right)?; + state.end() + } + Expr::Member(ref m) => { + let mut state = serializer.serialize_struct("Node", 4)?; + state.serialize_field("type", "MemberExpression")?; + state.serialize_field("object", &m.object)?; + state.serialize_field("property", &m.property)?; + state.serialize_field("computed", &m.computed)?; + state.end() + } + Expr::MetaProp(ref m) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "MetaProperty")?; + state.serialize_field("meta", &m.meta)?; + state.serialize_field("property", &m.property)?; + state.end() + } + Expr::New(ref n) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "NewExpression")?; + state.serialize_field("callee", &n.callee)?; + state.serialize_field("arguments", &n.arguments)?; + state.end() + } + Expr::Obj(ref o) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "ObjectExpression")?; + state.serialize_field("properties", o)?; + state.end() + } + Expr::Sequence(ref s) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "SequenceExpression")?; + state.serialize_field("expressions", s)?; + state.end() + } + Expr::Spread(ref s) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "SpreadElement")?; + state.serialize_field("argument", s)?; + state.end() + } + Expr::Super => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "Super")?; + state.end() + } + Expr::TaggedTemplate(ref t) => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "TaggedTemplateExpression")?; + state.serialize_field("tag", &t.tag)?; + state.serialize_field("quasi", &t.quasi)?; + state.end() + } + Expr::This => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "ThisExpression")?; + state.end() + } + Expr::Unary(ref u) => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "UnaryExpression")?; + state.serialize_field("argument", &u.argument)?; + state.serialize_field("operator", &u.operator)?; + state.serialize_field("prefix", &u.prefix)?; + state.end() + } + Expr::Update(ref u) => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "UpdateExpression")?; + state.serialize_field("argument", &u.argument)?; + state.serialize_field("operator", &u.operator)?; + state.serialize_field("prefix", &u.prefix)?; + state.end() + } + Expr::Yield(ref y) => { + let mut state = serializer.serialize_struct("Node", 1)?; + state.serialize_field("type", "YieldExpression")?; + state.serialize_field("argument", &y.argument)?; + state.serialize_field("delegate", &y.delegate)?; + state.end() + } + } + } +} + +impl<'a> Serialize for Pat<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Pat::Array(ref a) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "ArrayPattern")?; + state.serialize_field("elements", a)?; + state.end() + } + Pat::Assign(ref a) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "AssignmentPattern")?; + state.serialize_field("left", &a.left)?; + state.serialize_field("right", &a.right)?; + state.end() + } + Pat::Ident(ref i) => i.serialize(serializer), + Pat::Obj(ref o) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "ObjectPattern")?; + state.serialize_field("properties", o)?; + state.end() + } + Pat::RestElement(ref r) => { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "RestElement")?; + state.serialize_field("argument", r)?; + state.end() + } + } + } +} + +impl Serialize for PropKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + PropKind::Ctor => serializer.serialize_str("constructor"), + PropKind::Get => serializer.serialize_str("get"), + PropKind::Init => serializer.serialize_str("init"), + PropKind::Method => serializer.serialize_str("method"), + PropKind::Set => serializer.serialize_str("set"), + } + } +} + +impl<'a> Serialize for Prop<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "Property")?; + state.serialize_field("key", &self.key)?; + state.serialize_field("computed", &self.computed)?; + state.serialize_field("kind", &self.kind)?; + state.serialize_field("method", &self.method)?; + state.serialize_field("shorthand", &self.short_hand)?; + if self.short_hand && self.value == PropValue::None { + state.serialize_field("value", &self.key)?; + } else { + state.serialize_field("value", &self.value)?; + } + if self.is_static { + state.serialize_field("static", &self.is_static)?; + } + state.end() + } +} + +struct MethodDef<'a>(pub &'a Prop<'a>); + +impl<'a> Serialize for MethodDef<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "MethodDefinition")?; + state.serialize_field("static", &self.0.is_static)?; + state.serialize_field("static", &self.0.is_static)?; + state.serialize_field("value", &self.0.value)?; + state.serialize_field("computed", &self.0.computed)?; + state.serialize_field("kind", &self.0.kind)?; + state.serialize_field("key", &self.0.key)?; + state.end() + } +} + +impl<'a> Serialize for TemplateLit<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "TemplateLiteral")?; + state.serialize_field("expressions", &self.expressions)?; + state.serialize_field("quasis", &self.quasis)?; + state.end() + } +} + +impl<'a> Serialize for TemplateElement<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "TemplateElement")?; + state.serialize_field("tail", &self.tail)?; + let mut value = ::std::collections::HashMap::new(); + let cooked = if let Some(s) = unescaper(&self.cooked) { + s + } else { + self.cooked.to_string() + }; + value.insert("cooked", cooked.as_str()); + let end_len = if self.raw.ends_with("${") { 2 } else { 1 }; + value.insert("raw", &self.raw[1..self.raw.len() - end_len]); + state.serialize_field("value", &value)?; + state.end() + } +} +impl<'a> Serialize for BlockStmt<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "BlockStatement")?; + state.serialize_field("body", &self.0)?; + state.end() + } +} + +impl<'a> Serialize for CatchClause<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 2)?; + state.serialize_field("type", "CatchClause")?; + state.serialize_field("param", &self.param)?; + state.serialize_field("body", &self.body)?; + state.end() + } +} +impl<'a> Serialize for SwitchCase<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "SwitchCase")?; + state.serialize_field("test", &self.test)?; + state.serialize_field("consequent", &self.consequent)?; + state.end() + } +} + +impl Serialize for AssignOp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + AssignOp::Equal => "=", + AssignOp::PlusEqual => "+=", + AssignOp::MinusEqual => "-=", + AssignOp::TimesEqual => "*=", + AssignOp::DivEqual => "/=", + AssignOp::ModEqual => "%=", + AssignOp::LeftShiftEqual => "<<=", + AssignOp::RightShiftEqual => ">>=", + AssignOp::UnsignedRightShiftEqual => ">>>=", + AssignOp::OrEqual => "|=", + AssignOp::XOrEqual => "^=", + AssignOp::AndEqual => "&=", + AssignOp::PowerOfEqual => "**=", + }; + serializer.serialize_str(s) + } +} +impl Serialize for BinaryOp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + BinaryOp::Equal => "==", + BinaryOp::NotEqual => "!=", + BinaryOp::StrictEqual => "===", + BinaryOp::StrictNotEqual => "!==", + BinaryOp::LessThan => "<", + BinaryOp::GreaterThan => ">", + BinaryOp::LessThanEqual => "<=", + BinaryOp::GreaterThanEqual => ">=", + BinaryOp::LeftShift => "<<", + BinaryOp::RightShift => ">>", + BinaryOp::UnsignedRightShift => ">>>", + BinaryOp::Plus => "+", + BinaryOp::Minus => "-", + BinaryOp::Times => "*", + BinaryOp::Over => "/", + BinaryOp::Mod => "%", + BinaryOp::Or => "|", + BinaryOp::XOr => "^", + BinaryOp::And => "&", + BinaryOp::In => "in", + BinaryOp::InstanceOf => "instanceof", + BinaryOp::PowerOf => "**", + }; + serializer.serialize_str(s) + } +} +impl Serialize for LogicalOp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + LogicalOp::And => "&&", + LogicalOp::Or => "||", + }; + serializer.serialize_str(s) + } +} +impl Serialize for UnaryOp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + UnaryOp::Minus => "-", + UnaryOp::Plus => "+", + UnaryOp::Not => "!", + UnaryOp::Tilde => "~", + UnaryOp::TypeOf => "typeof", + UnaryOp::Void => "void", + UnaryOp::Delete => "delete", + }; + serializer.serialize_str(s) + } +} +impl Serialize for UpdateOp { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + UpdateOp::Increment => "++", + UpdateOp::Decrement => "--", + }; + serializer.serialize_str(s) + } +} +impl<'a> Serialize for LoopLeft<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + LoopLeft::Expr(ref e) => e.serialize(serializer), + LoopLeft::Pat(ref p) => p.serialize(serializer), + LoopLeft::Variable(ref kind, ref v) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "VariableDeclaration")?; + state.serialize_field("kind", kind)?; + state.serialize_field("declarations", &[v])?; + state.end() + } + } + } +} +impl<'a> Serialize for LoopInit<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + LoopInit::Expr(ref e) => e.serialize(serializer), + LoopInit::Variable(ref kind, ref v) => { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "VariableDeclaration")?; + state.serialize_field("kind", kind)?; + state.serialize_field("declarations", v)?; + state.end() + } + } + } +} +impl<'a> Serialize for AssignPat<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Node", 3)?; + state.serialize_field("type", "AssignmentPattern")?; + state.serialize_field("left", &self.left)?; + state.serialize_field("right", &self.right)?; + state.end() + } +} + +use std::collections::VecDeque; +fn unescaper(s: &str) -> Option { + let mut queue: VecDeque<_> = String::from(s).chars().collect(); + let mut s = String::new(); + + while let Some(c) = queue.pop_front() { + if c != '\\' { + s.push(c); + continue; + } + + match queue.pop_front() { + Some('b') => s.push('\u{0008}'), + Some('f') => s.push('\u{000C}'), + Some('n') => s.push('\n'), + Some('r') => s.push('\r'), + Some('t') => s.push('\t'), + Some('v') => s.push('\u{000b}'), + Some('\'') => s.push('\''), + Some('\"') => s.push('\"'), + Some('\\') => s.push('\\'), + Some('u') => { + if let Some(x) = unescape_unicode(&mut queue) { + s.push(x); + } else { + return None; + } + } + Some('x') => { + if let Some(x) = unescape_byte(&mut queue) { + s.push(x) + } else { + return None; + } + } + Some('\0') => s.push('\0'), + Some(c) => { + if c.is_digit(8) { + if let Some(x) = unescape_octal(c, &mut queue) { + s.push(x); + } else { + return None; + } + } else { + s.push(c) + } + } + _ => return None, + }; + } + + Some(s) +} + +fn unescape_unicode(queue: &mut VecDeque) -> Option { + let ret = hex_char_code(queue)?; + ::std::char::from_u32(ret) +} + +fn hex_char_code(queue: &mut VecDeque) -> Option { + if let Some(c) = queue.pop_front() { + if c == '{' { + let mut x = 0; + while let Some(c) = queue.pop_front() { + if c == '}' { + break; + } + x = x * 16 + c.to_digit(16)?; + } + Some(x) + } else { + let mut x = c.to_digit(16)?; + for _ in 0..3 { + if let Some(u) = queue.pop_front() { + x = x * 16 + u.to_digit(16)?; + } + } + if x >= 0xD800 && x <= 0xDBFF { + debug_assert!(queue.pop_front() == Some('\\')); + debug_assert!(queue.pop_front() == Some('u')); + let high = (x - 0xD800) * 0x400; + let low = hex_char_code(queue)? - 0xDC00; + x = 0x10000 + high + low; + } + Some(x) + } + } else { + None + } +} + +fn unescape_byte(queue: &mut VecDeque) -> Option { + let mut s = String::new(); + + for _ in 0..2 { + if let Some(c) = queue.pop_front() { + s.push(c) + } else { + return None; + } + } + match u32::from_str_radix(&s, 16) { + Ok(u) => ::std::char::from_u32(u), + Err(e) => { + panic!("{}", e); + } + } +} + +fn unescape_octal(c: char, queue: &mut VecDeque) -> Option { + let (ret, ct) = if let Some(next) = queue.get(0) { + if !next.is_digit(8) { + let d = c.to_digit(8)?; + return std::char::from_u32(d); + } else if c >= '0' && c < '4' { + let s = if let Some(third) = queue.get(1) { + if !third.is_digit(8) { + format!("{}{}", c, next) + } else { + format!("{}{}{}", c, next, third) + } + } else { + format!("{}{}", c, next) + }; + let ct = s.len().saturating_sub(1); + match u32::from_str_radix(&s, 8) { + Ok(r) => (::std::char::from_u32(r), ct), + Err(e) => panic!("{}", e), + } + } else { + match u32::from_str_radix(&format!("{}{}", c, next), 8) { + Ok(r) => (::std::char::from_u32(r), 1), + Err(e) => panic!("{}", e), + } + } + } else { + (Some(c), 0) + }; + for _ in 0..ct { + let _ = queue.pop_front(); + } + ret +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn four_hundred() { + let js = r#""\1\00\400\000\""#; + let expectation = "\"\u{1}\u{0} 0\u{0}\""; + assert_eq!(unescaper(js).unwrap(), expectation.to_string()); + } + #[test] + fn escape_lots() { + let js = "\"\\'\\\"\\\\\\b\\f\\n\\r\\t\\v\\0\""; + let expectation = "\"'\"\\\u{8}\u{c}\n\r\t\u{b}\u{0}\""; + assert_eq!(unescaper(js).unwrap(), expectation.to_string()); + } + + #[test] + fn escaped_new_line() { + let js = r#""\\\n""#; + let expectation = "\"\\\n\""; + assert_eq!(unescaper(js).unwrap(), expectation.to_string()); + } + + #[test] + fn unicode_ident() { + let js = "φ"; + let expectation = "φ"; + assert_eq!(unescaper(js).unwrap(), expectation.to_string()); + } + + #[test] + fn unicode_string() { + let js = r#""\uD834\uDF06\u2603\u03C6 \u{0000001F4a9}\u{1D306}\u{2603}\u{3c6} 𝌆☃φ""#; + let expectation = "\"𝌆☃φ 💩𝌆☃φ 𝌆☃φ\""; + assert_eq!(unescaper(js).unwrap(), expectation.to_string()); + } +} diff --git a/src/spanned/decl.rs b/src/spanned/decl.rs new file mode 100644 index 0000000..0be1091 --- /dev/null +++ b/src/spanned/decl.rs @@ -0,0 +1,627 @@ +use crate::spanned::expr::{Expr, Lit}; +use crate::spanned::pat::Pat; +use crate::spanned::VarKind; +use crate::spanned::{Class, Func, Ident}; + +use super::{ListEntry, Node, Slice, SourceLocation}; + +/// The declaration of a variable, function, class, import or export +#[derive(Debug, Clone, PartialEq)] +pub enum Decl<'a> { + /// A variable declaration + /// ```js + /// var x, b; + /// let y, a = 0; + /// const q = 100 + /// ``` + Var { + decls: VarDecls<'a>, + semi_colon: Option>, + }, + /// A function declaration + /// ```js + /// function thing() {} + /// ``` + Func(Func<'a>), + /// A class declaration + /// ```js + /// class Thing {} + /// ``` + Class(Class<'a>), + /// An import declaration + /// ```js + /// import * as moment from 'moment'; + /// import Thing, {thing} from 'stuff'; + /// ``` + Import { + import: Box>, + semi_colon: Option>, + }, + /// An export declaration + /// ```js + /// export function thing() {} + /// ``` + Export { + export: Box>, + semi_colon: Option>, + }, +} + +impl<'a> From> for crate::decl::Decl<'a> { + fn from(other: Decl<'a>) -> Self { + match other { + Decl::Var { decls, .. } => Self::Var( + decls.keyword.into(), + decls.decls.into_iter().map(|e| e.item.into()).collect(), + ), + Decl::Func(inner) => Self::Func(inner.into()), + Decl::Class(inner) => Self::Class(inner.into()), + Decl::Import { import, .. } => Self::Import(Box::new(From::from(*import))), + Decl::Export { export, .. } => Self::Export(Box::new(From::from(*export))), + } + } +} + +impl<'a> Node for Decl<'a> { + fn loc(&self) -> super::SourceLocation { + match self { + Decl::Var { decls, semi_colon } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: decls.loc().start, + end: semi.loc.end, + }; + } + decls.loc() + } + Decl::Func(inner) => inner.loc(), + Decl::Class(inner) => inner.loc(), + Decl::Import { import, semi_colon } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: import.loc().start, + end: semi.loc.end, + }; + } + import.loc() + } + Decl::Export { export, semi_colon } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: export.loc().start, + end: semi.loc.end, + }; + } + export.loc() + } + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct VarDecls<'a> { + pub keyword: VarKind<'a>, + pub decls: Vec>>, +} + +impl<'a> Node for VarDecls<'a> { + fn loc(&self) -> SourceLocation { + if let Some(last) = self.decls.last() { + SourceLocation { + start: self.keyword.loc().start, + end: last.loc().end, + } + } else { + self.keyword.loc() + } + } +} + +/// The identifier and optional value of a variable declaration +#[derive(Debug, Clone, PartialEq)] +pub struct VarDecl<'a> { + pub id: Pat<'a>, + pub eq: Option>, + pub init: Option>, +} + +impl<'a> Node for VarDecl<'a> { + fn loc(&self) -> SourceLocation { + if let Some(init) = &self.init { + SourceLocation { + start: self.id.loc().start, + end: init.loc().end, + } + } else { + self.id.loc() + } + } +} + +impl<'a> From> for crate::decl::VarDecl<'a> { + fn from(other: VarDecl<'a>) -> Self { + Self { + id: other.id.into(), + init: other.init.map(From::from), + } + } +} + +/// A module declaration, This would only be available +/// in an ES Mod, it would be either an import or +/// export at the top level +#[derive(PartialEq, Debug, Clone)] +pub enum ModDecl<'a> { + Import(ModImport<'a>), + Export(ModExport<'a>), +} + +impl<'a> Node for ModDecl<'a> { + fn loc(&self) -> SourceLocation { + match self { + ModDecl::Import(inner) => inner.loc(), + ModDecl::Export(inner) => inner.loc(), + } + } +} + +/// A declaration that imports exported +/// members of another module +/// +/// ```js +/// import {Thing} from './stuff.js'; +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct ModImport<'a> { + pub keyword_import: Slice<'a>, + pub specifiers: Vec>>, + pub keyword_from: Option>, + pub source: Lit<'a>, +} + +impl<'a> Node for ModImport<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword_import.loc.start, + end: self.source.loc().end, + } + } +} + +impl<'a> From> for crate::decl::ModImport<'a> { + fn from(other: ModImport<'a>) -> Self { + Self { + source: other.source.into(), + specifiers: other + .specifiers + .into_iter() + .map(|e| e.item.into()) + .collect(), + } + } +} + +/// The name of the thing being imported +#[derive(Debug, Clone, PartialEq)] +pub enum ImportSpecifier<'a> { + /// A specifier in curly braces, this might + /// have a local alias + /// + /// ```js + /// import {Thing} from './stuff.js'; + /// // ^^^^^^^ + /// import {People as Persons, x} from './places.js'; + /// // ^^^^^^^^^^^^^^^^^^^^^^ + /// ``` + Normal(NormalImportSpecs<'a>), + /// A specifier that has been exported with the + /// default keyword, this should not be wrapped in + /// curly braces. + /// ```js + /// import DefaultThing from './stuff/js'; + /// ``` + Default(DefaultImportSpec<'a>), + /// Import all exported members from a module + /// in a namespace. + /// + /// ```js + /// import * as Moment from 'moment.js'; + /// ``` + Namespace(NamespaceImportSpec<'a>), +} + +impl<'a> Node for ImportSpecifier<'a> { + fn loc(&self) -> SourceLocation { + match self { + ImportSpecifier::Normal(inner) => inner.loc(), + ImportSpecifier::Default(inner) => inner.loc(), + ImportSpecifier::Namespace(inner) => inner.loc(), + } + } +} + +impl<'a> From> for crate::decl::ImportSpecifier<'a> { + fn from(other: ImportSpecifier<'a>) -> Self { + match other { + ImportSpecifier::Normal(inner) => { + Self::Normal(inner.specs.into_iter().map(|e| e.item.into()).collect()) + } + ImportSpecifier::Default(inner) => Self::Default(inner.into()), + ImportSpecifier::Namespace(inner) => Self::Namespace(inner.into()), + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct NormalImportSpecs<'a> { + pub open_brace: Slice<'a>, + pub specs: Vec>>, + pub close_brace: Slice<'a>, +} + +impl<'a> Node for NormalImportSpecs<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_brace.loc.start, + end: self.close_brace.loc.end, + } + } +} + +impl<'a> From> for crate::decl::NormalImportSpec<'a> { + fn from(other: NormalImportSpec<'a>) -> Self { + let imported: crate::Ident = other.imported.into(); + let local: crate::Ident = if let Some(alias) = other.alias { + alias.into() + } else { + imported.clone() + }; + Self { local, imported } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct NormalImportSpec<'a> { + pub imported: Ident<'a>, + pub alias: Option>, +} + +impl<'a> Node for NormalImportSpec<'a> { + fn loc(&self) -> SourceLocation { + if let Some(alias) = &self.alias { + SourceLocation { + start: self.imported.loc().start, + end: alias.loc().end, + } + } else { + self.imported.loc() + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct DefaultImportSpec<'a> { + pub id: Ident<'a>, +} + +impl<'a> From> for crate::Ident<'a> { + fn from(other: DefaultImportSpec<'a>) -> Self { + other.id.into() + } +} + +impl<'a> Node for DefaultImportSpec<'a> { + fn loc(&self) -> SourceLocation { + self.id.loc() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct NamespaceImportSpec<'a> { + pub star: Slice<'a>, + pub keyword: Slice<'a>, + pub ident: Ident<'a>, +} + +impl<'a> Node for NamespaceImportSpec<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.star.loc.start, + end: self.ident.loc().end, + } + } +} + +impl<'a> From> for crate::Ident<'a> { + fn from(other: NamespaceImportSpec<'a>) -> Self { + other.ident.into() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ModExport<'a> { + pub keyword: Slice<'a>, + pub spec: ModExportSpecifier<'a>, +} + +impl<'a> Node for ModExport<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.spec.loc().end, + } + } +} + +impl<'a> From> for crate::decl::ModExport<'a> { + fn from(other: ModExport<'a>) -> Self { + other.spec.into() + } +} + +/// Something exported from this module +#[derive(Debug, Clone, PartialEq)] +pub enum ModExportSpecifier<'a> { + /// ```js + /// export default function() {}; + /// //or + /// export default 1; + /// ``` + Default { + keyword: Slice<'a>, + value: DefaultExportDeclValue<'a>, + }, + ///```js + /// export {foo} from 'mod'; + /// //or + /// export {foo as bar} from 'mod'; + /// //or + /// export var foo = 1; + /// //or + /// export function bar() { + /// } + /// ``` + Named(NamedExportDecl<'a>), + /// ```js + /// export * from 'mod'; + /// ``` + All { + star: Slice<'a>, + keyword: Slice<'a>, + name: Lit<'a>, + }, +} + +impl<'a> Node for ModExportSpecifier<'a> { + fn loc(&self) -> SourceLocation { + match self { + ModExportSpecifier::Default { keyword, value } => SourceLocation { + start: keyword.loc.start, + end: value.loc().end, + }, + ModExportSpecifier::Named(inner) => inner.loc(), + ModExportSpecifier::All { star, name, .. } => SourceLocation { + start: star.loc.start, + end: name.loc().end, + }, + } + } +} + +impl<'a> From> for crate::decl::ModExport<'a> { + fn from(other: ModExportSpecifier<'a>) -> Self { + match other { + ModExportSpecifier::Default { keyword: _, value } => Self::Default(value.into()), + ModExportSpecifier::Named(inner) => Self::Named(inner.into()), + ModExportSpecifier::All { + star: _, + keyword: _, + name, + } => Self::All(name.into()), + } + } +} + +/// An export that has a name +/// ```js +/// export function thing() {} +/// export {stuff} from 'place'; +#[derive(PartialEq, Debug, Clone)] +pub enum NamedExportDecl<'a> { + Decl(Decl<'a>), + Specifier(NamedExportSpec<'a>), +} + +impl<'a> Node for NamedExportDecl<'a> { + fn loc(&self) -> SourceLocation { + match self { + NamedExportDecl::Decl(inner) => inner.loc(), + NamedExportDecl::Specifier(inner) => inner.loc(), + } + } +} + +impl<'a> From> for crate::decl::NamedExportDecl<'a> { + fn from(other: NamedExportDecl<'a>) -> Self { + match other { + NamedExportDecl::Decl(inner) => Self::Decl(inner.into()), + NamedExportDecl::Specifier(inner) => Self::Specifier( + inner + .list + .elements + .into_iter() + .map(|e| e.item.into()) + .collect(), + inner.source.map(|s| s.module.into()), + ), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct DefaultExportDecl<'a> { + pub keyword: Slice<'a>, + pub value: DefaultExportDeclValue<'a>, +} + +impl<'a> Node for DefaultExportDecl<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.value.loc().end, + } + } +} + +/// A default export +/// ```js +/// export default class Thing {} +/// ``` +#[derive(Debug, Clone, PartialEq)] +pub enum ExportDeclValue<'a> { + Decl(Decl<'a>), + Expr(Expr<'a>), + List(ExportList<'a>), +} + +impl<'a> Node for ExportDeclValue<'a> { + fn loc(&self) -> SourceLocation { + match self { + ExportDeclValue::Decl(inner) => inner.loc(), + ExportDeclValue::Expr(inner) => inner.loc(), + ExportDeclValue::List(inner) => inner.loc(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DefaultExportDeclValue<'a> { + Decl(Decl<'a>), + Expr(Expr<'a>), +} + +impl<'a> Node for DefaultExportDeclValue<'a> { + fn loc(&self) -> SourceLocation { + match self { + Self::Decl(inner) => inner.loc(), + Self::Expr(inner) => inner.loc(), + } + } +} + +impl<'a> From> for crate::decl::DefaultExportDecl<'a> { + fn from(other: DefaultExportDeclValue<'a>) -> Self { + match other { + DefaultExportDeclValue::Decl(inner) => Self::Decl(inner.into()), + DefaultExportDeclValue::Expr(inner) => Self::Expr(inner.into()), + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct NamedExportSpec<'a> { + pub list: ExportList<'a>, + pub source: Option>, +} + +impl<'a> Node for NamedExportSpec<'a> { + fn loc(&self) -> SourceLocation { + if let Some(source) = &self.source { + SourceLocation { + start: self.list.loc().start, + end: source.loc().end, + } + } else { + self.list.loc() + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct NamedExportSource<'a> { + pub keyword_from: Slice<'a>, + pub module: Lit<'a>, +} + +impl<'a> Node for NamedExportSource<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword_from.loc.start, + end: self.module.loc().end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ExportList<'a> { + pub open_brace: Slice<'a>, + pub elements: Vec>>, + pub close_brace: Slice<'a>, +} + +impl<'a> Node for ExportList<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_brace.loc.start, + end: self.close_brace.loc.end, + } + } +} + +/// The name of the thing being exported +/// this might include an alias +/// ```js +/// //no-alias +/// export {Thing} from 'place'; +/// //aliased +/// export {Stuff as NewThing} from 'place' +/// ``` +#[derive(Debug, Clone, PartialEq)] +pub struct ExportSpecifier<'a> { + pub local: Ident<'a>, + pub alias: Option>, +} + +impl<'a> Node for ExportSpecifier<'a> { + fn loc(&self) -> SourceLocation { + if let Some(alias) = &self.alias { + SourceLocation { + start: self.local.loc().start, + end: alias.loc().end, + } + } else { + self.local.loc() + } + } +} + +impl<'a> From> for crate::decl::ExportSpecifier<'a> { + fn from(other: ExportSpecifier<'a>) -> Self { + let local: crate::Ident = other.local.into(); + Self { + local: local.clone(), + exported: other.alias.map(|a| a.ident.into()).unwrap_or(local), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Alias<'a> { + pub keyword: Slice<'a>, + pub ident: Ident<'a>, +} + +impl<'a> Node for Alias<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.ident.loc().end, + } + } +} + +impl<'a> From> for crate::Ident<'a> { + fn from(other: Alias<'a>) -> Self { + other.ident.into() + } +} diff --git a/src/spanned/expr.rs b/src/spanned/expr.rs new file mode 100644 index 0000000..4f719f6 --- /dev/null +++ b/src/spanned/expr.rs @@ -0,0 +1,1424 @@ +use std::borrow::Cow; + +use crate::spanned::pat::Pat; +use crate::spanned::{AssignOp, BinaryOp, LogicalOp, UnaryOp, UpdateOp}; +use crate::spanned::{Class, Func, FuncArg, FuncBody, Ident}; + +use super::{FuncArgEntry, ListEntry, Node, Position, Slice, SourceLocation}; + +/// A slightly more granular program part that a statement +#[derive(Debug, Clone, PartialEq)] +pub enum Expr<'a> { + /// `[0,,]` + Array(ArrayExpr<'a>), + /// An arrow function + /// ```js + /// () => console.log(); + /// x => { + /// return x; + /// } + /// ``` + ArrowFunc(ArrowFuncExpr<'a>), + /// Used for resolving possible sequence expressions + /// that are arrow parameters + ArrowParamPlaceHolder(ArrowParamPlaceHolder<'a>), + /// Assignment or update assignment + /// ```js + /// a = 0 + /// b += 1 + /// ``` + Assign(AssignExpr<'a>), + /// The `await` keyword followed by another `Expr` + Await(Box>), + /// An operation that has two arguments + Binary(BinaryExpr<'a>), + /// A class expression see `Class` + Class(Box>), + /// Calling a function or method + Call(CallExpr<'a>), + /// A ternery expression + Conditional(ConditionalExpr<'a>), + /// see `Function` + Func(Func<'a>), + /// An identifier + Ident(Ident<'a>), + /// A literal value, see `Literal` + Lit(Lit<'a>), + /// A specialized `BinaryExpr` for logical evaluation + /// ```js + /// true && true + /// false || true + /// ``` + Logical(LogicalExpr<'a>), + /// Accessing the member of a value + /// ```js + /// b['thing']; + /// c.stuff; + /// ``` + Member(MemberExpr<'a>), + /// currently just `new.target` + MetaProp(MetaProp<'a>), + /// ```js + /// var a = true ? 'stuff' : 'things'; + /// ``` + /// `{}` + /// Calling a constructor + New(NewExpr<'a>), + Obj(ObjExpr<'a>), + /// Any sequence of expressions separated with a comma + Sequence(SequenceExpr<'a>), + /// `...` followed by an `Expr` + Spread(Box>), + /// `super` + Super(Slice<'a>), + /// A template literal preceded by a tag function identifier + TaggedTemplate(TaggedTemplateExpr<'a>), + /// `this` + This(Slice<'a>), + /// An operation that has one argument + /// ```js + /// typeof 'a'; + /// +9; + /// ``` + Unary(UnaryExpr<'a>), + /// Increment or decrement + /// ```js + /// 1++ + /// --2 + /// ``` + Update(UpdateExpr<'a>), + Wrapped(Box>), + /// yield a value from inside of a generator function + Yield(YieldExpr<'a>), +} + +impl<'a> From> for crate::Expr<'a> { + fn from(other: Expr<'a>) -> Self { + match other { + Expr::Array(inner) => Self::Array( + inner + .elements + .into_iter() + .map(|e| e.item.map(From::from)) + .collect(), + ), + Expr::ArrowFunc(inner) => Self::ArrowFunc(inner.into()), + Expr::ArrowParamPlaceHolder(inner) => Self::ArrowParamPlaceHolder( + inner.args.into_iter().map(|e| From::from(e.item)).collect(), + inner.keyword.is_some(), + ), + Expr::Assign(inner) => Self::Assign(inner.into()), + Expr::Await(inner) => Self::Await(Box::new(inner.expr.into())), + Expr::Binary(inner) => Self::Binary(inner.into()), + Expr::Class(inner) => Self::Class((*inner).into()), + Expr::Call(inner) => Self::Call(inner.into()), + Expr::Conditional(inner) => Self::Conditional(inner.into()), + Expr::Func(inner) => Self::Func(inner.into()), + Expr::Ident(inner) => Self::Ident(inner.into()), + Expr::Lit(inner) => Self::Lit(inner.into()), + Expr::Logical(inner) => Self::Logical(inner.into()), + Expr::Member(inner) => Self::Member(inner.into()), + Expr::MetaProp(inner) => Self::MetaProp(inner.into()), + Expr::New(inner) => Self::New(inner.into()), + Expr::Obj(inner) => Self::Obj(inner.props.into_iter().map(|e| e.item.into()).collect()), + Expr::Sequence(inner) => { + Self::Sequence(inner.into_iter().map(|e| e.item.into()).collect()) + } + Expr::Spread(inner) => Self::Spread(Box::new(inner.expr.into())), + Expr::Super(_) => Self::Super, + Expr::TaggedTemplate(inner) => Self::TaggedTemplate(inner.into()), + Expr::This(_) => Self::This, + Expr::Unary(inner) => Self::Unary(inner.into()), + Expr::Update(inner) => Self::Update(inner.into()), + Expr::Yield(inner) => Self::Yield(inner.into()), + Expr::Wrapped(inner) => inner.expr.into(), + } + } +} + +impl<'a> Node for Expr<'a> { + fn loc(&self) -> SourceLocation { + match self { + Expr::Array(inner) => inner.loc(), + Expr::ArrowFunc(inner) => inner.loc(), + Expr::ArrowParamPlaceHolder(inner) => inner.loc(), + Expr::Assign(inner) => inner.loc(), + Expr::Await(inner) => inner.loc(), + Expr::Binary(inner) => inner.loc(), + Expr::Class(inner) => inner.loc(), + Expr::Call(inner) => inner.loc(), + Expr::Conditional(inner) => inner.loc(), + Expr::Func(inner) => inner.loc(), + Expr::Ident(inner) => inner.loc(), + Expr::Lit(inner) => inner.loc(), + Expr::Logical(inner) => inner.loc(), + Expr::Member(inner) => inner.loc(), + Expr::MetaProp(inner) => inner.loc(), + Expr::New(inner) => inner.loc(), + Expr::Obj(inner) => inner.loc(), + Expr::Sequence(inner) => inner.loc(), + Expr::Spread(inner) => inner.loc(), + Expr::Super(inner) => inner.loc, + Expr::TaggedTemplate(inner) => inner.loc(), + Expr::This(inner) => inner.loc, + Expr::Unary(inner) => inner.loc(), + Expr::Update(inner) => inner.loc(), + Expr::Yield(inner) => inner.loc(), + Expr::Wrapped(inner) => inner.loc(), + } + } +} + +type ArrayExprEntry<'a> = ListEntry<'a, Option>>; + +/// `[a, b, c]` +#[derive(Debug, Clone, PartialEq)] +pub struct ArrayExpr<'a> { + pub open_bracket: Slice<'a>, + pub elements: Vec>, + pub close_bracket: Slice<'a>, +} + +impl<'a> Node for ArrayExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_bracket.loc.start, + end: self.close_bracket.loc.end, + } + } +} + +/// `{a: 'b', c, ...d}` +#[derive(Debug, Clone, PartialEq)] +pub struct ObjExpr<'a> { + pub open_brace: Slice<'a>, + pub props: Vec>>, + pub close_brace: Slice<'a>, +} + +impl<'a> Node for ObjExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_brace.loc.start, + end: self.close_brace.loc.end, + } + } +} + +/// A single part of an object literal +#[derive(PartialEq, Debug, Clone)] +pub enum ObjProp<'a> { + Prop(Prop<'a>), + Spread(SpreadExpr<'a>), +} + +impl<'a> From> for crate::expr::ObjProp<'a> { + fn from(other: ObjProp<'a>) -> Self { + match other { + ObjProp::Prop(inner) => Self::Prop(inner.into()), + ObjProp::Spread(inner) => Self::Spread(inner.expr.into()), + } + } +} + +impl<'a> Node for ObjProp<'a> { + fn loc(&self) -> SourceLocation { + match self { + ObjProp::Prop(inner) => inner.loc(), + ObjProp::Spread(inner) => inner.loc(), + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct SpreadExpr<'a> { + pub dots: Slice<'a>, + pub expr: Expr<'a>, +} + +impl<'a> Node for SpreadExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.dots.loc.start, + end: self.expr.loc().end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Prop<'a> { + Init(PropInit<'a>), + Method(PropMethod<'a>), + Ctor(PropCtor<'a>), + Get(PropGet<'a>), + Set(PropSet<'a>), +} + +impl<'a> From> for crate::expr::Prop<'a> { + fn from(other: Prop<'a>) -> Self { + match other { + Prop::Init(inner) => Self { + computed: inner.key.brackets.is_some(), + short_hand: inner.colon.is_none(), + key: inner.key.into(), + value: inner + .value + .map(From::from) + .unwrap_or(crate::expr::PropValue::None), + kind: crate::PropKind::Init, + method: false, + is_static: false, + }, + Prop::Method(inner) => Self { + computed: inner.id.brackets.is_some(), + key: inner.id.into(), + value: crate::prelude::PropValue::Expr(crate::Expr::Func(crate::Func { + body: inner.body.into(), + generator: inner.star.is_some(), + id: None, + is_async: inner.keyword_async.is_some(), + params: inner.params.into_iter().map(|e| e.item.into()).collect(), + })), + kind: crate::PropKind::Method, + method: true, + short_hand: false, + is_static: inner.keyword_static.is_some(), + }, + Prop::Ctor(inner) => Self { + computed: inner.keyword.brackets.is_some(), + key: inner.keyword.into(), + value: crate::prelude::PropValue::Expr(crate::Expr::Func(crate::Func { + body: inner.body.into(), + generator: false, + id: None, + is_async: false, + params: inner.params.into_iter().map(|e| e.item.into()).collect(), + })), + kind: crate::PropKind::Ctor, + is_static: false, + method: true, + short_hand: false, + }, + Prop::Get(inner) => Self { + computed: inner.id.brackets.is_some(), + key: inner.id.into(), + value: crate::prelude::PropValue::Expr(crate::Expr::Func(crate::Func { + body: inner.body.into(), + generator: false, + id: None, + is_async: false, + params: Vec::new(), + })), + kind: crate::PropKind::Get, + method: false, + short_hand: false, + is_static: inner.keyword_static.is_some(), + }, + Prop::Set(inner) => Self { + computed: inner.id.brackets.is_some(), + key: inner.id.into(), + value: crate::prelude::PropValue::Expr(crate::Expr::Func(crate::Func { + body: inner.body.into(), + generator: false, + id: None, + is_async: false, + params: vec![inner.arg.item.into()], + })), + kind: crate::PropKind::Set, + method: false, + short_hand: false, + is_static: inner.keyword_static.is_some(), + }, + } + } +} + +impl<'a> Node for Prop<'a> { + fn loc(&self) -> SourceLocation { + match self { + Prop::Init(inner) => inner.loc(), + Prop::Method(inner) => inner.loc(), + Prop::Ctor(inner) => inner.loc(), + Prop::Get(inner) => inner.loc(), + Prop::Set(inner) => inner.loc(), + } + } +} + +impl<'a> Prop<'a> { + pub fn computed(&self) -> bool { + if let Self::Init(init) = self { + init.computed() + } else { + false + } + } + pub fn short_hand(&self) -> bool { + if let Self::Init(init) = self { + init.short_hand() + } else { + false + } + } + pub fn is_async(&self) -> bool { + if let Self::Method(meth) = self { + meth.keyword_async.is_some() + } else { + false + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PropInit<'a> { + pub key: PropInitKey<'a>, + pub colon: Option>, + pub value: Option>, +} + +impl<'a> Node for PropInit<'a> { + fn loc(&self) -> SourceLocation { + if let Some(value) = &self.value { + SourceLocation { + start: self.key.loc().start, + end: value.loc().end, + } + } else { + self.key.loc() + } + } +} + +impl<'a> PropInit<'a> { + pub fn computed(&self) -> bool { + self.key.brackets.is_some() + } + pub fn short_hand(&self) -> bool { + self.value.is_none() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PropInitKey<'a> { + pub value: PropKey<'a>, + pub brackets: Option<(Slice<'a>, Slice<'a>)>, +} + +impl<'a> From> for crate::expr::PropKey<'a> { + fn from(other: PropInitKey<'a>) -> Self { + other.value.into() + } +} + +impl<'a> Node for PropInitKey<'a> { + fn loc(&self) -> SourceLocation { + if let Some((open, close)) = &self.brackets { + SourceLocation { + start: open.loc.start, + end: close.loc.end, + } + } else { + self.value.loc() + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PropMethod<'a> { + pub keyword_static: Option>, + pub keyword_async: Option>, + pub id: PropInitKey<'a>, + pub star: Option>, + pub open_paren: Slice<'a>, + pub params: Vec>>, + pub close_paren: Slice<'a>, + pub body: FuncBody<'a>, +} + +impl<'a> Node for PropMethod<'a> { + fn loc(&self) -> SourceLocation { + let start = if let Some(keyword) = &self.keyword_async { + keyword.loc.start + } else if let Some(star) = &self.star { + star.loc.start + } else { + self.id.loc().start + }; + SourceLocation { + start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::Func<'a> { + fn from(other: PropMethod<'a>) -> Self { + crate::Func { + id: None, + params: other.params.into_iter().map(|e| e.item.into()).collect(), + body: other.body.into(), + generator: other.star.is_some(), + is_async: other.keyword_async.is_some(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PropCtor<'a> { + pub keyword: PropInitKey<'a>, + pub open_paren: Slice<'a>, + pub params: Vec>>, + pub close_paren: Slice<'a>, + pub body: FuncBody<'a>, +} + +impl<'a> Node for PropCtor<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc().start, + end: self.body.loc().end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PropGet<'a> { + pub keyword_static: Option>, + pub keyword_get: Slice<'a>, + pub id: PropInitKey<'a>, + pub open_paren: Slice<'a>, + pub close_paren: Slice<'a>, + pub body: FuncBody<'a>, +} + +impl<'a> Node for PropGet<'a> { + fn loc(&self) -> SourceLocation { + if let Some(keyword_static) = &self.keyword_static { + return SourceLocation { + start: keyword_static.loc.start, + end: self.body.loc().end, + }; + } + SourceLocation { + start: self.keyword_get.loc.start, + end: self.body.loc().end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PropSet<'a> { + pub keyword_static: Option>, + pub keyword_set: Slice<'a>, + pub id: PropInitKey<'a>, + pub open_paren: Slice<'a>, + pub arg: ListEntry<'a, FuncArg<'a>>, + pub close_paren: Slice<'a>, + pub body: FuncBody<'a>, +} + +impl<'a> Node for PropSet<'a> { + fn loc(&self) -> SourceLocation { + if let Some(keyword_static) = &self.keyword_static { + return SourceLocation { + start: keyword_static.loc.start, + end: self.body.loc().end, + }; + } + SourceLocation { + start: self.keyword_set.loc.start, + end: self.body.loc().end, + } + } +} + +/// An object literal or class property identifier +#[derive(PartialEq, Debug, Clone)] +pub enum PropKey<'a> { + Lit(Lit<'a>), + Expr(Expr<'a>), + Pat(Pat<'a>), +} + +impl<'a> From> for crate::expr::PropKey<'a> { + fn from(other: PropKey<'a>) -> Self { + match other { + PropKey::Lit(inner) => Self::Lit(inner.into()), + PropKey::Expr(inner) => Self::Expr(inner.into()), + PropKey::Pat(inner) => Self::Pat(inner.into()), + } + } +} + +impl<'a> Node for PropKey<'a> { + fn loc(&self) -> SourceLocation { + match self { + PropKey::Lit(inner) => inner.loc(), + PropKey::Expr(inner) => inner.loc(), + PropKey::Pat(inner) => inner.loc(), + } + } +} + +/// The value of an object literal or class property +#[derive(PartialEq, Debug, Clone)] +pub enum PropValue<'a> { + Expr(Expr<'a>), + Pat(Pat<'a>), + Method(PropMethod<'a>), +} + +impl<'a> From> for crate::expr::PropValue<'a> { + fn from(other: PropValue<'a>) -> Self { + match other { + PropValue::Expr(inner) => Self::Expr(inner.into()), + PropValue::Pat(inner) => Self::Pat(inner.into()), + PropValue::Method(inner) => Self::Expr(crate::expr::Expr::Func(inner.into())), + } + } +} + +impl<'a> Node for PropValue<'a> { + fn loc(&self) -> SourceLocation { + match self { + PropValue::Expr(inner) => inner.loc(), + PropValue::Pat(inner) => inner.loc(), + PropValue::Method(inner) => inner.loc(), + } + } +} + +/// An operation that takes one argument +#[derive(PartialEq, Debug, Clone)] +pub struct UnaryExpr<'a> { + pub operator: UnaryOp<'a>, + pub argument: Box>, +} + +impl<'a> UnaryExpr<'a> { + pub fn prefix(&self) -> bool { + self.operator.loc() < self.argument.loc() + } +} + +impl<'a> From> for crate::expr::UnaryExpr<'a> { + fn from(other: UnaryExpr<'a>) -> Self { + Self { + prefix: other.prefix(), + operator: other.operator.into(), + argument: Box::new(From::from(*other.argument)), + } + } +} + +impl<'a> Node for UnaryExpr<'a> { + fn loc(&self) -> SourceLocation { + let (start, end) = if self.prefix() { + (self.operator.loc().start, self.argument.loc().end) + } else { + (self.argument.loc().start, self.operator.loc().end) + }; + SourceLocation { start, end } + } +} + +/// Increment or decrementing a value +#[derive(PartialEq, Debug, Clone)] +pub struct UpdateExpr<'a> { + pub operator: UpdateOp<'a>, + pub argument: Box>, +} + +impl<'a> UpdateExpr<'a> { + pub fn prefix(&self) -> bool { + self.operator.loc().start < self.argument.loc().start + } +} + +impl<'a> From> for crate::expr::UpdateExpr<'a> { + fn from(other: UpdateExpr<'a>) -> Self { + let ret = Self { + prefix: other.prefix(), + operator: other.operator.into(), + argument: Box::new(From::from(*other.argument)), + }; + ret + } +} + +impl<'a> Node for UpdateExpr<'a> { + fn loc(&self) -> SourceLocation { + let op = self.operator.loc(); + let arg = self.argument.loc(); + if op < arg { + SourceLocation { + start: op.start, + end: arg.end, + } + } else { + SourceLocation { + start: arg.start, + end: op.end, + } + } + } +} + +/// An operation that requires 2 arguments +#[derive(PartialEq, Debug, Clone)] +pub struct BinaryExpr<'a> { + pub operator: BinaryOp<'a>, + pub left: Box>, + pub right: Box>, +} + +impl<'a> From> for crate::expr::BinaryExpr<'a> { + fn from(other: BinaryExpr<'a>) -> Self { + Self { + operator: other.operator.into(), + left: Box::new(From::from(*other.left)), + right: Box::new(From::from(*other.right)), + } + } +} + +impl<'a> Node for BinaryExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.left.loc().start, + end: self.right.loc().end, + } + } +} + +/// An assignment or update + assignment operation +#[derive(PartialEq, Debug, Clone)] +pub struct AssignExpr<'a> { + pub operator: AssignOp<'a>, + pub left: AssignLeft<'a>, + pub right: Box>, +} + +impl<'a> From> for crate::expr::AssignExpr<'a> { + fn from(other: AssignExpr<'a>) -> Self { + Self { + operator: other.operator.into(), + left: other.left.into(), + right: Box::new(From::from(*other.right)), + } + } +} + +impl<'a> Node for AssignExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.left.loc().start, + end: self.right.loc().end, + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct AwaitExpr<'a> { + pub keyword: Slice<'a>, + pub expr: Expr<'a>, +} + +impl<'a> Node for AwaitExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.expr.loc().end, + } + } +} + +/// The value being assigned to +#[derive(PartialEq, Debug, Clone)] +pub enum AssignLeft<'a> { + Pat(Pat<'a>), + Expr(Box>), +} + +impl<'a> From> for crate::expr::AssignLeft<'a> { + fn from(other: AssignLeft<'a>) -> Self { + match other { + AssignLeft::Pat(inner) => Self::Pat(inner.into()), + AssignLeft::Expr(inner) => Self::Expr(Box::new(From::from(*inner))), + } + } +} + +impl<'a> Node for AssignLeft<'a> { + fn loc(&self) -> SourceLocation { + match self { + AssignLeft::Pat(inner) => inner.loc(), + AssignLeft::Expr(inner) => inner.loc(), + } + } +} + +/// A specialized `BinaryExpr` for logical evaluation +/// ```js +/// true && true +/// false || true +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct LogicalExpr<'a> { + pub operator: LogicalOp<'a>, + pub left: Box>, + pub right: Box>, +} + +impl<'a> From> for crate::expr::LogicalExpr<'a> { + fn from(other: LogicalExpr<'a>) -> Self { + Self { + operator: other.operator.into(), + left: Box::new(From::from(*other.left)), + right: Box::new(From::from(*other.right)), + } + } +} + +impl<'a> Node for LogicalExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.left.loc().start, + end: self.right.loc().end, + } + } +} + +/// Accessing the member of a value +/// ```js +/// b['thing']; +/// c.stuff; +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct MemberExpr<'a> { + pub object: Box>, + pub property: Box>, + pub indexer: MemberIndexer<'a>, +} + +impl<'a> From> for crate::expr::MemberExpr<'a> { + fn from(other: MemberExpr<'a>) -> Self { + let computed = other.computed(); + Self { + object: Box::new(From::from(*other.object)), + property: Box::new(From::from(*other.property)), + computed, + } + } +} + +impl<'a> MemberExpr<'a> { + pub fn computed(&self) -> bool { + matches!(self.indexer, MemberIndexer::Computed { .. }) + } +} + +impl<'a> Node for MemberExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.object.loc().start, + end: if self.computed() { + self.indexer.loc().end + } else { + self.property.loc().end + }, + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub enum MemberIndexer<'a> { + Period(Slice<'a>), + Computed { + open_bracket: Slice<'a>, + close_bracket: Slice<'a>, + }, +} + +impl<'a> Node for MemberIndexer<'a> { + fn loc(&self) -> SourceLocation { + match self { + MemberIndexer::Period(inner) => inner.loc, + MemberIndexer::Computed { + open_bracket, + close_bracket, + } => SourceLocation { + start: open_bracket.loc.start, + end: close_bracket.loc.end, + }, + } + } +} + +/// A ternery expression +/// ```js +/// var a = true ? 'stuff' : 'things'; +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct ConditionalExpr<'a> { + pub test: Box>, + pub question_mark: Slice<'a>, + pub alternate: Box>, + pub colon: Slice<'a>, + pub consequent: Box>, +} + +impl<'a> From> for crate::expr::ConditionalExpr<'a> { + fn from(other: ConditionalExpr<'a>) -> Self { + Self { + test: Box::new(From::from(*other.test)), + alternate: Box::new(From::from(*other.alternate)), + consequent: Box::new(From::from(*other.consequent)), + } + } +} + +impl<'a> Node for ConditionalExpr<'a> { + fn loc(&self) -> SourceLocation { + let start = self.test.loc().start; + let end = self.alternate.loc().end; + SourceLocation { start, end } + } +} + +/// Calling a function or method +/// ```js +/// Math.random() +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct CallExpr<'a> { + pub callee: Box>, + pub open_paren: Slice<'a>, + pub arguments: Vec>>, + pub close_paren: Slice<'a>, +} + +impl<'a> From> for crate::expr::CallExpr<'a> { + fn from(other: CallExpr<'a>) -> Self { + Self { + callee: Box::new(From::from(*other.callee)), + arguments: other.arguments.into_iter().map(|e| e.item.into()).collect(), + } + } +} + +impl<'a> Node for CallExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.callee.loc().start, + end: self.close_paren.loc.end, + } + } +} + +/// Calling a constructor +/// ```js +/// new Uint8Array(32); +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct NewExpr<'a> { + pub keyword: Slice<'a>, + pub callee: Box>, + pub open_paren: Option>, + pub arguments: Vec>>, + pub close_paren: Option>, +} + +impl<'a> From> for crate::expr::NewExpr<'a> { + fn from(other: NewExpr<'a>) -> Self { + Self { + callee: Box::new(From::from(*other.callee)), + arguments: other.arguments.into_iter().map(|e| e.item.into()).collect(), + } + } +} + +impl<'a> Node for NewExpr<'a> { + fn loc(&self) -> SourceLocation { + let end = if let Some(close) = &self.close_paren { + close.loc.end + } else if let Some(last) = self.arguments.last() { + last.loc().end + } else { + self.callee.loc().end + }; + SourceLocation { + start: self.callee.loc().start, + end: end, + } + } +} + +/// A collection of `Exprs` separated by commas +pub type SequenceExpr<'a> = Vec>>; + +impl<'a> Node for SequenceExpr<'a> { + fn loc(&self) -> SourceLocation { + let first_loc = if let Some(first) = self.first() { + first.loc() + } else { + SourceLocation::zero() + }; + let last_loc = if let Some(last) = self.last() { + last.loc() + } else { + SourceLocation::zero() + }; + SourceLocation { + start: first_loc.start, + end: last_loc.end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ArrowParamPlaceHolder<'a> { + // async keyword + pub keyword: Option>, + pub open_paren: Option>, + pub args: Vec>>, + pub close_paren: Option>, +} + +impl<'a> Node for ArrowParamPlaceHolder<'a> { + fn loc(&self) -> SourceLocation { + let start = if let Some(keyword) = &self.keyword { + keyword.loc.start + } else if let Some(open) = &self.open_paren { + open.loc.start + } else if let Some(arg) = self.args.first() { + arg.loc().start + } else { + Position { line: 0, column: 0 } + }; + let end = if let Some(close) = &self.close_paren { + close.loc.end + } else if let Some(arg) = self.args.last() { + arg.loc().end + } else { + Position { line: 0, column: 0 } + }; + SourceLocation { start, end } + } +} + +/// An arrow function +/// ```js +/// let x = () => y; +/// let q = x => { +/// return x + 1; +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct ArrowFuncExpr<'a> { + pub keyword: Option>, + pub star: Option>, + pub open_paren: Option>, + pub params: Vec>>, + pub close_paren: Option>, + pub arrow: Slice<'a>, + pub body: ArrowFuncBody<'a>, +} + +impl<'a> From> for crate::expr::ArrowFuncExpr<'a> { + fn from(other: ArrowFuncExpr<'a>) -> Self { + let expression = matches!(&other.body, ArrowFuncBody::Expr(_)); + Self { + id: None, + params: other.params.into_iter().map(|e| e.item.into()).collect(), + body: other.body.into(), + expression, + generator: other.star.is_some(), + is_async: other.keyword.is_some(), + } + } +} + +impl<'a> Node for ArrowFuncExpr<'a> { + fn loc(&self) -> SourceLocation { + let start = if let Some(slice) = &self.open_paren { + slice.loc.start + } else if let Some(first) = self.params.first() { + first.loc().start + } else { + SourceLocation::zero().start + }; + SourceLocation { + start, + end: self.body.loc().end, + } + } +} + +/// The body portion of an arrow function can be either an expression or a block of statements +#[derive(PartialEq, Debug, Clone)] +pub enum ArrowFuncBody<'a> { + FuncBody(FuncBody<'a>), + Expr(Box>), +} + +impl<'a> From> for crate::expr::ArrowFuncBody<'a> { + fn from(other: ArrowFuncBody<'a>) -> Self { + match other { + ArrowFuncBody::FuncBody(inner) => Self::FuncBody(inner.into()), + ArrowFuncBody::Expr(inner) => Self::Expr(Box::new(From::from(*inner))), + } + } +} + +impl<'a> Node for ArrowFuncBody<'a> { + fn loc(&self) -> SourceLocation { + match self { + ArrowFuncBody::FuncBody(inner) => inner.loc(), + ArrowFuncBody::Expr(inner) => inner.loc(), + } + } +} + +/// yield a value from inside of a generator function +/// ```js +/// function *gen() { +/// while ((new Date() / 1000) < Number.MAX_VALUE) { +/// yield new Date() / 1000; +/// } +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct YieldExpr<'a> { + pub keyword: Slice<'a>, + pub argument: Option>>, + pub star: Option>, +} + +impl<'a> From> for crate::expr::YieldExpr<'a> { + fn from(other: YieldExpr<'a>) -> Self { + Self { + argument: other.argument.map(|e| Box::new(From::from(*e))), + delegate: other.star.is_some(), + } + } +} + +impl<'a> Node for YieldExpr<'a> { + fn loc(&self) -> SourceLocation { + let end = if let Some(arg) = &self.argument { + arg.loc().end + } else { + self.keyword.loc.end + }; + SourceLocation { + start: self.keyword.loc.start, + end, + } + } +} + +/// A Template literal preceded by a function identifier +/// see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates) for more details +#[derive(PartialEq, Debug, Clone)] +pub struct TaggedTemplateExpr<'a> { + pub tag: Box>, + pub quasi: TemplateLit<'a>, +} + +impl<'a> From> for crate::expr::TaggedTemplateExpr<'a> { + fn from(other: TaggedTemplateExpr<'a>) -> Self { + Self { + tag: Box::new(From::from(*other.tag)), + quasi: other.quasi.into(), + } + } +} + +impl<'a> Node for TaggedTemplateExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.tag.loc().start, + end: self.quasi.loc().end, + } + } +} + +/// A template string literal +/// ```js +/// `I own ${0} birds`; +/// ``` +#[derive(Debug, Clone, PartialEq)] +pub struct TemplateLit<'a> { + pub quasis: Vec>, + pub expressions: Vec>, +} + +impl<'a> From> for crate::expr::TemplateLit<'a> { + fn from(other: TemplateLit<'a>) -> Self { + Self { + quasis: other.quasis.into_iter().map(From::from).collect(), + expressions: other.expressions.into_iter().map(From::from).collect(), + } + } +} + +impl<'a> Node for TemplateLit<'a> { + fn loc(&self) -> SourceLocation { + let start = self + .quasis + .first() + .map(|q| q.loc()) + .unwrap_or_else(SourceLocation::zero); + let end = self + .quasis + .last() + .map(|q| q.loc()) + .unwrap_or_else(SourceLocation::zero); + SourceLocation { + start: start.start, + end: end.end, + } + } +} + +/// The text part of a `TemplateLiteral` +#[derive(Debug, Clone, PartialEq)] +pub struct TemplateElement<'a> { + /// Raw quoted element + pub raw: Slice<'a>, + pub cooked: Slice<'a>, +} + +impl<'a> From> for crate::expr::TemplateElement<'a> { + fn from(other: TemplateElement<'a>) -> Self { + let tail = other.is_tail(); + Self { + tail, + cooked: other.cooked.source, + raw: other.raw.source, + } + } +} + +impl<'a> Node for TemplateElement<'a> { + fn loc(&self) -> SourceLocation { + self.raw.loc + } +} + +impl<'a> TemplateElement<'a> { + pub fn is_tail(&self) -> bool { + self.raw.source.starts_with(|c| c == '`' || c == '}') && self.raw.source.ends_with('`') + } +} + +/// pretty much just `new.target` +/// ```js +/// function Thing(one, two) { +/// if (!new.target) { +/// return new Thing(one, two); +/// } +/// this.one = one; +/// this.two = two; +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct MetaProp<'a> { + pub meta: Ident<'a>, + pub dot: Slice<'a>, + pub property: Ident<'a>, +} + +impl<'a> From> for crate::expr::MetaProp<'a> { + fn from(other: MetaProp<'a>) -> Self { + Self { + meta: other.meta.into(), + property: other.property.into(), + } + } +} + +impl<'a> Node for MetaProp<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.meta.loc().start, + end: self.property.loc().end, + } + } +} + +/// A literal value +#[derive(Debug, Clone, PartialEq)] +pub enum Lit<'a> { + /// `null` + Null(Slice<'a>), + /// `"string"` + /// `'string'` + String(StringLit<'a>), + /// `0` + /// `0.0` + /// `.0` + /// `0.0e1` + /// `.0E1` + /// `0xf` + /// `0o7` + /// `0b1` + Number(Slice<'a>), + /// `true` + /// `false` + Boolean(Slice<'a>), + /// `/.+/g` + RegEx(RegEx<'a>), + /// ```js + /// `I have ${0} apples` + /// ``` + Template(TemplateLit<'a>), +} + +impl<'a> From> for crate::expr::Lit<'a> { + fn from(other: Lit<'a>) -> Self { + match other { + Lit::Null(_inner) => Self::Null, + Lit::String(inner) => Self::String(inner.into()), + Lit::Number(inner) => Self::Number(inner.source), + Lit::Boolean(inner) => Self::Boolean(inner.source == "true"), + Lit::RegEx(inner) => Self::RegEx(inner.into()), + Lit::Template(inner) => Self::Template(inner.into()), + } + } +} + +impl<'a> Node for Lit<'a> { + fn loc(&self) -> SourceLocation { + match self { + Lit::Null(inner) => inner.loc, + Lit::String(inner) => inner.loc(), + Lit::Number(inner) => inner.loc, + Lit::Boolean(inner) => inner.loc, + Lit::RegEx(inner) => inner.loc(), + Lit::Template(inner) => inner.loc(), + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct StringLit<'a> { + pub open_quote: Slice<'a>, + pub content: Slice<'a>, + pub close_quote: Slice<'a>, +} + +impl<'a> From> for crate::expr::StringLit<'a> { + fn from(other: StringLit<'a>) -> Self { + if other.open_quote.source == "\"" { + Self::Double(other.content.source) + } else { + Self::Single(other.content.source) + } + } +} + +impl<'a> Node for StringLit<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_quote.loc.start, + end: self.close_quote.loc.end, + } + } +} + +impl<'a> StringLit<'a> { + pub fn inner_matches(&self, o: &str) -> bool { + self.content.source == o + } +} +/// A regular expression literal +#[derive(PartialEq, Debug, Clone)] +pub struct RegEx<'a> { + pub open_slash: Slice<'a>, + pub pattern: Slice<'a>, + pub close_slash: Slice<'a>, + pub flags: Option>, +} + +impl<'a> From> for crate::expr::RegEx<'a> { + fn from(other: RegEx<'a>) -> Self { + Self { + pattern: other.pattern.source, + flags: other + .flags + .map(|f| f.source) + .unwrap_or_else(|| Cow::Borrowed("")), + } + } +} + +impl<'a> Node for RegEx<'a> { + fn loc(&self) -> SourceLocation { + let end = if let Some(flags) = &self.flags { + flags.loc.end + } else { + self.close_slash.loc.end + }; + SourceLocation { + start: self.open_slash.loc.start, + end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct WrappedExpr<'a> { + pub open_paren: Slice<'a>, + pub expr: Expr<'a>, + pub close_paren: Slice<'a>, +} + +impl<'a> Node for WrappedExpr<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_paren.loc.start, + end: self.close_paren.loc.end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct SequenceExprEntry<'a> { + pub expr: Expr<'a>, + pub comma: Option>, +} + +impl<'a> SequenceExprEntry<'a> { + pub fn no_comma(expr: Expr<'a>) -> Self { + Self { expr, comma: None } + } +} + +impl<'a> From> for FuncArgEntry<'a> { + fn from(other: SequenceExprEntry<'a>) -> Self { + Self { + value: FuncArg::Expr(other.expr), + comma: other.comma, + } + } +} + +impl<'a> Node for SequenceExprEntry<'a> { + fn loc(&self) -> SourceLocation { + if let Some(comma) = &self.comma { + return SourceLocation { + start: self.expr.loc().start, + end: comma.loc.end, + }; + } + self.expr.loc() + } +} + +impl<'a> From> for crate::expr::Expr<'a> { + fn from(other: SequenceExprEntry<'a>) -> Self { + other.expr.into() + } +} diff --git a/src/spanned/mod.rs b/src/spanned/mod.rs new file mode 100644 index 0000000..5439749 --- /dev/null +++ b/src/spanned/mod.rs @@ -0,0 +1,757 @@ +use std::borrow::Cow; + +pub mod decl; +pub mod expr; +pub mod pat; +pub mod stmt; + +use decl::Decl; +use expr::{Expr, Lit, Prop}; +use pat::Pat; +use stmt::Stmt; + +use self::pat::RestPat; + +pub trait Node { + fn loc(&self) -> SourceLocation; +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Ident<'a> { + pub slice: Slice<'a>, +} + +impl<'a> Node for Ident<'a> { + fn loc(&self) -> SourceLocation { + self.slice.loc + } +} + +impl<'a> From> for crate::Ident<'a> { + fn from(other: Ident<'a>) -> Self { + Self { + name: other.slice.source, + } + } +} + +impl<'a> From> for Ident<'a> { + fn from(slice: Slice<'a>) -> Self { + Self { slice } + } +} + +impl<'a> Ident<'a> { + pub fn name(&self) -> Cow<'a, str> { + self.slice.source.clone() + } +} + +/// A fully parsed javascript program. +/// +/// It is essentially a collection of `ProgramPart`s +/// with a flag denoting if the representation is +/// a ES6 Mod or a Script. +#[derive(Debug, Clone, PartialEq)] +pub enum Program<'a> { + /// An ES6 Mod + Mod(Vec>), + /// Not an ES6 Mod + Script(Vec>), +} + +impl<'a> From> for crate::Program<'a> { + fn from(other: Program<'a>) -> Self { + match other { + Program::Mod(inner) => Self::Mod(inner.into_iter().map(From::from).collect()), + Program::Script(inner) => Self::Script(inner.into_iter().map(From::from).collect()), + } + } +} + +impl<'a> Node for Program<'a> { + fn loc(&self) -> SourceLocation { + match self { + Self::Mod(inner) => inner.loc(), + Self::Script(inner) => inner.loc(), + } + } +} + +impl<'a> Program<'a> { + pub fn module(parts: Vec>) -> Self { + Program::Mod(parts) + } + pub fn script(parts: Vec>) -> Self { + Program::Script(parts) + } +} + +impl<'a> Node for Vec> { + fn loc(&self) -> SourceLocation { + let start = self + .first() + .map(|p| p.loc()) + .unwrap_or_else(SourceLocation::zero); + let end = self.last().map(|p| p.loc()).unwrap_or(start); + SourceLocation { + start: start.start, + end: end.end, + } + } +} + +/// A single part of a Javascript program. +/// This will be either a Directive, Decl or a Stmt +#[derive(Debug, Clone, PartialEq)] +pub enum ProgramPart<'a> { + /// A Directive like `'use strict';` + Dir(Dir<'a>), + /// A variable, function or module declaration + Decl(Decl<'a>), + /// Any other kind of statement + Stmt(Stmt<'a>), +} + +impl<'a> From> for crate::ProgramPart<'a> { + fn from(other: ProgramPart<'a>) -> Self { + match other { + ProgramPart::Dir(inner) => Self::Dir(inner.into()), + ProgramPart::Decl(inner) => Self::Decl(inner.into()), + ProgramPart::Stmt(inner) => Self::Stmt(inner.into()), + } + } +} + +impl<'a> Node for ProgramPart<'a> { + fn loc(&self) -> SourceLocation { + match self { + Self::Dir(inner) => inner.loc(), + Self::Decl(inner) => inner.loc(), + Self::Stmt(inner) => inner.loc(), + } + } +} + +impl<'a> ProgramPart<'a> { + pub fn decl(inner: Decl<'a>) -> Self { + ProgramPart::Decl(inner) + } + pub fn stmt(inner: Stmt<'a>) -> Self { + ProgramPart::Stmt(inner) + } +} + +/// pretty much always `'use strict'`, this can appear at the +/// top of a file or function +#[derive(Debug, Clone, PartialEq)] +pub struct Dir<'a> { + pub expr: Lit<'a>, + pub dir: Cow<'a, str>, + pub semi_colon: Option>, +} + +impl<'a> From> for crate::Dir<'a> { + fn from(other: Dir<'a>) -> Self { + Self { + expr: other.expr.into(), + dir: other.dir, + } + } +} + +impl<'a> Node for Dir<'a> { + fn loc(&self) -> SourceLocation { + self.expr.loc() + } +} + +/// A function, this will be part of either a function +/// declaration (ID is required) or a function expression +/// (ID is optional) +/// ```js +/// //function declaration +/// function thing() {} +/// //function expressions +/// var x = function() {} +/// let y = function q() {} +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct Func<'a> { + pub keyword: Slice<'a>, + pub id: Option>, + pub open_paren: Slice<'a>, + pub params: Vec>>, + pub close_paren: Slice<'a>, + pub body: FuncBody<'a>, + pub star: Option>, + pub keyword_async: Option>, +} + +impl<'a> Func<'a> { + pub fn is_async(&self) -> bool { + self.keyword_async.is_some() + } + pub fn generator(&self) -> bool { + self.star.is_some() + } +} + +impl<'a> From> for crate::Func<'a> { + fn from(other: Func<'a>) -> Self { + Self { + generator: other.generator(), + is_async: other.is_async(), + id: other.id.map(From::from), + params: other + .params + .into_iter() + .map(|e| From::from(e.item)) + .collect(), + body: other.body.into(), + } + } +} + +impl<'a> Node for Func<'a> { + fn loc(&self) -> SourceLocation { + let start = if let Some(keyword) = &self.keyword_async { + keyword.loc.start + } else { + self.keyword.loc.start + }; + let end = self.body.close_brace.loc.end.clone(); + SourceLocation { start, end } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FuncArgEntry<'a> { + pub value: FuncArg<'a>, + pub comma: Option>, +} + +impl<'a> From> for crate::FuncArg<'a> { + fn from(other: FuncArgEntry<'a>) -> Self { + other.value.into() + } +} + +impl<'a> Node for FuncArgEntry<'a> { + fn loc(&self) -> SourceLocation { + if let Some(comma) = &self.comma { + return SourceLocation { + start: self.value.loc().start, + end: comma.loc.end, + }; + } + self.value.loc() + } +} + +/// A single function argument from a function signature +#[derive(Debug, Clone, PartialEq)] +pub enum FuncArg<'a> { + Expr(Expr<'a>), + Pat(Pat<'a>), + Rest(Box>), +} + +impl<'a> From> for crate::FuncArg<'a> { + fn from(other: FuncArg<'a>) -> Self { + match other { + FuncArg::Expr(inner) => Self::Expr(inner.into()), + FuncArg::Pat(inner) => Self::Pat(inner.into()), + FuncArg::Rest(inner) => { + Self::Pat(crate::pat::Pat::RestElement(Box::new(inner.pat.into()))) + } + } + } +} + +impl<'a> Node for FuncArg<'a> { + fn loc(&self) -> SourceLocation { + match self { + FuncArg::Expr(inner) => inner.loc(), + FuncArg::Pat(inner) => inner.loc(), + FuncArg::Rest(inner) => inner.loc(), + } + } +} + +/// The block statement that makes up the function's body +#[derive(Debug, Clone, PartialEq)] +pub struct FuncBody<'a> { + pub open_brace: Slice<'a>, + pub stmts: Vec>, + pub close_brace: Slice<'a>, +} + +impl<'a> From> for crate::FuncBody<'a> { + fn from(other: FuncBody<'a>) -> Self { + Self(other.stmts.into_iter().map(From::from).collect()) + } +} + +impl<'a> Node for FuncBody<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_brace.loc.start, + end: self.close_brace.loc.end, + } + } +} + +/// A way to declare object templates +/// ```js +/// class Thing { +/// constructor() { +/// this._a = 0; +/// } +/// stuff() { +/// return 'stuff' +/// } +/// set a(value) { +/// if (value > 100) { +/// this._a = 0; +/// } else { +/// this._a = value; +/// } +/// } +/// get a() { +/// return this._a; +/// } +/// } +/// let y = class { +/// constructor() { +/// this.a = 100; +/// } +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct Class<'a> { + pub keyword: Slice<'a>, + pub id: Option>, + pub super_class: Option>, + pub body: ClassBody<'a>, +} + +impl<'a> From> for crate::Class<'a> { + fn from(other: Class<'a>) -> Self { + Self { + id: other.id.map(From::from), + super_class: other.super_class.map(|e| Box::new(From::from(e.expr))), + body: other.body.into(), + } + } +} + +impl<'a> Node for Class<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.close_brace.loc.end, + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct SuperClass<'a> { + pub keyword_extends: Slice<'a>, + pub expr: Expr<'a>, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ClassBody<'a> { + pub open_brace: Slice<'a>, + pub props: Vec>, + pub close_brace: Slice<'a>, +} + +impl<'a> From> for crate::ClassBody<'a> { + fn from(other: ClassBody<'a>) -> Self { + Self(other.props.into_iter().map(From::from).collect()) + } +} + +impl<'a> Node for ClassBody<'a> { + fn loc(&self) -> SourceLocation { + let start = self.open_brace.loc.start; + let end = self.close_brace.loc.end; + SourceLocation { start, end } + } +} + +/// The kind of variable being defined (`var`/`let`/`const`) +#[derive(Debug, Clone, PartialEq)] +pub enum VarKind<'a> { + Var(Option>), + Let(Slice<'a>), + Const(Slice<'a>), +} + +impl<'a> From> for crate::VarKind { + fn from(other: VarKind<'a>) -> Self { + match other { + VarKind::Var(_) => Self::Var, + VarKind::Let(_) => Self::Let, + VarKind::Const(_) => Self::Const, + } + } +} + +impl<'a> Node for VarKind<'a> { + fn loc(&self) -> SourceLocation { + match self { + VarKind::Var(Some(slice)) => slice.loc, + VarKind::Let(slice) => slice.loc, + VarKind::Const(slice) => slice.loc, + _ => SourceLocation::zero(), + } + } +} + +impl<'a> VarKind<'a> { + pub fn is_var(&self) -> bool { + matches!(self, VarKind::Var(_)) + } +} + +/// The available operators for assignment Exprs +#[derive(Debug, Clone, PartialEq)] +pub enum AssignOp<'a> { + Equal(Slice<'a>), + PlusEqual(Slice<'a>), + MinusEqual(Slice<'a>), + TimesEqual(Slice<'a>), + DivEqual(Slice<'a>), + ModEqual(Slice<'a>), + LeftShiftEqual(Slice<'a>), + RightShiftEqual(Slice<'a>), + UnsignedRightShiftEqual(Slice<'a>), + OrEqual(Slice<'a>), + XOrEqual(Slice<'a>), + AndEqual(Slice<'a>), + PowerOfEqual(Slice<'a>), +} + +impl<'a> From> for crate::AssignOp { + fn from(other: AssignOp<'a>) -> Self { + match other { + AssignOp::Equal(_) => Self::Equal, + AssignOp::PlusEqual(_) => Self::PlusEqual, + AssignOp::MinusEqual(_) => Self::MinusEqual, + AssignOp::TimesEqual(_) => Self::TimesEqual, + AssignOp::DivEqual(_) => Self::DivEqual, + AssignOp::ModEqual(_) => Self::ModEqual, + AssignOp::LeftShiftEqual(_) => Self::LeftShiftEqual, + AssignOp::RightShiftEqual(_) => Self::RightShiftEqual, + AssignOp::UnsignedRightShiftEqual(_) => Self::UnsignedRightShiftEqual, + AssignOp::OrEqual(_) => Self::OrEqual, + AssignOp::XOrEqual(_) => Self::XOrEqual, + AssignOp::AndEqual(_) => Self::AndEqual, + AssignOp::PowerOfEqual(_) => Self::PowerOfEqual, + } + } +} + +impl<'a> Node for AssignOp<'a> { + fn loc(&self) -> SourceLocation { + match self { + AssignOp::Equal(slice) => slice.loc, + AssignOp::PlusEqual(slice) => slice.loc, + AssignOp::MinusEqual(slice) => slice.loc, + AssignOp::TimesEqual(slice) => slice.loc, + AssignOp::DivEqual(slice) => slice.loc, + AssignOp::ModEqual(slice) => slice.loc, + AssignOp::LeftShiftEqual(slice) => slice.loc, + AssignOp::RightShiftEqual(slice) => slice.loc, + AssignOp::UnsignedRightShiftEqual(slice) => slice.loc, + AssignOp::OrEqual(slice) => slice.loc, + AssignOp::XOrEqual(slice) => slice.loc, + AssignOp::AndEqual(slice) => slice.loc, + AssignOp::PowerOfEqual(slice) => slice.loc, + } + } +} + +/// The available logical operators +#[derive(Debug, Clone, PartialEq)] +pub enum LogicalOp<'a> { + Or(Slice<'a>), + And(Slice<'a>), +} + +impl<'a> From> for crate::LogicalOp { + fn from(other: LogicalOp<'a>) -> Self { + match other { + LogicalOp::Or(_) => Self::Or, + LogicalOp::And(_) => Self::And, + } + } +} + +impl<'a> Node for LogicalOp<'a> { + fn loc(&self) -> SourceLocation { + match self { + LogicalOp::Or(slice) => slice.loc, + LogicalOp::And(slice) => slice.loc, + } + } +} + +/// The available operations for `Binary` Exprs +#[derive(Debug, Clone, PartialEq)] +pub enum BinaryOp<'a> { + Equal(Slice<'a>), + NotEqual(Slice<'a>), + StrictEqual(Slice<'a>), + StrictNotEqual(Slice<'a>), + LessThan(Slice<'a>), + GreaterThan(Slice<'a>), + LessThanEqual(Slice<'a>), + GreaterThanEqual(Slice<'a>), + LeftShift(Slice<'a>), + RightShift(Slice<'a>), + UnsignedRightShift(Slice<'a>), + Plus(Slice<'a>), + Minus(Slice<'a>), + Times(Slice<'a>), + Over(Slice<'a>), + Mod(Slice<'a>), + Or(Slice<'a>), + XOr(Slice<'a>), + And(Slice<'a>), + In(Slice<'a>), + InstanceOf(Slice<'a>), + PowerOf(Slice<'a>), +} + +impl<'a> From> for crate::BinaryOp { + fn from(other: BinaryOp<'a>) -> Self { + match other { + BinaryOp::Equal(_) => Self::Equal, + BinaryOp::NotEqual(_) => Self::NotEqual, + BinaryOp::StrictEqual(_) => Self::StrictEqual, + BinaryOp::StrictNotEqual(_) => Self::StrictNotEqual, + BinaryOp::LessThan(_) => Self::LessThan, + BinaryOp::GreaterThan(_) => Self::GreaterThan, + BinaryOp::LessThanEqual(_) => Self::LessThanEqual, + BinaryOp::GreaterThanEqual(_) => Self::GreaterThanEqual, + BinaryOp::LeftShift(_) => Self::LeftShift, + BinaryOp::RightShift(_) => Self::RightShift, + BinaryOp::UnsignedRightShift(_) => Self::UnsignedRightShift, + BinaryOp::Plus(_) => Self::Plus, + BinaryOp::Minus(_) => Self::Minus, + BinaryOp::Times(_) => Self::Times, + BinaryOp::Over(_) => Self::Over, + BinaryOp::Mod(_) => Self::Mod, + BinaryOp::Or(_) => Self::Or, + BinaryOp::XOr(_) => Self::XOr, + BinaryOp::And(_) => Self::And, + BinaryOp::In(_) => Self::In, + BinaryOp::InstanceOf(_) => Self::InstanceOf, + BinaryOp::PowerOf(_) => Self::PowerOf, + } + } +} + +impl<'a> Node for BinaryOp<'a> { + fn loc(&self) -> SourceLocation { + match self { + BinaryOp::Equal(slice) => slice.loc, + BinaryOp::NotEqual(slice) => slice.loc, + BinaryOp::StrictEqual(slice) => slice.loc, + BinaryOp::StrictNotEqual(slice) => slice.loc, + BinaryOp::LessThan(slice) => slice.loc, + BinaryOp::GreaterThan(slice) => slice.loc, + BinaryOp::LessThanEqual(slice) => slice.loc, + BinaryOp::GreaterThanEqual(slice) => slice.loc, + BinaryOp::LeftShift(slice) => slice.loc, + BinaryOp::RightShift(slice) => slice.loc, + BinaryOp::UnsignedRightShift(slice) => slice.loc, + BinaryOp::Plus(slice) => slice.loc, + BinaryOp::Minus(slice) => slice.loc, + BinaryOp::Times(slice) => slice.loc, + BinaryOp::Over(slice) => slice.loc, + BinaryOp::Mod(slice) => slice.loc, + BinaryOp::Or(slice) => slice.loc, + BinaryOp::XOr(slice) => slice.loc, + BinaryOp::And(slice) => slice.loc, + BinaryOp::In(slice) => slice.loc, + BinaryOp::InstanceOf(slice) => slice.loc, + BinaryOp::PowerOf(slice) => slice.loc, + } + } +} + +/// `++` or `--` +#[derive(Debug, Clone, PartialEq)] +pub enum UpdateOp<'a> { + Increment(Slice<'a>), + Decrement(Slice<'a>), +} + +impl<'a> From> for crate::UpdateOp { + fn from(other: UpdateOp<'a>) -> Self { + match other { + UpdateOp::Increment(_) => Self::Increment, + UpdateOp::Decrement(_) => Self::Decrement, + } + } +} + +impl<'a> Node for UpdateOp<'a> { + fn loc(&self) -> SourceLocation { + match self { + UpdateOp::Increment(slice) => slice.loc, + UpdateOp::Decrement(slice) => slice.loc, + } + } +} + +/// The allowed operators for an Expr +/// to be `Unary` +#[derive(Debug, Clone, PartialEq)] +pub enum UnaryOp<'a> { + Minus(Slice<'a>), + Plus(Slice<'a>), + Not(Slice<'a>), + Tilde(Slice<'a>), + TypeOf(Slice<'a>), + Void(Slice<'a>), + Delete(Slice<'a>), +} + +impl<'a> From> for crate::UnaryOp { + fn from(other: UnaryOp<'a>) -> Self { + match other { + UnaryOp::Minus(_) => Self::Minus, + UnaryOp::Plus(_) => Self::Plus, + UnaryOp::Not(_) => Self::Not, + UnaryOp::Tilde(_) => Self::Tilde, + UnaryOp::TypeOf(_) => Self::TypeOf, + UnaryOp::Void(_) => Self::Void, + UnaryOp::Delete(_) => Self::Delete, + } + } +} + +impl<'a> Node for UnaryOp<'a> { + fn loc(&self) -> SourceLocation { + match self { + UnaryOp::Minus(slice) => slice.loc, + UnaryOp::Plus(slice) => slice.loc, + UnaryOp::Not(slice) => slice.loc, + UnaryOp::Tilde(slice) => slice.loc, + UnaryOp::TypeOf(slice) => slice.loc, + UnaryOp::Void(slice) => slice.loc, + UnaryOp::Delete(slice) => slice.loc, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Slice<'a> { + pub source: Cow<'a, str>, + pub loc: SourceLocation, +} + +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct SourceLocation { + pub start: Position, + pub end: Position, +} + +impl SourceLocation { + pub fn new(start_line: usize, start_column: usize, end_line: usize, end_column: usize) -> Self { + Self { + start: Position { + line: start_line, + column: start_column, + }, + end: Position { + line: end_line, + column: end_column, + }, + } + } + fn zero() -> Self { + Self::new(0, 0, 0, 0) + } +} + +impl core::cmp::PartialOrd for SourceLocation { + fn partial_cmp(&self, other: &Self) -> Option { + match self.start.partial_cmp(&other.start) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + self.end.partial_cmp(&other.end) + } +} + +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct Position { + pub line: usize, + pub column: usize, +} + +impl std::cmp::PartialOrd for Position { + fn partial_cmp(&self, other: &Self) -> Option { + let line = self.line.partial_cmp(&other.line)?; + if matches!(line, core::cmp::Ordering::Equal) { + return self.column.partial_cmp(&other.column); + } + Some(line) + } +} + +impl std::ops::Add for Position { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + line: self.line + rhs.line, + column: self.column + rhs.column, + } + } +} + +impl std::ops::Sub for Position { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self { + line: self.line - rhs.line, + column: self.column - rhs.column, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ListEntry<'a, T> { + pub item: T, + pub comma: Option>, +} + +impl<'a, T> ListEntry<'a, T> { + pub fn no_comma(item: T) -> Self { + Self { item, comma: None } + } +} + +impl<'a, T> Node for ListEntry<'a, T> +where + T: Node, +{ + fn loc(&self) -> SourceLocation { + if let Some(comma) = &self.comma { + return SourceLocation { + start: self.item.loc().start, + end: comma.loc.end, + }; + } + self.item.loc() + } +} diff --git a/src/spanned/pat.rs b/src/spanned/pat.rs new file mode 100644 index 0000000..92992e0 --- /dev/null +++ b/src/spanned/pat.rs @@ -0,0 +1,193 @@ +use crate::spanned::expr::{Expr, Prop}; +use crate::spanned::Ident; + +use super::{AssignOp, ListEntry, Node, Slice, SourceLocation}; +/// All of the different ways you can declare an identifier +/// and/or value +#[derive(Debug, Clone, PartialEq)] +pub enum Pat<'a> { + Ident(Ident<'a>), + Obj(ObjPat<'a>), + Array(ArrayPat<'a>), + Assign(AssignPat<'a>), +} + +impl<'a> From> for crate::pat::Pat<'a> { + fn from(other: Pat<'a>) -> Self { + match other { + Pat::Ident(inner) => Self::Ident(inner.into()), + Pat::Obj(inner) => Self::Obj(inner.into()), + Pat::Array(inner) => Self::Array(inner.into()), + Pat::Assign(inner) => Self::Assign(inner.into()), + } + } +} + +impl<'a> Node for Pat<'a> { + fn loc(&self) -> super::SourceLocation { + match self { + Pat::Ident(inner) => inner.loc(), + Pat::Obj(inner) => inner.loc(), + Pat::Array(inner) => inner.loc(), + Pat::Assign(inner) => inner.loc(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ArrayPat<'a> { + pub open_bracket: Slice<'a>, + pub elements: Vec>>>, + pub close_bracket: Slice<'a>, +} + +impl<'a> Node for ArrayPat<'a> { + fn loc(&self) -> super::SourceLocation { + SourceLocation { + start: self.open_bracket.loc.start, + end: self.close_bracket.loc.end, + } + } +} + +impl<'a> From> for Vec>> { + fn from(other: ArrayPat<'a>) -> Self { + other + .elements + .into_iter() + .map(|e| e.item.map(Into::into)) + .collect() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ArrayElement<'a> { + pub part: Option>, + pub comma: Option>, +} + +impl<'a> From> for Option> { + fn from(other: ArrayElement<'a>) -> Self { + other.part.map(From::from) + } +} + +#[derive(PartialEq, Debug, Clone)] +pub enum ArrayPatPart<'a> { + Pat(Pat<'a>), + Expr(Expr<'a>), + Rest(RestPat<'a>), +} + +impl<'a> From> for crate::pat::ArrayPatPart<'a> { + fn from(other: ArrayPatPart<'a>) -> Self { + match other { + ArrayPatPart::Pat(inner) => Self::Pat(inner.into()), + ArrayPatPart::Expr(inner) => Self::Expr(inner.into()), + ArrayPatPart::Rest(inner) => { + Self::Pat(crate::pat::Pat::RestElement(Box::new(inner.pat.into()))) + } + } + } +} + +impl<'a> Node for ArrayPatPart<'a> { + fn loc(&self) -> SourceLocation { + match self { + ArrayPatPart::Pat(inner) => inner.loc(), + ArrayPatPart::Expr(inner) => inner.loc(), + ArrayPatPart::Rest(inner) => inner.loc(), + } + } +} + +type ObjEntry<'a> = ListEntry<'a, ObjPatPart<'a>>; + +/// similar to an `ObjectExpr` +#[derive(PartialEq, Debug, Clone)] +pub struct ObjPat<'a> { + pub open_brace: Slice<'a>, + pub props: Vec>, + pub close_brace: Slice<'a>, +} + +impl<'a> Node for ObjPat<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_brace.loc.start, + end: self.close_brace.loc.end, + } + } +} + +impl<'a> From> for crate::pat::ObjPat<'a> { + fn from(other: ObjPat<'a>) -> Self { + other.props.into_iter().map(|e| e.item.into()).collect() + } +} + +/// A single part of an ObjectPat +#[derive(PartialEq, Debug, Clone)] +pub enum ObjPatPart<'a> { + Assign(Prop<'a>), + Rest(Box>), +} + +impl<'a> Node for ObjPatPart<'a> { + fn loc(&self) -> SourceLocation { + match self { + ObjPatPart::Assign(prop) => prop.loc(), + ObjPatPart::Rest(inner) => inner.loc(), + } + } +} + +impl<'a> From> for crate::pat::ObjPatPart<'a> { + fn from(other: ObjPatPart<'a>) -> Self { + match other { + ObjPatPart::Assign(prop) => Self::Assign(prop.into()), + ObjPatPart::Rest(inner) => Self::Rest(Box::new(From::from(inner.pat))), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RestPat<'a> { + pub dots: Slice<'a>, + pub pat: Pat<'a>, +} + +impl<'a> Node for RestPat<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.dots.loc.start, + end: self.pat.loc().end, + } + } +} + +/// An assignment as a pattern +#[derive(Debug, Clone, PartialEq)] +pub struct AssignPat<'a> { + pub left: Box>, + pub operator: AssignOp<'a>, + pub right: Box>, +} + +impl<'a> Node for AssignPat<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.left.loc().start, + end: self.right.loc().end, + } + } +} + +impl<'a> From> for crate::pat::AssignPat<'a> { + fn from(other: AssignPat<'a>) -> Self { + Self { + left: Box::new(From::from(*other.left)), + right: Box::new(From::from(*other.right)), + } + } +} diff --git a/src/spanned/stmt.rs b/src/spanned/stmt.rs new file mode 100644 index 0000000..e7e09fc --- /dev/null +++ b/src/spanned/stmt.rs @@ -0,0 +1,955 @@ +use crate::spanned::decl::VarDecl; +use crate::spanned::expr::Expr; +use crate::spanned::pat::Pat; +use crate::spanned::VarKind; +use crate::spanned::{Ident, ProgramPart}; + +use super::decl::VarDecls; +use super::{ListEntry, Node, Slice, SourceLocation}; + +/// A slightly more granular part of an es program than ProgramPart +#[derive(Debug, Clone, PartialEq)] +pub enum Stmt<'a> { + /// Any expression + Expr { + expr: Expr<'a>, + semi_colon: Option>, + }, + /// A collection of program parts wrapped in curly braces + Block(BlockStmt<'a>), + /// A single semi-colon + Empty(Slice<'a>), + /// The contextual keyword `debugger` + Debugger { + keyword: Slice<'a>, + semi_colon: Option>, + }, + /// A with statement, this puts one object at the top of + /// the identifier search tree. + /// > note: this cannot be used in a strict context + /// ```js + /// function random() { + /// return 0; + /// } + /// let rand; + /// with (Math) { + /// rand = floor(random() * 100) + 1; + /// } + /// //rand !== 0 + /// ``` + With(WithStmt<'a>), + /// A return statement + /// ```js + /// function thing() { + /// return 'stuff'; + /// } + /// function stuff() { + /// return; + /// } + Return { + keyword: Slice<'a>, + value: Option>, + semi_colon: Option>, + }, + /// A labeled statement + /// ```js + /// label: { + /// break label; + /// } + /// ``` + Labeled(LabeledStmt<'a>), + /// A break statement + /// ```js + /// label: { + /// break label; + /// } + /// while (true) { + /// break; + /// } + /// ``` + Break { + keyword: Slice<'a>, + label: Option>, + semi_colon: Option>, + }, + /// A short circuit continuation of a loop + /// ```js + /// label: while (true) { + /// if (Math.floor(Math.random() * 100) > 50) { + /// continue; + /// } else { + /// console.log('too low') + /// } + /// } + /// ``` + Continue { + keyword: Slice<'a>, + label: Option>, + semi_colon: Option>, + }, + /// An if statement + /// ```js + /// if (1 < 2) { + /// console.log('Always true'); + /// } else { + /// console.log('Never true'); + /// } + /// ``` + If(IfStmt<'a>), + /// A switch statement + /// ```js + /// switch (Math.floor(Math.random()) * 10) { + /// case 1: + /// + /// break; + /// case 2: + /// case 3: + /// case 4: + /// return false; + /// default: + /// return true; + /// } + /// ``` + Switch(SwitchStmt<'a>), + /// A statement that throws an error + /// ```js + /// function error() { + /// throw 'hahahaha'; + /// } + /// + /// function newError() { + /// throw new Error('hohoho'); + /// } + /// ``` + Throw { + keyword: Slice<'a>, + expr: Expr<'a>, + semi_colon: Option>, + }, + /// A try/catch block + /// ```js + /// try { + /// + /// } catch (e) { + /// + /// } finally { + /// + /// } + /// ``` + Try(TryStmt<'a>), + /// A while loop + /// ```js + /// while (false) { + /// + /// } + /// var i = 0; + /// while (i < 100) { + /// if (Math.floor(Math.random() * 100) > 50) { + /// i--; + /// } else { + /// i += 5; + /// } + /// } + /// ``` + While(WhileStmt<'a>), + /// A while loop that executes its body first + /// ```js + /// do { + /// console.log('at least once') + /// } while (Math.floor(Math.random() * 100) < 75) + /// ``` + DoWhile(DoWhileStmt<'a>), + /// A "c-style" for loop + /// ```js + /// for (var i = 0; i < 100; i++) console.log(i); + /// for (;;) { + /// console.log('forever!'); + /// } + /// ``` + For(ForStmt<'a>), + /// A for in statement, this kind of for statement + /// will extract each key from an indexable thing + /// ```js + /// for (var i in [2,3,4,5,6]) { + /// console.log(i); + /// } + /// //prints 0, 1, 2, 3, 4 + /// for (var k in {a: 'b', c: 'd'}) { + /// console.log(k); + /// } + /// //prints a, b + /// ``` + ForIn(ForInStmt<'a>), + /// A for of statement, this kind of for statement + /// will extract the value from a generator or iterator + /// ```js + /// for (var k of [2, 3, 4, 5, 6]) { + /// console.log(k); + /// } + /// //prints 2, 3, 4, 5, 6 + /// ``` + ForOf(ForOfStmt<'a>), + /// A var statement + /// ```js + /// var x; + /// var x, y = 'huh?'; + /// ``` + Var { + decls: VarDecls<'a>, + semi_colon: Option>, + }, +} + +impl<'a> From> for crate::stmt::Stmt<'a> { + fn from(other: Stmt<'a>) -> Self { + match other { + Stmt::Expr { expr, .. } => Self::Expr(expr.into()), + Stmt::Block(inner) => Self::Block(inner.into()), + Stmt::Empty(_) => Self::Empty, + Stmt::Debugger { .. } => Self::Debugger, + Stmt::With(inner) => Self::With(inner.into()), + Stmt::Return { value, .. } => Self::Return(value.map(From::from)), + Stmt::Labeled(inner) => Self::Labeled(inner.into()), + Stmt::Break { label, .. } => Self::Break(label.map(From::from)), + Stmt::Continue { label, .. } => Self::Continue(label.map(From::from)), + Stmt::If(inner) => Self::If(inner.into()), + Stmt::Switch(inner) => Self::Switch(inner.into()), + Stmt::Throw { expr, .. } => Self::Throw(expr.into()), + Stmt::Try(inner) => Self::Try(inner.into()), + Stmt::While(inner) => Self::While(inner.into()), + Stmt::DoWhile(inner) => Self::DoWhile(inner.into()), + Stmt::For(inner) => Self::For(inner.into()), + Stmt::ForIn(inner) => Self::ForIn(inner.into()), + Stmt::ForOf(inner) => Self::ForOf(inner.into()), + Stmt::Var { decls, .. } => { + Self::Var(decls.decls.into_iter().map(|e| e.item.into()).collect()) + } + } + } +} + +impl<'a> Node for Stmt<'a> { + fn loc(&self) -> super::SourceLocation { + match self { + Stmt::Expr { expr, semi_colon } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: expr.loc().start, + end: semi.loc.end, + }; + } + expr.loc() + } + Stmt::Block(inner) => inner.loc(), + Stmt::Empty(inner) => inner.loc, + Stmt::Debugger { + keyword, + semi_colon, + } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: keyword.loc.start, + end: semi.loc.end, + }; + } + keyword.loc + } + Stmt::With(inner) => inner.loc(), + Stmt::Return { + keyword, + value, + semi_colon, + } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: keyword.loc.start, + end: semi.loc.end, + }; + } + if let Some(value) = value { + return SourceLocation { + start: keyword.loc.start, + end: value.loc().end, + }; + } + keyword.loc + } + Stmt::Labeled(inner) => inner.loc(), + Stmt::Break { + keyword, + label, + semi_colon, + } => { + if let Some(semi_colon) = semi_colon { + return SourceLocation { + start: keyword.loc.start, + end: semi_colon.loc.end, + }; + } + if let Some(label) = label { + return SourceLocation { + start: keyword.loc.start, + end: label.loc().end, + }; + } + keyword.loc + } + Stmt::Continue { + keyword, + label, + semi_colon, + } => { + if let Some(semi_colon) = semi_colon { + return SourceLocation { + start: keyword.loc.start, + end: semi_colon.loc.end, + }; + } + if let Some(label) = label { + return SourceLocation { + start: keyword.loc.start, + end: label.loc().end, + }; + } + keyword.loc + } + Stmt::If(inner) => inner.loc(), + Stmt::Switch(inner) => inner.loc(), + Stmt::Throw { + keyword, + expr, + semi_colon, + } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: keyword.loc.start, + end: semi.loc.end, + }; + } + SourceLocation { + start: keyword.loc.start, + end: expr.loc().end, + } + } + Stmt::Try(inner) => inner.loc(), + Stmt::While(inner) => inner.loc(), + Stmt::DoWhile(inner) => inner.loc(), + Stmt::For(inner) => inner.loc(), + Stmt::ForIn(inner) => inner.loc(), + Stmt::ForOf(inner) => inner.loc(), + Stmt::Var { decls, semi_colon } => { + if let Some(semi) = semi_colon { + return SourceLocation { + start: decls.loc().start, + end: semi.loc.end, + }; + } + decls.loc() + } + } + } +} + +/// A with statement, this puts one object at the top of +/// the identifier search tree. +/// > note: this cannot be used in a strict context +/// ```js +/// function random() { +/// return 0; +/// } +/// let rand; +/// with (Math) { +/// rand = floor(random() * 100) + 1; +/// } +/// //rand !== 0 +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct WithStmt<'a> { + pub keyword: Slice<'a>, + pub open_paren: Slice<'a>, + pub object: Expr<'a>, + pub close_paren: Slice<'a>, + pub body: Box>, +} + +impl<'a> Node for WithStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::WithStmt<'a> { + fn from(other: WithStmt<'a>) -> Self { + Self { + object: other.object.into(), + body: Box::new(From::from(*other.body)), + } + } +} + +/// A labeled statement +/// ```js +/// label: { +/// break label; +/// } +/// loop: while (true) { +/// break loop; +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct LabeledStmt<'a> { + pub label: Ident<'a>, + pub colon: Slice<'a>, + pub body: Box>, +} + +impl<'a> Node for LabeledStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.label.loc().start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::LabeledStmt<'a> { + fn from(other: LabeledStmt<'a>) -> Self { + Self { + label: other.label.into(), + body: Box::new(From::from(*other.body)), + } + } +} + +/// An if statement +/// ```js +/// if (1 < 2) { +/// console.log('Always true'); +/// } else { +/// console.log('Never true'); +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct IfStmt<'a> { + pub keyword: Slice<'a>, + pub open_paren: Slice<'a>, + pub test: Expr<'a>, + pub close_paren: Slice<'a>, + pub consequent: Box>, + pub alternate: Option>>, +} + +impl<'a> Node for IfStmt<'a> { + fn loc(&self) -> SourceLocation { + let start = self.keyword.loc.start; + let end = if let Some(alt) = &self.alternate { + alt.loc().end + } else { + self.consequent.loc().end + }; + SourceLocation { start, end } + } +} + +impl<'a> From> for crate::stmt::IfStmt<'a> { + fn from(other: IfStmt<'a>) -> Self { + Self { + test: other.test.into(), + consequent: Box::new(From::from(*other.consequent)), + alternate: other.alternate.map(|s| Box::new(From::from(s.body))), + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct ElseStmt<'a> { + pub keyword: Slice<'a>, + pub body: Stmt<'a>, +} + +impl<'a> Node for ElseStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.loc().end, + } + } +} + +/// A switch statement +/// ```js +/// switch (Math.floor(Math.random()) * 10) { +/// case 1: +/// +/// break; +/// case 2: +/// case 3: +/// case 4: +/// return false; +/// default: +/// return true; +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct SwitchStmt<'a> { + pub keyword: Slice<'a>, + pub open_paren: Slice<'a>, + pub discriminant: Expr<'a>, + pub close_paren: Slice<'a>, + pub open_brace: Slice<'a>, + pub cases: Vec>, + pub close_brace: Slice<'a>, +} + +impl<'a> Node for SwitchStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.close_paren.loc.end, + } + } +} + +impl<'a> From> for crate::stmt::SwitchStmt<'a> { + fn from(other: SwitchStmt<'a>) -> Self { + Self { + discriminant: other.discriminant.into(), + cases: other.cases.into_iter().map(From::from).collect(), + } + } +} + +/// A single case part of a switch statement +#[derive(Debug, Clone, PartialEq)] +pub struct SwitchCase<'a> { + pub keyword: Slice<'a>, + pub test: Option>, + pub colon: Slice<'a>, + pub consequent: Vec>, +} + +impl<'a> Node for SwitchCase<'a> { + fn loc(&self) -> SourceLocation { + let end = if let Some(last) = self.consequent.last() { + last.loc().end + } else { + self.colon.loc.end + }; + SourceLocation { + start: self.keyword.loc.start, + end, + } + } +} + +impl<'a> From> for crate::stmt::SwitchCase<'a> { + fn from(other: SwitchCase<'a>) -> Self { + Self { + test: other.test.map(From::from), + consequent: other.consequent.into_iter().map(From::from).collect(), + } + } +} + +/// A collection of program parts wrapped in curly braces +#[derive(Debug, Clone, PartialEq)] +pub struct BlockStmt<'a> { + pub open_brace: Slice<'a>, + pub stmts: Vec>, + pub close_brace: Slice<'a>, +} + +impl<'a> From> for crate::stmt::BlockStmt<'a> { + fn from(other: BlockStmt<'a>) -> Self { + Self(other.stmts.into_iter().map(From::from).collect()) + } +} + +impl<'a> Node for BlockStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_brace.loc.start, + end: self.close_brace.loc.end, + } + } +} + +/// A try/catch block +/// ```js +/// try { +/// +/// } catch (e) { +/// +/// } finally { +/// +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct TryStmt<'a> { + pub keyword: Slice<'a>, + pub block: BlockStmt<'a>, + pub handler: Option>, + pub finalizer: Option>, +} + +impl<'a> Node for TryStmt<'a> { + fn loc(&self) -> SourceLocation { + let end = if let Some(finalizer) = &self.finalizer { + finalizer.loc().end + } else if let Some(catch) = &self.handler { + catch.loc().end + } else { + self.block.loc().end + }; + SourceLocation { + start: self.keyword.loc.start, + end, + } + } +} + +impl<'a> From> for crate::stmt::TryStmt<'a> { + fn from(other: TryStmt<'a>) -> Self { + Self { + block: other.block.into(), + handler: other.handler.map(From::from), + finalizer: other.finalizer.map(From::from), + } + } +} + +/// The error handling part of a `TryStmt` +#[derive(Debug, Clone, PartialEq)] +pub struct CatchClause<'a> { + pub keyword: Slice<'a>, + pub param: Option>, + pub body: BlockStmt<'a>, +} + +impl<'a> Node for CatchClause<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::CatchClause<'a> { + fn from(other: CatchClause<'a>) -> Self { + Self { + param: other.param.map(|a| a.param.into()), + body: other.body.into(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CatchArg<'a> { + pub open_paren: Slice<'a>, + pub param: Pat<'a>, + pub close_paren: Slice<'a>, +} + +impl<'a> Node for CatchArg<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.open_paren.loc.start, + end: self.close_paren.loc.end, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct FinallyClause<'a> { + pub keyword: Slice<'a>, + pub body: BlockStmt<'a>, +} + +impl<'a> Node for FinallyClause<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::BlockStmt<'a> { + fn from(other: FinallyClause<'a>) -> Self { + other.body.into() + } +} + +/// A while loop +/// ```js +/// while (false) { +/// +/// } +/// var i = 0; +/// while (i < 100) { +/// if (Math.floor(Math.random() * 100) > 50) { +/// i--; +/// } else { +/// i += 5; +/// } +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct WhileStmt<'a> { + pub keyword: Slice<'a>, + pub open_paren: Slice<'a>, + pub test: Expr<'a>, + pub close_paren: Slice<'a>, + pub body: Box>, +} + +impl<'a> Node for WhileStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::WhileStmt<'a> { + fn from(other: WhileStmt<'a>) -> Self { + Self { + test: other.test.into(), + body: Box::new(From::from(*other.body)), + } + } +} + +/// A while loop that executes its body first +/// ```js +/// do { +/// console.log('at least once') +/// } while (Math.floor(Math.random() * 100) < 75) +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct DoWhileStmt<'a> { + pub keyword_do: Slice<'a>, + pub body: Box>, + pub keyword_while: Slice<'a>, + pub open_paren: Slice<'a>, + pub test: Expr<'a>, + pub close_paren: Slice<'a>, + pub semi_colon: Option>, +} + +impl<'a> Node for DoWhileStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword_do.loc.start, + end: self.close_paren.loc.end, + } + } +} + +impl<'a> From> for crate::stmt::DoWhileStmt<'a> { + fn from(other: DoWhileStmt<'a>) -> Self { + Self { + test: other.test.into(), + body: Box::new(From::from(*other.body)), + } + } +} + +/// A "c-style" for loop +/// ```js +/// for (var i = 0; i < 100; i++) console.log(i); +/// for (;;) { +/// console.log('forever!'); +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct ForStmt<'a> { + pub keyword: Slice<'a>, + pub open_paren: Slice<'a>, + pub init: Option>, + pub semi1: Slice<'a>, + pub test: Option>, + pub semi2: Slice<'a>, + pub update: Option>, + pub close_paren: Slice<'a>, + pub body: Box>, +} + +impl<'a> Node for ForStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::ForStmt<'a> { + fn from(other: ForStmt<'a>) -> Self { + Self { + init: other.init.map(From::from), + test: other.test.map(From::from), + update: other.update.map(From::from), + body: Box::new(From::from(*other.body)), + } + } +} + +/// The left most triple of a for loops parenthetical +/// ```js +/// // vvvvvvvvv +/// for (var i = 0;i < 100; i++) +#[derive(Debug, Clone, PartialEq)] +pub enum LoopInit<'a> { + Variable(VarKind<'a>, Vec>>), + Expr(Expr<'a>), +} + +impl<'a> Node for LoopInit<'a> { + fn loc(&self) -> SourceLocation { + match self { + LoopInit::Variable(kind, decls) => { + if let Some(last) = decls.last() { + SourceLocation { + start: kind.loc().start, + end: last.loc().end, + } + } else { + kind.loc() + } + } + LoopInit::Expr(inner) => inner.loc(), + } + } +} + +impl<'a> From> for crate::stmt::LoopInit<'a> { + fn from(other: LoopInit<'a>) -> Self { + match other { + LoopInit::Expr(inner) => Self::Expr(inner.into()), + LoopInit::Variable(kind, decls) => Self::Variable( + kind.into(), + decls.into_iter().map(|e| e.item.into()).collect(), + ), + } + } +} + +/// A for in statement, this kind of for statement +/// will extract each key from an indexable thing +/// ```js +/// for (var i in [2,3,4,5,6]) { +/// console.log(i); +/// } +/// //prints 0, 1, 2, 3, 4 +/// for (var k in {a: 'b', c: 'd'}) { +/// console.log(k); +/// } +/// //prints a, b +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct ForInStmt<'a> { + pub keyword_for: Slice<'a>, + pub open_paren: Slice<'a>, + pub left: LoopLeft<'a>, + pub keyword_in: Slice<'a>, + pub right: Expr<'a>, + pub close_paren: Slice<'a>, + pub body: Box>, +} + +impl<'a> Node for ForInStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword_for.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::ForInStmt<'a> { + fn from(other: ForInStmt<'a>) -> Self { + Self { + left: other.left.into(), + right: other.right.into(), + body: Box::new(From::from(*other.body)), + } + } +} + +/// A for of statement, this kind of for statement +/// will extract the value from a generator or iterator +/// ```js +/// for (var k of [2, 3, 4, 5, 6]) { +/// console.log(k); +/// } +/// //prints 2, 3, 4, 5, 6 +/// ``` +#[derive(PartialEq, Debug, Clone)] +pub struct ForOfStmt<'a> { + pub keyword_for: Slice<'a>, + pub open_paren: Slice<'a>, + pub left: LoopLeft<'a>, + pub keyword_of: Slice<'a>, + pub right: Expr<'a>, + pub close_paren: Slice<'a>, + pub body: Box>, + pub is_await: bool, +} + +impl<'a> Node for ForOfStmt<'a> { + fn loc(&self) -> SourceLocation { + SourceLocation { + start: self.keyword_for.loc.start, + end: self.body.loc().end, + } + } +} + +impl<'a> From> for crate::stmt::ForOfStmt<'a> { + fn from(other: ForOfStmt<'a>) -> Self { + Self { + left: other.left.into(), + right: other.right.into(), + body: Box::new(From::from(*other.body)), + is_await: other.is_await, + } + } +} + +/// The values on the left hand side of the keyword +/// in a for in or for of loop +#[derive(Debug, Clone, PartialEq)] +pub enum LoopLeft<'a> { + Expr(Expr<'a>), + Variable(VarKind<'a>, VarDecl<'a>), + Pat(Pat<'a>), +} + +impl<'a> From> for crate::stmt::LoopLeft<'a> { + fn from(other: LoopLeft<'a>) -> Self { + match other { + LoopLeft::Expr(inner) => Self::Expr(inner.into()), + LoopLeft::Variable(kind, decl) => Self::Variable(kind.into(), decl.into()), + LoopLeft::Pat(inner) => Self::Pat(inner.into()), + } + } +} + +impl<'a> Node for LoopLeft<'a> { + fn loc(&self) -> SourceLocation { + match self { + LoopLeft::Expr(inner) => inner.loc(), + LoopLeft::Variable(inner, decl) => SourceLocation { + start: inner.loc().start, + end: decl.loc().end, + }, + LoopLeft::Pat(inner) => inner.loc(), + } + } +} diff --git a/src/stmt.rs b/src/stmt.rs index 74b0aba..f289eca 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -1,417 +1,417 @@ -use crate::VarKind; -use crate::decl::VarDecl; -use crate::expr::Expr; -use crate::pat::Pat; -use crate::{Ident, ProgramPart}; -/// A slightly more granular part of an es program than ProgramPart -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum Stmt<'a> { - /// Any expression - Expr(Expr<'a>), - /// A collection of program parts wrapped in curly braces - Block(BlockStmt<'a>), - /// A single semi-colon - Empty, - /// The contextual keyword `debugger` - Debugger, - /// A with statement, this puts one object at the top of - /// the identifier search tree. - /// > note: this cannot be used in a strict context - /// ```js - /// function random() { - /// return 0; - /// } - /// let rand; - /// with (Math) { - /// rand = floor(random() * 100) + 1; - /// } - /// //rand !== 0 - /// ``` - With(WithStmt<'a>), - /// A return statement - /// ```js - /// function thing() { - /// return 'stuff'; - /// } - /// function stuff() { - /// return; - /// } - Return(Option>), - /// A labeled statement - /// ```js - /// label: { - /// break label; - /// } - /// ``` - Labeled(LabeledStmt<'a>), - /// A break statement - /// ```js - /// label: { - /// break label; - /// } - /// while (true) { - /// break; - /// } - /// ``` - Break(Option>), - /// A short circuit continuation of a loop - /// ```js - /// label: while (true) { - /// if (Math.floor(Math.random() * 100) > 50) { - /// continue; - /// } else { - /// console.log('too low') - /// } - /// } - /// ``` - Continue(Option>), - /// An if statement - /// ```js - /// if (1 < 2) { - /// console.log('Always true'); - /// } else { - /// console.log('Never true'); - /// } - /// ``` - If(IfStmt<'a>), - /// A switch statement - /// ```js - /// switch (Math.floor(Math.random()) * 10) { - /// case 1: - /// - /// break; - /// case 2: - /// case 3: - /// case 4: - /// return false; - /// default: - /// return true; - /// } - /// ``` - Switch(SwitchStmt<'a>), - /// A statement that throws an error - /// ```js - /// function error() { - /// throw 'hahahaha'; - /// } - /// - /// function newError() { - /// throw new Error('hohoho'); - /// } - /// ``` - Throw(Expr<'a>), - /// A try/catch block - /// ```js - /// try { - /// - /// } catch (e) { - /// - /// } finally { - /// - /// } - /// ``` - Try(TryStmt<'a>), - /// A while loop - /// ```js - /// while (false) { - /// - /// } - /// var i = 0; - /// while (i < 100) { - /// if (Math.floor(Math.random() * 100) > 50) { - /// i--; - /// } else { - /// i += 5; - /// } - /// } - /// ``` - While(WhileStmt<'a>), - /// A while loop that executes its body first - /// ```js - /// do { - /// console.log('at least once') - /// } while (Math.floor(Math.random() * 100) < 75) - /// ``` - DoWhile(DoWhileStmt<'a>), - /// A "c-style" for loop - /// ```js - /// for (var i = 0; i < 100; i++) console.log(i); - /// for (;;) { - /// console.log('forever!'); - /// } - /// ``` - For(ForStmt<'a>), - /// A for in statement, this kind of for statement - /// will extract each key from an indexable thing - /// ```js - /// for (var i in [2,3,4,5,6]) { - /// console.log(i); - /// } - /// //prints 0, 1, 2, 3, 4 - /// for (var k in {a: 'b', c: 'd'}) { - /// console.log(k); - /// } - /// //prints a, b - /// ``` - ForIn(ForInStmt<'a>), - /// A for of statement, this kind of for statement - /// will extract the value from a generator or iterator - /// ```js - /// for (var k of [2, 3, 4, 5, 6]) { - /// console.log(k); - /// } - /// //prints 2, 3, 4, 5, 6 - /// ``` - ForOf(ForOfStmt<'a>), - /// A var statement - /// ```js - /// var x; - /// var x, y = 'huh?'; - /// ``` - Var(Vec>), -} - -/// A with statement, this puts one object at the top of -/// the identifier search tree. -/// > note: this cannot be used in a strict context -/// ```js -/// function random() { -/// return 0; -/// } -/// let rand; -/// with (Math) { -/// rand = floor(random() * 100) + 1; -/// } -/// //rand !== 0 -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct WithStmt<'a> { - pub object: Expr<'a>, - pub body: Box>, -} - -/// A break statement -/// ```js -/// label: { -/// break label; -/// } -/// while (true) { -/// break; -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct LabeledStmt<'a> { - pub label: Ident<'a>, - pub body: Box>, -} - -/// An if statement -/// ```js -/// if (1 < 2) { -/// console.log('Always true'); -/// } else { -/// console.log('Never true'); -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct IfStmt<'a> { - pub test: Expr<'a>, - pub consequent: Box>, - pub alternate: Option>>, -} - -/// A switch statement -/// ```js -/// switch (Math.floor(Math.random()) * 10) { -/// case 1: -/// -/// break; -/// case 2: -/// case 3: -/// case 4: -/// return false; -/// default: -/// return true; -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct SwitchStmt<'a> { - pub discriminant: Expr<'a>, - pub cases: Vec>, -} - -/// A single case part of a switch statement -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct SwitchCase<'a> { - pub test: Option>, - pub consequent: Vec>, -} - -/// A collection of program parts wrapped in curly braces -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct BlockStmt<'a>(pub Vec>); - -/// A try/catch block -/// ```js -/// try { -/// -/// } catch (e) { -/// -/// } finally { -/// -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct TryStmt<'a> { - pub block: BlockStmt<'a>, - pub handler: Option>, - pub finalizer: Option>, -} - -/// The error handling part of a `TryStmt` -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub struct CatchClause<'a> { - pub param: Option>, - pub body: BlockStmt<'a>, -} - -/// A while loop -/// ```js -/// while (false) { -/// -/// } -/// var i = 0; -/// while (i < 100) { -/// if (Math.floor(Math.random() * 100) > 50) { -/// i--; -/// } else { -/// i += 5; -/// } -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct WhileStmt<'a> { - pub test: Expr<'a>, - pub body: Box>, -} - -/// A while loop that executes its body first -/// ```js -/// do { -/// console.log('at least once') -/// } while (Math.floor(Math.random() * 100) < 75) -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct DoWhileStmt<'a> { - pub test: Expr<'a>, - pub body: Box>, -} - -/// A "c-style" for loop -/// ```js -/// for (var i = 0; i < 100; i++) console.log(i); -/// for (;;) { -/// console.log('forever!'); -/// } -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct ForStmt<'a> { - pub init: Option>, - pub test: Option>, - pub update: Option>, - pub body: Box>, -} - -/// The left most triple of a for loops parenthetical -/// ```js -/// // vvvvvvvvv -/// for (var i = 0;i < 100; i++) -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum LoopInit<'a> { - Variable(VarKind, Vec>), - Expr(Expr<'a>), -} - -/// A for in statement, this kind of for statement -/// will extract each key from an indexable thing -/// ```js -/// for (var i in [2,3,4,5,6]) { -/// console.log(i); -/// } -/// //prints 0, 1, 2, 3, 4 -/// for (var k in {a: 'b', c: 'd'}) { -/// console.log(k); -/// } -/// //prints a, b -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct ForInStmt<'a> { - pub left: LoopLeft<'a>, - pub right: Expr<'a>, - pub body: Box>, -} - -/// A for of statement, this kind of for statement -/// will extract the value from a generator or iterator -/// ```js -/// for (var k of [2, 3, 4, 5, 6]) { -/// console.log(k); -/// } -/// //prints 2, 3, 4, 5, 6 -/// ``` -#[derive(PartialEq, Debug, Clone)] -#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] -pub struct ForOfStmt<'a> { - pub left: LoopLeft<'a>, - pub right: Expr<'a>, - pub body: Box>, - pub is_await: bool, -} - -/// The values on the left hand side of the keyword -/// in a for in or for of loop -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr( - all(feature = "serde", not(feature = "esprima")), - derive(Deserialize, Serialize) -)] -#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] -pub enum LoopLeft<'a> { - Expr(Expr<'a>), - Variable(VarKind, VarDecl<'a>), - Pat(Pat<'a>), -} +use crate::decl::VarDecl; +use crate::expr::Expr; +use crate::pat::Pat; +use crate::VarKind; +use crate::{Ident, ProgramPart}; +/// A slightly more granular part of an es program than ProgramPart +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum Stmt<'a> { + /// Any expression + Expr(Expr<'a>), + /// A collection of program parts wrapped in curly braces + Block(BlockStmt<'a>), + /// A single semi-colon + Empty, + /// The contextual keyword `debugger` + Debugger, + /// A with statement, this puts one object at the top of + /// the identifier search tree. + /// > note: this cannot be used in a strict context + /// ```js + /// function random() { + /// return 0; + /// } + /// let rand; + /// with (Math) { + /// rand = floor(random() * 100) + 1; + /// } + /// //rand !== 0 + /// ``` + With(WithStmt<'a>), + /// A return statement + /// ```js + /// function thing() { + /// return 'stuff'; + /// } + /// function stuff() { + /// return; + /// } + Return(Option>), + /// A labeled statement + /// ```js + /// label: { + /// break label; + /// } + /// ``` + Labeled(LabeledStmt<'a>), + /// A break statement + /// ```js + /// label: { + /// break label; + /// } + /// while (true) { + /// break; + /// } + /// ``` + Break(Option>), + /// A short circuit continuation of a loop + /// ```js + /// label: while (true) { + /// if (Math.floor(Math.random() * 100) > 50) { + /// continue; + /// } else { + /// console.log('too low') + /// } + /// } + /// ``` + Continue(Option>), + /// An if statement + /// ```js + /// if (1 < 2) { + /// console.log('Always true'); + /// } else { + /// console.log('Never true'); + /// } + /// ``` + If(IfStmt<'a>), + /// A switch statement + /// ```js + /// switch (Math.floor(Math.random()) * 10) { + /// case 1: + /// + /// break; + /// case 2: + /// case 3: + /// case 4: + /// return false; + /// default: + /// return true; + /// } + /// ``` + Switch(SwitchStmt<'a>), + /// A statement that throws an error + /// ```js + /// function error() { + /// throw 'hahahaha'; + /// } + /// + /// function newError() { + /// throw new Error('hohoho'); + /// } + /// ``` + Throw(Expr<'a>), + /// A try/catch block + /// ```js + /// try { + /// + /// } catch (e) { + /// + /// } finally { + /// + /// } + /// ``` + Try(TryStmt<'a>), + /// A while loop + /// ```js + /// while (false) { + /// + /// } + /// var i = 0; + /// while (i < 100) { + /// if (Math.floor(Math.random() * 100) > 50) { + /// i--; + /// } else { + /// i += 5; + /// } + /// } + /// ``` + While(WhileStmt<'a>), + /// A while loop that executes its body first + /// ```js + /// do { + /// console.log('at least once') + /// } while (Math.floor(Math.random() * 100) < 75) + /// ``` + DoWhile(DoWhileStmt<'a>), + /// A "c-style" for loop + /// ```js + /// for (var i = 0; i < 100; i++) console.log(i); + /// for (;;) { + /// console.log('forever!'); + /// } + /// ``` + For(ForStmt<'a>), + /// A for in statement, this kind of for statement + /// will extract each key from an indexable thing + /// ```js + /// for (var i in [2,3,4,5,6]) { + /// console.log(i); + /// } + /// //prints 0, 1, 2, 3, 4 + /// for (var k in {a: 'b', c: 'd'}) { + /// console.log(k); + /// } + /// //prints a, b + /// ``` + ForIn(ForInStmt<'a>), + /// A for of statement, this kind of for statement + /// will extract the value from a generator or iterator + /// ```js + /// for (var k of [2, 3, 4, 5, 6]) { + /// console.log(k); + /// } + /// //prints 2, 3, 4, 5, 6 + /// ``` + ForOf(ForOfStmt<'a>), + /// A var statement + /// ```js + /// var x; + /// var x, y = 'huh?'; + /// ``` + Var(Vec>), +} + +/// A with statement, this puts one object at the top of +/// the identifier search tree. +/// > note: this cannot be used in a strict context +/// ```js +/// function random() { +/// return 0; +/// } +/// let rand; +/// with (Math) { +/// rand = floor(random() * 100) + 1; +/// } +/// //rand !== 0 +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct WithStmt<'a> { + pub object: Expr<'a>, + pub body: Box>, +} + +/// A break statement +/// ```js +/// label: { +/// break label; +/// } +/// while (true) { +/// break; +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct LabeledStmt<'a> { + pub label: Ident<'a>, + pub body: Box>, +} + +/// An if statement +/// ```js +/// if (1 < 2) { +/// console.log('Always true'); +/// } else { +/// console.log('Never true'); +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct IfStmt<'a> { + pub test: Expr<'a>, + pub consequent: Box>, + pub alternate: Option>>, +} + +/// A switch statement +/// ```js +/// switch (Math.floor(Math.random()) * 10) { +/// case 1: +/// +/// break; +/// case 2: +/// case 3: +/// case 4: +/// return false; +/// default: +/// return true; +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct SwitchStmt<'a> { + pub discriminant: Expr<'a>, + pub cases: Vec>, +} + +/// A single case part of a switch statement +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct SwitchCase<'a> { + pub test: Option>, + pub consequent: Vec>, +} + +/// A collection of program parts wrapped in curly braces +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct BlockStmt<'a>(pub Vec>); + +/// A try/catch block +/// ```js +/// try { +/// +/// } catch (e) { +/// +/// } finally { +/// +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct TryStmt<'a> { + pub block: BlockStmt<'a>, + pub handler: Option>, + pub finalizer: Option>, +} + +/// The error handling part of a `TryStmt` +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub struct CatchClause<'a> { + pub param: Option>, + pub body: BlockStmt<'a>, +} + +/// A while loop +/// ```js +/// while (false) { +/// +/// } +/// var i = 0; +/// while (i < 100) { +/// if (Math.floor(Math.random() * 100) > 50) { +/// i--; +/// } else { +/// i += 5; +/// } +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct WhileStmt<'a> { + pub test: Expr<'a>, + pub body: Box>, +} + +/// A while loop that executes its body first +/// ```js +/// do { +/// console.log('at least once') +/// } while (Math.floor(Math.random() * 100) < 75) +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct DoWhileStmt<'a> { + pub test: Expr<'a>, + pub body: Box>, +} + +/// A "c-style" for loop +/// ```js +/// for (var i = 0; i < 100; i++) console.log(i); +/// for (;;) { +/// console.log('forever!'); +/// } +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct ForStmt<'a> { + pub init: Option>, + pub test: Option>, + pub update: Option>, + pub body: Box>, +} + +/// The left most triple of a for loops parenthetical +/// ```js +/// // vvvvvvvvv +/// for (var i = 0;i < 100; i++) +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum LoopInit<'a> { + Variable(VarKind, Vec>), + Expr(Expr<'a>), +} + +/// A for in statement, this kind of for statement +/// will extract each key from an indexable thing +/// ```js +/// for (var i in [2,3,4,5,6]) { +/// console.log(i); +/// } +/// //prints 0, 1, 2, 3, 4 +/// for (var k in {a: 'b', c: 'd'}) { +/// console.log(k); +/// } +/// //prints a, b +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct ForInStmt<'a> { + pub left: LoopLeft<'a>, + pub right: Expr<'a>, + pub body: Box>, +} + +/// A for of statement, this kind of for statement +/// will extract the value from a generator or iterator +/// ```js +/// for (var k of [2, 3, 4, 5, 6]) { +/// console.log(k); +/// } +/// //prints 2, 3, 4, 5, 6 +/// ``` +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(all(feature = "serialization"), derive(Deserialize, Serialize))] +pub struct ForOfStmt<'a> { + pub left: LoopLeft<'a>, + pub right: Expr<'a>, + pub body: Box>, + pub is_await: bool, +} + +/// The values on the left hand side of the keyword +/// in a for in or for of loop +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + all(feature = "serde", not(feature = "esprima")), + derive(Deserialize, Serialize) +)] +#[cfg_attr(all(feature = "serde", feature = "esprima"), derive(Deserialize))] +pub enum LoopLeft<'a> { + Expr(Expr<'a>), + Variable(VarKind, VarDecl<'a>), + Pat(Pat<'a>), +} diff --git a/tests/serde.rs b/tests/serde.rs index bf24279..b36a886 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -1,190 +1,169 @@ -#![cfg(feature = "esprima")] -use ressa::*; -use pretty_env_logger::try_init; - -use serde_json::{ - to_string_pretty, - Value, - from_str, -}; -use resast::prelude::*; -use std::{ - fs::{ - read_to_string, - }, - path::Path, -}; -#[test] -fn serde1() { - let ast = Program::Script(vec![ - ProgramPart::Decl( - Decl::Func( - Func { - id: Some(Ident::from("f")), - body: FuncBody(vec![]), - is_async: false, - generator: false, - params: vec![ - FuncArg::Expr( - Expr::Ident( - Ident::from("a") - ) - ) - ] - } - ) - ) - ]); - let json = to_string_pretty(&ast).expect("Failed to serialize ast"); - let expectation = r#"{ - "type": "Program", - "body": [ - { - "type": "FunctionDeclaration", - "id": { - "type": "Identifier", - "name": "f" - }, - "params": [ - { - "type": "Identifier", - "name": "a" - } - ], - "body": { - "type": "BlockStatement", - "body": [] - }, - "generator": false, - "expression": false, - "async": false - } - ], - "sourceType": "script" -} -"#; - - let r: serde_json::Value = serde_json::from_str(&json).expect("failed to deserialize json"); - let j: serde_json::Value = serde_json::from_str(&expectation).expect("failed to deserialize expectation"); - assert_eq!(r, j); -} - - -#[test] -fn serde_es5() { - let json = run_rs_parse("node_modules/everything.js/es5.js"); - let esparsed = esparse("node_modules/everything.js/es5.js"); - check_jsons("es5", json, esparsed); -} -#[test] -fn serde_es2015_script() { - let json = run_rs_parse("node_modules/everything.js/es2015-script.js"); - let esparsed = esparse("node_modules/everything.js/es2015-script.js"); - check_jsons("es2015-script", json, esparsed); -} -#[test] -fn serde_es2015_module() { - let json = run_rs_parse("node_modules/everything.js/es2015-module.js"); - let esparsed = esparse("node_modules/everything.js/es2015-module.js"); - check_jsons("es2015-module", json, esparsed); -} - -fn run_rs_parse(path: impl AsRef) -> Value { - let module = path.as_ref().ends_with("es2015-module.js"); - eprintln!("working on {:?} -> {}", path.as_ref(), module); - let js = get_js_file(path); - let mut b = Builder::new(); - b.set_module(module); - let mut parser = b.js(&js).build().expect("failed to create parser"); - let parsed = parser.parse().expect("failed to parse js"); - let raw = to_string_pretty(&parsed).expect("failed to convert to json string"); - from_str(&raw).expect("failed to revert to Value") -} - -fn check_jsons(name: &str, lhs: Value, rhs: Value) { - if lhs != rhs { - let f1 = ::std::fs::File::create( - &format!("{}.rs.json", name) - ).expect("failed to write rs.json"); - serde_json::to_writer_pretty(f1, &lhs).expect(""); - let f2 = ::std::fs::File::create( - &format!("{}.js.json", name) - ).expect("failed to write js.json"); - serde_json::to_writer_pretty(f2, &rhs).expect(""); - panic!("json doesn't match"); - } -} - -pub fn npm_install() { - let mut c = ::std::process::Command::new("npm"); - c.arg("i"); - c.output().expect("failed to run npm install"); -} - -pub fn get_js_file(path: impl AsRef<::std::path::Path>) -> String { - let path = path.as_ref(); - if !path.exists() { - npm_install(); - if !path.exists() { - panic!("npm install failed to make {:?} available", path); - } - } - read_to_string(path).expect(&format!("failed to read {} to a string", path.display())) -} - -pub fn esparse(path: impl AsRef<::std::path::Path>) -> Value { - let path = path.as_ref(); - if !path.exists() { - npm_install(); - if !path.exists() { - panic!("npm install failed to make {:?} available", path); - } - } - let esparse = ::std::process::Command::new("node") - .arg("run_es_parse.js") - .arg(path) - .output() - .expect("failed to execute run_es_parse.js"); - let json = String::from_utf8_lossy(&esparse.stdout).to_string(); - from_str(&json).expect(&format!("failed to convert {} to Value", path.display())) -} - -#[test] -fn func_args() { - let js = "function f(a, b = 0, [c,, d = 0, ...e], {f, g: h, i = 0, i: j = 0}, ...k){}"; - let mut parser = Parser::new(&js).expect(""); - let parsed = parser.parse().expect(""); - let raw = to_string_pretty(&parsed).expect("failed to convert ron to string"); - let json: Value = from_str(&raw).expect("failed to convert string to json"); - ::std::fs::write("args.js", js).expect("failed to write args.js"); - let esparsed = esparse("args.js"); - let _ = ::std::fs::remove_file("args.js"); - if json != esparsed { - let f1 = ::std::fs::File::create("func_args.rs.json").expect("failed to create rs.json"); - serde_json::to_writer_pretty(f1, &json).expect("failed to write rs.json"); - let f2 = ::std::fs::File::create("func_args.js.json").expect("failed to create js.json"); - serde_json::to_writer_pretty(f2, &esparsed).expect("failed to write js.json"); - panic!("json doesn't match"); - } -} - -#[test] -fn arrow_func_args() { - let _ = try_init(); - let js = "({i = 0}, ...k) => {;};"; - let mut parser = Parser::new(&js).expect(""); - let parsed = parser.parse().expect(""); - let raw = to_string_pretty(&parsed).expect("failed to convert ron to string"); - let json: Value = from_str(&raw).expect("failed to convert string to json"); - ::std::fs::write("arrow-args.js", js).expect("failed to write args.js"); - let esparsed = esparse("arrow-args.js"); - let _ = ::std::fs::remove_file("arrow-args.js"); - if json != esparsed { - let f1 = ::std::fs::File::create("arrow_func_args.rs.json").expect("failed to create rs.json"); - serde_json::to_writer_pretty(f1, &json).expect("failed to write rs.json"); - let f2 = ::std::fs::File::create("arrow_func_args.js.json").expect("failed to create js.json"); - serde_json::to_writer_pretty(f2, &esparsed).expect("failed to write js.json"); - let _ = ::std::fs::write("arrow_func_args2.ron", &format!("{:#?}", parsed)); - panic!("json doesn't match"); - } -} \ No newline at end of file +#![cfg(feature = "esprima")] +use pretty_env_logger::try_init; +use ressa::*; + +use resast::prelude::*; +use serde_json::{from_str, to_string_pretty, Value}; +use std::{fs::read_to_string, path::Path}; +#[test] +fn serde1() { + let ast = Program::Script(vec![ProgramPart::Decl(Decl::Func(Func { + id: Some(Ident::from("f")), + body: FuncBody(vec![]), + is_async: false, + generator: false, + params: vec![FuncArg::Expr(Expr::Ident(Ident::from("a")))], + }))]); + let json = to_string_pretty(&ast).expect("Failed to serialize ast"); + let expectation = r#"{ + "type": "Program", + "body": [ + { + "type": "FunctionDeclaration", + "id": { + "type": "Identifier", + "name": "f" + }, + "params": [ + { + "type": "Identifier", + "name": "a" + } + ], + "body": { + "type": "BlockStatement", + "body": [] + }, + "generator": false, + "expression": false, + "async": false + } + ], + "sourceType": "script" +} +"#; + + let r: serde_json::Value = serde_json::from_str(&json).expect("failed to deserialize json"); + let j: serde_json::Value = + serde_json::from_str(&expectation).expect("failed to deserialize expectation"); + assert_eq!(r, j); +} + +#[test] +fn serde_es5() { + let json = run_rs_parse("node_modules/everything.js/es5.js"); + let esparsed = esparse("node_modules/everything.js/es5.js"); + check_jsons("es5", json, esparsed); +} +#[test] +fn serde_es2015_script() { + let json = run_rs_parse("node_modules/everything.js/es2015-script.js"); + let esparsed = esparse("node_modules/everything.js/es2015-script.js"); + check_jsons("es2015-script", json, esparsed); +} +#[test] +fn serde_es2015_module() { + let json = run_rs_parse("node_modules/everything.js/es2015-module.js"); + let esparsed = esparse("node_modules/everything.js/es2015-module.js"); + check_jsons("es2015-module", json, esparsed); +} + +fn run_rs_parse(path: impl AsRef) -> Value { + let module = path.as_ref().ends_with("es2015-module.js"); + eprintln!("working on {:?} -> {}", path.as_ref(), module); + let js = get_js_file(path); + let mut b = Builder::new(); + b.set_module(module); + let mut parser = b.js(&js).build().expect("failed to create parser"); + let parsed = parser.parse().expect("failed to parse js"); + let raw = to_string_pretty(&parsed).expect("failed to convert to json string"); + from_str(&raw).expect("failed to revert to Value") +} + +fn check_jsons(name: &str, lhs: Value, rhs: Value) { + if lhs != rhs { + let f1 = + ::std::fs::File::create(&format!("{}.rs.json", name)).expect("failed to write rs.json"); + serde_json::to_writer_pretty(f1, &lhs).expect(""); + let f2 = + ::std::fs::File::create(&format!("{}.js.json", name)).expect("failed to write js.json"); + serde_json::to_writer_pretty(f2, &rhs).expect(""); + panic!("json doesn't match"); + } +} + +pub fn npm_install() { + let mut c = ::std::process::Command::new("npm"); + c.arg("i"); + c.output().expect("failed to run npm install"); +} + +pub fn get_js_file(path: impl AsRef<::std::path::Path>) -> String { + let path = path.as_ref(); + if !path.exists() { + npm_install(); + if !path.exists() { + panic!("npm install failed to make {:?} available", path); + } + } + read_to_string(path).expect(&format!("failed to read {} to a string", path.display())) +} + +pub fn esparse(path: impl AsRef<::std::path::Path>) -> Value { + let path = path.as_ref(); + if !path.exists() { + npm_install(); + if !path.exists() { + panic!("npm install failed to make {:?} available", path); + } + } + let esparse = ::std::process::Command::new("node") + .arg("run_es_parse.js") + .arg(path) + .output() + .expect("failed to execute run_es_parse.js"); + let json = String::from_utf8_lossy(&esparse.stdout).to_string(); + from_str(&json).expect(&format!("failed to convert {} to Value", path.display())) +} + +#[test] +fn func_args() { + let js = "function f(a, b = 0, [c,, d = 0, ...e], {f, g: h, i = 0, i: j = 0}, ...k){}"; + let mut parser = Parser::new(&js).expect(""); + let parsed = parser.parse().expect(""); + let raw = to_string_pretty(&parsed).expect("failed to convert ron to string"); + let json: Value = from_str(&raw).expect("failed to convert string to json"); + ::std::fs::write("args.js", js).expect("failed to write args.js"); + let esparsed = esparse("args.js"); + let _ = ::std::fs::remove_file("args.js"); + if json != esparsed { + let f1 = ::std::fs::File::create("func_args.rs.json").expect("failed to create rs.json"); + serde_json::to_writer_pretty(f1, &json).expect("failed to write rs.json"); + let f2 = ::std::fs::File::create("func_args.js.json").expect("failed to create js.json"); + serde_json::to_writer_pretty(f2, &esparsed).expect("failed to write js.json"); + panic!("json doesn't match"); + } +} + +#[test] +fn arrow_func_args() { + let _ = try_init(); + let js = "({i = 0}, ...k) => {;};"; + let mut parser = Parser::new(&js).expect(""); + let parsed = parser.parse().expect(""); + let raw = to_string_pretty(&parsed).expect("failed to convert ron to string"); + let json: Value = from_str(&raw).expect("failed to convert string to json"); + ::std::fs::write("arrow-args.js", js).expect("failed to write args.js"); + let esparsed = esparse("arrow-args.js"); + let _ = ::std::fs::remove_file("arrow-args.js"); + if json != esparsed { + let f1 = + ::std::fs::File::create("arrow_func_args.rs.json").expect("failed to create rs.json"); + serde_json::to_writer_pretty(f1, &json).expect("failed to write rs.json"); + let f2 = + ::std::fs::File::create("arrow_func_args.js.json").expect("failed to create js.json"); + serde_json::to_writer_pretty(f2, &esparsed).expect("failed to write js.json"); + let _ = ::std::fs::write("arrow_func_args2.ron", &format!("{:#?}", parsed)); + panic!("json doesn't match"); + } +}