mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
almost functional type checking for builtins; builtins return types work
as expected now (for List.get, Thread.await() etc)
This commit is contained in:
parent
5ca0373640
commit
862418ce98
@ -2,6 +2,7 @@
|
|||||||
// 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.
|
// 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::{
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
@ -301,7 +302,11 @@ pub mod to_runnable {
|
|||||||
} else {
|
} else {
|
||||||
// TODO: type-checking for builtins
|
// TODO: type-checking for builtins
|
||||||
if let Some(builtin) = BuiltinFunction::get(v) {
|
if let Some(builtin) = BuiltinFunction::get(v) {
|
||||||
RStatementEnum::BuiltinFunction(builtin, rargs)
|
if builtin.can_take(&rargs.iter().map(|v| v.out()).collect()) {
|
||||||
|
RStatementEnum::BuiltinFunction(builtin, rargs)
|
||||||
|
} else {
|
||||||
|
todo!("ERR: Builtin function with wrong args - this isn't a proper error yet, sorry.");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::UseOfUndefinedFunction(v.clone()));
|
return Err(ToRunnableError::UseOfUndefinedFunction(v.clone()));
|
||||||
}
|
}
|
||||||
@ -516,6 +521,19 @@ impl RFunction {
|
|||||||
})
|
})
|
||||||
.expect("invalid args for function! possible issue with type-checker if this can be reached! feel free to report a bug.")
|
.expect("invalid args for function! possible issue with type-checker if this can be reached! feel free to report a bug.")
|
||||||
}
|
}
|
||||||
|
pub fn out_vt(&self, input_types: &Vec<VType>) -> VType {
|
||||||
|
let mut out = VType { types: vec![] };
|
||||||
|
for (itype, otype) in self.input_output_map.iter() {
|
||||||
|
if itype
|
||||||
|
.iter()
|
||||||
|
.zip(input_types.iter())
|
||||||
|
.all(|(expected, got)| got.contains(expected))
|
||||||
|
{
|
||||||
|
out = out | otype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
pub fn out_all(&self) -> VType {
|
pub fn out_all(&self) -> VType {
|
||||||
self.block.out()
|
self.block.out()
|
||||||
}
|
}
|
||||||
@ -695,10 +713,7 @@ impl RStatementEnum {
|
|||||||
t.clone()
|
t.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::FunctionCall(f, _) => {
|
Self::FunctionCall(f, args) => f.out_vt(&args.iter().map(|v| v.out()).collect()),
|
||||||
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::Block(b) => b.out(),
|
||||||
Self::If(_, a, b) => {
|
Self::If(_, a, b) => {
|
||||||
if let Some(b) = b {
|
if let Some(b) = b {
|
||||||
@ -707,7 +722,28 @@ impl RStatementEnum {
|
|||||||
a.out()
|
a.out()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::While(c) => todo!("while loop output type"),
|
Self::While(c) => {
|
||||||
|
let mut output_types = VType { types: vec![] };
|
||||||
|
for t in c.out().types {
|
||||||
|
match t {
|
||||||
|
VSingleType::Bool => {
|
||||||
|
output_types = output_types | VSingleType::Tuple(vec![]).to();
|
||||||
|
}
|
||||||
|
VSingleType::Tuple(mut t) => {
|
||||||
|
if !t.is_empty() {
|
||||||
|
if t.len() != 1 {
|
||||||
|
unreachable!("while loop with tuple of length {}>1.", t.len());
|
||||||
|
}
|
||||||
|
output_types = output_types | t.pop().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(
|
||||||
|
"while loop statement didn't return bool or 0-1 length tuple."
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output_types
|
||||||
|
}
|
||||||
Self::For(_, _, b) => {
|
Self::For(_, _, b) => {
|
||||||
// returns the return value from the last iteration or nothing if there was no iteration
|
// returns the return value from the last iteration or nothing if there was no iteration
|
||||||
b.out()
|
b.out()
|
||||||
@ -715,11 +751,25 @@ impl RStatementEnum {
|
|||||||
types: vec![VSingleType::Tuple(vec![])],
|
types: vec![VSingleType::Tuple(vec![])],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::BuiltinFunction(f, _) => f.returns(),
|
Self::BuiltinFunction(f, args) => f.returns(args.iter().map(|rs| rs.out()).collect()),
|
||||||
Self::Switch(_, cases) => {
|
Self::Switch(switch_on, cases) => {
|
||||||
|
let switch_on = switch_on.out().types;
|
||||||
|
let mut might_return_empty = switch_on.is_empty();
|
||||||
let mut out = VSingleType::Tuple(vec![]).into(); // if nothing is executed
|
let mut out = VSingleType::Tuple(vec![]).into(); // if nothing is executed
|
||||||
for (_, case) in cases.iter() {
|
for switch_on in switch_on {
|
||||||
out = out | case.out();
|
let switch_on: VType = switch_on.into();
|
||||||
|
'search: {
|
||||||
|
for (on_type, case) in cases.iter() {
|
||||||
|
if switch_on.fits_in(&on_type).is_empty() {
|
||||||
|
out = out | case.out();
|
||||||
|
break 'search;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
might_return_empty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if might_return_empty {
|
||||||
|
out = out | VSingleType::Tuple(vec![]).to();
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
@ -816,7 +866,7 @@ impl Display for VSingleType {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Self::List(t) => write!(f, "[{t}]"),
|
Self::List(t) => write!(f, "[{t}]"),
|
||||||
Self::Function(args, out) => write!(f, "({args:?}) -> {out}"),
|
Self::Function(_) => write!(f, "FUNCTION"),
|
||||||
Self::Thread(_) => write!(f, "THREAD"),
|
Self::Thread(_) => write!(f, "THREAD"),
|
||||||
Self::Reference(r) => write!(f, "&{r}"),
|
Self::Reference(r) => write!(f, "&{r}"),
|
||||||
}
|
}
|
||||||
|
@ -91,8 +91,11 @@ impl BuiltinFunction {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// for invalid inputs, returns VType { types: vec![] }.
|
pub fn can_take(&self, input: &Vec<VType>) -> bool {
|
||||||
pub fn returns(&self, in: Vec<VType>) -> VType {
|
true // TODO!
|
||||||
|
}
|
||||||
|
/// for invalid inputs, may panic
|
||||||
|
pub fn returns(&self, input: Vec<VType>) -> VType {
|
||||||
match self {
|
match self {
|
||||||
// []
|
// []
|
||||||
Self::Print | Self::Println | Self::Debug | Self::Sleep => VType {
|
Self::Print | Self::Println | Self::Debug | Self::Sleep => VType {
|
||||||
@ -101,9 +104,53 @@ impl BuiltinFunction {
|
|||||||
// String
|
// String
|
||||||
Self::ToString | Self::Format => VSingleType::String.into(),
|
Self::ToString | Self::Format => VSingleType::String.into(),
|
||||||
// !
|
// !
|
||||||
Self::Run | Self::Thread | Self::Await | Self::Pop | Self::Remove | Self::Get => {
|
Self::Run | Self::Thread => {
|
||||||
VType { types: vec![] } // TODO!
|
if let Some(funcs) = input.first() {
|
||||||
// unreachable!("this has to be implemented somewhere else!")
|
let mut out = VType { types: vec![] };
|
||||||
|
for func in &funcs.types {
|
||||||
|
if let VSingleType::Function(io) = func {
|
||||||
|
for (i, o) in io {
|
||||||
|
if i.iter()
|
||||||
|
.zip(input.iter().skip(1))
|
||||||
|
.all(|(i, input)| input.contains(i))
|
||||||
|
{
|
||||||
|
out = out | o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!("run called, first arg not a function")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Run => out,
|
||||||
|
Self::Thread => VSingleType::Thread(out).to(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!("run or thread called without args")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Await => {
|
||||||
|
if let Some(v) = input.first() {
|
||||||
|
let mut out = VType { types: vec![] };
|
||||||
|
for v in &v.types {
|
||||||
|
if let VSingleType::Thread(v) = v {
|
||||||
|
out = out | v;
|
||||||
|
} else {
|
||||||
|
unreachable!("await called with non-thread arg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
} else {
|
||||||
|
unreachable!("await called without args")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Pop | Self::Remove | Self::Get => {
|
||||||
|
if let Some(v) = input.first() {
|
||||||
|
v.get_any().expect("cannot use get on this type")
|
||||||
|
} else {
|
||||||
|
unreachable!("get, pop or remove called without args")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Self::Exit => VType { types: vec![] }, // doesn't return
|
Self::Exit => VType { types: vec![] }, // doesn't return
|
||||||
Self::FsList => VType {
|
Self::FsList => VType {
|
||||||
|
@ -27,10 +27,7 @@ impl VData {
|
|||||||
VDataEnum::String(..) => VSingleType::String,
|
VDataEnum::String(..) => VSingleType::String,
|
||||||
VDataEnum::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out()).collect()),
|
VDataEnum::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out()).collect()),
|
||||||
VDataEnum::List(t, _) => VSingleType::List(t.clone()),
|
VDataEnum::List(t, _) => VSingleType::List(t.clone()),
|
||||||
VDataEnum::Function(f) => VSingleType::Function(f.in_types().clone(), {
|
VDataEnum::Function(f) => VSingleType::Function(f.input_output_map.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()),
|
VDataEnum::Thread(_, o) => VSingleType::Thread(o.clone()),
|
||||||
VDataEnum::Reference(r) => r.lock().unwrap().out_single(),
|
VDataEnum::Reference(r) => r.lock().unwrap().out_single(),
|
||||||
}
|
}
|
||||||
@ -153,6 +150,15 @@ impl VSingleType {
|
|||||||
Self::Reference(r) => r.get(i),
|
Self::Reference(r) => r.get(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl VType {
|
impl VType {
|
||||||
pub fn get(&self, i: usize) -> Option<VType> {
|
pub fn get(&self, i: usize) -> Option<VType> {
|
||||||
@ -162,6 +168,13 @@ impl VType {
|
|||||||
}
|
}
|
||||||
Some(out)
|
Some(out)
|
||||||
}
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@ -184,11 +197,14 @@ impl VType {
|
|||||||
let mut out = VType { types: vec![] };
|
let mut out = VType { types: vec![] };
|
||||||
for t in &self.types {
|
for t in &self.types {
|
||||||
for it in t.inner_types() {
|
for it in t.inner_types() {
|
||||||
out = out | it.into();
|
out = out | it.to();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
pub fn contains(&self, t: &VSingleType) -> bool {
|
||||||
|
self.types.contains(t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl BitOr for VType {
|
impl BitOr for VType {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
@ -202,6 +218,18 @@ impl BitOr for VType {
|
|||||||
Self { types }
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum VSingleType {
|
pub enum VSingleType {
|
||||||
@ -211,11 +239,14 @@ pub enum VSingleType {
|
|||||||
String,
|
String,
|
||||||
Tuple(Vec<VType>),
|
Tuple(Vec<VType>),
|
||||||
List(VType),
|
List(VType),
|
||||||
Function(Vec<VType>, VType),
|
Function(Vec<(Vec<VSingleType>, VType)>),
|
||||||
Thread(VType),
|
Thread(VType),
|
||||||
Reference(Box<Self>),
|
Reference(Box<Self>),
|
||||||
}
|
}
|
||||||
impl VSingleType {
|
impl VSingleType {
|
||||||
|
pub fn to(self) -> VType {
|
||||||
|
VType { types: vec![self] }
|
||||||
|
}
|
||||||
pub fn inner_types(&self) -> Vec<VSingleType> {
|
pub fn inner_types(&self) -> Vec<VSingleType> {
|
||||||
match self {
|
match self {
|
||||||
Self::Tuple(v) => {
|
Self::Tuple(v) => {
|
||||||
@ -252,11 +283,20 @@ impl VSingleType {
|
|||||||
(Self::Tuple(_), _) => false,
|
(Self::Tuple(_), _) => false,
|
||||||
(Self::List(a), Self::List(b)) => a.fits_in(b).is_empty(),
|
(Self::List(a), Self::List(b)) => a.fits_in(b).is_empty(),
|
||||||
(Self::List(_), _) => false,
|
(Self::List(_), _) => false,
|
||||||
(Self::Function(ai, ao), Self::Function(bi, bo)) => {
|
(Self::Function(a), Self::Function(b)) => 'func_out: {
|
||||||
ai.iter()
|
for a in a {
|
||||||
.zip(bi.iter())
|
'search: {
|
||||||
.all(|(a, b)| a.fits_in(b).is_empty())
|
for b in b {
|
||||||
&& ao.fits_in(bo).is_empty()
|
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::Function(..), _) => false,
|
||||||
(Self::Thread(a), Self::Thread(b)) => a.fits_in(b).is_empty(),
|
(Self::Thread(a), Self::Thread(b)) => a.fits_in(b).is_empty(),
|
||||||
|
Loading…
Reference in New Issue
Block a user