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
/// The `exec` method is called for file input, etc.
pub trait Runnable: Sized + Default {
@ -557,8 +572,9 @@ pub trait Runnable: Sized + Default {
process::exit(0);
}
fn run(cfg: ErgConfig) {
fn run(cfg: ErgConfig) -> ExitStatus {
let quiet_repl = cfg.quiet_repl;
let mut num_errors = 0;
let mut instance = Self::new(cfg);
let res = match instance.input() {
Input::File(_) | Input::Pipe(_) | Input::Str(_) => instance.exec(),
@ -624,8 +640,9 @@ pub trait Runnable: Sized + Default {
.map(|e| e.core().kind == ErrorKind::SystemExit)
.unwrap_or(false)
{
instance.quit_successfully(output);
return ExitStatus::new(0, num_errors);
}
num_errors += errs.len();
errs.fmt_all_stderr();
}
}
@ -636,9 +653,13 @@ pub trait Runnable: Sized + Default {
}
Input::Dummy => switch_unreachable!(),
};
if let Err(e) = res {
e.fmt_all_stderr();
instance.quit(1);
match res {
Err(errs) => {
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_parser;
use std::process;
use erg_common::config::{ErgConfig, ErgMode::*};
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::lower::ASTLowerer;
@ -19,33 +17,20 @@ use erg_parser::ParserRunner;
fn run() {
let cfg = ErgConfig::parse();
match cfg.mode {
Lex => {
LexerRunner::run(cfg);
}
Parse => {
ParserRunner::run(cfg);
}
TypeCheck => {
ASTLowerer::run(cfg);
}
FullCheck => {
HIRBuilder::run(cfg);
}
Transpile => {
Transpiler::run(cfg);
}
Compile | Execute => {
Compiler::run(cfg);
}
Read => {
Deserializer::run(cfg);
}
let stat = match cfg.mode {
Lex => LexerRunner::run(cfg),
Parse => ParserRunner::run(cfg),
TypeCheck => ASTLowerer::run(cfg),
FullCheck => HIRBuilder::run(cfg),
Transpile => Transpiler::run(cfg),
Compile | Execute => Compiler::run(cfg),
Read => Deserializer::run(cfg),
other => {
println!("invalid mode: {other}");
process::exit(1);
}
ExitStatus::ERR1
}
};
std::process::exit(stat.code);
}
fn main() {

View file

@ -1,5 +1,4 @@
//! バイトコードからオブジェクトを復元する
use std::process;
use std::string::FromUtf8Error;
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::python_util::PythonVersion;
use erg_common::serialize::DataTypePrefix;
use erg_common::traits::ExitStatus;
use erg_common::{fn_name, switch_lang};
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 {
eprintln!("{:?} is not a filename", cfg.input);
process::exit(1);
return ExitStatus::ERR1;
};
match CodeObj::from_pyc(&filename) {
Ok(codeobj) => {
println!("{}", codeobj.code_info(None));
ExitStatus::OK
}
Err(e) => {
eprintln!(
"failed to deserialize {}: {}",
filename.to_string_lossy(),
e.desc
)
);
ExitStatus::ERR1
}
}
}

View file

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

View file

@ -4,7 +4,7 @@ extern crate erg_parser;
use erg_common::config::{ErgConfig, ErgMode::*};
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::lex::LexerRunner;
@ -20,48 +20,32 @@ use erg::DummyVM;
fn run() {
let cfg = ErgConfig::parse();
match cfg.mode {
Lex => {
LexerRunner::run(cfg);
}
Parse => {
ParserRunner::run(cfg);
}
Desugar => {
ASTBuilder::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);
}
let stat = match cfg.mode {
Lex => LexerRunner::run(cfg),
Parse => ParserRunner::run(cfg),
Desugar => ASTBuilder::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 => {
#[cfg(feature = "els")]
{
use els::ErgLanguageServer;
let mut server = ErgLanguageServer::new(cfg);
server.run().unwrap();
ExitStatus::OK
}
#[cfg(not(feature = "els"))]
{
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() {

View file

@ -17,8 +17,11 @@ __already_loaded = False
__res = ''
while True:
try:
__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())
break
elif __order == 'load':

View file

@ -6,7 +6,7 @@ use erg_common::error::MultiErrorDisplay;
use erg_common::python_util::PythonVersion;
use erg_common::spawn::exec_new_thread;
use erg_common::style::{GREEN, RESET};
use erg_common::traits::{Runnable, Stream};
use erg_common::traits::{ExitStatus, Runnable, Stream};
use erg_compiler::error::CompileErrors;
@ -14,9 +14,9 @@ use erg::DummyVM;
pub(crate) fn expect_repl_success(lines: Vec<String>) -> Result<(), ()> {
match exec_repl(lines) {
Ok(0) => Ok(()),
Ok(i) => {
println!("err: should succeed, but end with {i}");
Ok(ExitStatus::OK) => Ok(()),
Ok(stat) => {
println!("err: should succeed, but got: {stat:?}");
Err(())
}
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)
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}");
let cfg = ErgConfig {
input: Input::DummyREPL(DummyStdin::new(lines)),
quiet_repl: true,
..Default::default()
};
<DummyVM as Runnable>::run(set_cfg(cfg));
Ok(0)
let stat = <DummyVM as Runnable>::run(set_cfg(cfg));
Ok(stat)
}
pub(crate) fn exec_file(file_path: &'static str) -> Result<i32, CompileErrors> {
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))
}