hush 0.1.2

Hush is a unix shell scripting language based on the Lua programming language
use std::fmt::Display as _;

use super::{
	ArgPart,
	ArgExpansion,
	ArgUnit,
	CommandOperator,
	Keyword,
	Literal,
	Operator,
	Token,
	TokenKind
};
use crate::{
	fmt::{self, Display},
	symbol,
	term::color,
};


impl std::fmt::Display for Keyword {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		color
			::Fg(
				color::Blue,
				match self {
					Self::Let => "let",
					Self::If => "if",
					Self::Then => "then",
					Self::Else => "else",
					Self::End => "end",
					Self::For => "for",
					Self::In => "in",
					Self::Do => "do",
					Self::While => "while",
					Self::Function => "function",
					Self::Return => "return",
					Self::Break => "break",
					Self::Self_ => "self",
				}
			)
			.fmt(f)
	}
}


impl std::fmt::Display for Literal {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		match self {
			Self::Nil => color::Fg(color::Blue, "nil").fmt(f),
			Self::True => color::Fg(color::Blue, "true").fmt(f),
			Self::False => color::Fg(color::Blue, "false").fmt(f),
			Self::Int(i) => i.fmt(f),
			Self::Float(n) => n.fmt(f),
			Self::Byte(c) => write!(f, "'{}'", color::Bold((*c as char).escape_debug())),
			Self::String(s) => write!(
				f,
				"\"{}\"",
				color::Bold(String::from_utf8_lossy(s).escape_debug())
			),
		}
	}
}


impl std::fmt::Display for Operator {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		match self {
			Self::Plus => color::Fg(color::Yellow, "+").fmt(f),
			Self::Minus => color::Fg(color::Yellow, "-").fmt(f),
			Self::Times => color::Fg(color::Yellow, "*").fmt(f),
			Self::Div => color::Fg(color::Yellow, "/").fmt(f),
			Self::Mod => color::Fg(color::Yellow, "%").fmt(f),
			Self::Equals => color::Fg(color::Yellow, "==").fmt(f),
			Self::NotEquals => color::Fg(color::Yellow, "!=").fmt(f),
			Self::Greater => color::Fg(color::Yellow, ">").fmt(f),
			Self::GreaterEquals => color::Fg(color::Yellow, ">=").fmt(f),
			Self::Lower => color::Fg(color::Yellow, "<").fmt(f),
			Self::LowerEquals => color::Fg(color::Yellow, "<=").fmt(f),
			Self::Not => color::Fg(color::Blue, "not").fmt(f),
			Self::And => color::Fg(color::Blue, "and").fmt(f),
			Self::Or => color::Fg(color::Blue, "or").fmt(f),
			Self::Concat => color::Fg(color::Yellow, "++").fmt(f),
			Self::Dot => color::Fg(color::Yellow, ".").fmt(f),
			Self::Assign => "=".fmt(f),
			Self::Try => color::Fg(color::Yellow, "?").fmt(f),
		}
	}
}


impl<'a> Display<'a> for ArgUnit {
	type Context = &'a symbol::Interner;

	fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
		match self {
			Self::Literal(s) => String::from_utf8_lossy(s).escape_debug().fmt(f),
			Self::Dollar { symbol, .. } => {
				"${{".fmt(f)?;
				symbol.fmt(f, context)?;
				"}}".fmt(f)
			}
		}
	}
}


impl<'a> Display<'a> for ArgExpansion {
	type Context = &'a symbol::Interner;

	fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
		match self {
			Self::Home => color::Fg(color::Yellow, "~/").fmt(f),
			Self::Range(start, end) => {
				color::Fg(color::Yellow, "{").fmt(f)?;
				start.fmt(f)?;
				color::Fg(color::Yellow, "..").fmt(f)?;
				end.fmt(f)?;
				color::Fg(color::Yellow, "}").fmt(f)
			},
			Self::Collection(items) => {
				color::Fg(color::Yellow, "{").fmt(f)?;

				fmt::sep_by(
					items.iter(),
					f,
					|item, f| item.fmt(f, context),
					color::Fg(color::Yellow, ",")
				)?;

				color::Fg(color::Yellow, "}").fmt(f)
			},

			Self::Star => color::Fg(color::Yellow, "*").fmt(f),
			Self::Percent => color::Fg(color::Yellow, "%").fmt(f),
			Self::CharClass(chars) => {
				color::Fg(color::Yellow, "[").fmt(f)?;

				color
					::Fg(
						color::Yellow,
						String::from_utf8_lossy(chars).escape_debug()
					)
					.fmt(f)?;

				color::Fg(color::Yellow, "]").fmt(f)
			},
		}
	}
}


impl<'a> Display<'a> for ArgPart {
	type Context = &'a symbol::Interner;

	fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
		match self {
			Self::Unquoted(arg) => arg.fmt(f, context),
			Self::SingleQuoted(s) => write!(f, "'{}'", String::from_utf8_lossy(s).escape_debug()),
			Self::DoubleQuoted(args) => {
				'"'.fmt(f)?;

				for arg in args.iter() {
					arg.fmt(f, context)?;
				}

				'"'.fmt(f)
			},
			Self::Expansion(expansion) => expansion.fmt(f, context),
			Self::EnvAssign => color::Fg(color::Yellow, "=").fmt(f),
		}
	}
}


impl std::fmt::Display for CommandOperator {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		color
			::Fg(
				color::Yellow,
				match self {
					Self::Output { append: true } => ">>",
					Self::Output { append: false } => ">",
					Self::Input { literal: true } => "<<",
					Self::Input { literal: false } => "<",
					Self::Try => "?",
				}
			)
			.fmt(f)
	}
}


impl<'a> Display<'a> for TokenKind {
	type Context = &'a symbol::Interner;

	fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
		match self {
			Self::Identifier(s) => s.fmt(f, context),
			Self::Keyword(kw) => kw.fmt(f),
			Self::Operator(op) => op.fmt(f),
			Self::Literal(lit) => lit.fmt(f),
			Self::Colon => ":".fmt(f),
			Self::Comma => ",".fmt(f),
			Self::OpenParens => "(".fmt(f),
			Self::CloseParens => ")".fmt(f),
			Self::OpenBracket => "[".fmt(f),
			Self::OpenDict => "@[".fmt(f),
			Self::CloseBracket => "]".fmt(f),
			Self::Command => "{".fmt(f),
			Self::CaptureCommand => "${".fmt(f),
			Self::AsyncCommand => "&{".fmt(f),
			Self::CloseCommand => "}".fmt(f),
			Self::Argument(parts) => {
				for part in parts.iter() {
					part.fmt(f, context)?
				}
				Ok(())
			}
			Self::CmdOperator(op) => op.fmt(f),
			Self::Semicolon => ";".fmt(f),
			Self::Pipe => color::Fg(color::Yellow, "|").fmt(f),
		}
	}
}


impl<'a> Display<'a> for Token {
	type Context = &'a symbol::Interner;

	fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
		write!(f, "{}:\t", fmt::Show(self.pos, context))?;
		self.kind.fmt(f, context)
	}
}