mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
changed "var type = value" syntax to "var::type = value" and added the tutor (mers -t)
This commit is contained in:
parent
7baa1c2aba
commit
ca1dbf2722
@ -44,6 +44,10 @@ To run a program, just run `mers your_file.txt`. The file needs to be valid utf8
|
|||||||
Alternatively, run `mers -e println("Hello, file-less world")`.
|
Alternatively, run `mers -e println("Hello, file-less world")`.
|
||||||
If you compiled mers in debug mode, it will print a lot of debugging information.
|
If you compiled mers in debug mode, it will print a lot of debugging information.
|
||||||
|
|
||||||
|
### tutor
|
||||||
|
|
||||||
|
Use `mers -t` to start the tutor, which will give you an interactive tour of the language.
|
||||||
|
|
||||||
### interactive mode
|
### interactive mode
|
||||||
|
|
||||||
Use `mers -i` to start interactive mode. mers will create a temporary file and open it in your default editor. Every time the file is saved, mers reloads and runs it, showing errors or the output.
|
Use `mers -i` to start interactive mode. mers will create a temporary file and open it in your default editor. Every time the file is saved, mers reloads and runs it, showing errors or the output.
|
||||||
|
@ -6,8 +6,8 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "mers"
|
name = "mers_libs"
|
||||||
path = "src/main.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
edit = "0.1.4"
|
edit = "0.1.4"
|
||||||
|
130
mers/src/interactive_mode.rs
Normal file
130
mers/src/interactive_mode.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/// creates a temporary file, then opens it in the user's default editor. watches the fs for changes to the file and acts upon them.
|
||||||
|
pub mod fs_watcher {
|
||||||
|
use notify::Watcher;
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Error(String);
|
||||||
|
|
||||||
|
/// on each file change, recompiles and runs the code. lets people experiment with mers without having to put a file anywhere
|
||||||
|
pub fn playground(spawn_new_terminal_for_editor: bool) -> Result<(), Error> {
|
||||||
|
main(
|
||||||
|
spawn_new_terminal_for_editor,
|
||||||
|
"// Welcome to mers! (interactive mode)
|
||||||
|
|
||||||
|
// put your name here, then save the file to run the script.
|
||||||
|
your_name = \"\"
|
||||||
|
greeting = \"Hello, {0}!\".format(your_name)
|
||||||
|
println(greeting)
|
||||||
|
",
|
||||||
|
Box::new(|temp_file: &PathBuf| {
|
||||||
|
println!();
|
||||||
|
if let Ok(file_contents) = fs::read_to_string(&temp_file) {
|
||||||
|
let mut file =
|
||||||
|
crate::parse::file::File::new(file_contents, temp_file.to_path_buf());
|
||||||
|
match crate::parse::parse::parse(&mut file) {
|
||||||
|
Ok(func) => {
|
||||||
|
println!(" - - - - -");
|
||||||
|
let output = func.run(vec![]);
|
||||||
|
println!(" - - - - -");
|
||||||
|
println!("{}", output);
|
||||||
|
}
|
||||||
|
Err(e) => println!("{}", e.with_file(&file)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("can't read file at {:?}!", temp_file);
|
||||||
|
std::process::exit(105);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)?
|
||||||
|
.0
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// when the file is changed, calls the provided closure with the file's path.
|
||||||
|
/// returns a JoinHandle which will finish when the user closes the editor and the file that is being used.
|
||||||
|
pub fn main(
|
||||||
|
spawn_new_terminal_for_editor: bool,
|
||||||
|
initial_file_contents: &str,
|
||||||
|
mut on_file_change: Box<dyn FnMut(&PathBuf) + Send>,
|
||||||
|
) -> Result<(JoinHandle<()>, PathBuf), Error> {
|
||||||
|
let temp_file_edit = edit::Builder::new().suffix(".mers").tempfile().unwrap();
|
||||||
|
let temp_file = temp_file_edit.path().to_path_buf();
|
||||||
|
eprintln!(
|
||||||
|
"Using temporary file at {temp_file:?}. Save the file to update the output here."
|
||||||
|
);
|
||||||
|
if let Ok(_) = std::fs::write(&temp_file, initial_file_contents) {
|
||||||
|
if let Ok(mut watcher) = {
|
||||||
|
let temp_file = temp_file.clone();
|
||||||
|
// the file watcher
|
||||||
|
notify::recommended_watcher(move |event: Result<notify::Event, notify::Error>| {
|
||||||
|
if let Ok(event) = event {
|
||||||
|
match &event.kind {
|
||||||
|
notify::EventKind::Modify(notify::event::ModifyKind::Data(_)) => {
|
||||||
|
on_file_change(&temp_file);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} {
|
||||||
|
if let Ok(_) = watcher.watch(&temp_file, notify::RecursiveMode::NonRecursive) {
|
||||||
|
let out = if spawn_new_terminal_for_editor {
|
||||||
|
if let Ok(term) = std::env::var("TERM") {
|
||||||
|
let editor = edit::get_editor().unwrap();
|
||||||
|
eprintln!("launching \"{term} -e {editor:?} {temp_file:?}...");
|
||||||
|
let mut editor = std::process::Command::new(term)
|
||||||
|
.arg("-e")
|
||||||
|
.arg(&editor)
|
||||||
|
.arg(&temp_file)
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
(
|
||||||
|
thread::spawn(move || {
|
||||||
|
// wait for the editor to finish
|
||||||
|
editor.wait().unwrap();
|
||||||
|
// stop the watcher (this is absolutely necessary because it also moves the watcher into the closure,
|
||||||
|
// which prevents it from being dropped too early)
|
||||||
|
drop(watcher);
|
||||||
|
// close and remove the temporary file
|
||||||
|
temp_file_edit.close().unwrap();
|
||||||
|
}),
|
||||||
|
temp_file,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return Err(Error(format!("TERM environment variable not set.")));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let tf = temp_file.clone();
|
||||||
|
(
|
||||||
|
thread::spawn(move || {
|
||||||
|
edit::edit_file(temp_file).unwrap();
|
||||||
|
drop(watcher);
|
||||||
|
temp_file_edit.close().unwrap();
|
||||||
|
}),
|
||||||
|
tf,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(out)
|
||||||
|
} else {
|
||||||
|
return Err(Error(format!(
|
||||||
|
"Cannot watch the file at \"{:?}\" for hot-reload.",
|
||||||
|
temp_file
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error(format!(
|
||||||
|
"Cannot use filesystem watcher for hot-reload."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error(format!("could not write file \"{:?}\".", temp_file)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
mers/src/lib.rs
Normal file
10
mers/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
mod libs;
|
||||||
|
mod parse;
|
||||||
|
mod script;
|
||||||
|
|
||||||
|
pub use libs::inlib::*;
|
||||||
|
pub use parse::*;
|
||||||
|
pub use script::{val_data::*, val_type::*};
|
123
mers/src/main.rs
123
mers/src/main.rs
@ -1,13 +1,16 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::{fs, time::Instant};
|
use std::{fs, time::Instant};
|
||||||
|
|
||||||
use notify::Watcher as FsWatcher;
|
use notify::Watcher as FsWatcher;
|
||||||
|
|
||||||
pub mod libs;
|
mod interactive_mode;
|
||||||
pub mod parse;
|
mod libs;
|
||||||
pub mod script;
|
mod parse;
|
||||||
|
mod script;
|
||||||
|
mod tutor;
|
||||||
|
|
||||||
// necessary because the lib target in Cargo.toml also points here. TODO: update Cargo.toml to have a lib target that is separate from the bin one (=> doesn't point to main)
|
|
||||||
#[allow(unused)]
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<_> = std::env::args().skip(1).collect();
|
let args: Vec<_> = std::env::args().skip(1).collect();
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@ -27,9 +30,11 @@ fn main() {
|
|||||||
_ => {
|
_ => {
|
||||||
if args[0].trim_start().starts_with("-") {
|
if args[0].trim_start().starts_with("-") {
|
||||||
let mut execute = false;
|
let mut execute = false;
|
||||||
|
let mut print_version = false;
|
||||||
let mut verbose = 0;
|
let mut verbose = 0;
|
||||||
let mut interactive = 0;
|
let mut interactive = 0;
|
||||||
let mut interactive_use_new_terminal = false;
|
let mut interactive_use_new_terminal = false;
|
||||||
|
let mut teachme = false;
|
||||||
let mut prev_char = None;
|
let mut prev_char = None;
|
||||||
let mut advanced = false;
|
let mut advanced = false;
|
||||||
for ch in args[0][1..].chars() {
|
for ch in args[0][1..].chars() {
|
||||||
@ -41,7 +46,9 @@ fn main() {
|
|||||||
match ch {
|
match ch {
|
||||||
'e' => execute = true,
|
'e' => execute = true,
|
||||||
'v' => verbose += 1,
|
'v' => verbose += 1,
|
||||||
|
'V' => print_version = true,
|
||||||
'i' => interactive += 1,
|
'i' => interactive += 1,
|
||||||
|
't' => teachme = true,
|
||||||
ch => {
|
ch => {
|
||||||
eprintln!("Ignoring -{ch}. (unknown char)");
|
eprintln!("Ignoring -{ch}. (unknown char)");
|
||||||
continue;
|
continue;
|
||||||
@ -65,103 +72,29 @@ fn main() {
|
|||||||
advanced = false;
|
advanced = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if print_version {
|
||||||
|
println!(
|
||||||
|
"mers {}",
|
||||||
|
option_env!("CARGO_PKG_VERSION")
|
||||||
|
.unwrap_or("[[ version unknown: no CARGO_PKG_VERSION ]]")
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if teachme {
|
||||||
|
tutor::start(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if verbose != 0 {
|
if verbose != 0 {
|
||||||
eprintln!("info: set verbosity level to {verbose}. this doesn't do anything yet. [TODO!]");
|
eprintln!("info: set verbosity level to {verbose}. this doesn't do anything yet. [TODO!]");
|
||||||
}
|
}
|
||||||
if interactive >= 0 {
|
if interactive >= 0 {
|
||||||
let (contents, path) = match interactive {
|
match interactive {
|
||||||
1 => {
|
_ => {
|
||||||
// basic: open file and watch for fs changes
|
// basic: open file and watch for fs changes
|
||||||
let temp_file_edit =
|
interactive_mode::fs_watcher::playground(interactive_use_new_terminal)
|
||||||
edit::Builder::new().suffix(".mers").tempfile().unwrap();
|
|
||||||
let temp_file = temp_file_edit.path();
|
|
||||||
eprintln!("Using temporary file at {temp_file:?}. Save the file to update the output here.");
|
|
||||||
if let Ok(_) = std::fs::write(&temp_file, []) {
|
|
||||||
if let Ok(mut watcher) = {
|
|
||||||
let temp_file = temp_file.to_path_buf();
|
|
||||||
// the file watcher
|
|
||||||
notify::recommended_watcher(
|
|
||||||
move |event: Result<notify::Event, notify::Error>| {
|
|
||||||
if let Ok(event) = event {
|
|
||||||
match &event.kind {
|
|
||||||
notify::EventKind::Modify(
|
|
||||||
notify::event::ModifyKind::Data(_),
|
|
||||||
) => {
|
|
||||||
println!();
|
|
||||||
if let Ok(file_contents) =
|
|
||||||
fs::read_to_string(&temp_file)
|
|
||||||
{
|
|
||||||
let mut file = parse::file::File::new(
|
|
||||||
file_contents,
|
|
||||||
temp_file.clone(),
|
|
||||||
);
|
|
||||||
match parse::parse::parse(&mut file) {
|
|
||||||
Ok(func) => {
|
|
||||||
println!(" - - - - -");
|
|
||||||
let output = func.run(vec![]);
|
|
||||||
println!(" - - - - -");
|
|
||||||
println!("{}", output);
|
|
||||||
}
|
|
||||||
Err(e) => println!(
|
|
||||||
"{}",
|
|
||||||
e.with_file(&file)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"can't read file at {:?}!",
|
|
||||||
temp_file
|
|
||||||
);
|
|
||||||
std::process::exit(105);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} {
|
|
||||||
if let Ok(_) = watcher
|
|
||||||
.watch(&temp_file, notify::RecursiveMode::NonRecursive)
|
|
||||||
{
|
|
||||||
if interactive_use_new_terminal {
|
|
||||||
if let Ok(term) = std::env::var("TERM") {
|
|
||||||
let editor = edit::get_editor().unwrap();
|
|
||||||
eprintln!("launching \"{term} -e {editor:?} {temp_file:?}...");
|
|
||||||
std::process::Command::new(term)
|
|
||||||
.arg("-e")
|
|
||||||
.arg(&editor)
|
|
||||||
.arg(temp_file)
|
|
||||||
.spawn()
|
|
||||||
.unwrap()
|
|
||||||
.wait()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
edit::edit_file(temp_file_edit.path()).unwrap();
|
|
||||||
}
|
|
||||||
temp_file_edit.close().unwrap();
|
|
||||||
std::process::exit(0);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"Cannot watch the file at \"{:?}\" for hot-reload.",
|
|
||||||
temp_file
|
|
||||||
);
|
|
||||||
std::process::exit(104);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Cannot use filesystem watcher for hot-reload.");
|
|
||||||
// TODO: don't exit here?
|
|
||||||
std::process::exit(103);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("could not write file \"{:?}\".", temp_file);
|
|
||||||
std::process::exit(102);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (String::new(), String::new()),
|
|
||||||
};
|
};
|
||||||
parse::file::File::new(contents, path.into())
|
return;
|
||||||
} else if execute {
|
} else if execute {
|
||||||
parse::file::File::new(
|
parse::file::File::new(
|
||||||
args.iter().skip(1).fold(String::new(), |mut s, v| {
|
args.iter().skip(1).fold(String::new(), |mut s, v| {
|
||||||
|
@ -455,7 +455,7 @@ fn parse_statement_adv(
|
|||||||
let mut start = String::new();
|
let mut start = String::new();
|
||||||
loop {
|
loop {
|
||||||
fn is_delimeter(ch: char) -> bool {
|
fn is_delimeter(ch: char) -> bool {
|
||||||
matches!(ch, '}' | ']' | ')' | '.' | '=')
|
matches!(ch, '}' | ']' | ')' | '.')
|
||||||
}
|
}
|
||||||
let nchar = match file.peek() {
|
let nchar = match file.peek() {
|
||||||
Some(ch) if is_delimeter(ch) => Some(ch),
|
Some(ch) if is_delimeter(ch) => Some(ch),
|
||||||
@ -463,6 +463,40 @@ fn parse_statement_adv(
|
|||||||
};
|
};
|
||||||
match nchar {
|
match nchar {
|
||||||
Some(':') => {
|
Some(':') => {
|
||||||
|
if let Some(':') = file.peek() {
|
||||||
|
_ = file.next();
|
||||||
|
let file_pos_before_pot_type = *file.get_pos();
|
||||||
|
let parsed_type = parse_type(file);
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('=') = file.next() {
|
||||||
|
let err_equals_sign = *file.get_pos();
|
||||||
|
let start = start.trim();
|
||||||
|
let derefs = start.chars().take_while(|c| *c == '*').count();
|
||||||
|
match parse_statement(file) {
|
||||||
|
Ok(v) => break v
|
||||||
|
.output_to(start[derefs..].to_owned(), derefs)
|
||||||
|
.force_output_type(Some(match parsed_type {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(mut e) => {
|
||||||
|
e.context.push((
|
||||||
|
format!("interpreted this as an assignment to a variable with the format <var>::<var_type> = <statement>"), Some((err_start_of_statement, Some(err_equals_sign)))
|
||||||
|
));
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
Err(mut e) => {
|
||||||
|
e.context.push((
|
||||||
|
format!(
|
||||||
|
"statement was supposed to be assigned to variable {start}"
|
||||||
|
),
|
||||||
|
Some((err_start_of_statement, Some(err_equals_sign))),
|
||||||
|
));
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.set_pos(file_pos_before_pot_type);
|
||||||
|
}
|
||||||
return Ok(SStatement::new(SStatementEnum::EnumVariant(
|
return Ok(SStatement::new(SStatementEnum::EnumVariant(
|
||||||
start,
|
start,
|
||||||
parse_statement(file)?,
|
parse_statement(file)?,
|
||||||
@ -484,8 +518,8 @@ fn parse_statement_adv(
|
|||||||
let err_equals_sign = *file.get_pos();
|
let err_equals_sign = *file.get_pos();
|
||||||
let start = start.trim();
|
let start = start.trim();
|
||||||
let derefs = start.chars().take_while(|c| *c == '*').count();
|
let derefs = start.chars().take_while(|c| *c == '*').count();
|
||||||
break match parse_statement(file) {
|
match parse_statement(file) {
|
||||||
Ok(v) => v,
|
Ok(v) => break v.output_to(start[derefs..].to_owned(), derefs),
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
e.context.push((
|
e.context.push((
|
||||||
format!(
|
format!(
|
||||||
@ -495,43 +529,8 @@ fn parse_statement_adv(
|
|||||||
));
|
));
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
.output_to(start[derefs..].to_owned(), derefs);
|
|
||||||
}
|
}
|
||||||
// var type = statement
|
|
||||||
let file_pos_before_pot_type = *file.get_pos();
|
|
||||||
let parsed_type = parse_type(file);
|
|
||||||
file.skip_whitespaces();
|
|
||||||
if let Some('=') = file.peek() {
|
|
||||||
file.next();
|
|
||||||
let err_equals_sign = *file.get_pos();
|
|
||||||
let start = start.trim();
|
|
||||||
let derefs = start.chars().take_while(|c| *c == '*').count();
|
|
||||||
break match parse_statement(file) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(mut e) => {
|
|
||||||
e.context.push((
|
|
||||||
format!(
|
|
||||||
"statement was supposed to be assigned to variable {start}"
|
|
||||||
),
|
|
||||||
Some((err_start_of_statement, Some(err_equals_sign))),
|
|
||||||
));
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.output_to(start[derefs..].to_owned(), derefs)
|
|
||||||
.force_output_type(Some(match parsed_type {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(mut e) => {
|
|
||||||
e.context.push((
|
|
||||||
format!("interpreted this as an assignment to a variable with the format <var> <var_type> = <statement>"), Some((err_start_of_statement, Some(err_equals_sign)))
|
|
||||||
));
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
// nevermind, not var type = statement
|
|
||||||
file.set_pos(file_pos_before_pot_type);
|
|
||||||
// parse normal statement
|
// parse normal statement
|
||||||
let start = start.trim();
|
let start = start.trim();
|
||||||
match start {
|
match start {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// A code block is any section of code. It contains its own local variables and functions, as well as a list of statements.
|
// A code block is any section of code. It contains its own local variables and functions, as well as a list of statements.
|
||||||
// Types starting with S are directly parsed from Strings and unchecked. Types starting with T are type-checked templates for R-types. Types starting with R are runnable. S are converted to T after parsing is done, and T are converted to R whenever they need to run.
|
// Types starting with S are directly parsed from Strings and unchecked. Types starting with T are type-checked templates for R-types. Types starting with R are runnable. S are converted to T after parsing is done, and T are converted to R whenever they need to run.
|
||||||
|
// There currently are no T-Types, but we might need them in the future.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
@ -133,7 +134,11 @@ pub mod to_runnable {
|
|||||||
WrongInputsForBuiltinFunction(BuiltinFunction, String, Vec<VType>),
|
WrongInputsForBuiltinFunction(BuiltinFunction, String, Vec<VType>),
|
||||||
WrongArgsForLibFunction(String, Vec<VType>),
|
WrongArgsForLibFunction(String, Vec<VType>),
|
||||||
ForLoopContainerHasNoInnerTypes,
|
ForLoopContainerHasNoInnerTypes,
|
||||||
StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(VType, VType, VType),
|
StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(
|
||||||
|
VType,
|
||||||
|
VType,
|
||||||
|
VType,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
impl Debug for ToRunnableError {
|
impl Debug for ToRunnableError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@ -658,7 +663,7 @@ pub mod to_runnable {
|
|||||||
if problematic_types.is_empty() {
|
if problematic_types.is_empty() {
|
||||||
statement.force_output_type = Some(force_opt.clone());
|
statement.force_output_type = Some(force_opt.clone());
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types }))
|
return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((opt, derefs)) = &s.output_to {
|
if let Some((opt, derefs)) = &s.output_to {
|
||||||
@ -669,7 +674,11 @@ pub mod to_runnable {
|
|||||||
var_derefd = if let Some(v) = var_derefd.dereference() {
|
var_derefd = if let Some(v) = var_derefd.dereference() {
|
||||||
v
|
v
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::CannotDereferenceTypeNTimes(var_out.clone(), *derefs, var_derefd));
|
return Err(ToRunnableError::CannotDereferenceTypeNTimes(
|
||||||
|
var_out.clone(),
|
||||||
|
*derefs,
|
||||||
|
var_derefd,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let inv_types = out.fits_in(&var_derefd);
|
let inv_types = out.fits_in(&var_derefd);
|
||||||
@ -693,16 +702,19 @@ pub mod to_runnable {
|
|||||||
out = if let Some(v) = out.dereference() {
|
out = if let Some(v) = out.dereference() {
|
||||||
v
|
v
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::CannotDereferenceTypeNTimes(statement.out(), *derefs, out));
|
return Err(ToRunnableError::CannotDereferenceTypeNTimes(
|
||||||
|
statement.out(),
|
||||||
|
*derefs,
|
||||||
|
out,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
linfo
|
linfo.vars.insert(opt.clone(), (ginfo.vars, out));
|
||||||
.vars
|
|
||||||
.insert(opt.clone(), (ginfo.vars, out));
|
|
||||||
statement.output_to = Some((ginfo.vars, *derefs));
|
statement.output_to = Some((ginfo.vars, *derefs));
|
||||||
ginfo.vars += 1;
|
ginfo.vars += 1;
|
||||||
}
|
}
|
||||||
} Ok(statement)
|
}
|
||||||
|
Ok(statement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use crate::libs;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
block::RStatement,
|
block::RStatement,
|
||||||
val_data::{VData, VDataEnum, VDataThreadEnum},
|
val_data::{thread::VDataThreadEnum, VData, VDataEnum},
|
||||||
val_type::{VSingleType, VType},
|
val_type::{VSingleType, VType},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -236,10 +236,21 @@ impl BuiltinFunction {
|
|||||||
Self::Run | Self::Thread => {
|
Self::Run | Self::Thread => {
|
||||||
if input.len() >= 1 {
|
if input.len() >= 1 {
|
||||||
input[0].types.iter().all(|v| {
|
input[0].types.iter().all(|v| {
|
||||||
|
// all possible types of the input function must be function types
|
||||||
if let VSingleType::Function(v) = v {
|
if let VSingleType::Function(v) = v {
|
||||||
if v.iter().any(|(i, _)| i.len() == input.len() - 1) {
|
// and all those functions must take as many inputs as were supplied to run() or thread() minus one (the function itself).
|
||||||
|
if v.iter()
|
||||||
|
.all(|(fn_in, _fn_out)| fn_in.len() == input.len() - 1)
|
||||||
|
{
|
||||||
eprintln!("Warn: Function inputs aren't type checked yet!)");
|
eprintln!("Warn: Function inputs aren't type checked yet!)");
|
||||||
// TODO!
|
// all functions have the correct length, now check their types:
|
||||||
|
// this is more difficult than it seems, because if a function covers all input types on the first and second argument, that doesn't necessarily mean that it covers all possible cases:
|
||||||
|
// say out function is of type fn((int string []) (string int) []).
|
||||||
|
// this covers int/string for the first two arguments, but the function actually can't handle two ints or two strings as arguments, it requires exactly one int and one string.
|
||||||
|
// the most obvious implementation here would be a recursive function that goes over each type in the first argument, then calls itself recursively to check the second element and so on,
|
||||||
|
// but this would likely become slower than it should for complex functions.
|
||||||
|
// because of this, we just trust the programmer not to provide wrong arguments to run() and thread() for now,
|
||||||
|
// until a better solution is found.
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
thread::JoinHandle,
|
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -25,7 +23,7 @@ pub enum VDataEnum {
|
|||||||
Tuple(Vec<VData>),
|
Tuple(Vec<VData>),
|
||||||
List(VType, Vec<VData>),
|
List(VType, Vec<VData>),
|
||||||
Function(RFunction),
|
Function(RFunction),
|
||||||
Thread(VDataThread, VType),
|
Thread(thread::VDataThread, VType),
|
||||||
Reference(Arc<Mutex<VData>>),
|
Reference(Arc<Mutex<VData>>),
|
||||||
EnumVariant(usize, Box<VData>),
|
EnumVariant(usize, Box<VData>),
|
||||||
}
|
}
|
||||||
@ -157,64 +155,75 @@ impl VType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub mod thread {
|
||||||
pub struct VDataThread(Arc<Mutex<VDataThreadEnum>>);
|
use std::{
|
||||||
impl VDataThread {
|
fmt::Debug,
|
||||||
pub fn try_get(&self) -> Option<VData> {
|
sync::{Arc, Mutex},
|
||||||
match &*self.lock() {
|
thread::JoinHandle,
|
||||||
VDataThreadEnum::Running(_) => None,
|
time::Duration,
|
||||||
VDataThreadEnum::Finished(v) => Some(v.clone()),
|
};
|
||||||
}
|
|
||||||
}
|
use super::{VData, VDataEnum};
|
||||||
pub fn get(&self) -> VData {
|
|
||||||
let dur = Duration::from_millis(100);
|
#[derive(Clone)]
|
||||||
loop {
|
pub struct VDataThread(Arc<Mutex<VDataThreadEnum>>);
|
||||||
|
impl VDataThread {
|
||||||
|
pub fn try_get(&self) -> Option<VData> {
|
||||||
match &*self.lock() {
|
match &*self.lock() {
|
||||||
VDataThreadEnum::Running(v) => {
|
VDataThreadEnum::Running(_) => None,
|
||||||
while !v.is_finished() {
|
VDataThreadEnum::Finished(v) => Some(v.clone()),
|
||||||
std::thread::sleep(dur);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VDataThreadEnum::Finished(v) => return v.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
pub fn get(&self) -> VData {
|
||||||
pub fn lock(&self) -> std::sync::MutexGuard<VDataThreadEnum> {
|
let dur = Duration::from_millis(100);
|
||||||
let mut mg = self.0.lock().unwrap();
|
loop {
|
||||||
match &*mg {
|
match &*self.lock() {
|
||||||
VDataThreadEnum::Running(v) => {
|
VDataThreadEnum::Running(v) => {
|
||||||
if v.is_finished() {
|
while !v.is_finished() {
|
||||||
let m = std::mem::replace(
|
std::thread::sleep(dur);
|
||||||
&mut *mg,
|
|
||||||
VDataThreadEnum::Finished(VDataEnum::Bool(false).to()),
|
|
||||||
);
|
|
||||||
match m {
|
|
||||||
VDataThreadEnum::Running(v) => {
|
|
||||||
*mg = VDataThreadEnum::Finished(v.join().unwrap())
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
VDataThreadEnum::Finished(v) => return v.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
mg
|
pub fn lock(&self) -> std::sync::MutexGuard<VDataThreadEnum> {
|
||||||
|
let mut mg = self.0.lock().unwrap();
|
||||||
|
match &*mg {
|
||||||
|
VDataThreadEnum::Running(v) => {
|
||||||
|
if v.is_finished() {
|
||||||
|
let m = std::mem::replace(
|
||||||
|
&mut *mg,
|
||||||
|
VDataThreadEnum::Finished(VDataEnum::Bool(false).to()),
|
||||||
|
);
|
||||||
|
match m {
|
||||||
|
VDataThreadEnum::Running(v) => {
|
||||||
|
*mg = VDataThreadEnum::Finished(v.join().unwrap())
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
mg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
impl Debug for VDataThread {
|
||||||
impl Debug for VDataThread {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
match &*self.lock() {
|
||||||
match &*self.lock() {
|
VDataThreadEnum::Running(_) => write!(f, "(thread running)"),
|
||||||
VDataThreadEnum::Running(_) => write!(f, "(thread running)"),
|
VDataThreadEnum::Finished(v) => write!(f, "(thread finished: {v})"),
|
||||||
VDataThreadEnum::Finished(v) => write!(f, "(thread finished: {v})"),
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub enum VDataThreadEnum {
|
||||||
|
Running(JoinHandle<VData>),
|
||||||
|
Finished(VData),
|
||||||
|
}
|
||||||
|
impl VDataThreadEnum {
|
||||||
|
pub fn to(self) -> VDataThread {
|
||||||
|
VDataThread(Arc::new(Mutex::new(self)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub enum VDataThreadEnum {
|
|
||||||
Running(JoinHandle<VData>),
|
|
||||||
Finished(VData),
|
|
||||||
}
|
|
||||||
impl VDataThreadEnum {
|
|
||||||
pub fn to(self) -> VDataThread {
|
|
||||||
VDataThread(Arc::new(Mutex::new(self)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
25
mers/src/tutor/base_comments.rs
Normal file
25
mers/src/tutor/base_comments.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use crate::script::val_data::VDataEnum;
|
||||||
|
|
||||||
|
use super::Tutor;
|
||||||
|
|
||||||
|
pub fn run(tutor: &mut Tutor) {
|
||||||
|
tutor.update(Some(
|
||||||
|
"
|
||||||
|
// Comments in mers start at // and end at the end of the line.
|
||||||
|
// They also work within strings, which can be unexpected in some cases (like \"http://www...\").
|
||||||
|
/* also works to start a comment.
|
||||||
|
This comment can even span multiple lines! */
|
||||||
|
// To return to the menu, uncomment the next line:
|
||||||
|
// true
|
||||||
|
",
|
||||||
|
));
|
||||||
|
loop {
|
||||||
|
match tutor.let_user_make_change().run(vec![]).data {
|
||||||
|
VDataEnum::Bool(true) => break,
|
||||||
|
other => {
|
||||||
|
tutor.set_status(format!(" - Returned {} instead of true.", other));
|
||||||
|
tutor.update(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
mers/src/tutor/base_return.rs
Normal file
38
mers/src/tutor/base_return.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use crate::script::val_data::VDataEnum;
|
||||||
|
|
||||||
|
use super::Tutor;
|
||||||
|
|
||||||
|
pub fn run(tutor: &mut Tutor) {
|
||||||
|
tutor.update(Some(
|
||||||
|
"
|
||||||
|
// Mers doesn't have a return statement.
|
||||||
|
// Instead, the value of the last statement is implicitly returned.
|
||||||
|
|
||||||
|
// This applies to blocks:
|
||||||
|
b = {
|
||||||
|
a = 10
|
||||||
|
a = a.add(15)
|
||||||
|
a
|
||||||
|
}
|
||||||
|
// b = 25
|
||||||
|
|
||||||
|
// To functions:
|
||||||
|
fn compute_sum(a int b int) {
|
||||||
|
a.add(b)
|
||||||
|
}
|
||||||
|
// returns a+b
|
||||||
|
|
||||||
|
// and to the program itself!
|
||||||
|
// to return to the menu, make the program return 15.
|
||||||
|
",
|
||||||
|
));
|
||||||
|
loop {
|
||||||
|
match tutor.let_user_make_change().run(vec![]).data {
|
||||||
|
VDataEnum::Int(15) => break,
|
||||||
|
other => {
|
||||||
|
tutor.set_status(format!(" - Returned {} instead of 15.", other));
|
||||||
|
tutor.update(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
mers/src/tutor/base_values.rs
Normal file
35
mers/src/tutor/base_values.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::script::val_data::VDataEnum;
|
||||||
|
|
||||||
|
use super::Tutor;
|
||||||
|
|
||||||
|
pub fn run(tutor: &mut Tutor) {
|
||||||
|
tutor.update(Some("
|
||||||
|
// Mers has the following values:
|
||||||
|
// bool: Either true or false
|
||||||
|
// int: An integer. Written 0, 1, 2, 3, -5, and so on
|
||||||
|
// float: A floating point number. Written 0.5, -12.378, and so on
|
||||||
|
// string: A piece of text. Surround something with \" and it will be a string: \"Hello, world!\"
|
||||||
|
// tuples: Multiple values in one place. A fixed-length collection. Surround types or statements with [] to create a tuple: [12 \"a tuple of ints and a string\" -5 -12]
|
||||||
|
// The empty tuple [] is often used to indicate nothing, while a 1-long tuple [v] indicates the opposite - something.
|
||||||
|
// list: Similar to tuples, but the closing ] is prefixed with 3 dots: [ ...]
|
||||||
|
// Unlike tuples, all elements in a list have the same type. Lists are resizable and can grow dynamically, while tuples cannot change their size after being created.
|
||||||
|
// function: A piece of code in data-form.
|
||||||
|
// value: anonymous_sum_function = (a int/float b int/float) a.add(b)
|
||||||
|
// type: fn((int int int)(int float float)(float int float)(float float float))
|
||||||
|
// the reason why the type syntax is so expressive is because the function doesn't return the same type for any inputs - add will return an int if it added two ints, but will return a float when at least one argument was a float.
|
||||||
|
// add will NOT return int/float, because if you know the exact input types, you also know the output type: either int and not float or float and not int.
|
||||||
|
// thread: Represents a different thread. The thread's return value can be retrieved by using .await(). Thread values are returned by the builtin thread() function.
|
||||||
|
// reference: A mutable reference to some data. Used by things like push() and remove() to avoid having to clone the entire list just to make a small change.
|
||||||
|
// enums: An enum can wrap any type. Enums are identified by their names and can be created using EnumName: inner_value. The type is written EnumName(InnerType).
|
||||||
|
// return a value of type GoBackToMenu([int]) to return to the menu.
|
||||||
|
"));
|
||||||
|
loop {
|
||||||
|
match tutor.let_user_make_change().run(vec![]).data {
|
||||||
|
VDataEnum::EnumVariant(..) => break,
|
||||||
|
other => {
|
||||||
|
tutor.set_status(format!(" - Returned {other} instead of an enum."));
|
||||||
|
tutor.update(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
mers/src/tutor/menu.rs
Normal file
39
mers/src/tutor/menu.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use crate::script::val_data::VDataEnum;
|
||||||
|
|
||||||
|
use super::Tutor;
|
||||||
|
|
||||||
|
pub const MAX_POS: usize = 3;
|
||||||
|
|
||||||
|
pub fn run(mut tutor: Tutor) {
|
||||||
|
loop {
|
||||||
|
tutor.current_pos = 0;
|
||||||
|
tutor.update(Some(
|
||||||
|
"
|
||||||
|
// Welcome to the mers tutor!
|
||||||
|
// This is the main menu. Change the number to navigate to a specific part.
|
||||||
|
0
|
||||||
|
// 1 Comments
|
||||||
|
// 2 Values
|
||||||
|
// 3 Returns
|
||||||
|
",
|
||||||
|
));
|
||||||
|
loop {
|
||||||
|
match tutor.let_user_make_change().run(vec![]).data {
|
||||||
|
VDataEnum::Int(pos) => {
|
||||||
|
tutor.current_pos = (pos.max(0) as usize).min(MAX_POS);
|
||||||
|
match tutor.current_pos {
|
||||||
|
0 => continue,
|
||||||
|
1 => super::base_comments::run(&mut tutor),
|
||||||
|
2 => super::base_values::run(&mut tutor),
|
||||||
|
3 => super::base_return::run(&mut tutor),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
tutor.set_status(format!(" - Returned {} instead of an integer", other));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
mers/src/tutor/mod.rs
Normal file
112
mers/src/tutor/mod.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use std::{path::PathBuf, thread::JoinHandle, time::Instant};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
parse::{self, parse::ScriptError},
|
||||||
|
script::{block::RScript, val_data::VDataEnum},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod base_comments;
|
||||||
|
mod base_return;
|
||||||
|
mod base_values;
|
||||||
|
mod menu;
|
||||||
|
|
||||||
|
pub fn start(spawn_new_terminal_for_editor: bool) {
|
||||||
|
let (sender, receiver) = std::sync::mpsc::channel();
|
||||||
|
let (editor_join_handle, file_path) = crate::interactive_mode::fs_watcher::main(
|
||||||
|
spawn_new_terminal_for_editor,
|
||||||
|
"// Welcome to the mers tutor!
|
||||||
|
|
||||||
|
// This is an interactive experience. After making a change to this file,
|
||||||
|
// save and then reload it to see the tutor's updates.
|
||||||
|
// To begin, change the following value from false to true:
|
||||||
|
|
||||||
|
false
|
||||||
|
",
|
||||||
|
Box::new(move |file| {
|
||||||
|
let mut file =
|
||||||
|
parse::file::File::new(std::fs::read_to_string(file).unwrap(), PathBuf::new());
|
||||||
|
sender.send(parse::parse::parse(&mut file)).unwrap();
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut tutor = Tutor {
|
||||||
|
current_pos: 0,
|
||||||
|
current_status: String::new(),
|
||||||
|
written_status_byte_len: 0,
|
||||||
|
editor_join_handle,
|
||||||
|
file_path,
|
||||||
|
receiver,
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
if let VDataEnum::Bool(true) = tutor.let_user_make_change().run(vec![]).data {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu::run(tutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
use menu::MAX_POS;
|
||||||
|
|
||||||
|
pub struct Tutor {
|
||||||
|
current_pos: usize,
|
||||||
|
current_status: String,
|
||||||
|
written_status_byte_len: usize,
|
||||||
|
editor_join_handle: JoinHandle<()>,
|
||||||
|
file_path: PathBuf,
|
||||||
|
receiver: std::sync::mpsc::Receiver<Result<RScript, ScriptError>>,
|
||||||
|
}
|
||||||
|
impl Tutor {
|
||||||
|
/// only returns after a successful compile. before returning, does not call self.update() - you have to do that manually.
|
||||||
|
pub fn let_user_make_change(&mut self) -> RScript {
|
||||||
|
// eprintln!(" - - - - - - - - - - - - - - - - - - - - - - - - -");
|
||||||
|
let script = loop {
|
||||||
|
match self.receiver.recv().unwrap() {
|
||||||
|
Err(e) => {
|
||||||
|
self.current_status = format!(
|
||||||
|
" - Error during build{}",
|
||||||
|
e.to_string()
|
||||||
|
.lines()
|
||||||
|
.map(|v| format!("\n// {v}"))
|
||||||
|
.collect::<String>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(script) => {
|
||||||
|
break script;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.update(None);
|
||||||
|
};
|
||||||
|
self.current_status = format!(" - OK");
|
||||||
|
script
|
||||||
|
}
|
||||||
|
pub fn set_status(&mut self, new_status: String) {
|
||||||
|
self.current_status = new_status;
|
||||||
|
}
|
||||||
|
pub fn update(&mut self, overwrite_contents_with: Option<&str>) {
|
||||||
|
if self.editor_join_handle.is_finished() {
|
||||||
|
eprintln!("Error has closed!");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
let string = std::fs::read_to_string(self.file_path()).unwrap();
|
||||||
|
let status = format!(
|
||||||
|
"// Tutor: {}/{MAX_POS}{}\n",
|
||||||
|
self.current_pos, self.current_status,
|
||||||
|
);
|
||||||
|
let status_len = status.len();
|
||||||
|
std::fs::write(
|
||||||
|
self.file_path(),
|
||||||
|
if let Some(new_content) = overwrite_contents_with {
|
||||||
|
status + new_content
|
||||||
|
} else {
|
||||||
|
status + &string[self.written_status_byte_len..]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.written_status_byte_len = status_len;
|
||||||
|
// ignore this update to the file
|
||||||
|
_ = self.receiver.recv().unwrap();
|
||||||
|
}
|
||||||
|
pub fn file_path(&self) -> &PathBuf {
|
||||||
|
&self.file_path
|
||||||
|
}
|
||||||
|
}
|
@ -9,13 +9,8 @@ use iced::{
|
|||||||
widget::{button, column, row, text},
|
widget::{button, column, row, text},
|
||||||
Application, Command, Element, Renderer, Settings, Subscription, Theme,
|
Application, Command, Element, Renderer, Settings, Subscription, Theme,
|
||||||
};
|
};
|
||||||
use mers::{
|
|
||||||
libs::inlib::{MyLib, MyLibTask},
|
use mers_libs::{MyLib, MyLibTask, VData, VDataEnum, VSingleType, VType};
|
||||||
script::{
|
|
||||||
val_data::{VData, VDataEnum},
|
|
||||||
val_type::{VSingleType, VType},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -264,7 +259,6 @@ impl Application for App {
|
|||||||
format!("{}", self.title)
|
format!("{}", self.title)
|
||||||
}
|
}
|
||||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||||
let mut commands = vec![];
|
|
||||||
match message {
|
match message {
|
||||||
Message::Tick => {
|
Message::Tick => {
|
||||||
let mut changed_layout = false;
|
let mut changed_layout = false;
|
||||||
@ -303,7 +297,7 @@ impl Application for App {
|
|||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}
|
}
|
||||||
Command::batch(commands)
|
Command::none()
|
||||||
}
|
}
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
time::every(Duration::from_millis(10)).map(|_| Message::Tick)
|
time::every(Duration::from_millis(10)).map(|_| Message::Tick)
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
use mers::{
|
use mers_libs::{MyLib, MyLibTask, VDataEnum, VSingleType, VType};
|
||||||
libs::inlib::{MyLib, MyLibTask},
|
|
||||||
script::{
|
|
||||||
val_data::VDataEnum,
|
|
||||||
val_type::{VSingleType, VType},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (mut my_lib, mut run) = MyLib::new(
|
let (mut my_lib, mut run) = MyLib::new(
|
||||||
|
Loading…
Reference in New Issue
Block a user