erg/compiler/erg_common/traits.rs
Shunsuke Shibayama f9d91aa38e Organize crates
2022-08-13 06:38:12 +09:00

416 lines
13 KiB
Rust

//! defines common traits used in the compiler.
//!
//! コンパイラ等で汎用的に使われるトレイトを定義する
use std::slice::{Iter, IterMut};
use std::vec::IntoIter;
use std::io::{BufWriter, Write, stdout};
use std::process;
use std::mem;
use crate::Str;
use crate::{addr_eq, switch_unreachable, chomp, log};
use crate::color::{GREEN, RESET};
use crate::config::{ErgConfig, Input};
use crate::error::{Location, ErrorDisplay, MultiErrorDisplay};
use crate::ty::Type;
pub trait Stream<T>: Sized {
fn payload(self) -> Vec<T>;
fn ref_payload(&self) -> &Vec<T>;
fn ref_mut_payload(&mut self) -> &mut Vec<T>;
#[inline]
fn clear(&mut self) { self.ref_mut_payload().clear(); }
#[inline]
fn len(&self) -> usize { self.ref_payload().len() }
fn size(&self) -> usize {
std::mem::size_of::<Vec<T>>() + std::mem::size_of::<T>() * self.ref_payload().capacity()
}
#[inline]
fn is_empty(&self) -> bool { self.ref_payload().is_empty() }
#[inline]
fn insert(&mut self, idx: usize, elem: T) {
self.ref_mut_payload().insert(idx, elem);
}
#[inline]
fn remove(&mut self, idx: usize) -> T { self.ref_mut_payload().remove(idx) }
#[inline]
fn push(&mut self, elem: T) { self.ref_mut_payload().push(elem); }
fn append<S: Stream<T>>(&mut self, s: &mut S) { self.ref_mut_payload().append(s.ref_mut_payload()); }
#[inline]
fn pop(&mut self) -> Option<T> { self.ref_mut_payload().pop() }
fn lpop(&mut self) -> Option<T> {
let len = self.len();
if len == 0 { None } else { Some(self.ref_mut_payload().remove(0)) }
}
#[inline]
fn get(&self, idx: usize) -> Option<&T> { self.ref_payload().get(idx) }
#[inline]
fn get_mut(&mut self, idx: usize) -> Option<&mut T> { self.ref_mut_payload().get_mut(idx) }
#[inline]
fn first(&self) -> Option<&T> { self.ref_payload().first() }
#[inline]
fn first_mut(&mut self) -> Option<&mut T> { self.ref_mut_payload().first_mut() }
#[inline]
fn last(&self) -> Option<&T> { self.ref_payload().last() }
#[inline]
fn last_mut(&mut self) -> Option<&mut T> { self.ref_mut_payload().last_mut() }
#[inline]
fn iter(&self) -> Iter<'_, T> { self.ref_payload().iter() }
#[inline]
fn iter_mut(&mut self) -> IterMut<'_, T> { self.ref_mut_payload().iter_mut() }
#[inline]
fn into_iter(self) -> IntoIter<T> { self.payload().into_iter() }
#[inline]
fn take_all(&mut self) -> Vec<T> { self.ref_mut_payload().drain(..).collect() }
}
#[macro_export]
macro_rules! impl_displayable_stream_for_wrapper {
($Strc: ident, $Inner: ident) => {
impl $Strc {
pub const fn new(v: Vec<$Inner>) -> $Strc { $Strc(v) }
#[inline]
pub fn empty() -> $Strc { $Strc(Vec::with_capacity(20)) }
}
impl From<Vec<$Inner>> for $Strc {
#[inline]
fn from(errs: Vec<$Inner>) -> Self { Self(errs) }
}
impl std::fmt::Display for $Strc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}]", erg_common::fmt_iter(self.iter()).replace("\n", "\\n"))
}
}
impl Default for $Strc {
#[inline]
fn default() -> Self { Self::empty() }
}
impl std::ops::Index<usize> for $Strc {
type Output = $Inner;
fn index(&self, idx: usize) -> &Self::Output { erg_common::traits::Stream::get(self, idx).unwrap() }
}
impl erg_common::traits::Stream<$Inner> for $Strc {
#[inline]
fn payload(self) -> Vec<$Inner> { self.0 }
#[inline]
fn ref_payload(&self) -> &Vec<$Inner> { &self.0 }
#[inline]
fn ref_mut_payload(&mut self) -> &mut Vec<$Inner> { &mut self.0 }
}
};
}
#[macro_export]
macro_rules! impl_stream_for_wrapper {
($Strc: ident, $Inner: ident) => {
impl $Strc {
pub const fn new(v: Vec<$Inner>) -> $Strc { $Strc(v) }
pub const fn empty() -> $Strc { $Strc(Vec::new()) }
#[inline]
pub fn with_capacity(capacity: usize) -> $Strc { $Strc(Vec::with_capacity(capacity)) }
}
impl Default for $Strc {
#[inline]
fn default() -> $Strc { $Strc::with_capacity(0) }
}
impl std::ops::Index<usize> for $Strc {
type Output = $Inner;
fn index(&self, idx: usize) -> &Self::Output { erg_common::traits::Stream::get(self, idx).unwrap() }
}
impl erg_common::traits::Stream<$Inner> for $Strc {
#[inline]
fn payload(self) -> Vec<$Inner> { self.0 }
#[inline]
fn ref_payload(&self) -> &Vec<$Inner> { &self.0 }
#[inline]
fn ref_mut_payload(&mut self) -> &mut Vec<$Inner> { &mut self.0 }
}
}
}
#[macro_export]
macro_rules! impl_stream {
($Strc: ident, $Inner: ident, $field: ident) => {
impl erg_common::traits::Stream<$Inner> for $Strc {
#[inline]
fn payload(self) -> Vec<$Inner> { self.$field }
#[inline]
fn ref_payload(&self) -> &Vec<$Inner> { &self.$field }
#[inline]
fn ref_mut_payload(&mut self) -> &mut Vec<$Inner> { &mut self.$field }
}
impl std::ops::Index<usize> for $Strc {
type Output = $Inner;
fn index(&self, idx: usize) -> &Self::Output { erg_common::traits::Stream::get(self, idx).unwrap() }
}
}
}
pub trait ImmutableStream<T>: Sized {
fn ref_payload(&self) -> &[T];
fn capacity(&self) -> usize;
#[inline]
fn len(&self) -> usize { self.ref_payload().len() }
fn size(&self) -> usize {
std::mem::size_of::<Vec<T>>() + std::mem::size_of::<T>() * self.capacity()
}
#[inline]
fn is_empty(&self) -> bool { self.ref_payload().is_empty() }
#[inline]
fn get(&self, idx: usize) -> Option<&T> { self.ref_payload().get(idx) }
#[inline]
fn first(&self) -> Option<&T> { self.ref_payload().first() }
#[inline]
fn last(&self) -> Option<&T> { self.ref_payload().last() }
#[inline]
fn iter(&self) -> Iter<'_, T> { self.ref_payload().iter() }
}
// for Runnable::run
fn expect_block(src: &str) -> bool {
src.ends_with(&['=', ':']) || src.ends_with(":=")
|| src.ends_with("->") || src.ends_with("=>")
|| src.ends_with("do") || src.ends_with("do!")
}
/// this trait implements REPL (Read-Eval-Print-Loop) automatically
pub trait Runnable: Sized {
type Err: ErrorDisplay;
type Errs: MultiErrorDisplay<Self::Err>;
fn new(cfg: ErgConfig) -> Self;
fn input(&self) -> &Input;
fn start_message(&self) -> String;
fn finish(&mut self); // called when the :exit command is received.
fn clear(&mut self);
fn eval(&mut self, src: Str) -> Result<String, Self::Errs>;
fn ps1(&self) -> String { ">>> ".to_string() } // TODO: &str (VMのせいで参照をとれない)
fn ps2(&self) -> String { "... ".to_string() }
#[inline]
fn quit(&self, code: i32) { process::exit(code); }
fn run(cfg: ErgConfig) {
let mut instance = Self::new(cfg);
let res = match instance.input() {
Input::File(_) | Input::Pipe(_) | Input::Str(_) => {
match instance.eval(instance.input().read()) {
Ok(s) => {
println!("{s}");
Ok(())
}
Err(e) => Err(e),
}
}
Input::REPL => {
let output = stdout();
let mut output = BufWriter::new(output.lock());
log!(f output, "{GREEN}[DEBUG] The REPL has started.{RESET}\n");
output.write(instance.start_message().as_bytes()).unwrap();
output.write(instance.ps1().as_bytes()).unwrap();
output.flush().unwrap();
let mut lines = String::new();
loop {
let line = chomp(&instance.input().read());
if &line[..] == ":quit" || &line[..] == ":exit" {
instance.finish();
log!(f output, "{GREEN}[DEBUG] The REPL has finished successfully.{RESET}\n");
process::exit(0);
}
lines.push_str(&line);
if expect_block(&line) || line.starts_with(' ') {
lines += "\n";
output.write(instance.ps2().as_bytes()).unwrap();
output.flush().unwrap();
continue;
}
match instance.eval(mem::take(&mut lines).into()) {
Ok(out) => {
output.write((out + "\n").as_bytes()).unwrap();
output.flush().unwrap();
},
Err(e) => { e.fmt_all_stderr(); }
}
output.write(instance.ps1().as_bytes()).unwrap();
output.flush().unwrap();
instance.clear();
}
},
Input::Dummy => switch_unreachable!(),
};
if let Err(e) = res {
e.fmt_all_stderr();
std::process::exit(1);
}
}
}
pub trait Locational {
fn loc(&self) -> Location;
fn ln_begin(&self) -> Option<usize> {
match self.loc() {
Location::RangePair{ ln_begin, .. }
| Location::Range{ ln_begin, .. }
| Location::LineRange(ln_begin, _) => Some(ln_begin),
Location::Line(lineno) => Some(lineno),
Location::Unknown => None,
}
}
fn ln_end(&self) -> Option<usize> {
match self.loc() {
Location::RangePair{ ln_end, .. }
| Location::Range{ ln_end, .. }
| Location::LineRange(_, ln_end) => Some(ln_end),
Location::Line(lineno) => Some(lineno),
Location::Unknown => None,
}
}
fn col_begin(&self) -> Option<usize> {
match self.loc() {
Location::Range{ col_begin, .. } => Some(col_begin),
_ => None,
}
}
fn col_end(&self) -> Option<usize> {
match self.loc() {
Location::Range{ col_end, .. } => Some(col_end),
_ => None,
}
}
}
#[macro_export]
macro_rules! impl_locational_for_enum {
($Enum: ident; $($Variant: ident $(,)?)*) => {
impl erg_common::traits::Locational for $Enum {
fn loc(&self) -> erg_common::error::Location {
match self {
$($Enum::$Variant(v) => v.loc(),)*
}
}
}
}
}
#[macro_export]
macro_rules! impl_locational {
($T: ty, $begin: ident, $end: ident) => {
impl Locational for $T {
fn loc(&self) -> Location {
match (self.$begin.ln_begin(), self.$begin.col_begin(), self.$end.ln_end(), self.$end.col_end()) {
(Some(lb), Some(cb), Some(le), Some(ce)) => Location::range(lb, cb, le, ce),
(Some(lb), _, Some(le), _) => Location::LineRange(lb, le),
(Some(l), _, _, _) | (_, _, Some(l), _) => Location::Line(l),
_ => Location::Unknown,
}
}
}
};
}
pub trait NestedDisplay {
fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result;
}
/// `impl<T: NestedDisplay> Display for T NestedDisplay`はorphan-ruleに違反するので個別定義する
#[macro_export]
macro_rules! impl_display_from_nested {
($T: ty) => {
impl std::fmt::Display for $T {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_nest(f, 0)
}
}
};
}
#[macro_export]
macro_rules! impl_nested_display_for_enum {
($Enum: ident; $($Variant: ident $(,)?)*) => {
impl NestedDisplay for $Enum {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
write!(f, "{}", " ".repeat(level))?;
match self {
$($Enum::$Variant(v) => v.fmt_nest(f, level),)*
}
}
}
}
}
/// cloneのコストがあるためなるべく.ref_tを使うようにすること
/// いくつかの構造体は直接Typeを保持していないので、その場合は.tを使う
#[allow(unused_variables)]
pub trait HasType {
fn ref_t(&self) -> &Type;
// 関数呼び出しの場合、.ref_t()は戻り値を返し、signature_t()は関数全体の型を返す
fn signature_t(&self) -> Option<&Type>;
#[inline]
fn t(&self) -> Type { self.ref_t().clone() }
#[inline]
fn inner_ts(&self) -> Vec<Type> { self.ref_t().inner_ts() }
#[inline]
fn lhs_t(&self) -> &Type { &self.ref_t().non_default_params().unwrap()[0].ty }
#[inline]
fn rhs_t(&self) -> &Type { &self.ref_t().non_default_params().unwrap()[1].ty }
}
#[macro_export]
macro_rules! impl_t {
($T: ty, $t: ident) => {
impl erg_common::traits::HasType for $T {
#[inline]
fn ref_t(&self) -> &common::ty::Type { &common::ty::Type::$t }
}
};
}
/// Pythonではis演算子に相当
pub trait AddrEq {
#[inline]
fn addr_eq(&self, other: &Self) -> bool { addr_eq!(self, other) }
}
pub trait __Str__ {
fn __str__(&self) -> String;
}