added custom types that can be defined using 'type <name> <type>'

This commit is contained in:
mark 2023-04-29 19:04:38 +02:00
parent 8bafe58593
commit ef5874747e
11 changed files with 285 additions and 108 deletions

19
custom_type.mers Normal file
View File

@ -0,0 +1,19 @@
// linked list
type elem [int []/elem]
fn print_linked_list(start elem) {
loop {
println(start.0.to_string())
elem = start.1
switch! elem {
[] {
println("[END]")
true // break
}
elem start = elem // continue
}
}
[]
}
[1 [2 [3 [5 [7 []]]]]].print_linked_list()

View File

@ -33,7 +33,7 @@ println(greeting)
println!(" - - - - -");
println!("{}", output);
}
Err(e) => println!("{}", e.with_file(&file)),
Err(e) => println!("{}", e.0.with_file_and_gsinfo(&file, e.1.as_ref())),
}
} else {
println!("can't read file at {:?}!", temp_file);

View File

@ -125,7 +125,7 @@ fn main() {
println!("Output ({}s)\n{out}", elapsed.as_secs_f64());
}
Err(e) => {
println!("Couldn't compile:\n{e}");
println!("Couldn't compile:\n{}", e.0.with_file_and_gsinfo(&file, e.1.as_ref()));
std::process::exit(99);
}
}

View File

