mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
added var_name var_type = statement
syntax, which forces the statements return type to fit in var_type. This is useful if your initial value doesn't cover all types (like [ ...], which is a list with an inner type that cannot exist because there are 0 valid types, making the list completely useless. but arr [int/string ...] = []
expands that inner type to int/string, making the list usable)
This commit is contained in:
parent
14d004d848
commit
9c1564b426
@ -10,8 +10,16 @@ pub mod script;
|
||||
#[allow(unused)]
|
||||
fn main() {
|
||||
let args: Vec<_> = std::env::args().skip(1).collect();
|
||||
#[cfg(debug_assertions)]
|
||||
let args = if args.len() == 0 {
|
||||
let mut args = args;
|
||||
args.push("../script.mers".to_owned());
|
||||
args
|
||||
} else {
|
||||
args
|
||||
};
|
||||
let path = std::env::args().nth(1).unwrap();
|
||||
let script = parse::parse::parse(&mut match args.len() {
|
||||
let mut file = match args.len() {
|
||||
0 => {
|
||||
println!("Please provide some arguments, such as the path to a file or \"-e <code>\".");
|
||||
std::process::exit(100);
|
||||
@ -43,16 +51,16 @@ fn main() {
|
||||
} else {
|
||||
if let Some(prev_char) = prev_char {
|
||||
match prev_char {
|
||||
'i' => {
|
||||
match ch {
|
||||
'i' => match ch {
|
||||
't' => interactive_use_new_terminal = true,
|
||||
_ => eprintln!("Ignoring i+{ch}. (unknown adv char)"),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
eprintln!("Ignoring advanced args because there was no previous argument.");
|
||||
eprintln!(
|
||||
"Ignoring advanced args because there was no previous argument."
|
||||
);
|
||||
}
|
||||
advanced = false;
|
||||
}
|
||||
@ -64,39 +72,58 @@ fn main() {
|
||||
let (contents, path) = match interactive {
|
||||
1 => {
|
||||
// basic: open file and watch for fs changes
|
||||
let temp_file_edit = edit::Builder::new().suffix(".mers").tempfile().unwrap();
|
||||
let temp_file_edit =
|
||||
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>| {
|
||||
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(_)) => {
|
||||
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());
|
||||
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)),
|
||||
}
|
||||
Err(e) => println!(
|
||||
"{}",
|
||||
e.with_file(&file)
|
||||
),
|
||||
}
|
||||
} else {
|
||||
println!("can't read file at {:?}!", temp_file);
|
||||
println!(
|
||||
"can't read file at {:?}!",
|
||||
temp_file
|
||||
);
|
||||
std::process::exit(105);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})} {
|
||||
if let Ok(_) = watcher.watch(&temp_file, notify::RecursiveMode::NonRecursive) {
|
||||
},
|
||||
)
|
||||
} {
|
||||
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();
|
||||
@ -116,7 +143,10 @@ fn main() {
|
||||
temp_file_edit.close().unwrap();
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
println!("Cannot watch the file at \"{:?}\" for hot-reload.", temp_file);
|
||||
println!(
|
||||
"Cannot watch the file at \"{:?}\" for hot-reload.",
|
||||
temp_file
|
||||
);
|
||||
std::process::exit(104);
|
||||
}
|
||||
} else {
|
||||
@ -131,12 +161,8 @@ fn main() {
|
||||
}
|
||||
_ => (String::new(), String::new()),
|
||||
};
|
||||
parse::file::File::new(
|
||||
contents,
|
||||
path.into()
|
||||
)
|
||||
}
|
||||
else if execute {
|
||||
parse::file::File::new(contents, path.into())
|
||||
} else if execute {
|
||||
parse::file::File::new(
|
||||
args.iter().skip(1).fold(String::new(), |mut s, v| {
|
||||
if !s.is_empty() {
|
||||
@ -155,12 +181,19 @@ fn main() {
|
||||
parse::file::File::new(std::fs::read_to_string(&args[0]).unwrap(), path.into())
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
};
|
||||
match parse::parse::parse(&mut file) {
|
||||
Ok(script) => {
|
||||
println!(" - - - - -");
|
||||
let start = Instant::now();
|
||||
let out = script.run(std::env::args().skip(2).collect());
|
||||
let elapsed = start.elapsed();
|
||||
println!(" - - - - -");
|
||||
println!("Output ({}s)\n{out}", elapsed.as_secs_f64());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Couldn't compile:\n{e}");
|
||||
std::process::exit(99);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +240,7 @@ impl std::fmt::Display for ParseError {
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum ParseErrors {
|
||||
StatementCannotStartWith(char),
|
||||
FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement,
|
||||
FoundClosingCurlyBracketInSingleStatementBlockBeforeAnyStatement,
|
||||
FoundEofInBlockBeforeStatementOrClosingCurlyBracket,
|
||||
@ -256,6 +257,9 @@ pub enum ParseErrors {
|
||||
impl std::fmt::Display for ParseErrors {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::StatementCannotStartWith(ch) => {
|
||||
write!(f, "statements cannot start with the {ch} character.",)
|
||||
}
|
||||
Self::FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement => write!(
|
||||
f,
|
||||
"closing round bracket in single-statement block before any statement"
|
||||
@ -451,18 +455,13 @@ fn parse_statement_adv(
|
||||
let mut start = String::new();
|
||||
loop {
|
||||
fn is_delimeter(ch: char) -> bool {
|
||||
matches!(ch, '}' | ']' | ')' | '.')
|
||||
matches!(ch, '}' | ']' | ')' | '.' | '=')
|
||||
}
|
||||
let nchar = match file.peek() {
|
||||
Some(ch) if is_delimeter(ch) => Some(ch),
|
||||
_ => file.next(),
|
||||
};
|
||||
match nchar {
|
||||
Some('=') => {
|
||||
let start = start.trim();
|
||||
let derefs = start.chars().take_while(|c| *c == '*').count();
|
||||
break parse_statement(file)?.output_to(start[derefs..].to_owned(), derefs);
|
||||
}
|
||||
Some(':') => {
|
||||
return Ok(SStatement::new(SStatementEnum::EnumVariant(
|
||||
start,
|
||||
@ -470,10 +469,70 @@ fn parse_statement_adv(
|
||||
)));
|
||||
}
|
||||
Some(ch) if ch.is_whitespace() || is_delimeter(ch) => {
|
||||
if start.trim().is_empty() {
|
||||
return Err(ParseError {
|
||||
err: ParseErrors::StatementCannotStartWith(ch),
|
||||
location: *file.get_pos(),
|
||||
location_end: None,
|
||||
context: vec![],
|
||||
});
|
||||
}
|
||||
file.skip_whitespaces();
|
||||
// var = statement
|
||||
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);
|
||||
}
|
||||
// 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() {
|
||||
continue;
|
||||
} else {
|
||||
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
|
||||
let start = start.trim();
|
||||
match start {
|
||||
"fn" => {
|
||||
@ -582,12 +641,8 @@ fn parse_statement_adv(
|
||||
}
|
||||
break SStatementEnum::Match(match_what, cases).into();
|
||||
}
|
||||
"true" => {
|
||||
break SStatementEnum::Value(VDataEnum::Bool(true).to()).into()
|
||||
}
|
||||
"false" => {
|
||||
break SStatementEnum::Value(VDataEnum::Bool(false).to()).into()
|
||||
}
|
||||
"true" => break SStatementEnum::Value(VDataEnum::Bool(true).to()).into(),
|
||||
"false" => break SStatementEnum::Value(VDataEnum::Bool(false).to()).into(),
|
||||
_ => {
|
||||
// int, float, var
|
||||
break {
|
||||
@ -604,8 +659,7 @@ fn parse_statement_adv(
|
||||
pot_float.push(ch);
|
||||
}
|
||||
if let Ok(v) = format!("{start}.{pot_float}").parse() {
|
||||
SStatementEnum::Value(VDataEnum::Float(v).to())
|
||||
.into()
|
||||
SStatementEnum::Value(VDataEnum::Float(v).to()).into()
|
||||
} else {
|
||||
file.set_pos(pos);
|
||||
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
|
||||
@ -620,15 +674,13 @@ fn parse_statement_adv(
|
||||
SStatementEnum::Variable(start[1..].to_string(), true)
|
||||
.into()
|
||||
} else {
|
||||
SStatementEnum::Variable(start.to_string(), false)
|
||||
.into()
|
||||
SStatementEnum::Variable(start.to_string(), false).into()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some('(') => {
|
||||
// parse_block_advanced: only treat ) as closing delimeter, don't use single-statement (missing {, so would be assumed otherwise)
|
||||
let name = start.trim();
|
||||
|
@ -43,18 +43,25 @@ impl SFunction {
|
||||
pub struct SStatement {
|
||||
pub output_to: Option<(String, usize)>,
|
||||
pub statement: Box<SStatementEnum>,
|
||||
pub force_output_type: Option<VType>,
|
||||
}
|
||||
impl SStatement {
|
||||
pub fn new(statement: SStatementEnum) -> Self {
|
||||
Self {
|
||||
output_to: None,
|
||||
statement: Box::new(statement),
|
||||
force_output_type: None,
|
||||
}
|
||||
}
|
||||
pub fn output_to(mut self, var: String, derefs: usize) -> Self {
|
||||
self.output_to = Some((var, derefs));
|
||||
self
|
||||
}
|
||||
// forces the statement's output to fit in a certain type.
|
||||
pub fn force_output_type(mut self, force_output_type: Option<VType>) -> Self {
|
||||
self.force_output_type = force_output_type;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -126,6 +133,7 @@ pub mod to_runnable {
|
||||
WrongInputsForBuiltinFunction(BuiltinFunction, String, Vec<VType>),
|
||||
WrongArgsForLibFunction(String, Vec<VType>),
|
||||
ForLoopContainerHasNoInnerTypes,
|
||||
StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(VType, VType, VType),
|
||||
}
|
||||
impl Debug for ToRunnableError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@ -173,6 +181,9 @@ pub mod to_runnable {
|
||||
Self::ForLoopContainerHasNoInnerTypes => {
|
||||
write!(f, "For loop: container had no inner types, cannot iterate.")
|
||||
}
|
||||
Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => write!(f,
|
||||
"the statement requires its output type to be {required}, but its real output type is {real}, which doesn not fit in the required type because of the problematic types {problematic}."
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -640,6 +651,18 @@ pub mod to_runnable {
|
||||
}, statement(s, ginfo, linfo)?),
|
||||
}
|
||||
.to();
|
||||
// if force_output_type is set, verify that the real output type actually fits in the forced one.
|
||||
if let Some(force_opt) = &s.force_output_type {
|
||||
let real_output_type = statement.out();
|
||||
let problematic_types = real_output_type.fits_in(force_opt);
|
||||
eprintln!("Real: {real_output_type}");
|
||||
eprintln!("Prob: {problematic_types:?}");
|
||||
if problematic_types.is_empty() {
|
||||
statement.force_output_type = Some(force_opt.clone());
|
||||
} else {
|
||||
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((var_id, var_out)) = linfo.vars.get(opt) {
|
||||
let out = statement.out();
|
||||
@ -681,8 +704,7 @@ pub mod to_runnable {
|
||||
statement.output_to = Some((ginfo.vars, *derefs));
|
||||
ginfo.vars += 1;
|
||||
}
|
||||
}
|
||||
Ok(statement)
|
||||
} Ok(statement)
|
||||
}
|
||||
}
|
||||
|
||||
@ -763,6 +785,7 @@ impl RFunction {
|
||||
pub struct RStatement {
|
||||
output_to: Option<(usize, usize)>,
|
||||
statement: Box<RStatementEnum>,
|
||||
force_output_type: Option<VType>,
|
||||
}
|
||||
impl RStatement {
|
||||
pub fn run(&self, vars: &Vec<Am<VData>>, libs: &Arc<Vec<libs::Lib>>) -> VData {
|
||||
@ -784,11 +807,15 @@ impl RStatement {
|
||||
}
|
||||
}
|
||||
pub fn out(&self) -> VType {
|
||||
// `a = b` evaluates to []
|
||||
if self.output_to.is_some() {
|
||||
return VType {
|
||||
types: vec![VSingleType::Tuple(vec![])],
|
||||
};
|
||||
}
|
||||
if let Some(t) = &self.force_output_type {
|
||||
return t.clone();
|
||||
}
|
||||
self.statement.out()
|
||||
}
|
||||
}
|
||||
@ -1028,6 +1055,7 @@ impl RStatementEnum {
|
||||
RStatement {
|
||||
output_to: None,
|
||||
statement: Box::new(self),
|
||||
force_output_type: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1245,7 +1273,7 @@ impl Display for VDataEnum {
|
||||
}
|
||||
}
|
||||
match self {
|
||||
Self::List(..) => write!(f, "...")?,
|
||||
Self::List(..) => write!(f, " ...")?,
|
||||
_ => (),
|
||||
}
|
||||
write!(f, "]")?;
|
||||
|
Loading…
Reference in New Issue
Block a user