mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
added custom types that can be defined using 'type <name> <type>'
This commit is contained in:
parent
8bafe58593
commit
ef5874747e
19
custom_type.mers
Normal file
19
custom_type.mers
Normal 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()
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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})")
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user