Add `ExitStatus`
Fix REPL tests
This commit is contained in:
Shunsuke Shibayama 2023-01-25 00:11:48 +09:00
parent 846aac89a7
commit fa5bb4f615
7 changed files with 80 additions and 90 deletions

View file

@ -503,6 +503,21 @@ fn is_in_the_expected_block(src: &str, lines: &str, in_block: &mut bool) -> bool
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ExitStatus {
pub code: i32,
pub num_errors: usize,
}
impl ExitStatus {
pub const OK: ExitStatus = ExitStatus::new(0, 0);
pub const ERR1: ExitStatus = ExitStatus::new(1, 0);
pub const fn new(code: i32, num_errors: usize) -> Self {
Self { code, num_errors }
}
}
/// This trait implements REPL (Read-Eval-Print-Loop) automatically /// This trait implements REPL (Read-Eval-Print-Loop) automatically
/// The `exec` method is called for file input, etc. /// The `exec` method is called for file input, etc.
pub trait Runnable: Sized + Default { pub trait Runnable: Sized + Default {
@ -557,8 +572,9 @@ pub trait Runnable: Sized + Default {
process::exit(0); process::exit(0);
} }
fn run(cfg: ErgConfig) { fn run(cfg: ErgConfig) -> ExitStatus {
let quiet_repl = cfg.quiet_repl; let quiet_repl = cfg.quiet_repl;
let mut num_errors = 0;
let mut instance = Self::new(cfg); let mut instance = Self::new(cfg);
let res = match instance.input() { let res = match instance.input() {
Input::File(_) | Input::Pipe(_) | Input::Str(_) => instance.exec(), Input::File(_) | Input::Pipe(_) | Input::Str(_) => instance.exec(),
@ -624,8 +640,9 @@ pub trait Runnable: Sized + Default {
.map(|e| e.core().kind == ErrorKind::SystemExit) .map(|e| e.core().kind == ErrorKind::SystemExit)
.unwrap_or(false) .unwrap_or(false)
{ {
instance.quit_successfully(output); return ExitStatus::new(0, num_errors);
} }
num_errors += errs.len();
errs.fmt_all_stderr(); errs.fmt_all_stderr();
} }
} }
@ -636,9 +653,13 @@ pub trait Runnable: Sized + Default {
} }
Input::Dummy => switch_unreachable!(), Input::Dummy => switch_unreachable!(),
}; };
if let Err(e) = res { match res {
e.fmt_all_stderr(); Err(errs) => {
instance.quit(1); num_errors += errs.len();
errs.fmt_all_stderr();
ExitStatus::new(1, num_errors)
}
Ok(i) => ExitStatus::new(i, num_errors),
} }
} }
} }

View file

@ -2,11 +2,9 @@ extern crate erg_common;
extern crate erg_compiler; extern crate erg_compiler;
extern crate erg_parser; extern crate erg_parser;
use std::process;
use erg_common::config::{ErgConfig, ErgMode::*}; use erg_common::config::{ErgConfig, ErgMode::*};
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::traits::Runnable; use erg_common::traits::{ExitStatus, Runnable};
use erg_compiler::build_hir::HIRBuilder; use erg_compiler::build_hir::HIRBuilder;
use erg_compiler::lower::ASTLowerer; use erg_compiler::lower::ASTLowerer;
@ -19,33 +17,20 @@ use erg_parser::ParserRunner;
fn run() { fn run() {
let cfg = ErgConfig::parse(); let cfg = ErgConfig::parse();
match cfg.mode { let stat = match cfg.mode {
Lex => { Lex => LexerRunner::run(cfg),
LexerRunner::run(cfg); Parse => ParserRunner::run(cfg),
} TypeCheck => ASTLowerer::run(cfg),
Parse => { FullCheck => HIRBuilder::run(cfg),
ParserRunner::run(cfg); Transpile => Transpiler::run(cfg),
} Compile | Execute => Compiler::run(cfg),
TypeCheck => { Read => Deserializer::run(cfg),
ASTLowerer::run(cfg);
}
FullCheck => {
HIRBuilder::run(cfg);
}
Transpile => {
Transpiler::run(cfg);
}
Compile | Execute => {
Compiler::run(cfg);
}
Read => {
Deserializer::run(cfg);
}
other => { other => {
println!("invalid mode: {other}"); println!("invalid mode: {other}");
process::exit(1); ExitStatus::ERR1
}
} }
};
std::process::exit(stat.code);
} }
fn main() { fn main() {

View file

@ -1,5 +1,4 @@
//! バイトコードからオブジェクトを復元する //! バイトコードからオブジェクトを復元する
use std::process;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use erg_common::cache::CacheSet; use erg_common::cache::CacheSet;
@ -8,6 +7,7 @@ use erg_common::dict::Dict;
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage}; use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
use erg_common::python_util::PythonVersion; use erg_common::python_util::PythonVersion;
use erg_common::serialize::DataTypePrefix; use erg_common::serialize::DataTypePrefix;
use erg_common::traits::ExitStatus;
use erg_common::{fn_name, switch_lang}; use erg_common::{fn_name, switch_lang};
use erg_common::{RcArray, Str}; use erg_common::{RcArray, Str};
@ -110,21 +110,23 @@ impl Deserializer {
} }
} }
pub fn run(cfg: ErgConfig) { pub fn run(cfg: ErgConfig) -> ExitStatus {
let Input::File(filename) = cfg.input else { let Input::File(filename) = cfg.input else {
eprintln!("{:?} is not a filename", cfg.input); eprintln!("{:?} is not a filename", cfg.input);
process::exit(1); return ExitStatus::ERR1;
}; };
match CodeObj::from_pyc(&filename) { match CodeObj::from_pyc(&filename) {
Ok(codeobj) => { Ok(codeobj) => {
println!("{}", codeobj.code_info(None)); println!("{}", codeobj.code_info(None));
ExitStatus::OK
} }
Err(e) => { Err(e) => {
eprintln!( eprintln!(
"failed to deserialize {}: {}", "failed to deserialize {}: {}",
filename.to_string_lossy(), filename.to_string_lossy(),
e.desc e.desc
) );
ExitStatus::ERR1
} }
} }
} }

View file

