fix socket connection problems

This commit is contained in:
Shunsuke Shibayama 2023-01-25 01:19:45 +09:00
parent fa5bb4f615
commit b76d63f9a5
6 changed files with 52 additions and 30 deletions

View file

@ -74,13 +74,15 @@ impl fmt::Display for ErgMode {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DummyStdin { pub struct DummyStdin {
pub name: String,
current_line: usize, current_line: usize,
lines: Vec<String>, lines: Vec<String>,
} }
impl DummyStdin { impl DummyStdin {
pub fn new(lines: Vec<String>) -> Self { pub fn new(name: String, lines: Vec<String>) -> Self {
Self { Self {
name,
current_line: 0, current_line: 0,
lines, lines,
} }
@ -160,7 +162,8 @@ impl Input {
pub fn filename(&self) -> &str { pub fn filename(&self) -> &str {
match self { match self {
Self::File(filename) => filename.file_name().and_then(|f| f.to_str()).unwrap_or("_"), Self::File(filename) => filename.file_name().and_then(|f| f.to_str()).unwrap_or("_"),
Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "stdin", Self::REPL | Self::Pipe(_) => "stdin",
Self::DummyREPL(stdin) => &stdin.name,
Self::Str(_) => "string", Self::Str(_) => "string",
Self::Dummy => "dummy", Self::Dummy => "dummy",
} }

View file

@ -1,5 +1,6 @@
//! defines the compiler for Erg (ergc). //! defines the compiler for Erg (ergc).
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
#![allow(clippy::result_large_err)]
extern crate erg_common; extern crate erg_common;
pub extern crate erg_parser; pub extern crate erg_parser;

View file

@ -61,7 +61,8 @@ impl Runnable for DummyVM {
} }
let port = find_available_port(); let port = find_available_port();
let code = include_str!("scripts/repl_server.py") let code = include_str!("scripts/repl_server.py")
.replace("__PORT__", port.to_string().as_str()); .replace("__PORT__", port.to_string().as_str())
.replace("__MODULE__", &cfg.dump_filename());
spawn_py(cfg.py_command, &code); spawn_py(cfg.py_command, &code);
let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, port); let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, port);
if !cfg.quiet_repl { if !cfg.quiet_repl {
@ -112,7 +113,7 @@ impl Runnable for DummyVM {
process::exit(1); process::exit(1);
} }
} }
remove_file("o.pyc").unwrap_or(()); remove_file(self.cfg().dump_pyc_filename()).unwrap_or(());
} }
} }
@ -142,9 +143,10 @@ impl Runnable for DummyVM {
} }
fn eval(&mut self, src: String) -> Result<String, EvalErrors> { fn eval(&mut self, src: String) -> Result<String, EvalErrors> {
let path = self.cfg().dump_pyc_filename();
let arti = self let arti = self
.compiler .compiler
.eval_compile_and_dump_as_pyc("o.pyc", src, "eval") .eval_compile_and_dump_as_pyc(path, src, "eval")
.map_err(|eart| eart.errors)?; .map_err(|eart| eart.errors)?;
let (last, warns) = (arti.object, arti.warns); let (last, warns) = (arti.object, arti.warns);
let mut res = warns.to_string(); let mut res = warns.to_string();
@ -207,17 +209,23 @@ impl DummyVM {
} }
} }
#[cfg(test)]
fn find_available_port() -> u16 {
require_free_port()
}
#[cfg(not(test))]
fn find_available_port() -> u16 { fn find_available_port() -> u16 {
const DEFAULT_PORT: u16 = 8736; const DEFAULT_PORT: u16 = 8736;
TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, DEFAULT_PORT)) TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, DEFAULT_PORT))
.is_ok() .is_ok()
.then_some(DEFAULT_PORT) .then_some(DEFAULT_PORT)
.unwrap_or_else(|| { .unwrap_or_else(require_free_port)
// localhost:0 will bind to a free port }
let socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
TcpListener::bind(socket) fn require_free_port() -> u16 {
.and_then(|listener| listener.local_addr()) let socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
.map(|sock_addr| sock_addr.port()) TcpListener::bind(socket)
.expect("No free port found.") .and_then(|listener| listener.local_addr())
}) .map(|sock_addr| sock_addr.port())
.expect("No free port found.")
} }

View file

@ -28,9 +28,10 @@ while True:
__sys.stdout = __io.StringIO() __sys.stdout = __io.StringIO()
try: try:
if __already_loaded: if __already_loaded:
__res = str(exec('__importlib.reload(o)')) # __MODULE__ will be replaced with module name
__res = str(exec('__importlib.reload(__MODULE__)'))
else: else:
__res = str(exec('import o')) __res = str(exec('import __MODULE__'))
except SystemExit: except SystemExit:
__client_socket.send('[Exception] SystemExit'.encode()) __client_socket.send('[Exception] SystemExit'.encode())
continue continue

View file

@ -12,8 +12,8 @@ use erg_compiler::error::CompileErrors;
use erg::DummyVM; use erg::DummyVM;
pub(crate) fn expect_repl_success(lines: Vec<String>) -> Result<(), ()> { pub(crate) fn expect_repl_success(name: &'static str, lines: Vec<String>) -> Result<(), ()> {
match exec_repl(lines) { match exec_repl(name, lines) {
Ok(ExitStatus::OK) => Ok(()), Ok(ExitStatus::OK) => Ok(()),
Ok(stat) => { Ok(stat) => {
println!("err: should succeed, but got: {stat:?}"); println!("err: should succeed, but got: {stat:?}");
@ -114,10 +114,10 @@ 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<ExitStatus, CompileErrors> { pub fn _exec_repl(name: &'static str, 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(name.to_string(), lines)),
quiet_repl: true, quiet_repl: true,
..Default::default() ..Default::default()
}; };
@ -129,6 +129,9 @@ 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<ExitStatus, CompileErrors> { pub(crate) fn exec_repl(
exec_new_thread(move || _exec_repl(lines)) name: &'static str,
lines: Vec<String>,
) -> Result<ExitStatus, CompileErrors> {
exec_new_thread(move || _exec_repl(name, lines))
} }

View file

@ -3,17 +3,23 @@ use common::expect_repl_success;
#[test] #[test]
fn exec_repl_helloworld() -> Result<(), ()> { fn exec_repl_helloworld() -> Result<(), ()> {
expect_repl_success(vec!["print! \"hello, world\"".into(), "exit()".into()]) expect_repl_success(
"repl_hello",
vec!["print! \"hello, world\"".into(), "exit()".into()],
)
} }
#[test] #[test]
fn exec_repl_def_func() -> Result<(), ()> { fn exec_repl_def_func() -> Result<(), ()> {
expect_repl_success(vec![ expect_repl_success(
"f i =".into(), "repl_def",
" i + 1".into(), vec![
"".into(), "f i =".into(),
"x = f 2".into(), " i + 1".into(),
"assert x == 3".into(), "".into(),
"exit()".into(), "x = f 2".into(),
]) "assert x == 3".into(),
"exit()".into(),
],
)
} }