mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 04:24:43 +00:00
416 lines
13 KiB
Rust
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;
|
|
}
|