@ -5,7 +5,7 @@ use std::process;
use erg_common::config::{ErgConfig, ErgMode::*}; use erg_common::config::{ErgConfig, ErgMode::*};
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::traits::Runnable; use erg_common::traits::{ExitStatus, Runnable};
use erg_parser::build_ast::ASTBuilder; use erg_parser::build_ast::ASTBuilder;
use erg_parser::lex::LexerRunner; use erg_parser::lex::LexerRunner;
@ -13,21 +13,16 @@ use erg_parser::ParserRunner;
fn run() { fn run() {
let cfg = ErgConfig::parse(); let cfg = ErgConfig::parse();
match cfg.mode { let stat = match cfg.mode {
Lex => { Lex => LexerRunner::run(cfg),
LexerRunner::run(cfg); Parse => ParserRunner::run(cfg),
} Desugar | Execute => ASTBuilder::run(cfg),
Parse => {
ParserRunner::run(cfg);
}
Desugar | Execute => {
ASTBuilder::run(cfg);
}
other => { other => {
eprintln!("invalid mode: {other}"); eprintln!("invalid mode: {other}");
process::exit(1); ExitStatus::ERR1
}
} }
};
process::exit(stat.code);
} }
fn main() { fn main() {

View file

@ -4,7 +4,7 @@ extern crate erg_parser;
use erg_common::config::{ErgConfig, ErgMode::*}; use erg_common::config::{ErgConfig, ErgMode::*};
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::traits::Runnable; use erg_common::traits::{ExitStatus, Runnable};
use erg_parser::build_ast::ASTBuilder; use erg_parser::build_ast::ASTBuilder;
use erg_parser::lex::LexerRunner; use erg_parser::lex::LexerRunner;
@ -20,48 +20,32 @@ use erg::DummyVM;
fn run() { fn run() {
let cfg = ErgConfig::parse(); let cfg = ErgConfig::parse();
match cfg.mode { let stat = match cfg.mode {
Lex => { Lex => LexerRunner::run(cfg),
LexerRunner::run(cfg); Parse => ParserRunner::run(cfg),
} Desugar => ASTBuilder::run(cfg),
Parse => { TypeCheck => ASTLowerer::run(cfg),
ParserRunner::run(cfg); FullCheck => HIRBuilder::run(cfg),
} Compile => Compiler::run(cfg),
Desugar => { Transpile => Transpiler::run(cfg),
ASTBuilder::run(cfg); Execute => DummyVM::run(cfg),
} Read => Deserializer::run(cfg),
TypeCheck => {
ASTLowerer::run(cfg);
}
FullCheck => {
HIRBuilder::run(cfg);
}
Compile => {
Compiler::run(cfg);
}
Transpile => {
Transpiler::run(cfg);
}
Execute => {
DummyVM::run(cfg);
}
Read => {
Deserializer::run(cfg);
}
LanguageServer => { LanguageServer => {
#[cfg(feature = "els")] #[cfg(feature = "els")]
{ {
use els::ErgLanguageServer; use els::ErgLanguageServer;
let mut server = ErgLanguageServer::new(cfg); let mut server = ErgLanguageServer::new(cfg);
server.run().unwrap(); server.run().unwrap();
ExitStatus::OK
} }
#[cfg(not(feature = "els"))] #[cfg(not(feature = "els"))]
{ {
eprintln!("This version of the build does not support language server mode"); eprintln!("This version of the build does not support language server mode");
std::process::exit(1); ExitStatus::ERR1
}
} }
} }
};
std::process::exit(stat.code);
} }
fn main() { fn main() {

View file

@ -17,8 +17,11 @@ __already_loaded = False
__res = '' __res = ''
while True: while True:
try:
__order = __client_socket.recv(1024).decode() __order = __client_socket.recv(1024).decode()
if __order == 'quit' or __order == 'exit': except ConnectionResetError: # when the server was crashed
break
if __order == 'quit' or __order == 'exit': # when the server was closed successfully
__client_socket.send('closed'.encode()) __client_socket.send('closed'.encode())
break break
elif __order == 'load': elif __order == 'load':

View file

@ -6,7 +6,7 @@ use erg_common::error::MultiErrorDisplay;
use erg_common::python_util::PythonVersion; use erg_common::python_util::PythonVersion;
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::style::{GREEN, RESET}; use erg_common::style::{GREEN, RESET};
use erg_common::traits::{Runnable, Stream}; use erg_common::traits::{ExitStatus, Runnable, Stream};
use erg_compiler::error::CompileErrors; use erg_compiler::error::CompileErrors;
@ -14,9 +14,9 @@ use erg::DummyVM;
pub(crate) fn expect_repl_success(lines: Vec<String>) -> Result<(), ()> { pub(crate) fn expect_repl_success(lines: Vec<String>) -> Result<(), ()> {
match exec_repl(lines) { match exec_repl(lines) {
Ok(0) => Ok(()), Ok(ExitStatus::OK) => Ok(()),
Ok(i) => { Ok(stat) => {
println!("err: should succeed, but end with {i}"); println!("err: should succeed, but got: {stat:?}");
Err(()) Err(())
} }
Err(errs) => { Err(errs) => {
@ -114,21 +114,21 @@ fn _exec_file(file_path: &'static str) -> Result<i32, CompileErrors> {
} }
/// WARN: You must quit REPL manually (use `:exit`, `:quit` or call something shutdowns the interpreter) /// WARN: You must quit REPL manually (use `:exit`, `:quit` or call something shutdowns the interpreter)
pub fn _exec_repl(lines: Vec<String>) -> Result<i32, CompileErrors> { pub fn _exec_repl(lines: Vec<String>) -> Result<ExitStatus, CompileErrors> {
println!("{GREEN}[test] exec dummy REPL: {lines:?}{RESET}"); println!("{GREEN}[test] exec dummy REPL: {lines:?}{RESET}");
let cfg = ErgConfig { let cfg = ErgConfig {
input: Input::DummyREPL(DummyStdin::new(lines)), input: Input::DummyREPL(DummyStdin::new(lines)),
quiet_repl: true, quiet_repl: true,
..Default::default() ..Default::default()
}; };
<DummyVM as Runnable>::run(set_cfg(cfg)); let stat = <DummyVM as Runnable>::run(set_cfg(cfg));
Ok(0) Ok(stat)
} }
pub(crate) fn exec_file(file_path: &'static str) -> Result<i32, CompileErrors> { pub(crate) fn exec_file(file_path: &'static str) -> Result<i32, CompileErrors> {
exec_new_thread(move || _exec_file(file_path)) exec_new_thread(move || _exec_file(file_path))
} }
pub(crate) fn exec_repl(lines: Vec<String>) -> Result<i32, CompileErrors> { pub(crate) fn exec_repl(lines: Vec<String>) -> Result<ExitStatus, CompileErrors> {
exec_new_thread(move || _exec_repl(lines)) exec_new_thread(move || _exec_repl(lines))
} }