1#![doc = include_str!("../ABOUT.md")]
16#![doc(
17 html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg",
18 html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg"
19)]
20#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
21#![allow(
22 clippy::module_name_repetitions,
23 clippy::too_many_lines,
24 clippy::option_if_let_else
25)]
26
27mod module_item_list;
28mod position;
29mod punctuator;
30mod source;
31mod source_text;
32mod statement_list;
33
34pub mod declaration;
35pub mod expression;
36pub mod function;
37pub mod keyword;
38pub mod operations;
39pub mod pattern;
40pub mod property;
41pub mod scope;
42pub mod scope_analyzer;
43pub mod statement;
44pub mod visitor;
45
46use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
47use boa_string::{JsStr, JsString};
48use expression::Identifier;
49
50pub use self::{
51 declaration::Declaration,
52 expression::Expression,
53 keyword::Keyword,
54 module_item_list::{ModuleItem, ModuleItemList},
55 position::{
56 LinearPosition, LinearSpan, LinearSpanIgnoreEq, Position, PositionGroup, Span, Spanned,
57 },
58 punctuator::Punctuator,
59 source::{Module, Script},
60 source_text::SourceText,
61 statement::Statement,
62 statement_list::{StatementList, StatementListItem},
63};
64
65fn join_nodes<N>(interner: &Interner, nodes: &[N]) -> String
67where
68 N: ToInternedString,
69{
70 let mut first = true;
71 let mut buf = String::new();
72 for e in nodes {
73 if first {
74 first = false;
75 } else {
76 buf.push_str(", ");
77 }
78 buf.push_str(&e.to_interned_string(interner));
79 }
80 buf
81}
82
83fn block_to_string(body: &StatementList, interner: &Interner, indentation: usize) -> String {
88 if body.statements().is_empty() {
89 "{}".to_owned()
90 } else {
91 format!(
92 "{{\n{}{}}}",
93 body.to_indented_string(interner, indentation + 1),
94 " ".repeat(indentation)
95 )
96 }
97}
98
99trait ToStringEscaped {
101 fn to_string_escaped(&self) -> String;
104}
105
106impl ToStringEscaped for [u16] {
107 fn to_string_escaped(&self) -> String {
108 char::decode_utf16(self.iter().copied())
109 .map(|r| match r {
110 Ok(c) => String::from(c),
111 Err(e) => format!("\\u{:04X}", e.unpaired_surrogate()),
112 })
113 .collect()
114 }
115}
116
117pub(crate) trait ToJsString {
118 fn to_js_string(&self, interner: &Interner) -> JsString;
119}
120
121impl ToJsString for Sym {
122 #[allow(clippy::cast_possible_truncation)]
123 fn to_js_string(&self, interner: &Interner) -> JsString {
124 let utf16 = interner.resolve_expect(*self).utf16();
125 if interner.is_latin1(*self) {
126 let bytes: Vec<u8> = utf16.iter().map(|&c| c as u8).collect();
127 JsString::from(JsStr::latin1(&bytes))
128 } else {
129 JsString::from(utf16)
130 }
131 }
132}
133
134impl ToJsString for Identifier {
135 fn to_js_string(&self, interner: &Interner) -> JsString {
136 self.sym().to_js_string(interner)
137 }
138}