moved Cargo.toml and src/ to /mers/Cargo.toml and /mers/src/ because rust-analyzer was apparently very confused when I was trying to edit projects in mers_libs/*/.

This commit is contained in:
Dummi26
2023-04-12 22:23:07 +02:00
parent 5b051e72f1
commit 2acdcd3f53
26 changed files with 194 additions and 5 deletions

42
mers/Cargo.lock generated Normal file
View File

@@ -0,0 +1,42 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mers"
version = "0.1.0"
dependencies = [
"regex",
]
[[package]]
name = "regex"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"

13
mers/Cargo.toml Executable file
View File

@@ -0,0 +1,13 @@
[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
[lib]
name = "mers"
path = "src/main.rs"
[dependencies]
regex = "1.7.2"

139
mers/src/libs/inlib.rs Executable file
View File

@@ -0,0 +1,139 @@
use std::{
collections::HashMap,
io::{BufRead, Write},
};
use crate::{
libs::DirectReader,
script::{
val_data::{VData, VDataEnum},
val_type::VType,
},
};
use super::{data_from_bytes, data_to_bytes};
pub struct MyLib {
name: String,
version: (u8, u8),
description: String,
functions: Vec<(String, Vec<VType>, VType)>,
enum_variants: HashMap<String, usize>,
}
impl MyLib {
pub fn new(
name: String,
version: (u8, u8),
description: String,
functions: Vec<(String, Vec<VType>, VType)>,
) -> (Self, MyLibTaskCompletion) {
(
Self {
name,
version,
description,
functions,
enum_variants: HashMap::new(),
},
MyLibTaskCompletion { _priv: () },
)
}
pub fn get_enum(&self, e: &str) -> usize {
*self.enum_variants.get(e).unwrap()
}
pub fn run<I, O>(
&mut self,
run: MyLibTaskCompletion,
stdin: &mut I,
stdout: &mut O,
) -> MyLibTask
where
I: BufRead,
O: Write,
{
drop(run);
match match stdin.one_byte().unwrap().into() {
'i' => {
assert_eq!(stdin.one_byte().unwrap() as char, '\n');
stdout.write(&[self.version.0, self.version.1]).unwrap();
writeln!(stdout, "{}", self.name).unwrap();
stdout
.write(&[self.description.split('\n').count() as _])
.unwrap();
writeln!(stdout, "{}", self.description).unwrap();
for func in self.functions.iter() {
writeln!(
stdout,
"f{}({}) {}",
func.0,
func.1
.iter()
.enumerate()
.map(|(i, v)| if i == 0 {
format!("{v}")
} else {
format!(" {v}")
})
.collect::<String>(),
func.2
)
.unwrap();
}
writeln!(stdout, "x").unwrap();
None
}
'I' => {
let mut line = String::new();
stdin.read_line(&mut line).unwrap();
if let Some((task, args)) = line.split_once(' ') {
match task {
"set_enum_id" => {
let (enum_name, enum_id) = args.split_once(' ').unwrap();
let name = enum_name.trim().to_string();
let id = enum_id.trim().parse().unwrap();
self.enum_variants.insert(name.clone(), id);
}
_ => todo!(),
}
}
None
}
'f' => {
let fnid = stdin.one_byte().unwrap() as usize;
Some(MyLibTask::RunFunction(MyLibTaskRunFunction {
function: fnid,
args: self.functions[fnid]
.1
.iter()
.map(|_| data_from_bytes(stdin).to())
.collect(),
}))
}
_ => None,
} {
Some(v) => v,
None => MyLibTask::None(MyLibTaskCompletion { _priv: () }),
}
}
}
pub enum MyLibTask {
None(MyLibTaskCompletion),
RunFunction(MyLibTaskRunFunction),
}
pub struct MyLibTaskRunFunction {
pub function: usize,
pub args: Vec<VData>,
}
impl MyLibTaskRunFunction {
pub fn done<O>(self, o: &mut O, returns: VData) -> MyLibTaskCompletion
where
O: Write,
{
data_to_bytes(&returns, o);
MyLibTaskCompletion { _priv: () }
}
}
pub struct MyLibTaskCompletion {
_priv: (),
}

307
mers/src/libs/mod.rs Executable file
View File

@@ -0,0 +1,307 @@
pub mod inlib;
pub mod path;
use std::{
collections::{HashMap, HashSet},
io::{self, BufRead, BufReader, Read, Write},
path::PathBuf,
process::{Child, ChildStdin, ChildStdout, Command, Stdio},
sync::{Arc, Mutex},
};
use crate::{
parse::{file::File, parse},
script::{
val_data::{VData, VDataEnum},
val_type::VType,
},
};
/*
Libraries are processes that communicate via stdout/stdin.
data in stdout is only expected after it was requested via stdin. ignoring this will likely cause issues.
requests in stdin can be identified via the first byte (ascii char) and end with a \n newline character.
the identifying ascii chars:
i init
reply format:
two bytes, the first for major and the second for minor version number.
the utf8-encoded name of the library followed by a newline
the number of lines in the description (0 for no description) as a byte. (more than 255 lines aren't supported)
a (optionally markdown-formatted [TODO!]) description of the library; all lines (including the last one) must end with a newline
the things you would like to register; one line each unless explicitly stated otherwise; the first byte (char) decides what type to register:
f a function: followed by the function signature, i.e. "my_func(string int/float [string]) string/[float int]"
x end: indicates that you are done registering things
I init 2
TODO! (currently nothing)
reply should be a single line (only the newline char). If there is data before the newline char, it will be reported as an error and the script will not run.
f call a function:
followed by the function id byte (0 <= id < #funcs; function ids are assigned in ascending order as they were registered in the reply to "i"
and the data for each argument, in order.
reply: the data for the returned value
x exit
sending data: (all ints are encoded so that the most significant data is sent FIRST)
the first byte (ascii char) identifies the type of data: (exceptions listed first: bools)
b false
B true
1 int
2 int as string
5 float
6 float as string
" string (format: ", 64-bit unsigned int indicating string length, so many bytes utf-8)
*/
#[derive(Debug)]
pub struct Lib {
process: Child,
stdin: Arc<Mutex<ChildStdin>>,
stdout: Arc<Mutex<BufReader<ChildStdout>>>,
pub registered_fns: Vec<(String, Vec<VType>, VType)>,
}
impl Lib {
pub fn launch(
mut exec: Command,
enum_variants: &mut HashMap<String, usize>,
) -> Result<Self, LaunchError> {
let mut handle = match exec
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
{
Ok(v) => v,
Err(e) => return Err(LaunchError::CouldNotSpawnProcess(e)),
};
if let (Some(mut stdin), Some(stdout)) = (handle.stdin.take(), handle.stdout.take()) {
let mut stdout = BufReader::new(stdout);
writeln!(stdin, "i").unwrap();
let vernum = {
let mut vernum = [0, 0];
stdout.read_exact(&mut vernum).unwrap();
(vernum[0], vernum[1])
};
let name = stdout.line().unwrap();
let name = name.trim();
eprintln!("- <<< ADDING LIB: {name} v{}.{} >>> -", vernum.0, vernum.1);
let lines_in_desc = stdout.one_byte().unwrap();
let mut lines_desc = Vec::with_capacity(lines_in_desc as _);
for _ in 0..lines_in_desc {
let line = stdout.line().unwrap();
let line = line.trim_end_matches('\n');
eprintln!("| {line}");
lines_desc.push(line.to_string());
}
let mut registered_fns = vec![];
loop {
let line = stdout.line().unwrap();
match line.chars().next() {
Some('f') => {
let (name, args) = line[1..]
.split_once('(')
.expect("function signature didn't include the ( character.");
let mut fn_signature = File::new(args.to_string(), PathBuf::new());
let mut fn_in = vec![];
fn_signature.skip_whitespaces();
if let Some(')') = fn_signature.peek() {
fn_signature.next();
} else {
loop {
let mut t = parse::parse_type_adv(&mut fn_signature, true).unwrap();
t.0.enum_variants(enum_variants);
fn_in.push(t.0);
if t.1 {
break;
}
}
}
let mut fn_out = parse::parse_type(&mut fn_signature).unwrap();
fn_out.enum_variants(enum_variants);
eprintln!("Registering function \"{name}\" with args \"{}\" and return type \"{fn_out}\"", &fn_in.iter().fold(String::new(), |mut s, v| { s.push_str(format!(" {}", v).as_str()); s }).trim_start_matches(' '));
registered_fns.push((name.to_string(), fn_in, fn_out));
}
Some('x') => break,
_ => todo!(),
}
}
for (enum_name, enum_id) in enum_variants.iter() {
writeln!(stdin, "Iset_enum_id {enum_name} {enum_id}").unwrap();
}
Ok(Self {
process: handle,
stdin: Arc::new(Mutex::new(stdin)),
stdout: Arc::new(Mutex::new(stdout)),
registered_fns,
})
} else {
return Err(LaunchError::NoStdio);
}
}
pub fn run_fn(&self, fnid: usize, args: &Vec<VData>) -> VData {
let mut stdin = self.stdin.lock().unwrap();
let mut stdout = self.stdout.lock().unwrap();
debug_assert!(args.len() == self.registered_fns[fnid].1.len());
write!(stdin, "f").unwrap();
stdin.write(&[fnid as _]).unwrap();
for (_i, arg) in args.iter().enumerate() {
data_to_bytes(arg, &mut *stdin);
}
let o = data_from_bytes(&mut *stdout).to();
o
}
}
#[derive(Debug)]
pub enum LaunchError {
NoStdio,
CouldNotSpawnProcess(io::Error),
}
pub trait DirectReader {
fn line(&mut self) -> Result<String, io::Error>;
fn one_byte(&mut self) -> Result<u8, io::Error>;
}
impl<T> DirectReader for T
where
T: BufRead,
{
fn line(&mut self) -> Result<String, io::Error> {
let mut buf = String::new();
self.read_line(&mut buf)?;
Ok(buf)
}
fn one_byte(&mut self) -> Result<u8, io::Error> {
let mut b = [0];
self.read(&mut b)?;
Ok(b[0])
}
}
pub fn data_to_bytes<T>(data: &VData, stdin: &mut T)
where
T: Write,
{
match &data.data {
VDataEnum::Bool(false) => write!(stdin, "b").unwrap(),
VDataEnum::Bool(true) => write!(stdin, "B").unwrap(),
VDataEnum::Int(v) => {
let mut v = *v;
let mut b = [0u8; 8];
for i in (0..8).rev() {
b[i] = (v & 0xFF) as _;
v >>= 8;
}
write!(stdin, "1").unwrap();
stdin.write(&b).unwrap();
}
VDataEnum::Float(f) => {
writeln!(stdin, "6{f}").unwrap();
}
VDataEnum::String(s) => {
write!(stdin, "\"").unwrap();
stdin.write(&(s.len() as u64).to_be_bytes()).unwrap();
stdin.write(s.as_bytes()).unwrap();
}
VDataEnum::Tuple(v) => {
write!(stdin, "t").unwrap();
for v in v {
write!(stdin, "+").unwrap();
data_to_bytes(v, stdin);
}
writeln!(stdin).unwrap();
}
VDataEnum::List(_, v) => {
write!(stdin, "l").unwrap();
for v in v {
write!(stdin, "+").unwrap();
data_to_bytes(v, stdin);
}
writeln!(stdin).unwrap();
}
VDataEnum::Function(..) | VDataEnum::Reference(..) | VDataEnum::Thread(..) => {
panic!("cannot use functions, references or threads in LibFunctions.")
}
VDataEnum::EnumVariant(e, v) => {
stdin
.write(
['E' as u8]
.into_iter()
.chain((*e as u64).to_be_bytes().into_iter())
.collect::<Vec<u8>>()
.as_slice(),
)
.unwrap();
data_to_bytes(v.as_ref(), stdin);
}
}
stdin.flush().unwrap();
}
pub fn data_from_bytes<T>(stdout: &mut T) -> VDataEnum
where
T: BufRead,
{
let id_byte = stdout.one_byte().unwrap().into();
match id_byte {
'b' => VDataEnum::Bool(false),
'B' => VDataEnum::Bool(true),
'1' => {
let mut num = 0;
for _ in 0..8 {
num <<= 8;
num |= stdout.one_byte().unwrap() as isize;
}
VDataEnum::Int(num)
}
'2' => {
let mut buf = String::new();
stdout.read_line(&mut buf).unwrap();
VDataEnum::Int(buf.parse().unwrap())
}
'5' => {
let mut num = 0;
for _ in 0..8 {
num <<= 8;
num |= stdout.one_byte().unwrap() as u64;
}
VDataEnum::Float(f64::from_bits(num))
}
'6' => {
let mut buf = String::new();
stdout.read_line(&mut buf).unwrap();
VDataEnum::Float(buf.parse().unwrap())
}
't' | 'l' => {
let mut v = vec![];
loop {
if stdout.one_byte().unwrap() == '\n' as _ {
break if id_byte == 't' {
VDataEnum::Tuple(v)
} else {
VDataEnum::List(VType { types: vec![] }, v)
};
}
v.push(data_from_bytes(stdout).to())
}
}
'"' => {
let mut len_bytes = 0u64;
for _ in 0..8 {
len_bytes <<= 8;
len_bytes |= stdout.one_byte().unwrap() as u64;
}
let mut buf = Vec::with_capacity(len_bytes as _);
for _ in 0..len_bytes {
buf.push(stdout.one_byte().unwrap());
}
VDataEnum::String(String::from_utf8_lossy(&buf).into_owned())
}
'E' => {
let mut u = [0u8; 8];
stdout.read_exact(&mut u).unwrap();
let u = u64::from_be_bytes(u) as _;
VDataEnum::EnumVariant(u, Box::new(data_from_bytes(stdout).to()))
}
other => todo!("data_from_bytes: found '{other}'."),
}
}

26
mers/src/libs/path.rs Executable file
View File

@@ -0,0 +1,26 @@
use std::path::PathBuf;
pub fn path_from_string(path: &str, script_directory: &PathBuf) -> Option<PathBuf> {
let path = PathBuf::from(path);
if path.is_absolute() {
return Some(path);
}
if let Some(p) = script_directory
.canonicalize()
.unwrap_or_else(|_| script_directory.clone())
.parent()
{
eprintln!("Parent: {:?}", p);
let p = p.join(&path);
if p.exists() {
return Some(p);
}
}
if let Ok(mers_lib_dir) = std::env::var("MERS_LIB_DIR") {
let p = PathBuf::from(mers_lib_dir).join(&path);
if p.exists() {
return Some(p);
}
}
None
}

20
mers/src/main.rs Executable file
View File

@@ -0,0 +1,20 @@
use std::time::Instant;
pub mod libs;
pub mod parse;
pub mod script;
fn main() {
let path = std::env::args().nth(1).unwrap();
let script = parse::parse::parse(&mut parse::file::File::new(
std::fs::read_to_string(&path).unwrap(),
path.into(),
))
.unwrap();
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());
}

185
mers/src/parse/file.rs Executable file
View File

@@ -0,0 +1,185 @@
use std::{
fmt::Display,
ops::{Index, Range, RangeFrom, RangeTo},
path::PathBuf,
};
pub struct File {
path: PathBuf,
data: String,
chars: Vec<(usize, char)>,
pos: FilePosition,
}
#[derive(Clone, Copy)]
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, path: PathBuf) -> Self {
let mut chs = data.chars();
let mut data = String::with_capacity(data.len());
loop {
match chs.next() {
Some('/') => match chs.next() {
Some('/') => loop {
match chs.next() {
Some('\n') | None => break,
_ => (),
}
},
Some('*') => loop {
match chs.next() {
Some('*') => {
if let Some('/') = chs.next() {
break;
}
}
None => break,
_ => (),
}
},
Some(ch) => {
data.push('/');
data.push(ch);
}
None => {
data.push('/');
break;
}
},
Some(ch) => data.push(ch),
None => break,
}
}
let chars = data.char_indices().collect();
Self {
path,
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 path(&self) -> &PathBuf {
&self.path
}
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,
}
}
pub fn next_line(&mut self) -> String {
let mut o = String::new();
for ch in self {
if ch == '\n' {
break;
} else {
o.push(ch);
}
}
o
}
pub fn peek(&self) -> Option<char> {
match self.chars.get(self.pos.current_char_index) {
Some((_, c)) => Some(*c),
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
mers/src/parse/mod.rs Executable file
View File

@@ -0,0 +1,2 @@
pub mod file;
pub mod parse;

577
mers/src/parse/parse.rs Executable file
View File

@@ -0,0 +1,577 @@
use std::{process::Command, sync::Arc};
use crate::{
libs,
script::{
block::{
to_runnable::ToRunnableError,
to_runnable::{self, GInfo},
RScript, SBlock, SFunction, SStatement, SStatementEnum,
},
val_data::VDataEnum,
val_type::{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 mut libs = vec![];
let mut enum_variants = GInfo::default_enum_variants();
loop {
file.skip_whitespaces();
let pos = file.get_pos().clone();
let line = file.next_line();
if line.starts_with("lib ") {
let path_to_executable = match libs::path::path_from_string(&line[4..], file.path()) {
Some(v) => v,
None => panic!("Couldn't find a path for the library with the path '{}'. Maybe set the MERS_LIB_DIR env variable?", &line[4..]),
};
let mut cmd = Command::new(&path_to_executable);
if let Some(parent) = path_to_executable.parent() {
cmd.current_dir(parent.clone());
}
match libs::Lib::launch(cmd, &mut enum_variants) {
Ok(lib) => {
libs.push(lib);
eprintln!("Loaded library!");
}
Err(e) => panic!(
"Unable to load library at {}: {e:?}",
path_to_executable.to_string_lossy().as_ref(),
),
}
} else {
file.set_pos(pos);
break;
}
}
let func = SFunction::new(
vec![(
"args".to_string(),
VSingleType::List(VSingleType::String.into()).into(),
)],
parse_block_advanced(file, Some(false), true, true, false)?,
);
eprintln!();
#[cfg(debug_assertions)]
eprintln!("Parsed: {func}");
#[cfg(debug_assertions)]
eprintln!("Parsed: {func:#?}");
let run = to_runnable::to_runnable(func, GInfo::new(Arc::new(libs), enum_variants))?;
#[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.peek() {
// Some('}') if treat_closed_block_bracket_as_closing_delimeter => break,
Some(')') if treat_closed_normal_bracket_as_closing_delimeter => {
file.next();
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.peek() {
Some('{') => Some(SStatementEnum::Block(parse_block(file)?).into()),
Some('[') => {
file.next();
let mut v = vec![];
let mut list = false;
loop {
file.skip_whitespaces();
if let Some(']') = file.peek() {
file.next();
break;
}
if file[file.get_char_index()..].starts_with("...]") {
list = true;
file.next();
file.next();
file.next();
file.next();
break;
}
v.push(parse_statement(file)?);
}
Some(if list {
SStatementEnum::List(v).into()
} else {
SStatementEnum::Tuple(v).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 match file.peek() {
Some(ch) if matches!(ch, '}' | ']' | ')' | '.') => Some(ch),
_ => file.next(),
} {
Some('=') => {
break parse_statement(file)?.output_to(start.trim().to_string());
}
Some(':') => {
return Ok(SStatement::new(SStatementEnum::EnumVariant(
start,
parse_statement(file)?,
)));
}
Some(ch) if ch.is_whitespace() || matches!(ch, '}' | ']' | ')' | '.') => {
file.skip_whitespaces();
if let Some('=') = file.peek() {
continue;
} else {
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,
}
}
let func = parse_function(file)?;
break SStatementEnum::FunctionDefinition(
Some(fn_name.trim().to_string()),
func,
)
.into();
}
"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 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() {
SStatementEnum::Value(VDataEnum::Int(v).to()).into()
} else if let Ok(v) = start.replace(",", ".").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()
}
}
};
}
}
}
}
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 !file[file.get_char_index()..].starts_with("..") {
// dot chain syntax only works if there is only one dot
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()
}
SStatementEnum::Value(vd) => match vd.data {
VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, i as _).into(),
_ => todo!("fixed-indexing not available with this type."),
},
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)?))
}
pub(crate) fn parse_type(file: &mut File) -> Result<VType, ParseError> {
match parse_type_adv(file, false) {
Ok((v, _)) => Ok(v),
Err(e) => Err(e),
}
}
pub(crate) 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.peek() {
Some('/') => {
file.next();
}
Some(')') => {
closed_fn_args = true;
file.next();
break;
}
Some(_) => break,
None => 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() {
Some('&') => {
let parse_output = parse_single_type_adv(file, in_fn_args)?;
if parse_output.1 {
closed_bracket_in_fn_args = true;
}
VSingleType::Reference(Box::new(parse_output.0))
}
// Tuple or Array
Some('[') => {
let mut types = vec![];
loop {
file.skip_whitespaces();
match file.peek() {
Some(']') => {
file.next();
break;
}
_ => (),
}
types.push(parse_type(file)?);
}
if in_fn_args {
file.skip_whitespaces();
if let Some(')') = file.peek() {
closed_bracket_in_fn_args = true;
file.next();
}
}
if types.len() == 1 {
VSingleType::List(types.pop().unwrap())
} else {
VSingleType::Tuple(types)
}
}
Some(ch) => 'parse_single_type: {
let mut name = ch.to_string();
loop {
match file.peek() {
Some(']') => break,
Some('/') => break,
_ => (),
}
match file.next() {
Some(ch) if ch.is_whitespace() => break,
Some('(') => {
break 'parse_single_type if name.as_str() == "fn" {
todo!("fn types");
} else {
VSingleType::EnumVariantS(name, {
let po = parse_type_adv(file, true)?;
if !po.1 {
// eprintln!("enum type should be closed by ')', but apparently wasn't?");
assert_eq!(file.next(), Some(')'));
}
po.0
})
};
}
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,
_ => {
eprintln!("in_fn_args: {in_fn_args}");
todo!("Err: Invalid type: \"{}\"", name.trim())
}
}
}
None => todo!("Err: EOF in type (1)"),
},
closed_bracket_in_fn_args,
))
}

1153
mers/src/script/block.rs Executable file

File diff suppressed because it is too large Load Diff

1413
mers/src/script/builtins.rs Executable file

File diff suppressed because it is too large Load Diff

5
mers/src/script/mod.rs Executable file
View File

@@ -0,0 +1,5 @@
pub mod block;
pub mod builtins;
pub mod val_data;
pub mod val_type;
pub mod value;

220
mers/src/script/val_data.rs Executable file
View File

@@ -0,0 +1,220 @@
use std::{
fmt::Debug,
sync::{Arc, Mutex},
thread::JoinHandle,
time::Duration,
};
use super::{
block::RFunction,
val_type::{VSingleType, VType},
};
#[derive(Clone, Debug, PartialEq)]
pub struct VData {
// parents: Vec<()>,
pub data: VDataEnum,
}
#[derive(Clone, Debug)]
pub enum VDataEnum {
Bool(bool),
Int(isize),
Float(f64),
String(String),
Tuple(Vec<VData>),
List(VType, Vec<VData>),
Function(RFunction),
Thread(VDataThread, VType),
Reference(Arc<Mutex<VData>>),
EnumVariant(usize, Box<VData>),
}
impl PartialEq for VDataEnum {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Reference(a), Self::Reference(b)) => *a.lock().unwrap() == *b.lock().unwrap(),
(Self::Reference(a), b) => a.lock().unwrap().data == *b,
(a, Self::Reference(b)) => *a == b.lock().unwrap().data,
(Self::Bool(a), Self::Bool(b)) => *a == *b,
(Self::Int(a), Self::Int(b)) => *a == *b,
(Self::Float(a), Self::Float(b)) => *a == *b,
(Self::String(a), Self::String(b)) => *a == *b,
(Self::Tuple(a), Self::Tuple(b)) | (Self::List(_, a), Self::List(_, b)) => {
a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| a == b)
}
(Self::EnumVariant(a1, a2), Self::EnumVariant(b1, b2)) => *a1 == *b1 && *a2 == *b2,
_ => false,
}
}
}
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(t, _) => VSingleType::List(t.clone()),
VDataEnum::Function(f) => VSingleType::Function(f.input_output_map.clone()),
VDataEnum::Thread(_, o) => VSingleType::Thread(o.clone()),
VDataEnum::Reference(r) => r.lock().unwrap().out_single(),
VDataEnum::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out()),
}
}
pub fn get(&self, i: usize) -> Option<Self> {
self.data.get(i)
}
pub fn noenum(self) -> Self {
self.data.noenum()
}
}
impl VDataEnum {
pub fn to(self) -> VData {
VData { data: self }
}
}
// get()
impl VDataEnum {
pub fn noenum(self) -> VData {
match self {
Self::EnumVariant(_, v) => *v,
v => v.to(),
}
}
pub fn get(&self, i: usize) -> Option<VData> {
match self {
Self::Bool(..)
| Self::Int(..)
| Self::Float(..)
| Self::Function(..)
| Self::Thread(..) => None,
Self::String(s) => match s.chars().nth(i) {
// Slow!
Some(ch) => Some(Self::String(format!("{ch}")).to()),
None => None,
},
Self::Tuple(v) | Self::List(_, v) => v.get(i).cloned(),
Self::Reference(r) => r.lock().unwrap().get(i),
Self::EnumVariant(_, v) => v.get(i),
}
}
pub fn matches_ref_bool(&self) -> bool {
match self {
VDataEnum::Tuple(v) => !v.is_empty(),
VDataEnum::Bool(false) => false,
_ => true,
}
}
pub fn matches(self) -> Option<VData> {
match self {
VDataEnum::Tuple(mut tuple) => tuple.pop(),
VDataEnum::Bool(v) => {
if v {
Some(VDataEnum::Bool(v).to())
} else {
None
}
}
VDataEnum::EnumVariant(..) => None,
other => Some(other.to()),
}
}
}
impl VSingleType {
/// returns (can_fail_to_match, matches_as)
pub fn matches(&self) -> (bool, VType) {
match self {
Self::Tuple(v) => match v.first() {
Some(v) => (false, v.clone()),
None => (true, VType { types: vec![] }),
},
Self::Bool => (true, Self::Bool.to()),
Self::EnumVariant(..) | Self::EnumVariantS(..) => (true, VType { types: vec![] }),
v => (false, v.clone().to()),
}
}
}
impl VType {
/// returns (can_fail_to_match, matches_as)
pub fn matches(&self) -> (bool, VType) {
let mut can_fail = false;
let mut matches_as = VType { types: vec![] };
for t in self.types.iter() {
let (f, t) = t.matches();
can_fail |= f;
matches_as = matches_as | t;
}
(can_fail, matches_as)
}
}
#[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(VDataEnum::Bool(false).to()),
);
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)))
}
}

245
mers/src/script/val_type.rs Executable file
View File

@@ -0,0 +1,245 @@
use std::{collections::HashMap, fmt::Debug, ops::BitOr};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VType {
pub types: Vec<VSingleType>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum VSingleType {
Bool,
Int,
Float,
String,
Tuple(Vec<VType>),
List(VType),
Function(Vec<(Vec<VSingleType>, VType)>),
Thread(VType),
Reference(Box<Self>),
EnumVariant(usize, VType),
EnumVariantS(String, VType),
}
impl VSingleType {
// None => Cannot get, Some(t) => getting can return t or nothing
pub fn get(&self, i: usize) -> Option<VType> {
match self {
Self::Bool | Self::Int | Self::Float | Self::Function(..) | Self::Thread(..) => None,
Self::String => Some(VSingleType::String.into()),
Self::Tuple(t) => t.get(i).cloned(),
Self::List(t) => Some(t.clone()),
Self::Reference(r) => r.get(i),
Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get(i),
}
}
}
impl VType {
pub fn get(&self, i: usize) -> Option<VType> {
let mut out = VType { types: vec![] };
for t in &self.types {
out = out | t.get(i)?; // if we can't use *get* on one type, we can't use it at all.
}
Some(out)
}
}
impl VSingleType {
pub fn get_any(&self) -> Option<VType> {
match self {
Self::Bool | Self::Int | Self::Float | Self::Function(..) | Self::Thread(..) => None,
Self::String => Some(VSingleType::String.into()),
Self::Tuple(t) => Some(t.iter().fold(VType { types: vec![] }, |a, b| a | b)),
Self::List(t) => Some(t.clone()),
Self::Reference(r) => r.get_any(),
Self::EnumVariant(_, t) => t.get_any(),
Self::EnumVariantS(..) => unreachable!(),
}
}
}
impl VType {
pub fn get_any(&self) -> Option<VType> {
let mut out = VType { types: vec![] };
for t in &self.types {
out = out | t.get_any()?; // if we can't use *get* on one type, we can't use it at all.
}
Some(out)
}
}
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 t doesnt fit in any of rhs's types
if !rhs.types.iter().any(|r| t.fits_in(r)) {
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.to();
}
}
out
}
pub fn enum_variants(&mut self, enum_variants: &mut HashMap<String, usize>) {
for t in &mut self.types {
t.enum_variants(enum_variants);
}
}
pub fn contains(&self, t: &VSingleType) -> bool {
self.types.contains(t)
}
pub fn noenum(self) -> Self {
let mut o = Self { types: vec![] };
for t in self.types {
o = o | t.noenum();
}
o
}
}
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 }
}
}
impl BitOr<&VType> 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.clone())
}
}
Self { types }
}
}
impl VSingleType {
pub fn to(self) -> VType {
VType { types: vec![self] }
}
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(),
// NOTE: to make ints work in for loops
Self::Int => vec![Self::Int],
_ => vec![],
}
}
pub fn noenum(self) -> VType {
match self {
Self::EnumVariant(_, v) | Self::EnumVariantS(_, v) => v,
v => v.to(),
}
}
pub fn enum_variants(&mut self, enum_variants: &mut HashMap<String, usize>) {
match self {
Self::Bool | Self::Int | Self::Float | Self::String => (),
Self::Tuple(v) => {
for t in v {
t.enum_variants(enum_variants);
}
}
Self::List(t) => t.enum_variants(enum_variants),
Self::Function(f) => {
for f in f {
for t in &mut f.0 {
t.enum_variants(enum_variants);
}
f.1.enum_variants(enum_variants);
}
}
Self::Thread(v) => v.enum_variants(enum_variants),
Self::Reference(v) => v.enum_variants(enum_variants),
Self::EnumVariant(_e, v) => v.enum_variants(enum_variants),
Self::EnumVariantS(e, v) => {
let e = if let Some(e) = enum_variants.get(e) {
*e
} else {
let v = enum_variants.len();
enum_variants.insert(e.clone(), v);
v
};
v.enum_variants(enum_variants);
*self = Self::EnumVariant(e, v.clone());
}
}
}
pub fn fits_in(&self, rhs: &Self) -> bool {
match (self, rhs) {
(Self::Reference(r), Self::Reference(b)) => r.fits_in(b),
(Self::Reference(_), _) | (_, Self::Reference(_)) => false,
(Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => {
*v1 == *v2 && t1.fits_in(&t2).is_empty()
}
(Self::EnumVariant(..), _) | (_, Self::EnumVariant(..)) => false,
(Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => unreachable!(),
(Self::Bool, Self::Bool)
| (Self::Int, Self::Int)
| (Self::Float, Self::Float)
| (Self::String, Self::String) => true,
(Self::Bool | Self::Int | Self::Float | Self::String, _) => false,
(Self::Tuple(a), Self::Tuple(b)) => {
if a.len() == b.len() {
a.iter().zip(b.iter()).all(|(a, b)| a.fits_in(b).is_empty())
} else {
false
}
}
(Self::Tuple(_), _) => false,
(Self::List(a), Self::List(b)) => a.fits_in(b).is_empty(),
(Self::List(_), _) => false,
(Self::Function(a), Self::Function(b)) => 'func_out: {
for a in a {
'search: {
for b in b {
if a.1.fits_in(&b.1).is_empty()
&& a.0.iter().zip(b.0.iter()).all(|(a, b)| *a == *b)
{
break 'search;
}
}
break 'func_out false;
}
}
true
}
(Self::Function(..), _) => false,
(Self::Thread(a), Self::Thread(b)) => a.fits_in(b).is_empty(),
(Self::Thread(..), _) => false,
}
}
}
impl Into<VType> for VSingleType {
fn into(self) -> VType {
VType { types: vec![self] }
}
}

1
mers/src/script/value.rs Executable file
View File

@@ -0,0 +1 @@
// moved to val_data and val_type