@ -6,7 +6,7 @@ use crate::{
code_macro::MacroError,
code_parsed::*,
code_runnable::RScript,
global_info::{GlobalScriptInfo},
global_info::{GlobalScriptInfo, GSInfo},
to_runnable::{self, ToRunnableError},
val_data::VDataEnum,
val_type::{VSingleType, VType},
@ -52,20 +52,50 @@ impl std::fmt::Display for ScriptError {
}
}
pub struct ScriptErrorWithFile<'a>(&'a ScriptError, &'a File);
pub struct ScriptErrorWithInfo<'a>(&'a ScriptError, &'a GlobalScriptInfo);
pub struct ScriptErrorWithFileAndInfo<'a>(&'a ScriptError, &'a File, &'a GlobalScriptInfo);
impl<'a> ScriptError {
pub fn with_file(&'a self, file: &'a File) -> ScriptErrorWithFile {
ScriptErrorWithFile(self, file)
}
pub fn with_gsinfo(&'a self, info: &'a GlobalScriptInfo) -> ScriptErrorWithInfo {
ScriptErrorWithInfo(self, info)
}
pub fn with_file_and_gsinfo(&'a self, file: &'a File, info: &'a GlobalScriptInfo) -> ScriptErrorWithFileAndInfo {
ScriptErrorWithFileAndInfo(self, file, info)
}
}
impl<'a> std::fmt::Display for ScriptErrorWithFile<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
self.0.fmt_custom(f, Some(self.1), None)
}
}
impl<'a> std::fmt::Display for ScriptErrorWithInfo<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt_custom(f, None, Some(self.1))
}
}
impl<'a> std::fmt::Display for ScriptErrorWithFileAndInfo<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt_custom(f, Some(self.1), Some(self.2))
}
}
impl ScriptError {
fn fmt_custom(&self, f: &mut std::fmt::Formatter<'_>, file: Option<&File>, info: Option<&GlobalScriptInfo>) -> std::fmt::Result {
match &self {
ScriptError::CannotFindPathForLibrary(e) => write!(f, "{e}"),
ScriptError::ParseError(e) => {
write!(f, "failed while parsing: {}", e.with_file(self.1))
write!(f, "failed while parsing: ")?;
e.fmt_custom(f, file)?;
Ok(())
}
ScriptError::UnableToLoadLibrary(e) => write!(f, "{e}"),
ScriptError::ToRunnableError(e) => write!(f, "failed to compile: {e}"),
ScriptError::ToRunnableError(e) => {
write!(f, "failed to compile: ")?;
e.fmtgs(f, info);
Ok(())
}
}
}
}
@ -73,13 +103,28 @@ impl<'a> std::fmt::Display for ScriptErrorWithFile<'a> {
pub const PARSE_VERSION: u64 = 0;
/// executes the 4 parse_steps in order: lib_paths => interpret => libs_load => compile
pub fn parse(file: &mut File) -> Result<RScript, ScriptError> {
pub fn parse(file: &mut File) -> Result<RScript, (ScriptError, GSInfo)> {
let mut ginfo = GlobalScriptInfo::default();
let libs = parse_step_lib_paths(file)?;
let func = parse_step_interpret(file)?;
ginfo.libs = parse_step_libs_load(libs, &mut ginfo)?;
let run = parse_step_compile(func, ginfo)?;
let libs = match parse_step_lib_paths(file) {
Ok(v) => v,
Err(e) => return Err((e.into(), ginfo.to_arc())),
};
let func = match parse_step_interpret(file) {
Ok(v) => v,
Err(e) => return Err((e.into(), ginfo.to_arc())),
};
ginfo.libs = match parse_step_libs_load(libs, &mut ginfo) {
Ok(v) => v,
Err(e) => return Err((e.into(), ginfo.to_arc())),
};
let run = match parse_step_compile(func, ginfo) {
Ok(v) => v,
Err(e) => return Err((e.0.into(), e.1)),
};
Ok(run)
}
@ -153,7 +198,7 @@ pub fn parse_step_libs_load(
Ok(libs)
}
pub fn parse_step_compile(main_func: SFunction, ginfo: GlobalScriptInfo) -> Result<RScript, ToRunnableError> {
pub fn parse_step_compile(main_func: SFunction, ginfo: GlobalScriptInfo) -> Result<RScript, (ToRunnableError, GSInfo)> {
to_runnable::to_runnable(main_func, ginfo)
}
@ -686,6 +731,10 @@ pub mod implementation {
}
break SStatementEnum::Match(match_what, cases).to();
}
"type" => {
file.skip_whitespaces();
break SStatementEnum::TypeDefinition(file.collect_to_whitespace(), parse_type(file)?).to();
}
"true" => break SStatementEnum::Value(VDataEnum::Bool(true).to()).to(),
"false" => {
break SStatementEnum::Value(VDataEnum::Bool(false).to()).to()

View File

@ -278,41 +278,41 @@ impl BuiltinFunction {
&& input[0]
.fits_in(&VType {
types: vec![VSingleType::Int, VSingleType::Float],
})
}, info)
.is_empty()
}
Self::Exit => {
input.len() == 0
|| (input.len() == 1 && input[0].fits_in(&VSingleType::Int.to()).is_empty())
|| (input.len() == 1 && input[0].fits_in(&VSingleType::Int.to(), info).is_empty())
}
// TODO!
Self::FsList => true,
Self::FsRead => {
input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty()
input.len() == 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty()
}
Self::FsWrite => {
input.len() == 2
&& input[0].fits_in(&VSingleType::String.to()).is_empty()
&& input[0].fits_in(&VSingleType::String.to(), info).is_empty()
&& input[1]
.fits_in(&VSingleType::List(VSingleType::Int.to()).to())
.fits_in(&VSingleType::List(VSingleType::Int.to()).to(), info)
.is_empty()
}
Self::BytesToString => {
input.len() == 1
&& input[0]
.fits_in(&VSingleType::List(VSingleType::Int.to()).to())
.fits_in(&VSingleType::List(VSingleType::Int.to()).to(), info)
.is_empty()
}
Self::StringToBytes => {
input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty()
input.len() == 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty()
}
Self::RunCommand | Self::RunCommandGetBytes => {
if input.len() >= 1 && input[0].fits_in(&VSingleType::String.to()).is_empty() {
if input.len() >= 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty() {
if input.len() == 1 {
true
} else if input.len() == 2 {
input[1]
.fits_in(&VSingleType::List(VSingleType::String.to()).to())
.fits_in(&VSingleType::List(VSingleType::String.to()).to(), info)
.is_empty()
} else {
false
@ -328,16 +328,16 @@ impl BuiltinFunction {
types: vec![VSingleType::Int, VSingleType::Float],
};
let st = VSingleType::String.to();
(input[0].fits_in(&num).is_empty() && input[1].fits_in(&num).is_empty())
|| (input[0].fits_in(&st).is_empty() && input[1].fits_in(&st).is_empty())
(input[0].fits_in(&num, info).is_empty() && input[1].fits_in(&num, info).is_empty())
|| (input[0].fits_in(&st, info).is_empty() && input[1].fits_in(&st, info).is_empty())
}
}
Self::Not => input.len() == 1 && input[0].fits_in(&VSingleType::Bool.to()).is_empty(),
Self::Not => input.len() == 1 && input[0].fits_in(&VSingleType::Bool.to(), info).is_empty(),
Self::And | Self::Or => {
input.len() == 2
&& input
.iter()
.all(|v| v.fits_in(&VSingleType::Bool.to()).is_empty())
.all(|v| v.fits_in(&VSingleType::Bool.to(), info).is_empty())
}
Self::Sub
| Self::Mul
@ -354,7 +354,7 @@ impl BuiltinFunction {
let num = VType {
types: vec![VSingleType::Int, VSingleType::Float],
};
input[0].fits_in(&num).is_empty() && input[1].fits_in(&num).is_empty()
input[0].fits_in(&num, info).is_empty() && input[1].fits_in(&num, info).is_empty()
}
}
// TODO! check that we pass a reference to a list!
@ -365,7 +365,7 @@ impl BuiltinFunction {
// if vec.is_reference().is_some_and(|v| v) { // unstable
if let Some(true) = vec.is_reference() {
if let Some(t) = vec.get_any(info) {
el.fits_in(&t).is_empty()
el.fits_in(&t, info).is_empty()
} else {
false
}
@ -381,7 +381,7 @@ impl BuiltinFunction {
let (vec, el) = (&input[0], &input[1]);
if let Some(true) = vec.is_reference() {
if let Some(t) = vec.get_any(info) {
el.fits_in(&t).is_empty()
el.fits_in(&t, info).is_empty()
} else {
false
}
@ -415,7 +415,7 @@ impl BuiltinFunction {
if let Some(true) = vec.is_reference() {
// TODO! same issue as in pop
if let Some(_) = vec.get_any(info) {
if index.fits_in(&VSingleType::Int.to()).is_empty() {
if index.fits_in(&VSingleType::Int.to(), info).is_empty() {
true
} else {
false
@ -436,11 +436,11 @@ impl BuiltinFunction {
if input.len() >= 2 && input.len() <= 3 {
let (s, start) = (&input[0], &input[1]);
let index_type = VSingleType::Int.to();
if s.fits_in(&VSingleType::String.to()).is_empty()
&& start.fits_in(&index_type).is_empty()
if s.fits_in(&VSingleType::String.to(), info).is_empty()
&& start.fits_in(&index_type, info).is_empty()
{
if let Some(end) = input.get(2) {
end.fits_in(&index_type).is_empty()
end.fits_in(&index_type, info).is_empty()
} else {
true
}
@ -456,7 +456,7 @@ impl BuiltinFunction {
input.len() == 2
&& input
.iter()
.all(|v| v.fits_in(&VSingleType::String.to()).is_empty())
.all(|v| v.fits_in(&VSingleType::String.to(), info).is_empty())
}
// two strings or &strings
Self::IndexOf => {
@ -467,12 +467,12 @@ impl BuiltinFunction {
VSingleType::String,
VSingleType::Reference(Box::new(VSingleType::String)),
],
})
}, info)
.is_empty()
})
}
Self::Trim => {
input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty()
input.len() == 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty()
}
}
}

View File

@ -43,7 +43,7 @@ fn parse_mers_code(file: &mut File) -> Result<RScript, MacroError> {
_ = file.next();
match parse::parse(file) {
Ok(v) => Ok(v),
Err(e) => Err(e.into()),
Err(e) => Err(e.0.into()),
}
} else {
let path = parse_string_val(file);
@ -56,7 +56,10 @@ fn parse_mers_code(file: &mut File) -> Result<RScript, MacroError> {
.expect("can't include mers code because the file could not be read"),
path.into(),
);
Ok(parse::parse(&mut file)?)
Ok(match parse::parse(&mut file) {
Ok(v) => v,
Err(e) => return Err(e.0.into()),
})
}
}

View File

@ -17,6 +17,7 @@ pub enum SStatementEnum {
Match(String, Vec<(SStatement, SStatement)>),
IndexFixed(SStatement, usize),
EnumVariant(String, SStatement),
TypeDefinition(String, VType),
Macro(Macro),
}
impl SStatementEnum {
@ -173,6 +174,7 @@ impl SStatementEnum {
write!(f, "{variant}: ")?;
inner.fmtgs(f, info)
}
Self::TypeDefinition(name, t) => write!(f, "type {name} {t}"),
Self::Macro(m) => {
write!(f, "!({m})")
}

View File

@ -260,7 +260,7 @@ impl RStatementEnum {
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() {
if switch_on_type.fits_in(case_type, info).is_empty() {
out = case_action.run(vars, info);
break;
}
@ -329,7 +329,7 @@ impl RStatementEnum {
let switch_on = switch_on.to();
'search: {
for (on_type, case) in cases.iter() {
if switch_on.fits_in(&on_type).is_empty() {
if switch_on.fits_in(&on_type, info).is_empty() {
out = out | case.out(info);
break 'search;
}

View File

@ -18,13 +18,14 @@ use super::{
builtins::BuiltinFunction,
code_macro::Macro,
code_parsed::{SBlock, SFunction, SStatement, SStatementEnum},
code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum},
code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, global_info::GSInfo,
};
pub enum ToRunnableError {
MainWrongInput,
UseOfUndefinedVariable(String),
UseOfUndefinedFunction(String),
UnknownType(String),
CannotDeclareVariableWithDereference(String),
CannotDereferenceTypeNTimes(VType, usize, VType),
FunctionWrongArgCount(String, usize, usize),
@ -51,6 +52,11 @@ impl Debug for ToRunnableError {
// - Show location in code where the error was found
impl Display for ToRunnableError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmtgs(f, None)
}
}
impl ToRunnableError {
pub fn fmtgs(&self, f: &mut std::fmt::Formatter, info: Option<&GlobalScriptInfo>) -> std::fmt::Result {
match self {
Self::MainWrongInput => write!(
f,
@ -58,41 +64,77 @@ impl Display for ToRunnableError {
),
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::UnknownType(name) => write!(f, "Unknown type \"{name}\"."),
Self::CannotDeclareVariableWithDereference(v) => write!(f, "Cannot declare a variable and dereference it (variable '{v}')."),
Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => write!(f,
"Cannot dereference type {og_type} {derefs_wanted} times (stopped at {last_valid_type})."
),
Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => {
write!(f, "Cannot dereference type ")?;
og_type.fmtgs(f, info)?;
write!(f, "{derefs_wanted} times (stopped at ")?;
last_valid_type.fmtgs(f, info);
write!(f, ")")?;
Ok(())
},
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.")
write!(f, "Invalid type: Expected ")?;
expected.fmtgs(f, info)?;
write!(f, " but found ")?;
found.fmtgs(f, info)?;
write!(f, ", which includes ")?;
problematic.fmtgs(f, info)?;
write!(f, " which is not covered.")?;
Ok(())
}
Self::CaseForceButTypeNotCovered(v) => {
write!(f, "Switch! statement, but not all types covered. Types to cover: ")?;
v.fmtgs(f, info)?;
Ok(())
}
Self::MatchConditionInvalidReturn(v) => {
write!(f, "match statement condition returned ")?;
v.fmtgs(f, info)?;
write!(f, ", which is not necessarily a tuple of size 0 to 1.")?;
Ok(())
}
Self::NotIndexableFixed(t, i) => {
write!(f, "Cannot use fixed-index {i} on type ")?;
t.fmtgs(f, info)?;
write!(f, ".")?;
Ok(())
}
Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"),
Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."),
Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."),
Self::WrongInputsForBuiltinFunction(_builtin, builtin_name, args) => {
write!(f, "Wrong arguments for builtin function {}:", builtin_name)?;
write!(f, "Wrong arguments for builtin function \"{}\":", builtin_name)?;
for arg in args {
write!(f, " {arg}")?;
write!(f, " ")?;
arg.fmtgs(f, info)?;
}
write!(f, ".")
}
Self::WrongArgsForLibFunction(name, args) => {
write!(f, "Wrong arguments for library function {}:", name)?;
for arg in args {
write!(f, " {arg}")?;
write!(f, " ")?;
arg.fmtgs(f, info)?;
}
write!(f, ".")
}
Self::ForLoopContainerHasNoInnerTypes => {
write!(f, "For loop: container had no inner types, cannot iterate.")
}
Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => write!(f,
"the statement requires its output type to be {required}, but its real output type is {real}, which doesn not fit in the required type because of the problematic types {problematic}."
),
Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => {
write!(f, "the statement requires its output type to be ")?;
required.fmtgs(f, info)?;
write!(f, ", but its real output type is ")?;
real.fmtgs(f, info)?;
write!(f, ", which doesn't fit in the required type because of the problematic types ")?;
problematic.fmtgs(f, info)?;
write!(f, ".")?;
Ok(())
}
}
}
}
@ -104,9 +146,9 @@ struct LInfo {
fns: HashMap<String, Arc<RFunction>>,
}
pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result<RScript, ToRunnableError> {
pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result<RScript, (ToRunnableError, GSInfo)> {
if s.inputs.len() != 1 || s.inputs[0].0 != "args" {
return Err(ToRunnableError::MainWrongInput);
return Err((ToRunnableError::MainWrongInput, ginfo.to_arc()));
}
assert_eq!(
s.inputs[0].1,
@ -116,18 +158,25 @@ pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result<RScript,
})],
}
);
let func = function(
let func = match function(
&s,
&mut ginfo,
LInfo {
vars: HashMap::new(),
fns: HashMap::new(),
},
)?;
Ok(RScript::new(
) {
Ok(v) => v,
Err(e) => return Err((e, ginfo.to_arc())),
};
let ginfo = ginfo.to_arc();
match RScript::new(
func,
ginfo.to_arc(),
)?)
ginfo.clone(),
) {
Ok(v) => Ok(v),
Err(e) => Err((e, ginfo)),
}
}
// go over every possible known-type input for the given function, returning all possible RFunctions.
@ -142,7 +191,9 @@ fn get_all_functions(
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());
let mut t = t.clone();
stype(&mut t, ginfo)?;
inputs.push(t);
get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?;
inputs.pop();
}
@ -150,7 +201,11 @@ fn get_all_functions(
} 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();
linfo.vars.get_mut(&varid.0).unwrap().1 = {
let mut vartype = vartype.clone();
stype(&mut vartype, ginfo)?;
vartype.to()
}
}
out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out(ginfo)));
Ok(())
@ -164,11 +219,13 @@ fn function(
let mut input_vars = vec![];
let mut input_types = vec![];
for (iname, itype) in &s.inputs {
let mut itype = itype.to_owned();
stypes(&mut itype, ginfo)?;
linfo
.vars
.insert(iname.clone(), (ginfo.vars, itype.clone()));
input_vars.push(ginfo.vars);
input_types.push(itype.clone());
input_types.push(itype);
ginfo.vars += 1;
}
let mut all_outs = vec![];
@ -200,18 +257,23 @@ fn block(s: &SBlock, ginfo: &mut GlobalScriptInfo, mut linfo: LInfo) -> Result<R
Ok(RBlock { statements })
}
fn stypes(t: &mut VType, ginfo: &mut GlobalScriptInfo) {
fn stypes(t: &mut VType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> {
for t in &mut t.types {
stype(t, ginfo);
stype(t, ginfo)?;
}
Ok(())
}
fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) {
fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> {
match t {
VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String => (),
VSingleType::Tuple(v) => {
for t in v {
stypes(t, ginfo);
stypes(t, ginfo)?;
}
}
VSingleType::List(t) => stypes(t, ginfo)?,
VSingleType::Reference(t) => stype(t, ginfo)?,
VSingleType::Thread(t) => stypes(t, ginfo)?,
VSingleType::EnumVariantS(e, v) => {
*t = VSingleType::EnumVariant(
{
@ -224,13 +286,30 @@ fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) {
}
},
{
stypes(v, ginfo);
stypes(v, ginfo)?;
v.clone()
},
)
}
_ => (),
VSingleType::Function(io_map) => {
for io_variant in io_map {
for i in &mut io_variant.0 {
stype(i, ginfo)?;
}
stypes(&mut io_variant.1, ginfo)?;
}
}
VSingleType::EnumVariant(_, t) => stypes(t, ginfo)?,
VSingleType::CustomTypeS(name) => {
*t = VSingleType::CustomType(if let Some(v) = ginfo.custom_type_names.get(name) {
*v
} else {
return Err(ToRunnableError::UnknownType(name.to_owned()));
})
}
VSingleType::CustomType(_) => ()
}
Ok(())
}
fn statement(
s: &SStatement,
@ -253,7 +332,7 @@ fn statement(
SStatementEnum::Variable(v, is_ref) => {
if let Some(var) = linfo.vars.get(v) {
RStatementEnum::Variable(var.0, {
let mut v = var.1.clone(); stypes(&mut v, ginfo); v }, *is_ref)
let mut v = var.1.clone(); stypes(&mut v, ginfo)?; v }, *is_ref)
} else {
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
}
@ -273,7 +352,7 @@ fn statement(
}
for (i, rarg) in rargs.iter().enumerate() {
let rarg = rarg.out(ginfo);
let out = rarg.fits_in(&func.input_types[i]);
let out = rarg.fits_in(&func.input_types[i], ginfo);
if !out.is_empty() {
return Err(ToRunnableError::InvalidType {
expected: func.input_types[i].clone(),
@ -296,7 +375,7 @@ fn statement(
// LIBRARY FUNCTION?
if let Some((libid, fnid)) = ginfo.lib_fns.get(v) {
let (_name, fn_in, fn_out) = &ginfo.libs[*libid].registered_fns[*fnid];
if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out(ginfo).fits_in(fn_in).is_empty()) {
if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out(ginfo).fits_in(fn_in, ginfo).is_empty()) {
RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone())
} else {
// TODO! better error here
@ -332,7 +411,7 @@ fn statement(
let condition = statement(&c, ginfo, linfo)?;
let out = condition.out(ginfo).fits_in(&VType {
types: vec![VSingleType::Bool],
});
}, ginfo);
if out.is_empty() {
condition
} else {
@ -374,7 +453,7 @@ fn statement(
let mut ncases = Vec::with_capacity(cases.len());
let og_type = switch_on_v.1.clone(); // linfo.vars.get(switch_on).unwrap().1.clone();
for case in cases {
let case0 = { let mut v = case.0.clone(); stypes(&mut v, ginfo); v };
let case0 = { let mut v = case.0.clone(); stypes(&mut v, ginfo)?; v };
linfo.vars.get_mut(switch_on).unwrap().1 = case0.clone();
ncases.push((case0, statement(&case.1, ginfo, linfo)?));
}
@ -391,8 +470,8 @@ fn statement(
'force: {
for (case_type, _) in cases {
let mut ct = case_type.clone();
stypes(&mut ct, ginfo);
if val_type.fits_in(&ct).is_empty() {
stypes(&mut ct, ginfo)?;
if val_type.fits_in(&ct, ginfo).is_empty() {
break 'force;
}
}
@ -493,23 +572,7 @@ fn statement(
SStatementEnum::IndexFixed(st, i) => {
let st = statement(st, ginfo, linfo)?;
let ok = 'ok: {
let mut one = false;
for t in st.out(ginfo).types {
one = true;
// only if all types are indexable by i
match t {
VSingleType::Tuple(v) => {
if v.len() <= *i {
break 'ok false;
}
}
_ => break 'ok false,
}
}
one
};
if ok {
if st.out(ginfo).get_always(*i, ginfo).is_some() {
RStatementEnum::IndexFixed(st, *i)
} else {
return Err(ToRunnableError::NotIndexableFixed(st.out(ginfo), *i));
@ -524,6 +587,14 @@ fn statement(
v
}
}, statement(s, ginfo, linfo)?),
SStatementEnum::TypeDefinition(name, t) => {
// insert to name map has to happen before stypes()
ginfo.custom_type_names.insert(name.to_owned(), ginfo.custom_types.len());
let mut t = t.to_owned();
stypes(&mut t, ginfo)?;
ginfo.custom_types.push(t);
RStatementEnum::Value(VDataEnum::Tuple(vec![]).to())
}
SStatementEnum::Macro(m) => match m {
Macro::StaticMers(val) => RStatementEnum::Value(val.clone()),
},
@ -531,10 +602,12 @@ fn statement(
.to();
// if force_output_type is set, verify that the real output type actually fits in the forced one.
if let Some(force_opt) = &s.force_output_type {
let mut force_opt = force_opt.to_owned();
stypes(&mut force_opt, ginfo)?;
let real_output_type = statement.out(ginfo);
let problematic_types = real_output_type.fits_in(force_opt);
let problematic_types = real_output_type.fits_in(&force_opt, ginfo);
if problematic_types.is_empty() {
statement.force_output_type = Some(force_opt.clone());
statement.force_output_type = Some(force_opt);
} else {
return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types }));
}
@ -554,7 +627,7 @@ fn statement(
));
}
}
let inv_types = out.fits_in(&var_derefd);
let inv_types = out.fits_in(&var_derefd, ginfo);
if !inv_types.is_empty() {
eprintln!("Warn: shadowing variable {opt} because statement's output type {out} does not fit in the original variable's {var_out}. This might become an error in the future, or it might stop shadowing the variiable entirely - for stable scripts, avoid this by giving the variable a different name.");
if *derefs != 0 {

View File

@ -4,7 +4,7 @@ use std::{
ops::BitOr,
};
use super::global_info::{GlobalScriptInfo, GSInfo};
use super::global_info::{GlobalScriptInfo, GSInfo, self};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VType {
@ -39,7 +39,18 @@ impl VSingleType {
Self::Reference(r) => r.get(i, gsinfo),
Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get(i, gsinfo),
Self::CustomType(t) => gsinfo.custom_types[*t].get(i, gsinfo),
&Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler error?"),
&Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler bug? [get]"),
}
}
// None => might not always return t, Some(t) => can only return t
pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option<VType> {
match self {
Self::Bool | Self::Int | Self::Float | Self::String | Self::List(_) | Self::Function(..) | Self::Thread(..) => None,
Self::Tuple(t) => t.get(i).cloned(),
Self::Reference(r) => r.get_always(i, info),
Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get_always(i, info),
Self::CustomType(t) => info.custom_types[*t].get_always(i, info),
Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]"),
}
}
}
@ -54,6 +65,13 @@ impl VType {
}
Some(out)
}
pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option<VType> {
let mut out = VType { types: vec![] };
for t in &self.types {
out = out | t.get_always(i, info)?; // if we can't use *get* on one type, we can't use it at all.
}
Some(out)
}
/// returns Some(true) or Some(false) if all types are references or not references. If it is mixed or types is empty, returns None.
pub fn is_reference(&self) -> Option<bool> {
let mut noref = false;
@ -263,18 +281,20 @@ impl VSingleType {
}
}
pub fn fits_in(&self, rhs: &Self, info: &GlobalScriptInfo) -> bool {
// #[cfg(debug_assertions)]
// eprintln!("{self} in {rhs}?");
match (self, rhs) {
(Self::Reference(r), Self::Reference(b)) => r.fits_in(b, info),
(Self::Reference(_), _) | (_, Self::Reference(_)) => false,
(Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => {
*v1 == *v2 && t1.fits_in(&t2, info).is_empty()
},
(Self::CustomType(a), Self::CustomType(b)) => *a == *b || info.custom_types[*a].fits_in(&info.custom_types[*b], info).is_empty(),
(Self::CustomType(a), b) => info.custom_types[*a].fits_in(&b.clone().to(), info).is_empty(),
(a, Self::CustomType(b)) => a.clone().to().fits_in(&info.custom_types[*b], info).is_empty(),
(Self::CustomType(a), Self::CustomType(b)) => info.custom_types[*a].fits_in(&info.custom_types[*b], info).is_empty(),
(Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => unreachable!(),
(Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => unreachable!("CustomTypeS instead of CustomType - compiler bug?"),
(Self::EnumVariant(..), _) | (_, Self::EnumVariant(..)) => false,
(Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => unreachable!(),
(Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => unreachable!("EnumVariantS instead of EnumVariant - compiler bug?"),
(Self::Bool, Self::Bool)
| (Self::Int, Self::Int)
| (Self::Float, Self::Float)
@ -295,6 +315,7 @@ impl VSingleType {
'search: {
for b in b {
if a.1.fits_in(&b.1, info).is_empty()
&& a.0.len() == b.0.len()
&& a.0.iter().zip(b.0.iter()).all(|(a, b)| *a == *b)
{
break 'search;
@ -396,6 +417,16 @@ impl VSingleType {
inner.fmtgs(f, info)?;
write!(f, ")")
}
Self::CustomType(t) => if let Some(info) = info {
#[cfg(not(debug_assertions))]
write!(f, "{}", info.custom_type_names.iter().find_map(|(name, id)| if *t == *id { Some(name.to_owned()) } else { None }).unwrap())?;
#[cfg(debug_assertions)]
write!(f, "{}/*{}*/", info.custom_type_names.iter().find_map(|(name, id)| if *t == *id { Some(name.to_owned()) } else { None }).unwrap(), &info.custom_types[*t])?;
Ok(())
} else {
write!(f, "[custom type #{t}]")
}
Self::CustomTypeS(t) => write!(f, "{t}"),
}
}
}

View File

@ -1,8 +1,8 @@
use std::{path::PathBuf, thread::JoinHandle, time::Instant};
use crate::{
parse::{self, parse::ScriptError},
script::{code_runnable::RScript, val_data::VDataEnum},
parse::{self, parse::ScriptError, file::File},
script::{code_runnable::RScript, val_data::VDataEnum, global_info::GSInfo},
};
mod base_comments;
@ -31,7 +31,7 @@ false
Box::new(move |file| {
let mut file =
parse::file::File::new(std::fs::read_to_string(file).unwrap(), PathBuf::new());
sender.send(parse::parse::parse(&mut file)).unwrap();
sender.send((parse::parse::parse(&mut file), file)).unwrap();
}),
)
.unwrap();
@ -60,7 +60,7 @@ pub struct Tutor {
written_status_byte_len: usize,
editor_join_handle: JoinHandle<()>,
file_path: PathBuf,
receiver: std::sync::mpsc::Receiver<Result<RScript, ScriptError>>,
receiver: std::sync::mpsc::Receiver<(Result<RScript, (ScriptError, GSInfo)>, File)>,
// i_ are inputs from the user
pub i_name: Option<String>,
}
@ -70,16 +70,16 @@ impl Tutor {
// eprintln!(" - - - - - - - - - - - - - - - - - - - - - - - - -");
let script = loop {
match self.receiver.recv().unwrap() {
Err(e) => {
(Err((e, info)), file) => {
self.current_status = format!(
" - Error during build{}",
e.to_string()
e.with_file_and_gsinfo(&file, info.as_ref()).to_string()
.lines()
.map(|v| format!("\n// {v}"))
.collect::<String>()
)
}
Ok(script) => {
(Ok(script), _) => {
break script;
}
}