mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
initial code commit, implemented switch statements and if-else syntax.
This commit is contained in:
parent
bb2f4b541c
commit
63721b9e26
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "mers"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
@ -29,7 +29,7 @@ In mers, each variable has a type. However, this type is actually a list of all
|
|||||||
|
|
||||||
To ensure that variables with more than one possible type won't cause issues, **every possible type has to be handeled**. This can, for example, be achieved using to_string(), which accepts arguments of types String, Int, Float, Tuple, or List.
|
To ensure that variables with more than one possible type won't cause issues, **every possible type has to be handeled**. This can, for example, be achieved using to_string(), which accepts arguments of types String, Int, Float, Tuple, or List.
|
||||||
If a variable could be a String, Int or Float and should be multiplied with another Float, the type-checker will complain that the String case isn't handeled because the mul() function doesn't accept String arguments.
|
If a variable could be a String, Int or Float and should be multiplied with another Float, the type-checker will complain that the String case isn't handeled because the mul() function doesn't accept String arguments.
|
||||||
To distinguish between different types, a *switch* statement has to be used. To pattern-match on known types, *match* statements can be used. (TODO! Both of these aren't implemented yet. There is currently no way to handle different types.)
|
To distinguish between different types, a *switch* statement has to be used. To pattern-match on known types, *match* statements can be used. (TODO! match isn't implemented yet.)
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
|
21
src/main.rs
Normal file
21
src/main.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crate::script::value::{VSingleType, VType};
|
||||||
|
|
||||||
|
pub(crate) mod parse;
|
||||||
|
pub(crate) mod script;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let str1: VType = VSingleType::String.into();
|
||||||
|
assert!(str1.fits_in(&VSingleType::String.into()).is_empty());
|
||||||
|
let script = parse::parse::parse(&mut parse::file::File::new(
|
||||||
|
std::fs::read_to_string("/tmp/script.txt").unwrap(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
println!(" - - - - -");
|
||||||
|
let start = Instant::now();
|
||||||
|
let out = script.run(std::env::args().collect());
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
println!(" - - - - -");
|
||||||
|
println!("Output ({}s)\n{out:?}", elapsed.as_secs_f64());
|
||||||
|
}
|
126
src/parse/file.rs
Normal file
126
src/parse/file.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
ops::{Index, Range, RangeFrom, RangeTo},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
data: String,
|
||||||
|
chars: Vec<(usize, char)>,
|
||||||
|
pos: FilePosition,
|
||||||
|
}
|
||||||
|
pub struct FilePosition {
|
||||||
|
current_char_index: usize,
|
||||||
|
current_line: usize,
|
||||||
|
current_column: usize,
|
||||||
|
}
|
||||||
|
impl Display for FilePosition {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"line {}, col. {}",
|
||||||
|
self.current_line, self.current_column
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn new(data: String) -> Self {
|
||||||
|
let chars = data.char_indices().collect();
|
||||||
|
Self {
|
||||||
|
data,
|
||||||
|
chars,
|
||||||
|
pos: FilePosition {
|
||||||
|
current_char_index: 0,
|
||||||
|
current_line: 0,
|
||||||
|
current_column: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn skip_whitespaces(&mut self) {
|
||||||
|
loop {
|
||||||
|
match self.chars.get(self.pos.current_char_index) {
|
||||||
|
Some(ch) if ch.1.is_whitespace() => _ = self.next(),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_pos(&self) -> &FilePosition {
|
||||||
|
&self.pos
|
||||||
|
}
|
||||||
|
pub fn set_pos(&mut self, pos: FilePosition) {
|
||||||
|
self.pos = pos;
|
||||||
|
}
|
||||||
|
pub fn get_line(&self) -> usize {
|
||||||
|
self.pos.current_line
|
||||||
|
}
|
||||||
|
pub fn get_column(&self) -> usize {
|
||||||
|
self.pos.current_column
|
||||||
|
}
|
||||||
|
pub fn get_char_index(&self) -> usize {
|
||||||
|
self.pos.current_char_index
|
||||||
|
}
|
||||||
|
pub fn get_char(&self, index: usize) -> Option<char> {
|
||||||
|
match self.chars.get(index) {
|
||||||
|
Some(v) => Some(v.1),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for File {
|
||||||
|
type Item = char;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let o = self.chars.get(self.pos.current_char_index);
|
||||||
|
self.pos.current_char_index += 1;
|
||||||
|
match o {
|
||||||
|
Some((_, ch)) => {
|
||||||
|
match *ch {
|
||||||
|
'\n' => {
|
||||||
|
self.pos.current_line += 1;
|
||||||
|
self.pos.current_column = 0;
|
||||||
|
}
|
||||||
|
_ => self.pos.current_column += 1,
|
||||||
|
}
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
eprint!("{ch}");
|
||||||
|
Some(*ch)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Range<usize>> for File {
|
||||||
|
type Output = str;
|
||||||
|
fn index(&self, index: Range<usize>) -> &Self::Output {
|
||||||
|
if let Some((start, _)) = self.chars.get(index.start) {
|
||||||
|
if let Some((end, _)) = self.chars.get(index.end) {
|
||||||
|
&self.data[*start..*end]
|
||||||
|
} else {
|
||||||
|
&self.data[*start..]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Index<RangeFrom<usize>> for File {
|
||||||
|
type Output = str;
|
||||||
|
fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
|
||||||
|
if let Some((start, _)) = self.chars.get(index.start) {
|
||||||
|
&self.data[*start..]
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Index<RangeTo<usize>> for File {
|
||||||
|
type Output = str;
|
||||||
|
fn index(&self, index: RangeTo<usize>) -> &Self::Output {
|
||||||
|
if let Some((end, _)) = self.chars.get(index.end) {
|
||||||
|
&self.data[..*end]
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
src/parse/mod.rs
Normal file
2
src/parse/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod file;
|
||||||
|
pub mod parse;
|
388
src/parse/parse.rs
Normal file
388
src/parse/parse.rs
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
use crate::script::{
|
||||||
|
block::{
|
||||||
|
to_runnable, to_runnable::ToRunnableError, RFunction, RScript, SBlock, SFunction,
|
||||||
|
SStatement, SStatementEnum,
|
||||||
|
},
|
||||||
|
value::{VData, VDataEnum, VSingleType, VType},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::file::File;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ScriptError {
|
||||||
|
ParseError(ParseError),
|
||||||
|
ToRunnableError(ToRunnableError),
|
||||||
|
}
|
||||||
|
impl From<ParseError> for ScriptError {
|
||||||
|
fn from(value: ParseError) -> Self {
|
||||||
|
Self::ParseError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<ToRunnableError> for ScriptError {
|
||||||
|
fn from(value: ToRunnableError) -> Self {
|
||||||
|
Self::ToRunnableError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ParseError {}
|
||||||
|
|
||||||
|
pub fn parse(file: &mut File) -> Result<RScript, ScriptError> {
|
||||||
|
let func = SFunction::new(
|
||||||
|
vec![(
|
||||||
|
"args".to_string(),
|
||||||
|
VSingleType::List(VSingleType::String.into()).into(),
|
||||||
|
)],
|
||||||
|
parse_block_advanced(file, Some(false), true, true, false)?,
|
||||||
|
);
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("Parsed: {func}");
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
eprintln!("Parsed: {func:#?}");
|
||||||
|
let run = to_runnable(func)?;
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
eprintln!("Runnable: {run:#?}");
|
||||||
|
Ok(run)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_block(file: &mut File) -> Result<SBlock, ParseError> {
|
||||||
|
parse_block_advanced(file, None, true, false, false)
|
||||||
|
}
|
||||||
|
fn parse_block_advanced(
|
||||||
|
file: &mut File,
|
||||||
|
assume_single_statement: Option<bool>,
|
||||||
|
treat_closed_block_bracket_as_closing_delimeter: bool,
|
||||||
|
treat_eof_as_closing_delimeter: bool,
|
||||||
|
treat_closed_normal_bracket_as_closing_delimeter: bool,
|
||||||
|
) -> Result<SBlock, ParseError> {
|
||||||
|
let mut statements = vec![];
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let single_statement = if let Some(v) = assume_single_statement {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
if let Some('{') = file.get_char(file.get_char_index()) {
|
||||||
|
file.next();
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
file.skip_whitespaces();
|
||||||
|
match file.get_char(file.get_char_index()) {
|
||||||
|
Some(')') if treat_closed_normal_bracket_as_closing_delimeter => {
|
||||||
|
if single_statement {
|
||||||
|
todo!("Err: closing function-arguments-delimeter in single-statement block before any statement (???fn with single-statement???)")
|
||||||
|
} else {
|
||||||
|
file.next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some('}') if treat_closed_block_bracket_as_closing_delimeter => {
|
||||||
|
if single_statement {
|
||||||
|
todo!("Err: closing block-delimeter in single-statement block before any statement")
|
||||||
|
} else {
|
||||||
|
file.next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None if treat_eof_as_closing_delimeter => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
None => todo!("eof in block before statement"),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
statements.push(parse_statement(file)?);
|
||||||
|
match file.get_char(file.get_char_index().saturating_sub(1)) {
|
||||||
|
// Some('}') if treat_closed_block_bracket_as_closing_delimeter => break,
|
||||||
|
Some(')') if treat_closed_normal_bracket_as_closing_delimeter => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
if single_statement && !statements.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(SBlock::new(statements))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_statement(file: &mut File) -> Result<SStatement, ParseError> {
|
||||||
|
parse_statement_adv(file, false)
|
||||||
|
}
|
||||||
|
fn parse_statement_adv(
|
||||||
|
file: &mut File,
|
||||||
|
is_part_of_chain_already: bool,
|
||||||
|
) -> Result<SStatement, ParseError> {
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let mut start = String::new();
|
||||||
|
let out = match file.get_char(file.get_char_index()) {
|
||||||
|
Some('{') => Some(SStatementEnum::Block(parse_block(file)?).into()),
|
||||||
|
Some('$') => {
|
||||||
|
file.next();
|
||||||
|
file.skip_whitespaces();
|
||||||
|
Some(SStatementEnum::Variable(file.take_while(|v| !v.is_whitespace()).collect()).into())
|
||||||
|
}
|
||||||
|
Some('"') => {
|
||||||
|
file.next();
|
||||||
|
let mut buf = String::new();
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
Some('\\') => {
|
||||||
|
if let Some(ch) = file.next() {
|
||||||
|
buf.push(match ch {
|
||||||
|
'\\' => '\\',
|
||||||
|
'n' => '\n',
|
||||||
|
't' => '\t',
|
||||||
|
'"' => '"',
|
||||||
|
ch => {
|
||||||
|
eprintln!("Warn: Weird char escape \"\\{ch}\", will be replaced with \"{ch}\".");
|
||||||
|
ch
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some('"') => break,
|
||||||
|
Some(ch) => buf.push(ch),
|
||||||
|
None => todo!("Err: EOF in string"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(SStatementEnum::Value(VDataEnum::String(buf).to()).into())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let mut out = if let Some(out) = out {
|
||||||
|
out
|
||||||
|
} else {
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
Some('=') => {
|
||||||
|
break parse_statement(file)?.output_to(start.trim().to_string());
|
||||||
|
}
|
||||||
|
Some(ch) if ch.is_whitespace() || matches!(ch, '}' | ')' | '.') => {
|
||||||
|
if let Some('=') = file.get_char(file.get_char_index()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
let start = start.trim();
|
||||||
|
match start {
|
||||||
|
"if" => {
|
||||||
|
// TODO: Else
|
||||||
|
let condition = parse_statement(file)?;
|
||||||
|
let then = parse_statement(file)?;
|
||||||
|
let mut then_else = None;
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let i = file.get_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;
|
||||||
|
}
|
||||||
|
buf.push(ch);
|
||||||
|
} 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 switch_on_what = parse_statement(file)?;
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('{') = file.next() {
|
||||||
|
} else {
|
||||||
|
eprintln!("switch statement 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.get_char(file.get_char_index()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cases.push((parse_type(file)?, parse_statement(file)?));
|
||||||
|
}
|
||||||
|
break SStatementEnum::Switch(switch_on_what, cases, force).into();
|
||||||
|
}
|
||||||
|
"true" => {
|
||||||
|
break SStatementEnum::Value(VDataEnum::Bool(true).to()).into()
|
||||||
|
}
|
||||||
|
"false" => {
|
||||||
|
break SStatementEnum::Value(VDataEnum::Bool(false).to()).into()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break {
|
||||||
|
if let Ok(v) = start.parse() {
|
||||||
|
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
|
||||||
|
} else if let Ok(v) = start.replace(",", ".").parse() {
|
||||||
|
SStatementEnum::Value(VDataEnum::Float(v).to()).into()
|
||||||
|
} else {
|
||||||
|
SStatementEnum::Variable(start.to_string()).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();
|
||||||
|
if name.is_empty() {
|
||||||
|
break SStatementEnum::FunctionDefinition(None, parse_function(file)?)
|
||||||
|
.into();
|
||||||
|
} else {
|
||||||
|
break SStatementEnum::FunctionCall(
|
||||||
|
name.to_string(),
|
||||||
|
parse_block_advanced(file, Some(false), false, false, true)?.statements,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ch) => start.push(ch),
|
||||||
|
None => todo!("EOF in statement"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
file.skip_whitespaces();
|
||||||
|
if let Some('.') = file.get_char(file.get_char_index()) {
|
||||||
|
// consume the dot (otherwise, a.b.c syntax will break in certain cases)
|
||||||
|
file.next();
|
||||||
|
}
|
||||||
|
if !is_part_of_chain_already {
|
||||||
|
while let Some('.') = file.get_char(file.get_char_index().saturating_sub(1)) {
|
||||||
|
let wrapper = parse_statement_adv(file, true)?;
|
||||||
|
out = match *wrapper.statement {
|
||||||
|
SStatementEnum::FunctionCall(func, args) => {
|
||||||
|
let args = [out].into_iter().chain(args.into_iter()).collect();
|
||||||
|
SStatementEnum::FunctionCall(func, args).into()
|
||||||
|
}
|
||||||
|
other => todo!("Wrapping in this type isn't implemented (yet?). Type: {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assumes the function name and opening bracket have already been parsed. File should continue like "name type name type ...) <statement>"
|
||||||
|
fn parse_function(file: &mut File) -> Result<SFunction, ParseError> {
|
||||||
|
// find the arguments to the function
|
||||||
|
let mut args = Vec::new();
|
||||||
|
file.skip_whitespaces();
|
||||||
|
loop {
|
||||||
|
let mut arg_name = String::new();
|
||||||
|
match file.next() {
|
||||||
|
Some(')') => break,
|
||||||
|
Some(ch) => arg_name.push(ch),
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
Some(ch) if ch.is_whitespace() => break,
|
||||||
|
Some(ch) => arg_name.push(ch),
|
||||||
|
None => todo!("Err: EOF in function"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (t, brk) = parse_type_adv(file, true)?;
|
||||||
|
args.push((arg_name, t));
|
||||||
|
if brk {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(SFunction::new(args, parse_block(file)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_type(file: &mut File) -> Result<VType, ParseError> {
|
||||||
|
match parse_type_adv(file, false) {
|
||||||
|
Ok((v, _)) => Ok(v),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn parse_type_adv(file: &mut File, in_fn_args: bool) -> Result<(VType, bool), ParseError> {
|
||||||
|
let mut types = vec![];
|
||||||
|
let mut closed_fn_args = false;
|
||||||
|
loop {
|
||||||
|
let (st, closed_bracket) = parse_single_type_adv(file, in_fn_args)?;
|
||||||
|
types.push(st);
|
||||||
|
if closed_bracket {
|
||||||
|
closed_fn_args = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file.skip_whitespaces();
|
||||||
|
match file.get_char(file.get_char_index()) {
|
||||||
|
Some('/') => (),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((VType { types }, closed_fn_args))
|
||||||
|
}
|
||||||
|
fn parse_single_type(file: &mut File) -> Result<VSingleType, ParseError> {
|
||||||
|
match parse_single_type_adv(file, false) {
|
||||||
|
Ok((v, _)) => Ok(v),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn parse_single_type_adv(
|
||||||
|
file: &mut File,
|
||||||
|
in_fn_args: bool,
|
||||||
|
) -> Result<(VSingleType, bool), ParseError> {
|
||||||
|
file.skip_whitespaces();
|
||||||
|
let mut closed_bracket_in_fn_args = false;
|
||||||
|
Ok((
|
||||||
|
match file.next() {
|
||||||
|
// Tuple or Array
|
||||||
|
Some('[') => {
|
||||||
|
let mut types = vec![];
|
||||||
|
loop {
|
||||||
|
types.push(parse_single_type(file)?.into());
|
||||||
|
file.skip_whitespaces();
|
||||||
|
match file.get_char(file.get_char_index()) {
|
||||||
|
Some(']') => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if types.len() == 1 {
|
||||||
|
VSingleType::List(types.pop().unwrap())
|
||||||
|
} else {
|
||||||
|
VSingleType::Tuple(types)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ch) => {
|
||||||
|
let mut name = ch.to_string();
|
||||||
|
loop {
|
||||||
|
match file.next() {
|
||||||
|
Some(ch) if ch.is_whitespace() => break,
|
||||||
|
Some(')') if in_fn_args => {
|
||||||
|
closed_bracket_in_fn_args = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(ch) => name.push(ch),
|
||||||
|
None => todo!("Err: EOF in type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match name.trim().to_lowercase().as_str() {
|
||||||
|
"bool" => VSingleType::Bool,
|
||||||
|
"int" => VSingleType::Int,
|
||||||
|
"float" => VSingleType::Float,
|
||||||
|
"string" => VSingleType::String,
|
||||||
|
_ => todo!("Err: Invalid type: \"{}\"", name.trim()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => todo!("Err: EOF in type (1)"),
|
||||||
|
},
|
||||||
|
closed_bracket_in_fn_args,
|
||||||
|
))
|
||||||
|
}
|
924
src/script/block.rs
Normal file
924
src/script/block.rs
Normal file
@ -0,0 +1,924 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Display,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use self::to_runnable::ToRunnableError;
|
||||||
|
|
||||||
|
use super::value::{VData, VDataEnum, VDataThreadEnum, VSingleType, VType};
|
||||||
|
|
||||||
|
// Represents a block of code
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SBlock {
|
||||||
|
pub statements: Vec<SStatement>,
|
||||||
|
}
|
||||||
|
impl SBlock {
|
||||||
|
pub fn new(statements: Vec<SStatement>) -> Self {
|
||||||
|
Self { statements }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A function is a block of code that starts with some local variables as inputs and returns some value as its output. The last statement in the block will be the output.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SFunction {
|
||||||
|
inputs: Vec<(String, VType)>,
|
||||||
|
block: SBlock,
|
||||||
|
}
|
||||||
|
impl SFunction {
|
||||||
|
pub fn new(inputs: Vec<(String, VType)>, block: SBlock) -> Self {
|
||||||
|
Self { inputs, block }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SStatement {
|
||||||
|
pub output_to: Option<String>,
|
||||||
|
pub statement: Box<SStatementEnum>,
|
||||||
|
}
|
||||||
|
impl SStatement {
|
||||||
|
pub fn new(statement: SStatementEnum) -> Self {
|
||||||
|
Self {
|
||||||
|
output_to: None,
|
||||||
|
statement: Box::new(statement),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn output_to(mut self, var: String) -> Self {
|
||||||
|
self.output_to = Some(var);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SStatementEnum {
|
||||||
|
Value(VData),
|
||||||
|
Variable(String),
|
||||||
|
FunctionCall(String, Vec<SStatement>),
|
||||||
|
FunctionDefinition(Option<String>, SFunction),
|
||||||
|
Block(SBlock),
|
||||||
|
If(SStatement, SStatement, Option<SStatement>),
|
||||||
|
While(SStatement),
|
||||||
|
For(String, SStatement, SStatement),
|
||||||
|
Switch(SStatement, Vec<(VType, SStatement)>, bool),
|
||||||
|
// Match(???),
|
||||||
|
}
|
||||||
|
impl Into<SStatement> for SStatementEnum {
|
||||||
|
fn into(self) -> SStatement {
|
||||||
|
SStatement::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversion
|
||||||
|
|
||||||
|
type Am<T> = Arc<Mutex<T>>;
|
||||||
|
fn am<T>(i: T) -> Am<T> {
|
||||||
|
Arc::new(Mutex::new(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_runnable(f: SFunction) -> Result<RScript, ToRunnableError> {
|
||||||
|
to_runnable::to_runnable(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod to_runnable {
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::script::value::{VData, VDataEnum, VSingleType, VType};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Am, BuiltinFunction, RBlock, RFunction, RScript, RStatement, RStatementEnum, SBlock,
|
||||||
|
SFunction, SStatement, SStatementEnum,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum ToRunnableError {
|
||||||
|
MainWrongInput,
|
||||||
|
UseOfUndefinedVariable(String),
|
||||||
|
UseOfUndefinedFunction(String),
|
||||||
|
FunctionWrongArgCount(String, usize, usize),
|
||||||
|
InvalidType {
|
||||||
|
expected: VType,
|
||||||
|
found: VType,
|
||||||
|
problematic: Vec<VSingleType>,
|
||||||
|
},
|
||||||
|
InvalidTypeForWhileLoop(VType),
|
||||||
|
CaseForceButTypeNotCovered(VType),
|
||||||
|
}
|
||||||
|
impl Debug for ToRunnableError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for ToRunnableError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::MainWrongInput => write!(
|
||||||
|
f,
|
||||||
|
"Main function had the wrong input. This is a bug and should never happen."
|
||||||
|
),
|
||||||
|
Self::UseOfUndefinedVariable(v) => write!(f, "Cannot use variable \"{v}\" as it isn't defined (yet?)."),
|
||||||
|
Self::UseOfUndefinedFunction(v) => write!(f, "Cannot use function \"{v}\" as it isn't defined (yet?)."),
|
||||||
|
Self::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."),
|
||||||
|
Self::InvalidType {
|
||||||
|
expected,
|
||||||
|
found,
|
||||||
|
problematic,
|
||||||
|
} => {
|
||||||
|
write!(f, "Invalid type: Expected {expected:?} but found {found:?}, which includes {problematic:?}, which is not covered.")
|
||||||
|
}
|
||||||
|
Self::InvalidTypeForWhileLoop(v) => write!(f, "Invalid type: Expected bool or Tuples of length 0 or 1 as return types for the while loop, but found {v:?} instead."),
|
||||||
|
Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global, shared between all
|
||||||
|
struct GInfo {
|
||||||
|
vars: usize,
|
||||||
|
}
|
||||||
|
// Local, used to keep local variables separated
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LInfo {
|
||||||
|
vars: HashMap<String, (usize, VType)>,
|
||||||
|
fns: HashMap<String, Arc<RFunction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_runnable(s: SFunction) -> Result<RScript, ToRunnableError> {
|
||||||
|
if s.inputs.len() != 1 || s.inputs[0].0 != "args" {
|
||||||
|
return Err(ToRunnableError::MainWrongInput);
|
||||||
|
}
|
||||||
|
if s.inputs[0].1
|
||||||
|
!= (VType {
|
||||||
|
types: vec![VSingleType::List(VType {
|
||||||
|
types: vec![VSingleType::String],
|
||||||
|
})],
|
||||||
|
})
|
||||||
|
{}
|
||||||
|
let mut ginfo = GInfo { vars: 0 };
|
||||||
|
let func = function(
|
||||||
|
&s,
|
||||||
|
&mut ginfo,
|
||||||
|
LInfo {
|
||||||
|
vars: HashMap::new(),
|
||||||
|
fns: HashMap::new(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(RScript::new(func, ginfo.vars)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// go over every possible known-type input for the given function, returning all possible RFunctions.
|
||||||
|
fn get_all_functions(
|
||||||
|
s: &SFunction,
|
||||||
|
ginfo: &mut GInfo,
|
||||||
|
linfo: &mut LInfo,
|
||||||
|
input_vars: &Vec<usize>,
|
||||||
|
inputs: &mut Vec<VSingleType>,
|
||||||
|
out: &mut Vec<(Vec<VSingleType>, VType)>,
|
||||||
|
) -> Result<(), ToRunnableError> {
|
||||||
|
if s.inputs.len() > inputs.len() {
|
||||||
|
let input_here = &s.inputs[inputs.len()].1;
|
||||||
|
for t in &input_here.types {
|
||||||
|
inputs.push(t.clone().into());
|
||||||
|
get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?;
|
||||||
|
inputs.pop();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// set the types
|
||||||
|
for (varid, vartype) in s.inputs.iter().zip(inputs.iter()) {
|
||||||
|
linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone().into();
|
||||||
|
}
|
||||||
|
out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn function(
|
||||||
|
s: &SFunction,
|
||||||
|
ginfo: &mut GInfo,
|
||||||
|
mut linfo: LInfo,
|
||||||
|
) -> Result<RFunction, ToRunnableError> {
|
||||||
|
let mut input_vars = vec![];
|
||||||
|
let mut input_types = vec![];
|
||||||
|
for (iname, itype) in &s.inputs {
|
||||||
|
linfo
|
||||||
|
.vars
|
||||||
|
.insert(iname.clone(), (ginfo.vars, itype.clone()));
|
||||||
|
input_vars.push(ginfo.vars);
|
||||||
|
input_types.push(itype.clone());
|
||||||
|
ginfo.vars += 1;
|
||||||
|
}
|
||||||
|
let mut all_outs = vec![];
|
||||||
|
get_all_functions(
|
||||||
|
s,
|
||||||
|
ginfo,
|
||||||
|
&mut linfo,
|
||||||
|
&input_vars,
|
||||||
|
&mut Vec::with_capacity(s.inputs.len()),
|
||||||
|
&mut all_outs,
|
||||||
|
)?;
|
||||||
|
// set the types to all possible types (get_all_functions sets the types to one single type to get the return type of the block for that case)
|
||||||
|
for (varid, vartype) in s.inputs.iter().zip(input_types.iter()) {
|
||||||
|
linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone();
|
||||||
|
}
|
||||||
|
Ok(RFunction {
|
||||||
|
inputs: input_vars,
|
||||||
|
input_types,
|
||||||
|
input_output_map: all_outs,
|
||||||
|
block: block(&s.block, ginfo, linfo)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block(s: &SBlock, ginfo: &mut GInfo, mut linfo: LInfo) -> Result<RBlock, ToRunnableError> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
for st in &s.statements {
|
||||||
|
statements.push(statement(st, ginfo, &mut linfo)?);
|
||||||
|
}
|
||||||
|
Ok(RBlock { statements })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(
|
||||||
|
s: &SStatement,
|
||||||
|
ginfo: &mut GInfo,
|
||||||
|
linfo: &mut LInfo,
|
||||||
|
) -> Result<RStatement, ToRunnableError> {
|
||||||
|
let mut statement = match &*s.statement {
|
||||||
|
SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()),
|
||||||
|
SStatementEnum::Variable(v) => {
|
||||||
|
if let Some(var) = linfo.vars.get(v) {
|
||||||
|
RStatementEnum::Variable(var.0, var.1.clone())
|
||||||
|
} else {
|
||||||
|
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SStatementEnum::FunctionCall(v, args) => {
|
||||||
|
let mut rargs = Vec::with_capacity(args.len());
|
||||||
|
for arg in args.iter() {
|
||||||
|
rargs.push(statement(arg, ginfo, linfo)?);
|
||||||
|
}
|
||||||
|
if let Some(func) = linfo.fns.get(v) {
|
||||||
|
if rargs.len() != func.inputs.len() {
|
||||||
|
return Err(ToRunnableError::FunctionWrongArgCount(
|
||||||
|
v.clone(),
|
||||||
|
func.inputs.len(),
|
||||||
|
rargs.len(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for (i, rarg) in rargs.iter().enumerate() {
|
||||||
|
let rarg = rarg.out();
|
||||||
|
let out = rarg.fits_in(&func.input_types[i]);
|
||||||
|
if !out.is_empty() {
|
||||||
|
return Err(ToRunnableError::InvalidType {
|
||||||
|
expected: func.input_types[i].clone(),
|
||||||
|
found: rarg,
|
||||||
|
problematic: out,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RStatementEnum::FunctionCall(func.clone(), rargs)
|
||||||
|
} else {
|
||||||
|
RStatementEnum::BuiltinFunction(
|
||||||
|
// TODO: type-checking for builtins
|
||||||
|
match v.as_str() {
|
||||||
|
"print" => BuiltinFunction::Print,
|
||||||
|
"debug" => BuiltinFunction::Debug,
|
||||||
|
"to_string" => BuiltinFunction::ToString,
|
||||||
|
"format" => BuiltinFunction::Format,
|
||||||
|
"run" => BuiltinFunction::Run,
|
||||||
|
"thread" => BuiltinFunction::Thread,
|
||||||
|
"await" => BuiltinFunction::Await,
|
||||||
|
"sleep" => BuiltinFunction::Sleep,
|
||||||
|
_ => return Err(ToRunnableError::UseOfUndefinedFunction(v.clone())),
|
||||||
|
},
|
||||||
|
rargs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SStatementEnum::FunctionDefinition(name, f) => {
|
||||||
|
if let Some(name) = name {
|
||||||
|
// named function => add to global functions
|
||||||
|
linfo
|
||||||
|
.fns
|
||||||
|
.insert(name.clone(), Arc::new(function(f, ginfo, linfo.clone())?));
|
||||||
|
RStatementEnum::Value(VDataEnum::Tuple(vec![]).to())
|
||||||
|
} else {
|
||||||
|
// anonymous function => return as value
|
||||||
|
RStatementEnum::Value(
|
||||||
|
VDataEnum::Function(function(f, ginfo, linfo.clone())?).to(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?),
|
||||||
|
SStatementEnum::If(c, t, e) => RStatementEnum::If(
|
||||||
|
{
|
||||||
|
let condition = statement(&c, ginfo, linfo)?;
|
||||||
|
let out = condition.out().fits_in(&VType {
|
||||||
|
types: vec![VSingleType::Bool],
|
||||||
|
});
|
||||||
|
if out.is_empty() {
|
||||||
|
condition
|
||||||
|
} else {
|
||||||
|
return Err(ToRunnableError::InvalidType {
|
||||||
|
expected: VSingleType::Bool.into(),
|
||||||
|
found: condition.out(),
|
||||||
|
problematic: out,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
statement(&t, ginfo, linfo)?,
|
||||||
|
match e {
|
||||||
|
Some(v) => Some(statement(&v, ginfo, linfo)?),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SStatementEnum::While(c) => RStatementEnum::While({
|
||||||
|
let condition = statement(&c, ginfo, linfo)?;
|
||||||
|
let out = condition.out();
|
||||||
|
let out1 = out.fits_in(&VSingleType::Bool.into());
|
||||||
|
if out1.is_empty() {
|
||||||
|
condition
|
||||||
|
} else {
|
||||||
|
if out.types.is_empty() {
|
||||||
|
return Err(ToRunnableError::InvalidTypeForWhileLoop(out));
|
||||||
|
}
|
||||||
|
for t in out.types.iter() {
|
||||||
|
if let VSingleType::Tuple(v) = t {
|
||||||
|
if v.len() > 1 {
|
||||||
|
return Err(ToRunnableError::InvalidTypeForWhileLoop(out));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(ToRunnableError::InvalidTypeForWhileLoop(out));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
condition
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
SStatementEnum::For(v, c, b) => {
|
||||||
|
let mut linfo = linfo.clone();
|
||||||
|
let container = statement(&c, ginfo, &mut linfo)?;
|
||||||
|
linfo
|
||||||
|
.vars
|
||||||
|
.insert(v.clone(), (ginfo.vars, container.out().inner_types()));
|
||||||
|
let for_loop_var = ginfo.vars;
|
||||||
|
ginfo.vars += 1;
|
||||||
|
let block = statement(&b, ginfo, &mut linfo)?;
|
||||||
|
let o = RStatementEnum::For(for_loop_var, container, block);
|
||||||
|
o
|
||||||
|
}
|
||||||
|
SStatementEnum::Switch(switch_on, cases, force) => {
|
||||||
|
let mut ncases = Vec::with_capacity(cases.len());
|
||||||
|
for case in cases {
|
||||||
|
ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?));
|
||||||
|
eprintln!("NCASE: {:#?}", ncases.last().unwrap().0);
|
||||||
|
}
|
||||||
|
let switch_on = statement(switch_on, ginfo, linfo)?;
|
||||||
|
let switch_on_out = switch_on.out();
|
||||||
|
if *force {
|
||||||
|
for val_type in switch_on_out.types.iter() {
|
||||||
|
let val_type: VType = val_type.clone().into();
|
||||||
|
'force: {
|
||||||
|
for (case_type, _) in ncases.iter() {
|
||||||
|
if val_type.fits_in(&case_type).is_empty() {
|
||||||
|
eprintln!("Breaking.");
|
||||||
|
break 'force;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(ToRunnableError::CaseForceButTypeNotCovered(val_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RStatementEnum::Switch(switch_on, ncases)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to();
|
||||||
|
if let Some(opt) = &s.output_to {
|
||||||
|
if let Some(var) = linfo.vars.get_mut(opt) {
|
||||||
|
var.1 = var.1.clone() | statement.out();
|
||||||
|
statement.output_to = Some(var.0);
|
||||||
|
} else {
|
||||||
|
linfo
|
||||||
|
.vars
|
||||||
|
.insert(opt.clone(), (ginfo.vars, statement.out()));
|
||||||
|
statement.output_to = Some(ginfo.vars);
|
||||||
|
ginfo.vars += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(statement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runnable
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RBlock {
|
||||||
|
statements: Vec<RStatement>,
|
||||||
|
}
|
||||||
|
impl RBlock {
|
||||||
|
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
|
||||||
|
let mut last = None;
|
||||||
|
for statement in &self.statements {
|
||||||
|
last = Some(statement.run(vars));
|
||||||
|
}
|
||||||
|
if let Some(v) = last {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn out(&self) -> VType {
|
||||||
|
if let Some(last) = self.statements.last() {
|
||||||
|
last.out()
|
||||||
|
} else {
|
||||||
|
VType {
|
||||||
|
types: vec![VSingleType::Tuple(vec![])],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RFunction {
|
||||||
|
inputs: Vec<usize>,
|
||||||
|
input_types: Vec<VType>,
|
||||||
|
input_output_map: Vec<(Vec<VSingleType>, VType)>,
|
||||||
|
block: RBlock,
|
||||||
|
}
|
||||||
|
impl RFunction {
|
||||||
|
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
|
||||||
|
self.block.run(vars)
|
||||||
|
}
|
||||||
|
pub fn out(&self, input_types: &Vec<VSingleType>) -> VType {
|
||||||
|
self.input_output_map
|
||||||
|
.iter()
|
||||||
|
.find_map(|v| {
|
||||||
|
if v.0 == *input_types {
|
||||||
|
Some(v.1.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("invalid args for function! possible issue with type-checker if this can be reached! feel free to report a bug.")
|
||||||
|
}
|
||||||
|
pub fn out_all(&self) -> VType {
|
||||||
|
self.block.out()
|
||||||
|
}
|
||||||
|
pub fn in_types(&self) -> &Vec<VType> {
|
||||||
|
&self.input_types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RStatement {
|
||||||
|
output_to: Option<usize>,
|
||||||
|
statement: Box<RStatementEnum>,
|
||||||
|
}
|
||||||
|
impl RStatement {
|
||||||
|
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
|
||||||
|
let out = self.statement.run(vars);
|
||||||
|
if let Some(v) = self.output_to {
|
||||||
|
*vars[v].lock().unwrap() = out;
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
} else {
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn out(&self) -> VType {
|
||||||
|
if self.output_to.is_some() {
|
||||||
|
return VType {
|
||||||
|
types: vec![VSingleType::Tuple(vec![])],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.statement.out()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum RStatementEnum {
|
||||||
|
Value(VData),
|
||||||
|
Variable(usize, VType), // Arc<Mutex<..>> here, because imagine variable in for loop that is used in a different thread -> we need multiple "same" variables
|
||||||
|
FunctionCall(Arc<RFunction>, Vec<RStatement>),
|
||||||
|
BuiltinFunction(BuiltinFunction, Vec<RStatement>),
|
||||||
|
Block(RBlock),
|
||||||
|
If(RStatement, RStatement, Option<RStatement>),
|
||||||
|
While(RStatement),
|
||||||
|
For(usize, RStatement, RStatement),
|
||||||
|
Switch(RStatement, Vec<(VType, RStatement)>),
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum BuiltinFunction {
|
||||||
|
// print
|
||||||
|
Print,
|
||||||
|
Debug,
|
||||||
|
// format
|
||||||
|
ToString,
|
||||||
|
Format,
|
||||||
|
// math and basic operators (not possible, need to be different for each type)
|
||||||
|
// Add,
|
||||||
|
// Sub,
|
||||||
|
// Mul,
|
||||||
|
// Div,
|
||||||
|
// Mod,
|
||||||
|
// functions
|
||||||
|
Run,
|
||||||
|
Thread,
|
||||||
|
Await,
|
||||||
|
Sleep,
|
||||||
|
}
|
||||||
|
impl RStatementEnum {
|
||||||
|
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
|
||||||
|
match self {
|
||||||
|
Self::Value(v) => v.clone(),
|
||||||
|
Self::Variable(v, _) => vars[*v].lock().unwrap().clone(),
|
||||||
|
Self::FunctionCall(func, args) => {
|
||||||
|
for (i, input) in func.inputs.iter().enumerate() {
|
||||||
|
*vars[*input].lock().unwrap() = args[i].run(vars);
|
||||||
|
}
|
||||||
|
func.run(vars)
|
||||||
|
}
|
||||||
|
Self::Block(b) => b.run(vars),
|
||||||
|
Self::If(c, t, e) => {
|
||||||
|
if let VDataEnum::Bool(v) = c.run(vars).data {
|
||||||
|
if v {
|
||||||
|
t.run(vars)
|
||||||
|
} else {
|
||||||
|
if let Some(e) = e {
|
||||||
|
e.run(vars)
|
||||||
|
} else {
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::While(c) => loop {
|
||||||
|
// While loops blocks can return a bool (false to break from the loop) or a 0-1 length tuple (0-length => continue, 1-length => break with value)
|
||||||
|
match c.run(vars).data {
|
||||||
|
VDataEnum::Bool(v) => {
|
||||||
|
if !v {
|
||||||
|
break VDataEnum::Tuple(vec![]).to();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VDataEnum::Tuple(v) if v.len() == 1 => break v[0].clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::For(v, c, b) => {
|
||||||
|
let c = c.run(vars);
|
||||||
|
let mut vars = vars.clone();
|
||||||
|
let mut in_loop = |c| {
|
||||||
|
vars[*v] = Arc::new(Mutex::new(c));
|
||||||
|
b.run(&vars);
|
||||||
|
};
|
||||||
|
match c.data {
|
||||||
|
VDataEnum::Int(v) => {
|
||||||
|
for i in 0..v {
|
||||||
|
in_loop(VDataEnum::Int(i).to());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VDataEnum::String(v) => {
|
||||||
|
for ch in v.chars() {
|
||||||
|
in_loop(VDataEnum::String(ch.to_string()).to())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VDataEnum::Tuple(v) | VDataEnum::List(v) => {
|
||||||
|
for v in v {
|
||||||
|
in_loop(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
}
|
||||||
|
Self::BuiltinFunction(v, args) => match v {
|
||||||
|
BuiltinFunction::Print => {
|
||||||
|
if let VDataEnum::String(arg) = args[0].run(vars).data {
|
||||||
|
println!("{}", arg);
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinFunction::Debug => {
|
||||||
|
println!("{:?}", args[0].run(vars).data);
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
}
|
||||||
|
BuiltinFunction::ToString => {
|
||||||
|
VDataEnum::String(format!("{}", args[0].run(vars).data)).to()
|
||||||
|
}
|
||||||
|
BuiltinFunction::Format => todo!("Format function (and validity check!)"),
|
||||||
|
BuiltinFunction::Run => {
|
||||||
|
if args.len() >= 1 {
|
||||||
|
if let VDataEnum::Function(f) = args[0].run(vars).data {
|
||||||
|
if f.inputs.len() != args.len() - 1 {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
for (i, var) in f.inputs.iter().enumerate() {
|
||||||
|
let val = args[i + 1].run(vars);
|
||||||
|
*vars[*var].lock().unwrap() = val;
|
||||||
|
}
|
||||||
|
f.run(vars)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinFunction::Thread => {
|
||||||
|
if args.len() >= 1 {
|
||||||
|
if let VDataEnum::Function(f) = args[0].run(vars).data {
|
||||||
|
if f.inputs.len() != args.len() - 1 {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
// to prevent weird stuff from happening, the function args will be stored in different Arc<Mutex<_>>s. This means that the args are different for each thread, while any variables that are captured from outside will be shared.
|
||||||
|
let mut thread_vars = vars.clone();
|
||||||
|
let mut run_input_types = vec![];
|
||||||
|
for (i, var) in f.inputs.iter().enumerate() {
|
||||||
|
let val = args[i + 1].run(vars);
|
||||||
|
run_input_types.push(val.out_single());
|
||||||
|
thread_vars[*var] = Arc::new(Mutex::new(val));
|
||||||
|
}
|
||||||
|
let out_type = f.out(&run_input_types);
|
||||||
|
VDataEnum::Thread(
|
||||||
|
VDataThreadEnum::Running(std::thread::spawn(move || {
|
||||||
|
f.run(&thread_vars)
|
||||||
|
}))
|
||||||
|
.to(),
|
||||||
|
out_type,
|
||||||
|
)
|
||||||
|
.to()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinFunction::Await => {
|
||||||
|
if args.len() == 1 {
|
||||||
|
if let VDataEnum::Thread(t, _) = args[0].run(vars).data {
|
||||||
|
t.get()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinFunction::Sleep => {
|
||||||
|
if args.len() == 1 {
|
||||||
|
match args[0].run(vars).data {
|
||||||
|
VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(v as _)),
|
||||||
|
VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(v)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
VDataEnum::Tuple(vec![]).to()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Switch(switch_on, cases) => {
|
||||||
|
let switch_on = switch_on.run(vars);
|
||||||
|
let switch_on_type = switch_on.out();
|
||||||
|
let mut out = VDataEnum::Tuple(vec![]).to();
|
||||||
|
for (case_type, case_action) in cases.iter() {
|
||||||
|
if switch_on_type.fits_in(case_type).is_empty() {
|
||||||
|
out = case_action.run(vars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn out(&self) -> VType {
|
||||||
|
match self {
|
||||||
|
Self::Value(v) => v.out(),
|
||||||
|
Self::Variable(_, t) => t.clone(),
|
||||||
|
Self::FunctionCall(f, _) => {
|
||||||
|
eprintln!("Warn: generalizing a functions return type regardless of the inputs. Type-checker might assume this value can have more types than it really can.");
|
||||||
|
f.out_all()
|
||||||
|
}
|
||||||
|
Self::Block(b) => b.out(),
|
||||||
|
Self::If(_, a, b) => {
|
||||||
|
if let Some(b) = b {
|
||||||
|
a.out() | b.out()
|
||||||
|
} else {
|
||||||
|
a.out()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::While(c) => todo!("while loop output type"),
|
||||||
|
Self::For(_, _, b) => {
|
||||||
|
// returns the return value from the last iteration or nothing if there was no iteration
|
||||||
|
b.out()
|
||||||
|
| VType {
|
||||||
|
types: vec![VSingleType::Tuple(vec![])],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::BuiltinFunction(f, _) => match f {
|
||||||
|
BuiltinFunction::Print | BuiltinFunction::Debug | BuiltinFunction::Sleep => VType {
|
||||||
|
types: vec![VSingleType::Tuple(vec![])],
|
||||||
|
},
|
||||||
|
BuiltinFunction::ToString | BuiltinFunction::Format => VSingleType::String.into(),
|
||||||
|
BuiltinFunction::Run | BuiltinFunction::Thread | BuiltinFunction::Await => {
|
||||||
|
VType { types: vec![] } // TODO!
|
||||||
|
// unreachable!("this has to be implemented somewhere else!")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Switch(_, cases) => {
|
||||||
|
let mut out = VSingleType::Tuple(vec![]).into(); // if nothing is executed
|
||||||
|
for (_, case) in cases.iter() {
|
||||||
|
out = out | case.out();
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to(self) -> RStatement {
|
||||||
|
RStatement {
|
||||||
|
output_to: None,
|
||||||
|
statement: Box::new(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RScript {
|
||||||
|
main: RFunction,
|
||||||
|
vars: usize,
|
||||||
|
}
|
||||||
|
impl RScript {
|
||||||
|
fn new(main: RFunction, vars: usize) -> Result<Self, ToRunnableError> {
|
||||||
|
if main.inputs.len() != 1 {
|
||||||
|
return Err(ToRunnableError::MainWrongInput);
|
||||||
|
}
|
||||||
|
Ok(Self { main, vars })
|
||||||
|
}
|
||||||
|
pub fn run(&self, args: Vec<String>) -> VData {
|
||||||
|
let mut vars = Vec::with_capacity(self.vars);
|
||||||
|
vars.push(am(VDataEnum::List(
|
||||||
|
args.into_iter()
|
||||||
|
.map(|v| VDataEnum::String(v).to())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.to()));
|
||||||
|
for _i in 1..self.vars {
|
||||||
|
vars.push(am(VDataEnum::Tuple(vec![]).to()));
|
||||||
|
}
|
||||||
|
self.main.run(&vars)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "(")?;
|
||||||
|
for (i, (n, t)) in self.inputs.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{n} {t}")?;
|
||||||
|
}
|
||||||
|
write!(f, ") {}", self.block)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for SBlock {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "{{")?;
|
||||||
|
for s in self.statements.iter() {
|
||||||
|
writeln!(f, "{s}")?;
|
||||||
|
}
|
||||||
|
write!(f, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for VType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for (i, t) in self.types.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, "/")?;
|
||||||
|
}
|
||||||
|
write!(f, "{t}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for VSingleType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Bool => write!(f, "bool"),
|
||||||
|
Self::Int => write!(f, "int"),
|
||||||
|
Self::Float => write!(f, "float"),
|
||||||
|
Self::String => write!(f, "string"),
|
||||||
|
Self::Tuple(types) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
for t in types {
|
||||||
|
write!(f, "{t}")?;
|
||||||
|
}
|
||||||
|
write!(f, "]")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Self::List(t) => write!(f, "[{t}]"),
|
||||||
|
Self::Function(args, out) => write!(f, "({args:?}) -> {out}"),
|
||||||
|
Self::Thread(_) => write!(f, "THREAD"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for SStatement {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(to) = self.output_to.as_ref() {
|
||||||
|
write!(f, "{} = ", to.as_str())?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", self.statement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for SStatementEnum {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
SStatementEnum::Value(v) => write!(f, "{v}"),
|
||||||
|
SStatementEnum::Variable(v) => write!(f, "{v}"),
|
||||||
|
SStatementEnum::FunctionCall(func, args) => {
|
||||||
|
write!(f, "{func}(")?;
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{arg}")?;
|
||||||
|
}
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
SStatementEnum::FunctionDefinition(name, func) => {
|
||||||
|
if let Some(name) = name {
|
||||||
|
write!(f, "{name}{func}")
|
||||||
|
} else {
|
||||||
|
write!(f, "{func}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SStatementEnum::Block(b) => write!(f, "{b}"),
|
||||||
|
SStatementEnum::If(c, a, b) => {
|
||||||
|
write!(f, "if {c} {a}")?;
|
||||||
|
if let Some(b) = b {
|
||||||
|
write!(f, " else {b}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
SStatementEnum::While(c) => write!(f, "while {c}"),
|
||||||
|
SStatementEnum::For(v, c, b) => write!(f, "for {v} {c} {b}"),
|
||||||
|
SStatementEnum::Switch(switch_on, cases, force) => {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"switch{} {} {{",
|
||||||
|
if *force { "!" } else { "" },
|
||||||
|
switch_on
|
||||||
|
)?;
|
||||||
|
for (case_type, case_action) in cases.iter() {
|
||||||
|
writeln!(f, "{} {}", case_type, case_action)?;
|
||||||
|
}
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for VData {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for VDataEnum {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Bool(v) => {
|
||||||
|
if *v {
|
||||||
|
write!(f, "true")
|
||||||
|
} else {
|
||||||
|
write!(f, "false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Int(v) => write!(f, "{v}"),
|
||||||
|
Self::Float(v) => write!(f, "{v}"),
|
||||||
|
Self::String(v) => write!(f, "{v}"),
|
||||||
|
Self::Tuple(v) | Self::List(v) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
for v in v {
|
||||||
|
write!(f, "{v}")?;
|
||||||
|
}
|
||||||
|
write!(f, "]")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Self::Function(v) => write!(f, "{v}"),
|
||||||
|
Self::Thread(..) => write!(f, "THREAD"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for RFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({}) {:#?}", self.inputs.len(), self.block)
|
||||||
|
}
|
||||||
|
}
|
2
src/script/mod.rs
Normal file
2
src/script/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod block;
|
||||||
|
pub mod value;
|
198
src/script/value.rs
Normal file
198
src/script/value.rs
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
ops::BitOr,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::JoinHandle,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::block::RFunction;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct VData {
|
||||||
|
// parents: Vec<()>,
|
||||||
|
pub data: VDataEnum,
|
||||||
|
}
|
||||||
|
impl VData {
|
||||||
|
pub fn out(&self) -> VType {
|
||||||
|
VType {
|
||||||
|
types: vec![self.out_single()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn out_single(&self) -> VSingleType {
|
||||||
|
match &self.data {
|
||||||
|
VDataEnum::Bool(..) => VSingleType::Bool,
|
||||||
|
VDataEnum::Int(..) => VSingleType::Int,
|
||||||
|
VDataEnum::Float(..) => VSingleType::Float,
|
||||||
|
VDataEnum::String(..) => VSingleType::String,
|
||||||
|
VDataEnum::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out()).collect()),
|
||||||
|
VDataEnum::List(v) => {
|
||||||
|
VSingleType::List(v.iter().fold(VType { types: vec![] }, |a, b| a | b.out()))
|
||||||
|
}
|
||||||
|
VDataEnum::Function(f) => VSingleType::Function(f.in_types().clone(), {
|
||||||
|
eprintln!("Warn: generalizing function return type, disregarding input types. might make the type checker think it can return types it can only return with different arguments as the ones that were actually provided.");
|
||||||
|
f.out_all()
|
||||||
|
}),
|
||||||
|
VDataEnum::Thread(_, o) => VSingleType::Thread(o.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum VDataEnum {
|
||||||
|
Bool(bool),
|
||||||
|
Int(isize),
|
||||||
|
Float(f64),
|
||||||
|
String(String),
|
||||||
|
Tuple(Vec<VData>),
|
||||||
|
List(Vec<VData>),
|
||||||
|
Function(RFunction),
|
||||||
|
Thread(VDataThread, VType),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VDataThread(Arc<Mutex<VDataThreadEnum>>);
|
||||||
|
impl VDataThread {
|
||||||
|
pub fn try_get(&self) -> Option<VData> {
|
||||||
|
match &*self.lock() {
|
||||||
|
VDataThreadEnum::Running(_) => None,
|
||||||
|
VDataThreadEnum::Finished(v) => Some(v.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get(&self) -> VData {
|
||||||
|
let dur = Duration::from_millis(100);
|
||||||
|
loop {
|
||||||
|
match &*self.lock() {
|
||||||
|
VDataThreadEnum::Running(v) => {
|
||||||
|
while !v.is_finished() {
|
||||||
|
std::thread::sleep(dur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VDataThreadEnum::Finished(v) => return v.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(VData {
|
||||||
|
data: VDataEnum::Bool(false),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
match m {
|
||||||
|
VDataThreadEnum::Running(v) => {
|
||||||
|
*mg = VDataThreadEnum::Finished(v.join().unwrap())
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
mg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Debug for VDataThread {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &*self.lock() {
|
||||||
|
VDataThreadEnum::Running(_) => write!(f, "(thread running)"),
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VDataEnum {
|
||||||
|
pub fn to(self) -> VData {
|
||||||
|
VData { data: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct VType {
|
||||||
|
pub types: Vec<VSingleType>,
|
||||||
|
}
|
||||||
|
impl VType {
|
||||||
|
/// Returns a vec with all types in self that aren't covered by rhs. If the returned vec is empty, self fits in rhs.
|
||||||
|
pub fn fits_in(&self, rhs: &Self) -> Vec<VSingleType> {
|
||||||
|
let mut no = vec![];
|
||||||
|
for t in &self.types {
|
||||||
|
if !rhs.types.contains(t) {
|
||||||
|
no.push(t.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
no
|
||||||
|
}
|
||||||
|
pub fn inner_types(&self) -> VType {
|
||||||
|
let mut out = VType { types: vec![] };
|
||||||
|
for t in &self.types {
|
||||||
|
for it in t.inner_types() {
|
||||||
|
out = out | it.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BitOr for VType {
|
||||||
|
type Output = Self;
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
let mut types = self.types;
|
||||||
|
for t in rhs.types {
|
||||||
|
if !types.contains(&t) {
|
||||||
|
types.push(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self { types }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum VSingleType {
|
||||||
|
Bool,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
String,
|
||||||
|
Tuple(Vec<VType>),
|
||||||
|
List(VType),
|
||||||
|
Function(Vec<VType>, VType),
|
||||||
|
Thread(VType),
|
||||||
|
}
|
||||||
|
impl VSingleType {
|
||||||
|
pub fn inner_types(&self) -> Vec<VSingleType> {
|
||||||
|
match self {
|
||||||
|
Self::Tuple(v) => {
|
||||||
|
let mut types = vec![];
|
||||||
|
for it in v {
|
||||||
|
// the tuple values
|
||||||
|
for it in &it.types {
|
||||||
|
// the possible types for each value
|
||||||
|
if !types.contains(it) {
|
||||||
|
types.push(it.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types
|
||||||
|
}
|
||||||
|
Self::List(v) => v.types.clone(),
|
||||||
|
_ => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<VType> for VSingleType {
|
||||||
|
fn into(self) -> VType {
|
||||||
|
VType { types: vec![self] }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user