Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
**/target
.VSCodeCounter
.idea
.idea
.DS_Store
4 changes: 2 additions & 2 deletions bytecode/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub struct Ctx<'a> {
/// be modified via the [`Ctx::signal`] and [`Ctx::clear_signal`] methods.
exit_state: Box<InstructionExitState>,
/// The arguments to the function.
args: Cow<'a, Vec<Primitive>>,
args: Cow<'a, [Primitive]>,
/// A separate variable mapping for closures and objects. If `None`, this function is neither.
/// Otherwise, contains a reference shared amongst all instances of the callback to point to
/// shared/global data.
Expand All @@ -100,7 +100,7 @@ impl<'a> Ctx<'a> {
pub fn new(
function: &'a Function,
call_stack: Rc<RefCell<Stack>>,
args: Cow<'a, Vec<Primitive>>,
args: Cow<'a, [Primitive]>,
callback_state: Option<VariableMapping>,
) -> Self {
Self {
Expand Down
2 changes: 1 addition & 1 deletion bytecode/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl MScriptFile {
pub fn run_function(
&self,
name: &String,
args: Cow<Vec<Primitive>>,
args: Cow<[Primitive]>,
current_frame: Rc<RefCell<Stack>>,
callback_state: Option<VariableMapping>,
jump_callback: &mut impl Fn(&JumpRequest) -> Result<ReturnValue>,
Expand Down
29 changes: 14 additions & 15 deletions bytecode/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ impl BuiltInFunction {
let result: Primitive = match this {
Primitive::Int(i32) => Primitive::Float(f64::from(*i32).powf(*power)),
Primitive::BigInt(i128) => {
Primitive::Float(f64::from(*i128 as i32).powf(*power))
Primitive::Float((*i128 as f64).powf(*power))
}
Primitive::Byte(u8) => Primitive::Float((*u8 as f64).powf(*power)),
Primitive::Float(f64) => Primitive::Float(f64.powf(*power)),
Expand All @@ -814,7 +814,7 @@ impl BuiltInFunction {

let result: Primitive = match this {
Primitive::Int(i32) => Primitive::Float(f64::from(*i32).sqrt()),
Primitive::BigInt(i128) => Primitive::Float(f64::from(*i128 as i32).sqrt()),
Primitive::BigInt(i128) => Primitive::Float((*i128 as f64).sqrt()),
Primitive::Byte(u8) => Primitive::Float((*u8 as f64).sqrt()),
Primitive::Float(f64) => Primitive::Float(f64.sqrt()),
bad => unreachable!("{bad}"),
Expand Down Expand Up @@ -891,10 +891,7 @@ impl BuiltInFunction {

let result: Primitive = match this {
Primitive::Int(i32) => Primitive::Float((*i32).into()),
Primitive::BigInt(i128) => Primitive::Float(f64::from(
i32::try_from(*i128)
.with_context(|| format!("`{i128}` cannot be made into a float"))?,
)),
Primitive::BigInt(i128) => Primitive::Float(*i128 as f64),
Primitive::Byte(u8) => Primitive::Float(*u8 as f64),
Primitive::Float(f64) => Primitive::Float(*f64),
bad => unreachable!("{bad}"),
Expand Down Expand Up @@ -1348,14 +1345,14 @@ impl Function {
/// # Arguments
/// * `args` - The arguments to this function, provided by the caller.
/// * `current_frame` - A shared reference to the current stack trace. The caller
/// **SHOULD NOT** push a stack frame for a bytecode function
/// before calling it; this method will handle that.
/// **SHOULD NOT** push a stack frame for a bytecode function
/// before calling it; this method will handle that.
/// * `callback_state` - A shared reference to the [`VariableMapping`] that this
/// function can access. This argument is used for callbacks
/// and closures exclusively. Normal variables should be
/// added to `current_frame`.
/// function can access. This argument is used for callbacks
/// and closures exclusively. Normal variables should be
/// added to `current_frame`.
/// * `jump_callback` - A callback that defines how this function's jumps are handled.
/// The implementation is up to the caller.
/// The implementation is up to the caller.
///
/// # Errors
/// This function can error if an instruction raises an error during execution.
Expand All @@ -1364,7 +1361,7 @@ impl Function {
/// This function will `panic!` if the instruction byte falls outside of (0..[`INSTRUCTION_COUNT`][crate::instruction_constants::INSTRUCTION_COUNT])
pub(crate) fn run(
&self,
args: Cow<Vec<Primitive>>,
args: Cow<[Primitive]>,
current_frame: Rc<RefCell<Stack>>,
callback_state: Option<VariableMapping>,
jump_callback: &mut impl Fn(&JumpRequest) -> Result<ReturnValue>,
Expand Down Expand Up @@ -1414,7 +1411,9 @@ impl Function {
.checked_add_signed(offset)
.with_context(|| format!("numeric overflow ({instruction_ptr} + {offset})"))?;
#[cfg(not(feature = "debug"))]
let new_val = (instruction_ptr as isize + offset) as usize;
let new_val = instruction_ptr
.checked_add_signed(offset)
.with_context(|| format!("numeric overflow ({instruction_ptr} + {offset})"))?;

if new_val >= instruction_len {
bail!("goto position index {new_val} is too big, instruction length is {instruction_len}.");
Expand Down Expand Up @@ -1537,7 +1536,7 @@ impl<'a> Functions {
pub(crate) fn run_function(
&self,
name: &String,
args: Cow<Vec<Primitive>>,
args: Cow<[Primitive]>,
current_frame: Rc<RefCell<Stack>>,
callback_state: Option<VariableMapping>,
jump_callback: &mut impl Fn(&JumpRequest) -> Result<ReturnValue>,
Expand Down
19 changes: 8 additions & 11 deletions bytecode/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ use super::function::InstructionExitState;
use super::stack::{Stack, VariableMapping};
use super::variables::{ObjectBuilder, Primitive};
use anyhow::{bail, Context, Result};
use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashSet;
use std::fmt::Debug;
use std::io::{stdin, stdout, Write};
use std::rc::Rc;
use std::sync::Mutex;

/// This variable allows instructions to register objects on the fly.
static mut OBJECT_BUILDER: Lazy<Mutex<ObjectBuilder>> =
Lazy::new(|| Mutex::new(ObjectBuilder::new()));
// This variable allows instructions to register objects on the fly.
thread_local! {
static OBJECT_BUILDER: RefCell<ObjectBuilder> = RefCell::new(ObjectBuilder::new());
}

/// Used for type declarations in lookup tables.
pub type InstructionSignature = fn(&mut Ctx, &[String]) -> Result<()>;
Expand Down Expand Up @@ -565,9 +564,7 @@ pub mod implementations {
let function = ctx.owner();
let name = function.name();

let obj = unsafe {
let mut lock = OBJECT_BUILDER.lock().unwrap();

let obj = OBJECT_BUILDER.with_borrow_mut(|lock| -> Result<_> {
if !lock.has_class_been_registered(name) {
let location = &function.location();
let object_path = format!("{}#{name}$", location.upgrade().unwrap().path());
Expand All @@ -593,10 +590,10 @@ pub mod implementations {
lock.register_class(name.to_owned(), mapping);
}

lock.name(name.to_owned())
Ok(lock.name(name.to_owned())
.object_variables(object_variables)
.build()
};
.build())
})?;

ctx.push(object!(obj));

Expand Down
1 change: 1 addition & 0 deletions bytecode/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This is the API for all-things-bytecode.

#![allow(dead_code)]
#![allow(non_local_definitions)]

pub(crate) mod context;
pub(crate) mod file;
Expand Down
2 changes: 1 addition & 1 deletion bytecode/src/variables/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl Debug for Object {

result
} else if let Some(ref name) = self.name {
write!(f, "<self-reference to {}>", name)
write!(f, "<self-reference to {name}>")
} else {
write!(f, "...")
}
Expand Down
2 changes: 1 addition & 1 deletion bytecode/src/variables/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ impl Primitive {
write!(f, "]")
}
Module(module) => {
write!(f, "module {:#?}", module)
write!(f, "module {module:#?}")

// if cfg!(feature = "debug") {
// write!(f, "module {:#?}", module)
Expand Down
2 changes: 1 addition & 1 deletion bytecode_dev_transpiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl Instruction {
fn repr(&self) -> String {
let mut args = String::new();

if self.arguments.len() >= 1 {
if !self.arguments.is_empty() {
for arg in &self.arguments[..] {
args.push(' ');
if arg.contains(' ') {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ impl CompiledItem {
Self::Instruction { id, arguments } => {
let mut args = String::new();

if arguments.len() >= 1 {
if !arguments.is_empty() {
for arg in &arguments[..] {
args.push(' ');
let replaced = arg.replace('"', "\\\"");
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/ast/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub(crate) enum Declaration {
ReturnStatement(ReturnStatement),
IfStatement(IfStatement),
WhileLoop(WhileLoop),
NumberLoop(NumberLoop),
NumberLoop(Box<NumberLoop>),
Continue(Continue),
Break(Break),
Assertion(Assertion),
Expand Down Expand Up @@ -114,7 +114,7 @@ impl Parser {
Declaration::Continue(Self::continue_statement(declaration).to_err_vec()?)
}
Rule::break_statement => Declaration::Break(Self::break_statement(input).to_err_vec()?),
Rule::number_loop => Declaration::NumberLoop(Self::number_loop(declaration)?),
Rule::number_loop => Declaration::NumberLoop(Box::new(Self::number_loop(declaration)?)),
Rule::reassignment => Declaration::Reassignment(Self::reassignment(declaration)?),
Rule::assertion => Declaration::Assertion(Self::assertion(declaration)?),
Rule::class => Declaration::Class(Self::class(declaration)?),
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/ast/dot_lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,7 @@ impl Parser {
ident_span,
&source_name,
format!(
"this field has the type `{}`, which is not callable",
type_of_property
"this field has the type `{type_of_property}`, which is not callable"
),
)]);
};
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/ast/function_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl Parser {
let mut result_len: usize = 0;
let mut errors = vec![];

let expected_types: Cow<Vec<Cow<TypeLayout>>> = expected_parameters.to_types();
let expected_types: Cow<[Cow<TypeLayout>]> = expected_parameters.to_types();

let mut child_span = input.as_span();

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/ast/function_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl FunctionParameters {
}
}

pub fn to_types(&self) -> Cow<'_, Vec<Cow<'static, TypeLayout>>> {
pub fn to_types(&self) -> Cow<'_, [Cow<'static, TypeLayout>]> {
match self {
FunctionParameters::Named(names) => {
Cow::Owned(names.iter().map(|x| x.ty().unwrap().clone()).collect())
Expand All @@ -43,7 +43,7 @@ impl Display for FunctionParameters {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf: String = String::new();

let types: Cow<Vec<Cow<TypeLayout>>> = self.to_types();
let types: Cow<[Cow<TypeLayout>]> = self.to_types();

let mut iter = types.iter();
let Some(first) = iter.next() else {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/ast/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl ListType {
pub fn upper_bound(&self) -> ListBound {
match self {
Self::Open { .. } => ListBound::Infinite,
Self::Mixed(types) => ListBound::Numeric(types.len() - 1),
Self::Mixed(types) => ListBound::Numeric(types.len().saturating_sub(1)),
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/ast/reassignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ impl Parser {
let path = children.next().unwrap();
let value = children.next().unwrap();

let path_span = value.as_span();
let path_span = path.as_span();

let (path, is_const) = ReassignmentPath::parse(path)?;

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/ast/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ impl Display for TypeLayout {
Self::Optional(None) => write!(f, "nil"),
Self::Void => write!(f, "void"),
Self::Generic(generic) => write!(f, "{generic}"),
Self::Module(module) if cfg!(test) => write!(f, "<module {:?}>", module),
Self::Module(module) if cfg!(test) => write!(f, "<module {module:?}>"),
Self::Module(module) => write!(f, "<module {:?}>", module.name.as_os_str()),
Self::Map(map) => write!(f, "map[{}, {}]", map.key_type(), map.value_type()),
}
Expand Down Expand Up @@ -1677,7 +1677,7 @@ impl TypeLayout {
usize::MAX
),
ValToUsize::NotConstexpr => match me {
TypeLayout::List(ListType::Open(ty)) => return Ok(Cow::Borrowed(ty.as_ref())),
TypeLayout::List(ListType::Open(ty)) => Ok(Cow::Borrowed(ty.as_ref())),
TypeLayout::List(ListType::Mixed(ty)) => bail!("Indexing into a mixed type list ({:?}) requires that the index be evaluable at compile time", ty.iter().map(|x| x.to_string()).collect::<Vec<_>>()),
_ => todo!()
},
Expand Down
93 changes: 93 additions & 0 deletions compiler/src/tests/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,96 @@ fn number_properties() {
)
.unwrap();
}

#[test]
fn string_chars() {
eval(
r#"
chars = "hello".chars()
assert typeof chars == "[str...]"
assert chars.len() == 5
assert chars[0] == "h"
assert chars[4] == "o"

reconstructed = ""
from 0 to chars.len(), i {
reconstructed += chars[i]
}
assert reconstructed == "hello"

assert "".chars().len() == 0
assert "x".chars() == ["x"]
"#,
)
.unwrap();
}

#[test]
fn number_to_str() {
eval(
r#"
assert 42.to_str() == "42"
assert (-7).to_str() == "-7"
assert 0.to_str() == "0"
assert 3.14.to_str() == "3.14"

n = 255
assert n.to_str() == "255"
"#,
)
.unwrap();
}

#[test]
fn array_to_str() {
eval(
r#"
assert [1, 2, 3].to_str() == "[1, 2, 3]"
assert (["a", "b"]).to_str() == "[\"a\", \"b\"]"
empty: [int...] = []
assert empty.to_str() == "[]"
"#,
)
.unwrap();
}

#[test]
fn array_filter_map_chain() {
eval(
r#"
words: [str...] = ["banana", "apple", "cherry"]
lengths = words.map(fn(w: str) -> int { return w.len() })

assert lengths == [6, 5, 6]

short_words = words.filter(fn(w: str) -> bool { return w.len() <= 5 })
assert short_words == ["apple"]
assert typeof short_words == "[str...]"
"#,
)
.unwrap();
}

#[test]
fn count_chars_with_chars_method() {
eval(
r#"
count_vowels = fn(s: str) -> int {
vowels = "aeiouAEIOU"
count = 0
chars = s.chars()
from 0 to chars.len(), i {
if vowels.contains(chars[i]) {
count += 1
}
}
return count
}

assert count_vowels("hello") == 2
assert count_vowels("rhythm") == 0
assert count_vowels("aeiou") == 5
"#,
)
.unwrap();
}
Loading
Loading