mirror of
https://github.com/Dummi26/mers.git
synced 2025-12-20 14:06:30 +01:00
initial code commit, implemented switch statements and if-else syntax.
This commit is contained in:
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] }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user