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
129
mers/src/main.rs
129
mers/src/main.rs
@ -10,8 +10,16 @@ pub mod script;
|
|||||||
#[allow(unused)]
|
#[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)]
|
||||||
|
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 path = std::env::args().nth(1).unwrap();
|
||||||
let script = parse::parse::parse(&mut match args.len() {
|
let mut file = match args.len() {
|
||||||
0 => {
|
0 => {
|
||||||
println!("Please provide some arguments, such as the path to a file or \"-e <code>\".");
|
println!("Please provide some arguments, such as the path to a file or \"-e <code>\".");
|
||||||
std::process::exit(100);
|
std::process::exit(100);
|
||||||
@ -43,16 +51,16 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
if let Some(prev_char) = prev_char {
|
if let Some(prev_char) = prev_char {
|
||||||
match prev_char {
|
match prev_char {
|
||||||
'i' => {
|
'i' => match ch {
|
||||||
match ch {
|
't' => interactive_use_new_terminal = true,
|
||||||
't' => interactive_use_new_terminal = true,
|
_ => eprintln!("Ignoring i+{ch}. (unknown adv char)"),
|
||||||
_ => eprintln!("Ignoring i+{ch}. (unknown adv char)"),
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Ignoring advanced args because there was no previous argument.");
|
eprintln!(
|
||||||
|
"Ignoring advanced args because there was no previous argument."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
advanced = false;
|
advanced = false;
|
||||||
}
|
}
|
||||||
@ -64,39 +72,58 @@ fn main() {
|
|||||||
let (contents, path) = match interactive {
|
let (contents, path) = match interactive {
|
||||||
1 => {
|
1 => {
|
||||||
// basic: open file and watch for fs changes
|
// 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();
|
let temp_file = temp_file_edit.path();
|
||||||
eprintln!("Using temporary file at {temp_file:?}. Save the file to update the output here.");
|
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(_) = std::fs::write(&temp_file, []) {
|
||||||
if let Ok(mut watcher) = {
|
if let Ok(mut watcher) = {
|
||||||
let temp_file = temp_file.to_path_buf();
|
let temp_file = temp_file.to_path_buf();
|
||||||
// the file watcher
|
// the file watcher
|
||||||
notify::recommended_watcher(move |event: Result<notify::Event, notify::Error>| {
|
notify::recommended_watcher(
|
||||||
if let Ok(event) = event {
|
move |event: Result<notify::Event, notify::Error>| {
|
||||||
match &event.kind {
|
if let Ok(event) = event {
|
||||||
notify::EventKind::Modify(notify::event::ModifyKind::Data(_)) => {
|
match &event.kind {
|
||||||
println!();
|
notify::EventKind::Modify(
|
||||||
if let Ok(file_contents) = fs::read_to_string(&temp_file) {
|
notify::event::ModifyKind::Data(_),
|
||||||
let mut file = parse::file::File::new(file_contents, temp_file.clone());
|
) => {
|
||||||
match parse::parse::parse(&mut file) {
|
println!();
|
||||||
Ok(func) => {
|
if let Ok(file_contents) =
|
||||||
println!(" - - - - -");
|
fs::read_to_string(&temp_file)
|
||||||
let output = func.run(vec![]);
|
{
|
||||||
println!(" - - - - -");
|
let mut file = parse::file::File::new(
|
||||||
println!("{}", output);
|
file_contents,
|
||||||
},
|
temp_file.clone(),
|
||||||
Err(e) => println!("{}", e.with_file(&file)),
|
);
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
_ => (),
|
||||||
println!("can't read file at {:?}!", temp_file);
|
|
||||||
std::process::exit(105);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
},
|
||||||
}
|
)
|
||||||
}
|
} {
|
||||||
})} {
|
if let Ok(_) = watcher
|
||||||
if let Ok(_) = watcher.watch(&temp_file, notify::RecursiveMode::NonRecursive) {
|
.watch(&temp_file, notify::RecursiveMode::NonRecursive)
|
||||||
|
{
|
||||||
if interactive_use_new_terminal {
|
if interactive_use_new_terminal {
|
||||||
if let Ok(term) = std::env::var("TERM") {
|
if let Ok(term) = std::env::var("TERM") {
|
||||||
let editor = edit::get_editor().unwrap();
|
let editor = edit::get_editor().unwrap();
|
||||||
@ -111,12 +138,15 @@ fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
edit::edit_file(temp_file_edit.path()).unwrap();
|
edit::edit_file(temp_file_edit.path()).unwrap();
|
||||||
}
|
}
|
||||||
temp_file_edit.close().unwrap();
|
temp_file_edit.close().unwrap();
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
} else {
|
} 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);
|
std::process::exit(104);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -131,12 +161,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
_ => (String::new(), String::new()),
|
_ => (String::new(), String::new()),
|
||||||
};
|
};
|
||||||
parse::file::File::new(
|
parse::file::File::new(contents, path.into())
|
||||||
contents,
|
} else if execute {
|
||||||
path.into()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
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| {
|
||||||
if !s.is_empty() {
|
if !s.is_empty() {
|
||||||
@ -155,12 +181,19 @@ fn main() {
|
|||||||
parse::file::File::new(std::fs::read_to_string(&args[0]).unwrap(), path.into())
|
parse::file::File::new(std::fs::read_to_string(&args[0]).unwrap(), path.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
.unwrap();
|
match parse::parse::parse(&mut file) {
|
||||||
println!(" - - - - -");
|
Ok(script) => {
|
||||||
let start = Instant::now();
|
println!(" - - - - -");
|
||||||
let out = script.run(std::env::args().skip(2).collect());
|
let start = Instant::now();
|
||||||
let elapsed = start.elapsed();
|
let out = script.run(std::env::args().skip(2).collect());
|
||||||
println!(" - - - - -");
|
let elapsed = start.elapsed();
|
||||||
println!("Output ({}s)\n{out}", elapsed.as_secs_f64());
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum ParseErrors {
|
pub enum ParseErrors {
|
||||||
|
StatementCannotStartWith(char),
|
||||||
FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement,
|
FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement,
|
||||||
FoundClosingCurlyBracketInSingleStatementBlockBeforeAnyStatement,
|
FoundClosingCurlyBracketInSingleStatementBlockBeforeAnyStatement,
|
||||||
FoundEofInBlockBeforeStatementOrClosingCurlyBracket,
|
FoundEofInBlockBeforeStatementOrClosingCurlyBracket,
|
||||||
@ -256,6 +257,9 @@ pub enum ParseErrors {
|
|||||||
impl std::fmt::Display for ParseErrors {
|
impl std::fmt::Display for ParseErrors {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Self::StatementCannotStartWith(ch) => {
|
||||||
|
write!(f, "statements cannot start with the {ch} character.",)
|
||||||
|
}
|
||||||
Self::FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement => write!(
|
Self::FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement => write!(
|
||||||
f,
|
f,
|
||||||
"closing round bracket in single-statement block before any statement"
|
"closing round bracket in single-statement block before any statement"
|
||||||
@ -451,18 +455,13 @@ 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),
|
||||||
_ => file.next(),
|
_ => file.next(),
|
||||||
};
|
};
|
||||||
match nchar {
|
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(':') => {
|
Some(':') => {
|
||||||
return Ok(SStatement::new(SStatementEnum::EnumVariant(
|
return Ok(SStatement::new(SStatementEnum::EnumVariant(
|
||||||
start,
|
start,
|
||||||
@ -470,162 +469,215 @@ fn parse_statement_adv(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Some(ch) if ch.is_whitespace() || is_delimeter(ch) => {
|
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();
|
file.skip_whitespaces();
|
||||||
if let Some('=') = file.peek() {
|
if let Some('=') = file.peek() {
|
||||||
continue;
|
file.next();
|
||||||
} else {
|
let err_equals_sign = *file.get_pos();
|
||||||
let start = start.trim();
|
let start = start.trim();
|
||||||
match start {
|
let derefs = start.chars().take_while(|c| *c == '*').count();
|
||||||
"fn" => {
|
break match parse_statement(file) {
|
||||||
file.skip_whitespaces();
|
Ok(v) => v,
|
||||||
let mut fn_name = String::new();
|
Err(mut e) => {
|
||||||
loop {
|
e.context.push((
|
||||||
match file.next() {
|
format!(
|
||||||
Some('(') => break,
|
"statement was supposed to be assigned to variable {start}"
|
||||||
Some(ch) => fn_name.push(ch),
|
),
|
||||||
None => break,
|
Some((err_start_of_statement, Some(err_equals_sign))),
|
||||||
}
|
));
|
||||||
}
|
return Err(e);
|
||||||
let func = parse_function(file, Some(err_start_of_statement))?;
|
|
||||||
break SStatementEnum::FunctionDefinition(
|
|
||||||
Some(fn_name.trim().to_string()),
|
|
||||||
func,
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
"if" => {
|
}
|
||||||
// TODO: Else
|
.output_to(start[derefs..].to_owned(), derefs)
|
||||||
let condition = parse_statement(file)?;
|
.force_output_type(Some(match parsed_type {
|
||||||
let then = parse_statement(file)?;
|
Ok(v) => v,
|
||||||
let mut then_else = None;
|
Err(mut e) => {
|
||||||
file.skip_whitespaces();
|
e.context.push((
|
||||||
let i = file.get_pos().current_char_index;
|
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)))
|
||||||
if file[i..].starts_with("else ") {
|
));
|
||||||
while let Some('e' | 'l' | 's') = file.next() {}
|
return Err(e);
|
||||||
then_else = Some(parse_statement(file)?);
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// nevermind, not var type = statement
|
||||||
|
file.set_pos(file_pos_before_pot_type);
|
||||||
|
// parse normal statement
|
||||||
|
let start = start.trim();
|
||||||
|
match start {
|
||||||
|
"fn" => {
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let mut fn_name = String::new();
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
Some('(') => break,
|
||||||
|
Some(ch) => fn_name.push(ch),
|
||||||
|
None => break,
|
||||||
}
|
}
|
||||||
break SStatementEnum::If(condition, then, then_else).into();
|
|
||||||
}
|
}
|
||||||
"for" => {
|
let func = parse_function(file, Some(err_start_of_statement))?;
|
||||||
break SStatementEnum::For(
|
break SStatementEnum::FunctionDefinition(
|
||||||
{
|
Some(fn_name.trim().to_string()),
|
||||||
file.skip_whitespaces();
|
func,
|
||||||
let mut buf = String::new();
|
)
|
||||||
loop {
|
.into();
|
||||||
if let Some(ch) = file.next() {
|
}
|
||||||
if ch.is_whitespace() {
|
"if" => {
|
||||||
break;
|
// TODO: Else
|
||||||
}
|
let condition = parse_statement(file)?;
|
||||||
buf.push(ch);
|
let then = parse_statement(file)?;
|
||||||
} else {
|
let mut then_else = None;
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let i = file.get_pos().current_char_index;
|
||||||
|
if file[i..].starts_with("else ") {
|
||||||
|
while let Some('e' | 'l' | 's') = file.next() {}
|
||||||
|
then_else = Some(parse_statement(file)?);
|
||||||
|
}
|
||||||
|
break SStatementEnum::If(condition, then, then_else).into();
|
||||||
|
}
|
||||||
|
"for" => {
|
||||||
|
break SStatementEnum::For(
|
||||||
|
{
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let mut buf = String::new();
|
||||||
|
loop {
|
||||||
|
if let Some(ch) = file.next() {
|
||||||
|
if ch.is_whitespace() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
buf.push(ch);
|
||||||
buf
|
|
||||||
},
|
|
||||||
parse_statement(file)?,
|
|
||||||
parse_statement(file)?,
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
"while" => {
|
|
||||||
break SStatementEnum::While(parse_statement(file)?).into();
|
|
||||||
}
|
|
||||||
"switch" | "switch!" => {
|
|
||||||
let force = start.ends_with("!");
|
|
||||||
let mut switch_on_what = String::new();
|
|
||||||
loop {
|
|
||||||
match file.next() {
|
|
||||||
None => break,
|
|
||||||
Some(ch) if ch.is_whitespace() => break,
|
|
||||||
Some(ch) => switch_on_what.push(ch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.skip_whitespaces();
|
|
||||||
if let Some('{') = file.next() {
|
|
||||||
} else {
|
|
||||||
eprintln!("switch statements should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
|
||||||
}
|
|
||||||
let mut cases = vec![];
|
|
||||||
loop {
|
|
||||||
file.skip_whitespaces();
|
|
||||||
if let Some('}') = file.peek() {
|
|
||||||
file.next();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cases.push((parse_type(file)?, parse_statement(file)?));
|
|
||||||
}
|
|
||||||
break SStatementEnum::Switch(switch_on_what, cases, force).into();
|
|
||||||
}
|
|
||||||
"match" => {
|
|
||||||
let mut match_what = String::new();
|
|
||||||
loop {
|
|
||||||
match file.next() {
|
|
||||||
None => break,
|
|
||||||
Some(ch) if ch.is_whitespace() => break,
|
|
||||||
Some(ch) => match_what.push(ch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.skip_whitespaces();
|
|
||||||
if let Some('{') = file.next() {
|
|
||||||
} else {
|
|
||||||
eprintln!("match statements should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
|
||||||
}
|
|
||||||
let mut cases = vec![];
|
|
||||||
loop {
|
|
||||||
file.skip_whitespaces();
|
|
||||||
if let Some('}') = file.peek() {
|
|
||||||
file.next();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cases.push((parse_statement(file)?, parse_statement(file)?));
|
|
||||||
}
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// int, float, var
|
|
||||||
break {
|
|
||||||
if let Ok(v) = start.parse() {
|
|
||||||
if let Some('.') = nchar {
|
|
||||||
let pos = *file.get_pos();
|
|
||||||
file.next();
|
|
||||||
let mut pot_float = String::new();
|
|
||||||
for ch in &mut *file {
|
|
||||||
if ch.is_whitespace() || is_delimeter(ch) {
|
|
||||||
file.set_pos(*file.get_ppos());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pot_float.push(ch);
|
|
||||||
}
|
|
||||||
if let Ok(v) = format!("{start}.{pot_float}").parse() {
|
|
||||||
SStatementEnum::Value(VDataEnum::Float(v).to())
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
file.set_pos(pos);
|
|
||||||
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
},
|
||||||
|
parse_statement(file)?,
|
||||||
|
parse_statement(file)?,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
"while" => {
|
||||||
|
break SStatementEnum::While(parse_statement(file)?).into();
|
||||||
|
}
|
||||||
|
"switch" | "switch!" => {
|
||||||
|
let force = start.ends_with("!");
|
||||||
|
let mut switch_on_what = String::new();
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
None => break,
|
||||||
|
Some(ch) if ch.is_whitespace() => break,
|
||||||
|
Some(ch) => switch_on_what.push(ch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('{') = file.next() {
|
||||||
|
} else {
|
||||||
|
eprintln!("switch statements should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
||||||
|
}
|
||||||
|
let mut cases = vec![];
|
||||||
|
loop {
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('}') = file.peek() {
|
||||||
|
file.next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cases.push((parse_type(file)?, parse_statement(file)?));
|
||||||
|
}
|
||||||
|
break SStatementEnum::Switch(switch_on_what, cases, force).into();
|
||||||
|
}
|
||||||
|
"match" => {
|
||||||
|
let mut match_what = String::new();
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
None => break,
|
||||||
|
Some(ch) if ch.is_whitespace() => break,
|
||||||
|
Some(ch) => match_what.push(ch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('{') = file.next() {
|
||||||
|
} else {
|
||||||
|
eprintln!("match statements should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
||||||
|
}
|
||||||
|
let mut cases = vec![];
|
||||||
|
loop {
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('}') = file.peek() {
|
||||||
|
file.next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cases.push((parse_statement(file)?, parse_statement(file)?));
|
||||||
|
}
|
||||||
|
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(),
|
||||||
|
_ => {
|
||||||
|
// int, float, var
|
||||||
|
break {
|
||||||
|
if let Ok(v) = start.parse() {
|
||||||
|
if let Some('.') = nchar {
|
||||||
|
let pos = *file.get_pos();
|
||||||
|
file.next();
|
||||||
|
let mut pot_float = String::new();
|
||||||
|
for ch in &mut *file {
|
||||||
|
if ch.is_whitespace() || is_delimeter(ch) {
|
||||||
|
file.set_pos(*file.get_ppos());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pot_float.push(ch);
|
||||||
|
}
|
||||||
|
if let Ok(v) = format!("{start}.{pot_float}").parse() {
|
||||||
|
SStatementEnum::Value(VDataEnum::Float(v).to()).into()
|
||||||
|
} else {
|
||||||
|
file.set_pos(pos);
|
||||||
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
|
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
|
||||||
}
|
}
|
||||||
// } else if let Ok(v) = start.parse() {
|
|
||||||
// SStatementEnum::Value(VDataEnum::Float(v).to()).into()
|
|
||||||
} else {
|
} else {
|
||||||
if start.starts_with('&') {
|
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
|
||||||
SStatementEnum::Variable(start[1..].to_string(), true)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
SStatementEnum::Variable(start.to_string(), false)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
// } else if let Ok(v) = start.parse() {
|
||||||
}
|
// SStatementEnum::Value(VDataEnum::Float(v).to()).into()
|
||||||
|
} else {
|
||||||
|
if start.starts_with('&') {
|
||||||
|
SStatementEnum::Variable(start[1..].to_string(), true)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
SStatementEnum::Variable(start.to_string(), false).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,18 +43,25 @@ impl SFunction {
|
|||||||
pub struct SStatement {
|
pub struct SStatement {
|
||||||
pub output_to: Option<(String, usize)>,
|
pub output_to: Option<(String, usize)>,
|
||||||
pub statement: Box<SStatementEnum>,
|
pub statement: Box<SStatementEnum>,
|
||||||
|
pub force_output_type: Option<VType>,
|
||||||
}
|
}
|
||||||
impl SStatement {
|
impl SStatement {
|
||||||
pub fn new(statement: SStatementEnum) -> Self {
|
pub fn new(statement: SStatementEnum) -> Self {
|
||||||
Self {
|
Self {
|
||||||
output_to: None,
|
output_to: None,
|
||||||
statement: Box::new(statement),
|
statement: Box::new(statement),
|
||||||
|
force_output_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn output_to(mut self, var: String, derefs: usize) -> Self {
|
pub fn output_to(mut self, var: String, derefs: usize) -> Self {
|
||||||
self.output_to = Some((var, derefs));
|
self.output_to = Some((var, derefs));
|
||||||
self
|
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)]
|
#[derive(Debug)]
|
||||||
@ -126,6 +133,7 @@ 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),
|
||||||
}
|
}
|
||||||
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 {
|
||||||
@ -173,6 +181,9 @@ pub mod to_runnable {
|
|||||||
Self::ForLoopContainerHasNoInnerTypes => {
|
Self::ForLoopContainerHasNoInnerTypes => {
|
||||||
write!(f, "For loop: container had no inner types, cannot iterate.")
|
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)?),
|
}, statement(s, ginfo, linfo)?),
|
||||||
}
|
}
|
||||||
.to();
|
.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((opt, derefs)) = &s.output_to {
|
||||||
if let Some((var_id, var_out)) = linfo.vars.get(opt) {
|
if let Some((var_id, var_out)) = linfo.vars.get(opt) {
|
||||||
let out = statement.out();
|
let out = statement.out();
|
||||||
@ -681,8 +704,7 @@ pub mod to_runnable {
|
|||||||
statement.output_to = Some((ginfo.vars, *derefs));
|
statement.output_to = Some((ginfo.vars, *derefs));
|
||||||
ginfo.vars += 1;
|
ginfo.vars += 1;
|
||||||
}
|
}
|
||||||
}
|
} Ok(statement)
|
||||||
Ok(statement)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,6 +785,7 @@ impl RFunction {
|
|||||||
pub struct RStatement {
|
pub struct RStatement {
|
||||||
output_to: Option<(usize, usize)>,
|
output_to: Option<(usize, usize)>,
|
||||||
statement: Box<RStatementEnum>,
|
statement: Box<RStatementEnum>,
|
||||||
|
force_output_type: Option<VType>,
|
||||||
}
|
}
|
||||||
impl RStatement {
|
impl RStatement {
|
||||||
pub fn run(&self, vars: &Vec<Am<VData>>, libs: &Arc<Vec<libs::Lib>>) -> VData {
|
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 {
|
pub fn out(&self) -> VType {
|
||||||
|
// `a = b` evaluates to []
|
||||||
if self.output_to.is_some() {
|
if self.output_to.is_some() {
|
||||||
return VType {
|
return VType {
|
||||||
types: vec![VSingleType::Tuple(vec![])],
|
types: vec![VSingleType::Tuple(vec![])],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if let Some(t) = &self.force_output_type {
|
||||||
|
return t.clone();
|
||||||
|
}
|
||||||
self.statement.out()
|
self.statement.out()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1028,6 +1055,7 @@ impl RStatementEnum {
|
|||||||
RStatement {
|
RStatement {
|
||||||
output_to: None,
|
output_to: None,
|
||||||
statement: Box::new(self),
|
statement: Box::new(self),
|
||||||
|
force_output_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1245,7 +1273,7 @@ impl Display for VDataEnum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
Self::List(..) => write!(f, "...")?,
|
Self::List(..) => write!(f, " ...")?,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
write!(f, "]")?;
|
write!(f, "]")?;
|
||||||
|
Loading…
Reference in New Issue
Block a user