[add] modules::standard::require

allows you to import external scripts and use their return
[add] modules::methods::table::enumerate
[add] modules::methods::table::iter (enumerate alias)
[chore] bump version (v0.7.0 -> v0.7.1)
This commit is contained in:
hkau 2024-02-18 22:38:20 -05:00
parent d083f18f3b
commit 70ed891d74
9 changed files with 218 additions and 68 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "amethystine"
version = "0.7.0"
version = "0.7.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,7 +2,7 @@
Amethystine (named after the "Amethystine Python") is a lightweight interpretter for a mix of Python and Lua written in Rust. It interprets an AST tree generated by a PEG parser.
Documentation is available at <https://docs.stellular.org/amethystine/modules/index.html>, and can be built locally with `cargo doc --no-deps --document-private-items`.
Documentation is available at <https://docs.stellular.org/amethystine/index.html>, and can be built locally with `cargo doc --no-deps --document-private-items`.
## Usage

View File

@ -205,7 +205,7 @@ pub fn from_tree(
functions = concat_functions(functions, crate::modules::methods::string::import());
functions = concat_functions(functions, crate::modules::methods::array::import());
functions = concat_functions(functions, crate::modules::methods::tuple::import());
functions = concat_functions(functions, crate::modules::methods::json::import());
functions = concat_functions(functions, crate::modules::methods::table::import());
// traits
let supports_default = &["string", "number", "array", "tuple", "table"];
@ -592,7 +592,7 @@ pub fn from_tree(
);
// return
let res = crate::modules::methods::json::__setitem__(args, vars);
let res = crate::modules::methods::table::__setitem__(args, vars);
// remove dynamic attribute
let mut new_value = res.children.unwrap().get(0).unwrap().to_owned();
@ -1590,7 +1590,7 @@ fn get_block_value(
);
// return
return crate::modules::methods::json::__getitem__(args, vars);
return crate::modules::methods::table::__getitem__(args, vars);
}
// convert IDENTIFIER values to their variable values

View File

