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!(" - - - - -");
|
||||||
println!("{}", output);
|
println!("{}", output);
|
||||||
}
|
}
|
||||||
Err(e) => println!("{}", e.with_file(&file)),
|
Err(e) => println!("{}", e.0.with_file_and_gsinfo(&file, e.1.as_ref())),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("can't read file at {:?}!", temp_file);
|
println!("can't read file at {:?}!", temp_file);
|
||||||
|
@ -125,7 +125,7 @@ fn main() {
|
|||||||
println!("Output ({}s)\n{out}", elapsed.as_secs_f64());
|
println!("Output ({}s)\n{out}", elapsed.as_secs_f64());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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);
|
std::process::exit(99);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
code_macro::MacroError,
|
code_macro::MacroError,
|
||||||
code_parsed::*,
|
code_parsed::*,
|
||||||
code_runnable::RScript,
|
code_runnable::RScript,
|
||||||
global_info::{GlobalScriptInfo},
|
global_info::{GlobalScriptInfo, GSInfo},
|
||||||
to_runnable::{self, ToRunnableError},
|
to_runnable::{self, ToRunnableError},
|
||||||
val_data::VDataEnum,
|
val_data::VDataEnum,
|
||||||
val_type::{VSingleType, VType},
|
val_type::{VSingleType, VType},
|
||||||
@ -52,20 +52,50 @@ impl std::fmt::Display for ScriptError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct ScriptErrorWithFile<'a>(&'a ScriptError, &'a File);
|
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 {
|
impl<'a> ScriptError {
|
||||||
pub fn with_file(&'a self, file: &'a File) -> ScriptErrorWithFile {
|
pub fn with_file(&'a self, file: &'a File) -> ScriptErrorWithFile {
|
||||||
ScriptErrorWithFile(self, file)
|
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> {
|
impl<'a> std::fmt::Display for ScriptErrorWithFile<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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::CannotFindPathForLibrary(e) => write!(f, "{e}"),
|
||||||
ScriptError::ParseError(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::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;
|
pub const PARSE_VERSION: u64 = 0;
|
||||||
|
|
||||||
/// executes the 4 parse_steps in order: lib_paths => interpret => libs_load => compile
|
/// 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 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)
|
Ok(run)
|
||||||
}
|
}
|
||||||
@ -153,7 +198,7 @@ pub fn parse_step_libs_load(
|
|||||||
Ok(libs)
|
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)
|
to_runnable::to_runnable(main_func, ginfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -686,6 +731,10 @@ pub mod implementation {
|
|||||||
}
|
}
|
||||||
break SStatementEnum::Match(match_what, cases).to();
|
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(),
|
"true" => break SStatementEnum::Value(VDataEnum::Bool(true).to()).to(),
|
||||||
"false" => {
|
"false" => {
|
||||||
break SStatementEnum::Value(VDataEnum::Bool(false).to()).to()
|
break SStatementEnum::Value(VDataEnum::Bool(false).to()).to()
|
||||||
|
@ -278,41 +278,41 @@ impl BuiltinFunction {
|
|||||||
&& input[0]
|
&& input[0]
|
||||||
.fits_in(&VType {
|
.fits_in(&VType {
|
||||||
types: vec![VSingleType::Int, VSingleType::Float],
|
types: vec![VSingleType::Int, VSingleType::Float],
|
||||||
})
|
}, info)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
}
|
}
|
||||||
Self::Exit => {
|
Self::Exit => {
|
||||||
input.len() == 0
|
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!
|
// TODO!
|
||||||
Self::FsList => true,
|
Self::FsList => true,
|
||||||
Self::FsRead => {
|
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 => {
|
Self::FsWrite => {
|
||||||
input.len() == 2
|
input.len() == 2
|
||||||
&& input[0].fits_in(&VSingleType::String.to()).is_empty()
|
&& input[0].fits_in(&VSingleType::String.to(), info).is_empty()
|
||||||
&& input[1]
|
&& input[1]
|
||||||
.fits_in(&VSingleType::List(VSingleType::Int.to()).to())
|
.fits_in(&VSingleType::List(VSingleType::Int.to()).to(), info)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
}
|
}
|
||||||
Self::BytesToString => {
|
Self::BytesToString => {
|
||||||
input.len() == 1
|
input.len() == 1
|
||||||
&& input[0]
|
&& input[0]
|
||||||
.fits_in(&VSingleType::List(VSingleType::Int.to()).to())
|
.fits_in(&VSingleType::List(VSingleType::Int.to()).to(), info)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
}
|
}
|
||||||
Self::StringToBytes => {
|
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 => {
|
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 {
|
if input.len() == 1 {
|
||||||
true
|
true
|
||||||
} else if input.len() == 2 {
|
} else if input.len() == 2 {
|
||||||
input[1]
|
input[1]
|
||||||
.fits_in(&VSingleType::List(VSingleType::String.to()).to())
|
.fits_in(&VSingleType::List(VSingleType::String.to()).to(), info)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -328,16 +328,16 @@ impl BuiltinFunction {
|
|||||||
types: vec![VSingleType::Int, VSingleType::Float],
|
types: vec![VSingleType::Int, VSingleType::Float],
|
||||||
};
|
};
|
||||||
let st = VSingleType::String.to();
|
let st = VSingleType::String.to();
|
||||||
(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())
|
||||||
|| (input[0].fits_in(&st).is_empty() && input[1].fits_in(&st).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 => {
|
Self::And | Self::Or => {
|
||||||
input.len() == 2
|
input.len() == 2
|
||||||
&& input
|
&& input
|
||||||
.iter()
|
.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::Sub
|
||||||
| Self::Mul
|
| Self::Mul
|
||||||
@ -354,7 +354,7 @@ impl BuiltinFunction {
|
|||||||
let num = VType {
|
let num = VType {
|
||||||
types: vec![VSingleType::Int, VSingleType::Float],
|
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!
|
// 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 vec.is_reference().is_some_and(|v| v) { // unstable
|
||||||
if let Some(true) = vec.is_reference() {
|
if let Some(true) = vec.is_reference() {
|
||||||
if let Some(t) = vec.get_any(info) {
|
if let Some(t) = vec.get_any(info) {
|
||||||
el.fits_in(&t).is_empty()
|
el.fits_in(&t, info).is_empty()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ impl BuiltinFunction {
|
|||||||
let (vec, el) = (&input[0], &input[1]);
|
let (vec, el) = (&input[0], &input[1]);
|
||||||
if let Some(true) = vec.is_reference() {
|
if let Some(true) = vec.is_reference() {
|
||||||
if let Some(t) = vec.get_any(info) {
|
if let Some(t) = vec.get_any(info) {
|
||||||
el.fits_in(&t).is_empty()
|
el.fits_in(&t, info).is_empty()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -415,7 +415,7 @@ impl BuiltinFunction {
|
|||||||
if let Some(true) = vec.is_reference() {
|
if let Some(true) = vec.is_reference() {
|
||||||
// TODO! same issue as in pop
|
// TODO! same issue as in pop
|
||||||
if let Some(_) = vec.get_any(info) {
|
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
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -436,11 +436,11 @@ impl BuiltinFunction {
|
|||||||
if input.len() >= 2 && input.len() <= 3 {
|
if input.len() >= 2 && input.len() <= 3 {
|
||||||
let (s, start) = (&input[0], &input[1]);
|
let (s, start) = (&input[0], &input[1]);
|
||||||
let index_type = VSingleType::Int.to();
|
let index_type = VSingleType::Int.to();
|
||||||
if s.fits_in(&VSingleType::String.to()).is_empty()
|
if s.fits_in(&VSingleType::String.to(), info).is_empty()
|
||||||
&& start.fits_in(&index_type).is_empty()
|
&& start.fits_in(&index_type, info).is_empty()
|
||||||
{
|
{
|
||||||
if let Some(end) = input.get(2) {
|
if let Some(end) = input.get(2) {
|
||||||
end.fits_in(&index_type).is_empty()
|
end.fits_in(&index_type, info).is_empty()
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -456,7 +456,7 @@ impl BuiltinFunction {
|
|||||||
input.len() == 2
|
input.len() == 2
|
||||||
&& input
|
&& input
|
||||||
.iter()
|
.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
|
// two strings or &strings
|
||||||
Self::IndexOf => {
|
Self::IndexOf => {
|
||||||
@ -467,12 +467,12 @@ impl BuiltinFunction {
|
|||||||
VSingleType::String,
|
VSingleType::String,
|
||||||
VSingleType::Reference(Box::new(VSingleType::String)),
|
VSingleType::Reference(Box::new(VSingleType::String)),
|
||||||
],
|
],
|
||||||
})
|
}, info)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Self::Trim => {
|
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();
|
_ = file.next();
|
||||||
match parse::parse(file) {
|
match parse::parse(file) {
|
||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.0.into()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let path = parse_string_val(file);
|
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"),
|
.expect("can't include mers code because the file could not be read"),
|
||||||
path.into(),
|
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)>),
|
Match(String, Vec<(SStatement, SStatement)>),
|
||||||
IndexFixed(SStatement, usize),
|
IndexFixed(SStatement, usize),
|
||||||
EnumVariant(String, SStatement),
|
EnumVariant(String, SStatement),
|
||||||
|
TypeDefinition(String, VType),
|
||||||
Macro(Macro),
|
Macro(Macro),
|
||||||
}
|
}
|
||||||
impl SStatementEnum {
|
impl SStatementEnum {
|
||||||
@ -173,6 +174,7 @@ impl SStatementEnum {
|
|||||||
write!(f, "{variant}: ")?;
|
write!(f, "{variant}: ")?;
|
||||||
inner.fmtgs(f, info)
|
inner.fmtgs(f, info)
|
||||||
}
|
}
|
||||||
|
Self::TypeDefinition(name, t) => write!(f, "type {name} {t}"),
|
||||||
Self::Macro(m) => {
|
Self::Macro(m) => {
|
||||||
write!(f, "!({m})")
|
write!(f, "!({m})")
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ impl RStatementEnum {
|
|||||||
let switch_on_type = switch_on.out();
|
let switch_on_type = switch_on.out();
|
||||||
let mut out = VDataEnum::Tuple(vec![]).to();
|
let mut out = VDataEnum::Tuple(vec![]).to();
|
||||||
for (case_type, case_action) in cases.iter() {
|
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);
|
out = case_action.run(vars, info);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -329,7 +329,7 @@ impl RStatementEnum {
|
|||||||
let switch_on = switch_on.to();
|
let switch_on = switch_on.to();
|
||||||
'search: {
|
'search: {
|
||||||
for (on_type, case) in cases.iter() {
|
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);
|
out = out | case.out(info);
|
||||||
break 'search;
|
break 'search;
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,14 @@ use super::{
|
|||||||
builtins::BuiltinFunction,
|
builtins::BuiltinFunction,
|
||||||
code_macro::Macro,
|
code_macro::Macro,
|
||||||
code_parsed::{SBlock, SFunction, SStatement, SStatementEnum},
|
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 {
|
pub enum ToRunnableError {
|
||||||
MainWrongInput,
|
MainWrongInput,
|
||||||
UseOfUndefinedVariable(String),
|
UseOfUndefinedVariable(String),
|
||||||
UseOfUndefinedFunction(String),
|
UseOfUndefinedFunction(String),
|
||||||
|
UnknownType(String),
|
||||||
CannotDeclareVariableWithDereference(String),
|
CannotDeclareVariableWithDereference(String),
|
||||||
CannotDereferenceTypeNTimes(VType, usize, VType),
|
CannotDereferenceTypeNTimes(VType, usize, VType),
|
||||||
FunctionWrongArgCount(String, usize, usize),
|
FunctionWrongArgCount(String, usize, usize),
|
||||||
@ -51,6 +52,11 @@ impl Debug for ToRunnableError {
|
|||||||
// - Show location in code where the error was found
|
// - Show location in code where the error was found
|
||||||
impl Display for ToRunnableError {
|
impl Display for ToRunnableError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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 {
|
match self {
|
||||||
Self::MainWrongInput => write!(
|
Self::MainWrongInput => write!(
|
||||||
f,
|
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::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::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::CannotDeclareVariableWithDereference(v) => write!(f, "Cannot declare a variable and dereference it (variable '{v}')."),
|
||||||
Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => write!(f,
|
Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => {
|
||||||
"Cannot dereference type {og_type} {derefs_wanted} times (stopped at {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::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."),
|
||||||
Self::InvalidType {
|
Self::InvalidType {
|
||||||
expected,
|
expected,
|
||||||
found,
|
found,
|
||||||
problematic,
|
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) => {
|
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 {
|
for arg in args {
|
||||||
write!(f, " {arg}")?;
|
write!(f, " ")?;
|
||||||
|
arg.fmtgs(f, info)?;
|
||||||
}
|
}
|
||||||
write!(f, ".")
|
write!(f, ".")
|
||||||
}
|
}
|
||||||
Self::WrongArgsForLibFunction(name, args) => {
|
Self::WrongArgsForLibFunction(name, args) => {
|
||||||
write!(f, "Wrong arguments for library function {}:", name)?;
|
write!(f, "Wrong arguments for library function {}:", name)?;
|
||||||
for arg in args {
|
for arg in args {
|
||||||
write!(f, " {arg}")?;
|
write!(f, " ")?;
|
||||||
|
arg.fmtgs(f, info)?;
|
||||||
}
|
}
|
||||||
write!(f, ".")
|
write!(f, ".")
|
||||||
}
|
}
|
||||||
Self::ForLoopContainerHasNoInnerTypes => {
|
Self::ForLoopContainerHasNoInnerTypes => {
|
||||||
write!(f, "For loop: container had no inner types, cannot iterate.")
|
write!(f, "For loop: container had no inner types, cannot iterate.")
|
||||||
}
|
}
|
||||||
Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => write!(f,
|
Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => {
|
||||||
"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}."
|
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>>,
|
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" {
|
if s.inputs.len() != 1 || s.inputs[0].0 != "args" {
|
||||||
return Err(ToRunnableError::MainWrongInput);
|
return Err((ToRunnableError::MainWrongInput, ginfo.to_arc()));
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
s.inputs[0].1,
|
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,
|
&s,
|
||||||
&mut ginfo,
|
&mut ginfo,
|
||||||
LInfo {
|
LInfo {
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
fns: 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,
|
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.
|
// 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() {
|
if s.inputs.len() > inputs.len() {
|
||||||
let input_here = &s.inputs[inputs.len()].1;
|
let input_here = &s.inputs[inputs.len()].1;
|
||||||
for t in &input_here.types {
|
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)?;
|
get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?;
|
||||||
inputs.pop();
|
inputs.pop();
|
||||||
}
|
}
|
||||||
@ -150,7 +201,11 @@ fn get_all_functions(
|
|||||||
} else {
|
} else {
|
||||||
// set the types
|
// set the types
|
||||||
for (varid, vartype) in s.inputs.iter().zip(inputs.iter()) {
|
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)));
|
out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out(ginfo)));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -164,11 +219,13 @@ fn function(
|
|||||||
let mut input_vars = vec![];
|
let mut input_vars = vec![];
|
||||||
let mut input_types = vec![];
|
let mut input_types = vec![];
|
||||||
for (iname, itype) in &s.inputs {
|
for (iname, itype) in &s.inputs {
|
||||||
|
let mut itype = itype.to_owned();
|
||||||
|
stypes(&mut itype, ginfo)?;
|
||||||
linfo
|
linfo
|
||||||
.vars
|
.vars
|
||||||
.insert(iname.clone(), (ginfo.vars, itype.clone()));
|
.insert(iname.clone(), (ginfo.vars, itype.clone()));
|
||||||
input_vars.push(ginfo.vars);
|
input_vars.push(ginfo.vars);
|
||||||
input_types.push(itype.clone());
|
input_types.push(itype);
|
||||||
ginfo.vars += 1;
|
ginfo.vars += 1;
|
||||||
}
|
}
|
||||||
let mut all_outs = vec![];
|
let mut all_outs = vec![];
|
||||||
@ -200,18 +257,23 @@ fn block(s: &SBlock, ginfo: &mut GlobalScriptInfo, mut linfo: LInfo) -> Result<R
|
|||||||
Ok(RBlock { statements })
|
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 {
|
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 {
|
match t {
|
||||||
|
VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String => (),
|
||||||
VSingleType::Tuple(v) => {
|
VSingleType::Tuple(v) => {
|
||||||
for t in 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) => {
|
VSingleType::EnumVariantS(e, v) => {
|
||||||
*t = VSingleType::EnumVariant(
|
*t = VSingleType::EnumVariant(
|
||||||
{
|
{
|
||||||
@ -224,13 +286,30 @@ fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stypes(v, ginfo);
|
stypes(v, ginfo)?;
|
||||||
v.clone()
|
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(
|
fn statement(
|
||||||
s: &SStatement,
|
s: &SStatement,
|
||||||
@ -253,7 +332,7 @@ fn statement(
|
|||||||
SStatementEnum::Variable(v, is_ref) => {
|
SStatementEnum::Variable(v, is_ref) => {
|
||||||
if let Some(var) = linfo.vars.get(v) {
|
if let Some(var) = linfo.vars.get(v) {
|
||||||
RStatementEnum::Variable(var.0, {
|
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 {
|
} else {
|
||||||
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
|
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
|
||||||
}
|
}
|
||||||
@ -273,7 +352,7 @@ fn statement(
|
|||||||
}
|
}
|
||||||
for (i, rarg) in rargs.iter().enumerate() {
|
for (i, rarg) in rargs.iter().enumerate() {
|
||||||
let rarg = rarg.out(ginfo);
|
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() {
|
if !out.is_empty() {
|
||||||
return Err(ToRunnableError::InvalidType {
|
return Err(ToRunnableError::InvalidType {
|
||||||
expected: func.input_types[i].clone(),
|
expected: func.input_types[i].clone(),
|
||||||
@ -296,7 +375,7 @@ fn statement(
|
|||||||
// LIBRARY FUNCTION?
|
// LIBRARY FUNCTION?
|
||||||
if let Some((libid, fnid)) = ginfo.lib_fns.get(v) {
|
if let Some((libid, fnid)) = ginfo.lib_fns.get(v) {
|
||||||
let (_name, fn_in, fn_out) = &ginfo.libs[*libid].registered_fns[*fnid];
|
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())
|
RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone())
|
||||||
} else {
|
} else {
|
||||||
// TODO! better error here
|
// TODO! better error here
|
||||||
@ -332,7 +411,7 @@ fn statement(
|
|||||||
let condition = statement(&c, ginfo, linfo)?;
|
let condition = statement(&c, ginfo, linfo)?;
|
||||||
let out = condition.out(ginfo).fits_in(&VType {
|
let out = condition.out(ginfo).fits_in(&VType {
|
||||||
types: vec![VSingleType::Bool],
|
types: vec![VSingleType::Bool],
|
||||||
});
|
}, ginfo);
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
condition
|
condition
|
||||||
} else {
|
} else {
|
||||||
@ -374,7 +453,7 @@ fn statement(
|
|||||||
let mut ncases = Vec::with_capacity(cases.len());
|
let mut ncases = Vec::with_capacity(cases.len());
|
||||||
let og_type = switch_on_v.1.clone(); // linfo.vars.get(switch_on).unwrap().1.clone();
|
let og_type = switch_on_v.1.clone(); // linfo.vars.get(switch_on).unwrap().1.clone();
|
||||||
for case in cases {
|
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();
|
linfo.vars.get_mut(switch_on).unwrap().1 = case0.clone();
|
||||||
ncases.push((case0, statement(&case.1, ginfo, linfo)?));
|
ncases.push((case0, statement(&case.1, ginfo, linfo)?));
|
||||||
}
|
}
|
||||||
@ -391,8 +470,8 @@ fn statement(
|
|||||||
'force: {
|
'force: {
|
||||||
for (case_type, _) in cases {
|
for (case_type, _) in cases {
|
||||||
let mut ct = case_type.clone();
|
let mut ct = case_type.clone();
|
||||||
stypes(&mut ct, ginfo);
|
stypes(&mut ct, ginfo)?;
|
||||||
if val_type.fits_in(&ct).is_empty() {
|
if val_type.fits_in(&ct, ginfo).is_empty() {
|
||||||
break 'force;
|
break 'force;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,23 +572,7 @@ fn statement(
|
|||||||
|
|
||||||
SStatementEnum::IndexFixed(st, i) => {
|
SStatementEnum::IndexFixed(st, i) => {
|
||||||
let st = statement(st, ginfo, linfo)?;
|
let st = statement(st, ginfo, linfo)?;
|
||||||
let ok = 'ok: {
|
if st.out(ginfo).get_always(*i, ginfo).is_some() {
|
||||||
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 {
|
|
||||||
RStatementEnum::IndexFixed(st, *i)
|
RStatementEnum::IndexFixed(st, *i)
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::NotIndexableFixed(st.out(ginfo), *i));
|
return Err(ToRunnableError::NotIndexableFixed(st.out(ginfo), *i));
|
||||||
@ -524,6 +587,14 @@ fn statement(
|
|||||||
v
|
v
|
||||||
}
|
}
|
||||||
}, statement(s, ginfo, linfo)?),
|
}, 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 {
|
SStatementEnum::Macro(m) => match m {
|
||||||
Macro::StaticMers(val) => RStatementEnum::Value(val.clone()),
|
Macro::StaticMers(val) => RStatementEnum::Value(val.clone()),
|
||||||
},
|
},
|
||||||
@ -531,10 +602,12 @@ fn statement(
|
|||||||
.to();
|
.to();
|
||||||
// if force_output_type is set, verify that the real output type actually fits in the forced one.
|
// 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 {
|
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 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() {
|
if problematic_types.is_empty() {
|
||||||
statement.force_output_type = Some(force_opt.clone());
|
statement.force_output_type = Some(force_opt);
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types }));
|
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() {
|
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.");
|
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 {
|
if *derefs != 0 {
|
||||||
|
@ -4,7 +4,7 @@ use std::{
|
|||||||
ops::BitOr,
|
ops::BitOr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::global_info::{GlobalScriptInfo, GSInfo};
|
use super::global_info::{GlobalScriptInfo, GSInfo, self};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct VType {
|
pub struct VType {
|
||||||
@ -39,7 +39,18 @@ impl VSingleType {
|
|||||||
Self::Reference(r) => r.get(i, gsinfo),
|
Self::Reference(r) => r.get(i, gsinfo),
|
||||||
Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get(i, gsinfo),
|
Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get(i, gsinfo),
|
||||||
Self::CustomType(t) => gsinfo.custom_types[*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)
|
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.
|
/// 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> {
|
pub fn is_reference(&self) -> Option<bool> {
|
||||||
let mut noref = false;
|
let mut noref = false;
|
||||||
@ -263,18 +281,20 @@ impl VSingleType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn fits_in(&self, rhs: &Self, info: &GlobalScriptInfo) -> bool {
|
pub fn fits_in(&self, rhs: &Self, info: &GlobalScriptInfo) -> bool {
|
||||||
|
// #[cfg(debug_assertions)]
|
||||||
|
// eprintln!("{self} in {rhs}?");
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Self::Reference(r), Self::Reference(b)) => r.fits_in(b, info),
|
(Self::Reference(r), Self::Reference(b)) => r.fits_in(b, info),
|
||||||
(Self::Reference(_), _) | (_, Self::Reference(_)) => false,
|
(Self::Reference(_), _) | (_, Self::Reference(_)) => false,
|
||||||
(Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => {
|
(Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => {
|
||||||
*v1 == *v2 && t1.fits_in(&t2, info).is_empty()
|
*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(),
|
(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(),
|
(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!("CustomTypeS instead of CustomType - compiler bug?"),
|
||||||
(Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => unreachable!(),
|
|
||||||
(Self::EnumVariant(..), _) | (_, Self::EnumVariant(..)) => false,
|
(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::Bool, Self::Bool)
|
||||||
| (Self::Int, Self::Int)
|
| (Self::Int, Self::Int)
|
||||||
| (Self::Float, Self::Float)
|
| (Self::Float, Self::Float)
|
||||||
@ -295,6 +315,7 @@ impl VSingleType {
|
|||||||
'search: {
|
'search: {
|
||||||
for b in b {
|
for b in b {
|
||||||
if a.1.fits_in(&b.1, info).is_empty()
|
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)
|
&& a.0.iter().zip(b.0.iter()).all(|(a, b)| *a == *b)
|
||||||
{
|
{
|
||||||
break 'search;
|
break 'search;
|
||||||
@ -396,6 +417,16 @@ impl VSingleType {
|
|||||||
inner.fmtgs(f, info)?;
|
inner.fmtgs(f, info)?;
|
||||||
write!(f, ")")
|
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 std::{path::PathBuf, thread::JoinHandle, time::Instant};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse::{self, parse::ScriptError},
|
parse::{self, parse::ScriptError, file::File},
|
||||||
script::{code_runnable::RScript, val_data::VDataEnum},
|
script::{code_runnable::RScript, val_data::VDataEnum, global_info::GSInfo},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod base_comments;
|
mod base_comments;
|
||||||
@ -31,7 +31,7 @@ false
|
|||||||
Box::new(move |file| {
|
Box::new(move |file| {
|
||||||
let mut file =
|
let mut file =
|
||||||
parse::file::File::new(std::fs::read_to_string(file).unwrap(), PathBuf::new());
|
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();
|
.unwrap();
|
||||||
@ -60,7 +60,7 @@ pub struct Tutor {
|
|||||||
written_status_byte_len: usize,
|
written_status_byte_len: usize,
|
||||||
editor_join_handle: JoinHandle<()>,
|
editor_join_handle: JoinHandle<()>,
|
||||||
file_path: PathBuf,
|
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
|
// i_ are inputs from the user
|
||||||
pub i_name: Option<String>,
|
pub i_name: Option<String>,
|
||||||
}
|
}
|
||||||
@ -70,16 +70,16 @@ impl Tutor {
|
|||||||
// eprintln!(" - - - - - - - - - - - - - - - - - - - - - - - - -");
|
// eprintln!(" - - - - - - - - - - - - - - - - - - - - - - - - -");
|
||||||
let script = loop {
|
let script = loop {
|
||||||
match self.receiver.recv().unwrap() {
|
match self.receiver.recv().unwrap() {
|
||||||
Err(e) => {
|
(Err((e, info)), file) => {
|
||||||
self.current_status = format!(
|
self.current_status = format!(
|
||||||
" - Error during build{}",
|
" - Error during build{}",
|
||||||
e.to_string()
|
e.with_file_and_gsinfo(&file, info.as_ref()).to_string()
|
||||||
.lines()
|
.lines()
|
||||||
.map(|v| format!("\n// {v}"))
|
.map(|v| format!("\n// {v}"))
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Ok(script) => {
|
(Ok(script), _) => {
|
||||||
break script;
|
break script;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user