mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 12:24:45 +00:00
commit
6d7c20fa99
13 changed files with 143 additions and 36 deletions
5
.github/workflows/rust.yml
vendored
5
.github/workflows/rust.yml
vendored
|
@ -19,8 +19,9 @@ jobs:
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
- name: Run tests
|
# Removed because it caused a segmentation fault and would not stop executing. Testing should be done locally.
|
||||||
run: cargo test --verbose
|
# - name: Run tests
|
||||||
|
# run: cargo test --verbose -- --test-threads=2
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
|
|
|
@ -7,6 +7,11 @@ repos:
|
||||||
entry: cargo fmt --all
|
entry: cargo fmt --all
|
||||||
language: system
|
language: system
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
- id: cargo-test
|
||||||
|
name: Cargo test
|
||||||
|
entry: cargo test
|
||||||
|
language: system
|
||||||
|
# pass_filenames: false
|
||||||
- id: rust-clippy
|
- id: rust-clippy
|
||||||
name: Rust clippy
|
name: Rust clippy
|
||||||
description: Run cargo clippy on files included in the commit
|
description: Run cargo clippy on files included in the commit
|
||||||
|
|
|
@ -180,7 +180,15 @@ impl Default for ErgConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErgConfig {
|
impl ErgConfig {
|
||||||
pub fn with_path(path: PathBuf) -> Self {
|
pub fn with_main_path(path: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
module: "<module>",
|
||||||
|
input: Input::File(path),
|
||||||
|
..ErgConfig::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_module_path(path: PathBuf) -> Self {
|
||||||
Self {
|
Self {
|
||||||
module: Box::leak(path.to_str().unwrap().to_string().into_boxed_str()),
|
module: Box::leak(path.to_str().unwrap().to_string().into_boxed_str()),
|
||||||
input: Input::File(path),
|
input: Input::File(path),
|
||||||
|
|
|
@ -54,7 +54,7 @@ pub fn detect_magic_number() -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// executes over a shell, cause `python` may not exist as an executable file (like pyenv)
|
/// executes over a shell, cause `python` may not exist as an executable file (like pyenv)
|
||||||
pub fn exec_pyc<S: Into<String>>(file: S) {
|
pub fn exec_pyc<S: Into<String>>(file: S) -> Option<i32> {
|
||||||
let mut out = if cfg!(windows) {
|
let mut out = if cfg!(windows) {
|
||||||
Command::new("cmd")
|
Command::new("cmd")
|
||||||
.arg("/C")
|
.arg("/C")
|
||||||
|
@ -70,7 +70,7 @@ pub fn exec_pyc<S: Into<String>>(file: S) {
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("cannot execute python")
|
.expect("cannot execute python")
|
||||||
};
|
};
|
||||||
out.wait().expect("python doesn't work");
|
out.wait().expect("python doesn't work").code()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// evaluates over a shell, cause `python` may not exist as an executable file (like pyenv)
|
/// evaluates over a shell, cause `python` may not exist as an executable file (like pyenv)
|
||||||
|
@ -94,19 +94,20 @@ pub fn eval_pyc<S: Into<String>>(file: S) -> String {
|
||||||
String::from_utf8_lossy(&out.stdout).to_string()
|
String::from_utf8_lossy(&out.stdout).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_py(code: &str) {
|
pub fn exec_py(code: &str) -> Option<i32> {
|
||||||
if cfg!(windows) {
|
let mut child = if cfg!(windows) {
|
||||||
Command::new(which_python())
|
Command::new(which_python())
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(code)
|
.arg(code)
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("cannot execute python");
|
.expect("cannot execute python")
|
||||||
} else {
|
} else {
|
||||||
let python_command = format!("{} -c \"{}\"", which_python(), code);
|
let python_command = format!("{} -c \"{}\"", which_python(), code);
|
||||||
Command::new("sh")
|
Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(python_command)
|
.arg(python_command)
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("cannot execute python");
|
.expect("cannot execute python")
|
||||||
}
|
};
|
||||||
|
child.wait().expect("python doesn't work").code()
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,7 +359,7 @@ pub trait Runnable: Sized {
|
||||||
fn finish(&mut self); // called when the :exit command is received.
|
fn finish(&mut self); // called when the :exit command is received.
|
||||||
fn clear(&mut self);
|
fn clear(&mut self);
|
||||||
fn eval(&mut self, src: String) -> Result<String, Self::Errs>;
|
fn eval(&mut self, src: String) -> Result<String, Self::Errs>;
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs>;
|
fn exec(&mut self) -> Result<i32, Self::Errs>;
|
||||||
|
|
||||||
fn input(&self) -> &Input {
|
fn input(&self) -> &Input {
|
||||||
&self.cfg().input
|
&self.cfg().input
|
||||||
|
|
|
@ -45,12 +45,12 @@ impl Runnable for HIRBuilder {
|
||||||
|
|
||||||
fn clear(&mut self) {}
|
fn clear(&mut self) {}
|
||||||
|
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs> {
|
fn exec(&mut self) -> Result<i32, Self::Errs> {
|
||||||
let mut builder = ASTBuilder::new(self.cfg().copy());
|
let mut builder = ASTBuilder::new(self.cfg().copy());
|
||||||
let ast = builder.build(self.input().read())?;
|
let ast = builder.build(self.input().read())?;
|
||||||
let hir = self.check(ast, "exec").map_err(|(_, errs)| errs)?;
|
let hir = self.check(ast, "exec").map_err(|(_, errs)| errs)?;
|
||||||
println!("{hir}");
|
println!("{hir}");
|
||||||
Ok(())
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
|
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
|
||||||
|
|
|
@ -129,9 +129,10 @@ impl Runnable for Compiler {
|
||||||
self.code_generator.clear();
|
self.code_generator.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs> {
|
fn exec(&mut self) -> Result<i32, Self::Errs> {
|
||||||
let path = self.input().filename().replace(".er", ".pyc");
|
let path = self.input().filename().replace(".er", ".pyc");
|
||||||
self.compile_and_dump_as_pyc(path, self.input().read(), "exec")
|
self.compile_and_dump_as_pyc(path, self.input().read(), "exec")?;
|
||||||
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
|
fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
|
||||||
|
|
|
@ -888,7 +888,7 @@ impl Context {
|
||||||
if mod_cache.get(&path).is_some() {
|
if mod_cache.get(&path).is_some() {
|
||||||
return Ok(path);
|
return Ok(path);
|
||||||
}
|
}
|
||||||
let cfg = ErgConfig::with_path(path.clone());
|
let cfg = ErgConfig::with_module_path(path.clone());
|
||||||
let src = cfg.input.read();
|
let src = cfg.input.read();
|
||||||
let mut builder =
|
let mut builder =
|
||||||
HIRBuilder::new_with_cache(cfg, __name__, mod_cache.clone(), py_mod_cache.clone());
|
HIRBuilder::new_with_cache(cfg, __name__, mod_cache.clone(), py_mod_cache.clone());
|
||||||
|
@ -986,7 +986,7 @@ impl Context {
|
||||||
if py_mod_cache.get(&path).is_some() {
|
if py_mod_cache.get(&path).is_some() {
|
||||||
return Ok(path);
|
return Ok(path);
|
||||||
}
|
}
|
||||||
let cfg = ErgConfig::with_path(path.clone());
|
let cfg = ErgConfig::with_module_path(path.clone());
|
||||||
let src = cfg.input.read();
|
let src = cfg.input.read();
|
||||||
let mut builder =
|
let mut builder =
|
||||||
HIRBuilder::new_with_cache(cfg, __name__, py_mod_cache.clone(), py_mod_cache.clone());
|
HIRBuilder::new_with_cache(cfg, __name__, py_mod_cache.clone(), py_mod_cache.clone());
|
||||||
|
|
|
@ -82,7 +82,7 @@ impl Runnable for ASTLowerer {
|
||||||
self.warns.clear();
|
self.warns.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs> {
|
fn exec(&mut self) -> Result<i32, Self::Errs> {
|
||||||
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
|
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
|
||||||
let ast = ast_builder.build(self.input().read())?;
|
let ast = ast_builder.build(self.input().read())?;
|
||||||
let (hir, warns) = self.lower(ast, "exec").map_err(|(_, errs)| errs)?;
|
let (hir, warns) = self.lower(ast, "exec").map_err(|(_, errs)| errs)?;
|
||||||
|
@ -90,7 +90,7 @@ impl Runnable for ASTLowerer {
|
||||||
warns.fmt_all_stderr();
|
warns.fmt_all_stderr();
|
||||||
}
|
}
|
||||||
println!("{hir}");
|
println!("{hir}");
|
||||||
Ok(())
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
|
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
|
||||||
|
|
|
@ -37,13 +37,13 @@ impl Runnable for LexerRunner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clear(&mut self) {}
|
fn clear(&mut self) {}
|
||||||
|
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs> {
|
fn exec(&mut self) -> Result<i32, Self::Errs> {
|
||||||
let lexer = Lexer::from_str(self.input().read());
|
let lexer = Lexer::from_str(self.input().read());
|
||||||
let ts = lexer
|
let ts = lexer
|
||||||
.lex()
|
.lex()
|
||||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?;
|
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?;
|
||||||
println!("{ts}");
|
println!("{ts}");
|
||||||
Ok(())
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&mut self, src: String) -> Result<String, LexerRunnerErrors> {
|
fn eval(&mut self, src: String) -> Result<String, LexerRunnerErrors> {
|
||||||
|
|
|
@ -186,10 +186,10 @@ impl Runnable for ParserRunner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clear(&mut self) {}
|
fn clear(&mut self) {}
|
||||||
|
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs> {
|
fn exec(&mut self) -> Result<i32, Self::Errs> {
|
||||||
let ast = self.parse(self.input().read())?;
|
let ast = self.parse(self.input().read())?;
|
||||||
println!("{ast}");
|
println!("{ast}");
|
||||||
Ok(())
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
|
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
|
||||||
|
|
19
src/dummy.rs
19
src/dummy.rs
|
@ -94,12 +94,21 @@ impl Runnable for DummyVM {
|
||||||
self.compiler.clear();
|
self.compiler.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&mut self) -> Result<(), Self::Errs> {
|
fn exec(&mut self) -> Result<i32, Self::Errs> {
|
||||||
|
// Parallel execution is not possible without dumping with a unique file name.
|
||||||
|
let filename = self
|
||||||
|
.cfg()
|
||||||
|
.input
|
||||||
|
.filename()
|
||||||
|
.split('/')
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.replace(".er", ".pyc");
|
||||||
self.compiler
|
self.compiler
|
||||||
.compile_and_dump_as_pyc("o.pyc", self.input().read(), "exec")?;
|
.compile_and_dump_as_pyc(&filename, self.input().read(), "exec")?;
|
||||||
exec_pyc("o.pyc");
|
let code = exec_pyc(&filename);
|
||||||
remove_file("o.pyc").unwrap();
|
remove_file(&filename).unwrap();
|
||||||
Ok(())
|
Ok(code.unwrap_or(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&mut self, src: String) -> Result<String, EvalErrors> {
|
fn eval(&mut self, src: String) -> Result<String, EvalErrors> {
|
||||||
|
|
|
@ -1,11 +1,93 @@
|
||||||
extern crate erg;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod tests {
|
use erg_common::config::ErgConfig;
|
||||||
/*
|
use erg_common::error::MultiErrorDisplay;
|
||||||
use erg_common::config::{ErgConfig, Input};
|
use erg_common::traits::Runnable;
|
||||||
use erg_common::error::MultiErrorFmt;
|
|
||||||
use erg_common::traits::Runnable;
|
|
||||||
|
|
||||||
const FILE3: &str = "tests/test3_object_system.er";
|
use erg::dummy::DummyVM;
|
||||||
*/
|
|
||||||
|
#[test]
|
||||||
|
fn exec_class() -> Result<(), ()> {
|
||||||
|
expect_success("examples/class.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_fib() -> Result<(), ()> {
|
||||||
|
expect_success("examples/fib.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_hello_world() -> Result<(), ()> {
|
||||||
|
expect_success("examples/helloworld.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_import() -> Result<(), ()> {
|
||||||
|
expect_success("examples/import.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_move_check() -> Result<(), ()> {
|
||||||
|
expect_failure("examples/move_check.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_record() -> Result<(), ()> {
|
||||||
|
expect_success("examples/record.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_side_effect() -> Result<(), ()> {
|
||||||
|
expect_failure("examples/side_effect.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_trait() -> Result<(), ()> {
|
||||||
|
expect_success("examples/trait.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_tuple() -> Result<(), ()> {
|
||||||
|
expect_success("examples/tuple.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_unpack() -> Result<(), ()> {
|
||||||
|
expect_success("examples/unpack.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_use_py() -> Result<(), ()> {
|
||||||
|
expect_success("examples/use_py.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exec_with() -> Result<(), ()> {
|
||||||
|
expect_success("examples/with.er")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_success(file_path: &'static str) -> Result<(), ()> {
|
||||||
|
let cfg = ErgConfig::with_main_path(PathBuf::from(file_path));
|
||||||
|
let mut vm = DummyVM::new(cfg);
|
||||||
|
match vm.exec() {
|
||||||
|
Ok(0) => Ok(()),
|
||||||
|
Ok(_) => Err(()),
|
||||||
|
Err(errs) => {
|
||||||
|
errs.fmt_all_stderr();
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_failure(file_path: &'static str) -> Result<(), ()> {
|
||||||
|
let cfg = ErgConfig::with_main_path(PathBuf::from(file_path));
|
||||||
|
let mut vm = DummyVM::new(cfg);
|
||||||
|
match vm.exec() {
|
||||||
|
Ok(0) => Err(()),
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(errs) => {
|
||||||
|
errs.fmt_all_stderr();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue