- Added assume_no_enum() because the Err enum is used at least as often as [] for reporting fails.

- changed substring(a b) behavior from "b is the max length of the resulting string" to "b is the exclusive end index, unless it is negative, in which case its abs() value is the maximum length".
- fixed a bug in libs/path
- added the http_requests library, which can be used to make very basic GET requests
- fixed a bug in the gui library that would mess up button handling
- you can now escape comments using a backslash `\`: \// will turn into the literal //, \/* will turn into the literal /*. Useful for URLs (because comments work in string literals). Putting a backslash before a linebreak will also ignore that linebreak (useful in long string literals)
This commit is contained in:
Dummi26
2023-04-13 03:04:47 +02:00
parent 2acdcd3f53
commit ce61749260
15 changed files with 1600 additions and 179 deletions

View File

@@ -5,10 +5,7 @@ use std::{
use crate::{
libs::DirectReader,
script::{
val_data::{VData, VDataEnum},
val_type::VType,
},
script::{val_data::VData, val_type::VType},
};
use super::{data_from_bytes, data_to_bytes};
@@ -38,8 +35,8 @@ impl MyLib {
MyLibTaskCompletion { _priv: () },
)
}
pub fn get_enum(&self, e: &str) -> usize {
*self.enum_variants.get(e).unwrap()
pub fn get_enum(&self, e: &str) -> Option<usize> {
self.enum_variants.get(e).map(|v| *v)
}
pub fn run<I, O>(
&mut self,
@@ -84,19 +81,27 @@ impl MyLib {
}
'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);
loop {
line.clear();
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!(),
}
} else {
match line.trim_end() {
"init_finished" => break,
_ => unreachable!(),
}
_ => todo!(),
}
}
None
Some(MyLibTask::FinishedInit(MyLibTaskCompletion { _priv: () }))
}
'f' => {
let fnid = stdin.one_byte().unwrap() as usize;
@@ -119,6 +124,7 @@ impl MyLib {
pub enum MyLibTask {
None(MyLibTaskCompletion),
FinishedInit(MyLibTaskCompletion),
RunFunction(MyLibTaskRunFunction),
}
pub struct MyLibTaskRunFunction {

View File

@@ -32,7 +32,8 @@ the identifying ascii chars:
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)
can send some tasks,
must end with a line saying 'init_finished'.
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"
@@ -124,9 +125,11 @@ impl Lib {
_ => todo!(),
}
}
write!(stdin, "I").unwrap();
for (enum_name, enum_id) in enum_variants.iter() {
writeln!(stdin, "Iset_enum_id {enum_name} {enum_id}").unwrap();
writeln!(stdin, "set_enum_id {enum_name} {enum_id}").unwrap();
}
writeln!(stdin, "init_finished").unwrap();
Ok(Self {
process: handle,
stdin: Arc::new(Mutex::new(stdin)),

View File

@@ -10,7 +10,6 @@ pub fn path_from_string(path: &str, script_directory: &PathBuf) -> Option<PathBu
.unwrap_or_else(|_| script_directory.clone())
.parent()
{
eprintln!("Parent: {:?}", p);
let p = p.join(&path);
if p.exists() {
return Some(p);

View File

@@ -32,6 +32,18 @@ impl File {
let mut data = String::with_capacity(data.len());
loop {
match chs.next() {
Some('\\') => match chs.next() {
// backslash can escape these characters:
Some('\n') => data.push('\\'),
// backshash invalidates comments, so \// will just be //.
Some('/') => data.push('/'),
// backslash does nothing otherwise.
Some(ch) => {
data.push('\\');
data.push(ch);
}
None => data.push('\\'),
},
Some('/') => match chs.next() {
Some('/') => loop {
match chs.next() {

View File

@@ -20,6 +20,7 @@ pub const EVS: [&'static str; 1] = ["Err"];
pub enum BuiltinFunction {
// core
Assume1, // assume []/[t] is [t], return t. Optionally provide a reason as to why (2nd arg)
AssumeNoEnum, // assume enum(*)/t is t.
NoEnum,
Matches,
// print
@@ -83,6 +84,7 @@ impl BuiltinFunction {
pub fn get(s: &str) -> Option<Self> {
Some(match s {
"assume1" => Self::Assume1,
"assume_no_enum" => Self::AssumeNoEnum,
"noenum" => Self::NoEnum,
"matches" => Self::Matches,
"print" => Self::Print,
@@ -151,7 +153,7 @@ impl BuiltinFunction {
}
}
if !len0 {
eprintln!("Warn: calling assume1 on a value of type {}, which will never be a length-0 tuple and therefore will not cannot fail.", input[0]);
eprintln!("Warn: calling assume1 on a value of type {}, which will never be a length-0 tuple and therefore cannot fail.", input[0]);
}
if !len1 {
eprintln!("Warn: calling assume1 on a value of type {}, which will always be a length-0 tuple!", input[0]);
@@ -169,6 +171,37 @@ impl BuiltinFunction {
false
}
}
Self::AssumeNoEnum => {
if input.len() >= 1 {
let mut someenum = false;
let mut noenum = false;
for t in input[0].types.iter() {
match t {
VSingleType::EnumVariant(..) | VSingleType::EnumVariantS(..) => {
someenum = true
}
_ => noenum = true,
}
}
if !someenum {
eprintln!("Warn: calling assume_no_enum on a value of type {}, which will never be an enum and therefore cannot fail.", input[0]);
}
if !noenum {
eprintln!("Warn: calling assume_no_enum on a value of type {}, which will always be an enum!", input[0]);
}
if input.len() >= 2 {
if input.len() == 2 {
input[1].fits_in(&VSingleType::String.to()).is_empty()
} else {
false
}
} else {
true
}
} else {
false
}
}
Self::NoEnum => input.len() == 1,
Self::Matches => input.len() == 1,
Self::Print | Self::Println => {
@@ -341,6 +374,16 @@ impl BuiltinFunction {
}
out
}
Self::AssumeNoEnum => {
let mut out = VType { types: vec![] };
for t in &input[0].types {
match t {
VSingleType::EnumVariant(..) | VSingleType::EnumVariantS(..) => (),
t => out = out | t.clone().to(),
}
}
out
}
Self::NoEnum => input[0].clone().noenum(),
Self::Matches => input[0].matches().1,
// []
@@ -557,12 +600,31 @@ impl BuiltinFunction {
}
} else {
String::new()
}
},
);
}
}
v => v.to(),
},
Self::AssumeNoEnum => {
let data = args[0].run(vars, libs);
match data.data {
VDataEnum::EnumVariant(..) => panic!(
"ASSUMPTION FAILED: assume_no_enum :: found {} :: {}",
data,
if args.len() > 1 {
if let VDataEnum::String(v) = args[1].run(vars, libs).data {
v
} else {
String::new()
}
} else {
String::new()
}
),
d => d.to(),
}
}
Self::NoEnum => args[0].run(vars, libs).noenum(),
Self::Matches => match args[0].run(vars, libs).data.matches() {
Some(v) => VDataEnum::Tuple(vec![v]).to(),
@@ -1232,25 +1294,32 @@ impl BuiltinFunction {
}
Self::Get => {
if args.len() == 2 {
if let (VDataEnum::Reference(v), VDataEnum::Int(i)) =
if let (container, VDataEnum::Int(i)) =
(args[0].run(vars, libs).data, args[1].run(vars, libs).data)
{
if let VDataEnum::List(_, v) | VDataEnum::Tuple(v) =
&mut v.lock().unwrap().data
{
if i >= 0 {
match v.get(i as usize) {
Some(v) => VDataEnum::Tuple(vec![v.clone()]).to(),
None => VDataEnum::Tuple(vec![]).to(),
if i >= 0 {
match match container {
VDataEnum::Reference(v) => match &v.lock().unwrap().data {
VDataEnum::List(_, v) | VDataEnum::Tuple(v) => {
v.get(i as usize).map(|v| v.clone())
}
_ => unreachable!(
"get: reference to something other than list/tuple"
),
},
VDataEnum::List(_, v) | VDataEnum::Tuple(v) => {
v.get(i as usize).map(|v| v.clone())
}
} else {
VDataEnum::Tuple(vec![]).to()
_ => unreachable!("get: not a reference/list/tuple"),
} {
Some(v) => VDataEnum::Tuple(vec![v]).to(),
None => VDataEnum::Tuple(vec![]).to(),
}
} else {
unreachable!("get: not a list/tuple")
VDataEnum::Tuple(vec![]).to()
}
} else {
unreachable!("get: not a reference and index")
unreachable!("get: not a list/tuple/reference and index")
}
} else {
unreachable!("get: not 2 args")
@@ -1347,12 +1416,21 @@ impl BuiltinFunction {
};
let left = if left >= 0 { left as usize } else { 0 };
if let Some(len) = len {
let len = if len >= 0 {
len as usize
if len >= 0 {
VDataEnum::String(
a.chars()
.skip(left)
.take((len as usize).saturating_sub(left))
.collect(),
)
.to()
} else {
todo!("negative len - shorthand for backwards? not sure yet...")
};
VDataEnum::String(a.chars().skip(left).take(len).collect()).to()
// negative end index => max length
VDataEnum::String(
a.chars().skip(left).take(len.abs() as usize).collect(),
)
.to()
}
} else {
VDataEnum::String(a.chars().skip(left).collect()).to()
}