@ -32,48 +32,7 @@ fn main() {
// begin
let start = unix_epoch_timestamp(); // save beginning timestamp (for --benchmark)
let mut file_contents = file.unwrap();
// every file is going to call the "dump" function, which does nothing
// this makes it where we can end with a function/conditional
file_contents += "\ndump()";
// convert lua comments to python comments
file_contents = file_contents.replace(r"-- ", "# ");
// TODO: fix weird comma bug with arrays/tuples
// file_contents = file_contents.replace(", ", ",");
// TODO: remove \n in arrays and tuples
// add ending tags
// we'll need these to detect the end of a scoped block
let file_contents_c = file_contents.clone();
let lines = file_contents_c.lines().collect::<Vec<&str>>();
for (i, line) in lines.clone().iter().enumerate() {
// get whitespace
let whitespace = line.replace(line.trim_start(), "");
// get next line
let next = get_next_line_with_content(lines.clone(), i);
if next.is_none() {
continue;
}
// if next line does not start with the same whitespace, add closing tag
let unwrap = next.unwrap();
let unwrap_whitespace = unwrap.replace(unwrap.trim_start(), "");
if (unwrap_whitespace.len() < whitespace.len()) | (!unwrap.starts_with(&whitespace)) {
file_contents = file_contents.replace(
line,
&format!(
"{}\n{unwrap_whitespace}dump()\n{unwrap_whitespace}<end>",
line
),
);
}
}
let file_contents = parser::fix_input(file.unwrap());
// ...
if do_log_text {
@ -97,24 +56,6 @@ fn main() {
}
}
fn get_next_line_with_content(lines: Vec<&str>, starting_index: usize) -> Option<String> {
let mut i = starting_index.clone() + 1;
let mut next = lines.get(i);
// if the line doesn't exist OR is empty and is NOT the last line, look for next line
while (next.is_none() | (next.is_some() && next.unwrap().is_empty())) && i < lines.len() {
i += 1;
next = lines.get(i);
}
if next.is_some() {
return Option::Some(next.unwrap().to_string());
}
// return
return Option::None;
}
pub fn unix_epoch_timestamp() -> u128 {
let right_now = SystemTime::now();
let time_since = right_now

View File

@ -276,7 +276,6 @@ pub fn index(args: Vec<ReturnValue>, vars: VariablesStore) -> ReturnValue {
/// Returns a copy of the array with each item represented as a tuple containing the index and item
///
///
/// # Arguments:
/// * `check_for` - [any](Rule)
///
@ -286,6 +285,13 @@ pub fn index(args: Vec<ReturnValue>, vars: VariablesStore) -> ReturnValue {
/// arr.enumerate() # [(0, "apples"), (1, "oranges"), (2, "bananas")]
/// ```
///
/// ## Numbers Example:
/// ```py
/// arr = [1, 2, 3, 4, 5]
/// for tup in arr.enumerate():
/// printr(tup)
/// ```
///
/// # See:
/// * Rust <https://doc.rust-lang.org/std/iter/struct.Enumerate.html>
pub fn enumerate(vars: VariablesStore) -> ReturnValue {

View File

@ -1,5 +1,5 @@
pub mod array;
pub mod default; // default methods, inherited by all instances
pub mod json;
pub mod string;
pub mod table;
pub mod tuple;

View File

@ -220,6 +220,78 @@ pub fn has(args: Vec<ReturnValue>, vars: VariablesStore) -> ReturnValue {
contains(args, vars)
}
/// Returns a copy of the table with each item represented as a tuple containing the key and item
///
/// # Arguments:
/// * `check_for` - [any](Rule)
///
/// # Example:
/// ```py
/// arr = ["apples", "oranges", "bananas"]
/// arr.enumerate() # [(0, "apples"), (1, "oranges"), (2, "bananas")]
/// ```
///
/// # See:
/// * Rust <https://doc.rust-lang.org/std/iter/struct.Enumerate.html>
///
/// # Aliases:
/// - [`iter()`]
pub fn enumerate(vars: VariablesStore) -> ReturnValue {
// get the object this method is being called on
let fn_self = vars.get("self"); // 'self'
if (fn_self.is_none()) | (fn_self.is_some() && fn_self.unwrap().rule != Rule::table) {
error_exit(
"TYPE",
"expected method to be called on an object with type table",
)
}
// get children
let old_children = fn_self.unwrap().attributes.as_ref().unwrap();
// create new children array
let mut children: Vec<ReturnValue> = Vec::new();
for (i, v) in old_children.kv.iter() {
let mut tuple_children: Vec<ReturnValue> = Vec::new();
// ...index
tuple_children.push(ReturnValue {
rule: Rule::string,
value: format!("\"{}\"", i.to_string()),
children: Option::None,
attributes: Option::None,
});
// ...value
tuple_children.push(v.to_owned());
// push
children.push(ReturnValue {
rule: Rule::tuple,
value: String::from("dynamic"),
children: Option::Some(tuple_children),
attributes: Option::None,
})
}
// ...
return ReturnValue {
rule: Rule::array,
value: String::from("dynamic"),
children: Option::Some(children),
attributes: Option::None,
};
}
/// Returns a copy of the table with each item represented as a tuple containing the key and item
///
/// See: [`enumerate()`]
pub fn iter(vars: VariablesStore) -> ReturnValue {
enumerate(vars)
}
// ...
/// Import module
pub fn import() -> FunctionsStore {
@ -251,7 +323,12 @@ pub fn import() -> FunctionsStore {
Box::new(|args, vars| has(args, vars)),
);
// TODO: table::into_array/table::into_tuple
functions.insert(
String::from("#table::enumerate"),
Box::new(|_, vars| enumerate(vars)),
);
functions.insert(String::from("#table::iter"), Box::new(|_, vars| iter(vars)));
// return
return functions;

View File

@ -321,6 +321,66 @@ pub fn r#type(args: Vec<ReturnValue>) -> ReturnValue {
};
}
/// Import an external script
///
/// <div class="warning">
/// This could likely be replaced with support for <code>import</code> at some point in the future.
/// </div>
///
/// # Arguments:
/// * `path` - The path of the external script relative to the current working directory
///
/// # Example:
/// ```py
/// # import from
/// module = table {}
///
/// def module:testfn():
/// print("called module function")
///
/// module.testkey = "got module value"
///
/// return module
/// ```
///
/// ```py
/// # import to
/// imported = require("./other.py")
/// print(imported.testkey)
/// imported:testfn()
/// print(imported.iter())
/// ```
pub fn require(args: Vec<ReturnValue>) -> ReturnValue {
// get args
let arg = args.get(0);
if (arg.is_none()) | (arg.is_some() && arg.unwrap().rule != Rule::string) {
error_exit(
"ARGS,TYPE",
"expected function to be called with an argument of type string",
)
}
// import
let file_content = crate::parser::fix_input(
std::fs::read_to_string(arg.unwrap().value.clone().replace("\"", ""))
.expect("failed to read file from import"),
);
// parse
let content = crate::parser::parse(&file_content);
let res = crate::interpret::from_tree(&content.into_inner(), Option::None, Option::None);
// read "return" variable
let returned = res
.kv
.get("return")
.expect("imported module does not export anything");
// return
return returned.to_owned();
}
// ...
/// Import module
pub fn import() -> FunctionsStore {
@ -420,6 +480,7 @@ pub fn import() -> FunctionsStore {
// R
functions.insert(String::from("#range"), Box::new(|args, _| range(args)));
functions.insert(String::from("#repr"), Box::new(|args, _| repr(args)));
functions.insert(String::from("#require"), Box::new(|args, _| require(args)));
// TODO: "reversed" (https://docs.python.org/3/library/functions.html#reversed)
// TODO: "round" (https://docs.python.org/3/library/functions.html#round)

View File

@ -16,3 +16,68 @@ pub fn parse(input: &str) -> Pair<'_, Rule> {
// return
return res.unwrap().next().unwrap();
}
/// Fix file content to support nested syntax
pub fn fix_input(mut file_contents: String) -> String {
// every file is going to call the "dump" function, which does nothing
// this makes it where we can end with a function/conditional
file_contents += "\ndump()";
// convert lua comments to python comments
file_contents = file_contents.replace(r"-- ", "# ");
// TODO: fix weird comma bug with arrays/tuples
// file_contents = file_contents.replace(", ", ",");
// TODO: remove \n in arrays and tuples
// add ending tags
// we'll need these to detect the end of a scoped block
let file_contents_c = file_contents.clone();
let lines = file_contents_c.lines().collect::<Vec<&str>>();
for (i, line) in lines.clone().iter().enumerate() {
// get whitespace
let whitespace = line.replace(line.trim_start(), "");
// get next line
let next = get_next_line_with_content(lines.clone(), i);
if next.is_none() {
continue;
}
// if next line does not start with the same whitespace, add closing tag
let unwrap = next.unwrap();
let unwrap_whitespace = unwrap.replace(unwrap.trim_start(), "");
if (unwrap_whitespace.len() < whitespace.len()) | (!unwrap.starts_with(&whitespace)) {
file_contents = file_contents.replace(
line,
&format!(
"{}\n{unwrap_whitespace}dump()\n{unwrap_whitespace}<end>",
line
),
);
}
}
// return
return file_contents;
}
fn get_next_line_with_content(lines: Vec<&str>, starting_index: usize) -> Option<String> {
let mut i = starting_index.clone() + 1;
let mut next = lines.get(i);
// if the line doesn't exist OR is empty and is NOT the last line, look for next line
while (next.is_none() | (next.is_some() && next.unwrap().is_empty())) && i < lines.len() {
i += 1;
next = lines.get(i);
}
if next.is_some() {
return Option::Some(next.unwrap().to_string());
}
// return
return Option::None;
}