1use self::syntax::OptionSpec;
35use crate::common::output;
36use crate::common::report::{merge_reports, report_error, report_failure};
37use thiserror::Error;
38use yash_env::Env;
39use yash_env::function::Function;
40use yash_env::option::State;
41use yash_env::semantics::Field;
42use yash_env::source::Location;
43use yash_env::source::pretty::{Report, ReportType, Snippet, Span, SpanRole, add_span};
44use yash_env::system::{Fcntl, Isatty, Write};
45use yash_env::variable::{Value, Variable};
46
47mod print_functions;
48mod print_variables;
49mod set_functions;
50mod set_variables;
51pub mod syntax;
52
53#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55#[non_exhaustive]
56pub enum VariableAttr {
57 ReadOnly,
59 Export,
61}
62
63impl VariableAttr {
64 #[must_use]
66 pub fn test(&self, var: &Variable) -> State {
67 let is_on = match self {
68 VariableAttr::ReadOnly => var.is_read_only(),
69 VariableAttr::Export => var.is_exported,
70 };
71 State::from(is_on)
72 }
73}
74
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
77#[non_exhaustive]
78pub enum Scope {
79 Global,
87
88 Local,
96}
97
98#[derive(Clone, Debug, Eq, PartialEq)]
100pub struct SetVariables {
101 pub variables: Vec<Field>,
103 pub attrs: Vec<(VariableAttr, State)>,
105 pub scope: Scope,
107}
108
109#[derive(Clone, Debug, Eq, PartialEq)]
111pub struct PrintVariables {
112 pub variables: Vec<Field>,
116 pub attrs: Vec<(VariableAttr, State)>,
118 pub scope: Scope,
120}
121
122#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
124#[non_exhaustive]
125pub enum FunctionAttr {
126 ReadOnly,
128}
129
130impl FunctionAttr {
131 #[must_use]
133 fn test<S>(&self, function: &Function<S>) -> State {
134 let is_on = match self {
135 Self::ReadOnly => function.is_read_only(),
136 };
137 State::from(is_on)
138 }
139}
140
141#[derive(Clone, Debug, Eq, PartialEq)]
143pub struct SetFunctions {
144 pub functions: Vec<Field>,
146 pub attrs: Vec<(FunctionAttr, State)>,
148}
149
150#[derive(Clone, Debug, Eq, PartialEq)]
152pub struct PrintFunctions {
153 pub functions: Vec<Field>,
157 pub attrs: Vec<(FunctionAttr, State)>,
159}
160
161#[derive(Clone, Debug, Eq, PartialEq)]
167pub struct PrintContext<'a> {
168 pub builtin_name: &'a str,
171
172 pub builtin_is_significant: bool,
186
187 pub options_allowed: &'a [OptionSpec<'a>],
193}
194
195pub const PRINT_CONTEXT: PrintContext<'static> = PrintContext {
197 builtin_name: "typeset",
198 builtin_is_significant: false,
199 options_allowed: self::syntax::ALL_OPTIONS,
200};
201
202#[derive(Clone, Debug, Eq, PartialEq)]
209pub enum Command {
210 SetVariables(SetVariables),
211 PrintVariables(PrintVariables),
212 SetFunctions(SetFunctions),
213 PrintFunctions(PrintFunctions),
214}
215
216impl From<SetVariables> for Command {
217 fn from(v: SetVariables) -> Self {
218 Self::SetVariables(v)
219 }
220}
221
222impl From<PrintVariables> for Command {
223 fn from(v: PrintVariables) -> Self {
224 Self::PrintVariables(v)
225 }
226}
227
228impl From<SetFunctions> for Command {
229 fn from(v: SetFunctions) -> Self {
230 Self::SetFunctions(v)
231 }
232}
233
234impl From<PrintFunctions> for Command {
235 fn from(v: PrintFunctions) -> Self {
236 Self::PrintFunctions(v)
237 }
238}
239
240impl Command {
241 pub fn execute<S>(
248 self,
249 env: &mut Env<S>,
250 print_context: &PrintContext,
251 ) -> Result<String, Vec<ExecuteError>> {
252 match self {
253 Self::SetVariables(command) => command.execute(env),
254 Self::PrintVariables(command) => command.execute(&env.variables, print_context),
255 Self::SetFunctions(command) => command.execute(&mut env.functions),
256 Self::PrintFunctions(command) => command.execute(&env.functions, print_context),
257 }
258 }
259}
260
261#[derive(Clone, Debug, Eq, Error, PartialEq)]
263#[error("cannot assign to read-only variable {name:?}")]
264pub struct AssignReadOnlyError {
265 pub name: String,
267 pub new_value: Value,
269 pub assigned_location: Location,
271 pub read_only_location: Location,
273}
274
275impl From<AssignReadOnlyError> for yash_env::variable::AssignError {
276 fn from(e: AssignReadOnlyError) -> Self {
277 Self {
278 new_value: e.new_value,
279 assigned_location: Some(e.assigned_location),
280 read_only_location: e.read_only_location,
281 }
282 }
283}
284
285#[cfg(feature = "yash-semantics")]
288impl From<AssignReadOnlyError> for yash_semantics::expansion::AssignReadOnlyError {
289 fn from(e: AssignReadOnlyError) -> Self {
290 Self {
291 name: e.name,
292 new_value: e.new_value,
293 read_only_location: e.read_only_location,
294 vacancy: None,
295 }
296 }
297}
298
299impl AssignReadOnlyError {
300 #[must_use]
302 pub fn to_report(&self) -> Report<'_> {
303 let mut report = Report::new();
304 report.r#type = ReportType::Error;
305 report.title = "error assigning to variable".into();
306 report.snippets =
307 Snippet::with_primary_span(&self.assigned_location, self.to_string().into());
308 add_span(
309 &self.read_only_location.code,
310 Span {
311 range: self.read_only_location.byte_range(),
312 role: SpanRole::Supplementary {
313 label: "the variable was made read-only here".into(),
314 },
315 },
316 &mut report.snippets,
317 );
318 report
319 }
320}
321
322impl<'a> From<&'a AssignReadOnlyError> for Report<'a> {
323 #[inline]
324 fn from(error: &'a AssignReadOnlyError) -> Self {
325 error.to_report()
326 }
327}
328
329#[derive(Clone, Debug, Error, Eq, PartialEq)]
332#[error("cannot cancel read-only-ness of {name}")]
333pub struct UndoReadOnlyError {
334 pub name: Field,
336 pub read_only_location: Location,
338}
339
340#[derive(Clone, Debug, Error, Eq, PartialEq)]
342pub enum ExecuteError {
343 AssignReadOnlyVariable(#[from] AssignReadOnlyError),
345 UndoReadOnlyVariable(UndoReadOnlyError),
347 UndoReadOnlyFunction(UndoReadOnlyError),
349 ModifyUnsetFunction(Field),
351 PrintUnsetVariable(Field),
353 PrintUnsetFunction(Field),
355}
356
357impl std::fmt::Display for ExecuteError {
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 match self {
360 Self::AssignReadOnlyVariable(e) => e.fmt(f),
361 Self::UndoReadOnlyVariable(e) => {
362 write!(f, "cannot cancel read-only-ness of variable `{}`", e.name)
363 }
364 Self::UndoReadOnlyFunction(e) => {
365 write!(f, "cannot cancel read-only-ness of function `{}`", e.name)
366 }
367 Self::ModifyUnsetFunction(field) => {
368 write!(f, "cannot modify non-existing function `{}`", field)
369 }
370 Self::PrintUnsetVariable(field) => {
371 write!(f, "cannot print non-existing variable `{}`", field)
372 }
373 Self::PrintUnsetFunction(field) => {
374 write!(f, "cannot print non-existing function `{}`", field)
375 }
376 }
377 }
378}
379
380impl ExecuteError {
381 #[must_use]
383 pub fn to_report(&self) -> Report<'_> {
384 let (title, location, label) = match self {
385 Self::AssignReadOnlyVariable(error) => return error.to_report(),
386 Self::UndoReadOnlyVariable(error) => (
387 "cannot cancel read-only-ness of variable",
388 &error.name.origin,
389 format!("read-only variable `{}`", error.name.value).into(),
390 ),
391 Self::UndoReadOnlyFunction(error) => (
392 "cannot cancel read-only-ness of function",
393 &error.name.origin,
394 format!("read-only function `{}`", error.name.value).into(),
395 ),
396 Self::ModifyUnsetFunction(field) => (
397 "cannot modify non-existing function",
398 &field.origin,
399 format!("non-existing function `{field}`").into(),
400 ),
401 Self::PrintUnsetVariable(field) => (
402 "cannot print non-existing variable",
403 &field.origin,
404 format!("non-existing variable `{field}`").into(),
405 ),
406 Self::PrintUnsetFunction(field) => (
407 "cannot print non-existing function",
408 &field.origin,
409 format!("non-existing function `{field}`").into(),
410 ),
411 };
412
413 let mut report = Report::new();
414 report.r#type = ReportType::Error;
415 report.title = title.into();
416 report.snippets = Snippet::with_primary_span(location, label);
417 match self {
418 Self::UndoReadOnlyVariable(error) => add_span(
419 &error.read_only_location.code,
420 Span {
421 range: error.read_only_location.byte_range(),
422 role: SpanRole::Supplementary {
423 label: "the variable was made read-only here".into(),
424 },
425 },
426 &mut report.snippets,
427 ),
428 Self::UndoReadOnlyFunction(error) => add_span(
429 &error.read_only_location.code,
430 Span {
431 range: error.read_only_location.byte_range(),
432 role: SpanRole::Supplementary {
433 label: "the function was made read-only here".into(),
434 },
435 },
436 &mut report.snippets,
437 ),
438 _ => { }
439 }
440 report
441 }
442}
443
444impl<'a> From<&'a ExecuteError> for Report<'a> {
445 #[inline]
446 fn from(error: &'a ExecuteError) -> Self {
447 error.to_report()
448 }
449}
450
451pub async fn main<S>(env: &mut Env<S>, args: Vec<Field>) -> yash_env::builtin::Result
453where
454 S: Fcntl + Isatty + Write,
455{
456 match syntax::parse(syntax::ALL_OPTIONS, args) {
457 Ok((options, operands)) => match syntax::interpret(options, operands) {
458 Ok(command) => match command.execute(env, &PRINT_CONTEXT) {
459 Ok(result) => output(env, &result).await,
460 Err(errors) => report_failure(env, merge_reports(&errors).unwrap()).await,
461 },
462 Err(error) => report_error(env, &error).await,
463 },
464 Err(error) => report_error(env, &error).await,
465 }
466}