From fc1a334ed93cb5d524132a31548f55e1ce1b5520 Mon Sep 17 00:00:00 2001 From: Phoenix Himself Date: Mon, 28 Oct 2024 12:31:12 +0100 Subject: [PATCH] Feature: Len #441 (#545) Co-authored-by: Daniele Scasciafratte --- src/modules/builtin/len.rs | 75 ++++++++++++++++++++++++++++++++++ src/modules/builtin/mod.rs | 1 + src/modules/expression/expr.rs | 16 +++++--- src/modules/variable/mod.rs | 3 +- src/std/fs.ab | 2 +- src/std/text.ab | 11 ----- src/tests/stdlib/len_list.ab | 8 ---- src/tests/stdlib/len_string.ab | 8 ---- src/tests/validity/len.ab | 22 ++++++++++ 9 files changed, 111 insertions(+), 35 deletions(-) create mode 100644 src/modules/builtin/len.rs delete mode 100644 src/tests/stdlib/len_list.ab delete mode 100644 src/tests/stdlib/len_string.ab create mode 100644 src/tests/validity/len.ab diff --git a/src/modules/builtin/len.rs b/src/modules/builtin/len.rs new file mode 100644 index 00000000..a48d283c --- /dev/null +++ b/src/modules/builtin/len.rs @@ -0,0 +1,75 @@ +use crate::docs::module::DocumentationModule; +use crate::modules::expression::expr::Expr; +use crate::modules::expression::unop::UnOp; +use crate::modules::types::{Type, Typed}; +use crate::translate::module::TranslateModule; +use crate::utils::{ParserMetadata, TranslateMetadata}; +use heraclitus_compiler::prelude::*; + +#[derive(Debug, Clone)] +pub struct Len { + value: Box, +} + +impl Typed for Len { + fn get_type(&self) -> Type { + Type::Num + } +} + +impl UnOp for Len { + fn set_expr(&mut self, expr: Expr) { + self.value = Box::new(expr); + } + + fn parse_operator(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + token(meta, "len")?; + Ok(()) + } +} + +impl SyntaxModule for Len { + syntax_name!("Length"); + + fn new() -> Self { + Len { + value: Box::new(Expr::new()), + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + if !matches!(self.value.get_type(), Type::Text | Type::Array(_)) { + let msg = self + .value + .get_error_message(meta) + .message("Length can only be applied to text or array types"); + return Err(Failure::Loud(msg)); + } + Ok(()) + } +} + +impl TranslateModule for Len { + fn translate(&self, meta: &mut TranslateMetadata) -> String { + let value = self.value.translate(meta); + if self.value.get_type() == Type::Text { + meta.stmt_queue.push_back(format!("__AMBER_LEN={value}")); + return String::from("\"${#__AMBER_LEN}\"") + } + // Case for Array passed as a reference + if value.starts_with("\"${!") { + meta.stmt_queue.push_back(format!("__AMBER_LEN=({value})")); + String::from("\"${#__AMBER_LEN[@]}\"") + } else { + format!("\"${{#{}", value.trim_start_matches("\"${")) + .trim_end() + .to_string() + } + } +} + +impl DocumentationModule for Len { + fn document(&self, _meta: &ParserMetadata) -> String { + "".to_string() + } +} diff --git a/src/modules/builtin/mod.rs b/src/modules/builtin/mod.rs index b3042c40..4d80b82c 100644 --- a/src/modules/builtin/mod.rs +++ b/src/modules/builtin/mod.rs @@ -3,3 +3,4 @@ pub mod echo; pub mod mv; pub mod nameof; pub mod exit; +pub mod len; diff --git a/src/modules/expression/expr.rs b/src/modules/expression/expr.rs index 368f70aa..0c964d91 100644 --- a/src/modules/expression/expr.rs +++ b/src/modules/expression/expr.rs @@ -1,5 +1,6 @@ use heraclitus_compiler::prelude::*; use crate::docs::module::DocumentationModule; +use crate::modules::builtin::len::Len; use crate::modules::command::cmd::Command; use crate::modules::expression::binop::BinOp; use crate::modules::types::{Typed, Type}; @@ -79,6 +80,7 @@ pub enum ExprType { Cast(Cast), Status(Status), Nameof(Nameof), + Len(Len), Is(Is), } @@ -143,11 +145,11 @@ impl SyntaxModule for Expr { addition @ BinOp => [ Add, Sub ], multiplication @ BinOp => [ Mul, Div, Modulo ], types @ TypeOp => [ Is, Cast ], - unops @ UnOp => [ Neg, Not ], + unops @ UnOp => [ Neg, Not, Len ], literals @ Literal => [ // Literals Parentheses, Bool, Number, Text, - Array, Null, Nameof, Status, + Array, Null, Status, Nameof, // Function invocation FunctionInvocation, Command, // Variable access @@ -173,9 +175,10 @@ impl TranslateModule for Expr { // Binary operators Range, Cast, Is, // Unary operators - Not, Neg, Nameof, + Not, Neg, Nameof, Len, // Literals - Parentheses, Bool, Number, Text, Array, Null, Status, + Parentheses, Bool, Number, Text, + Array, Null, Status, // Function invocation FunctionInvocation, Command, // Variable access @@ -198,9 +201,10 @@ impl DocumentationModule for Expr { // Binary operators Range, Cast, Is, // Unary operators - Not, Neg, Nameof, + Not, Neg, Nameof, Len, // Literals - Parentheses, Bool, Number, Text, Array, Null, Status, + Parentheses, Bool, Number, Text, + Array, Null, Status, // Function invocation FunctionInvocation, Command, // Variable access diff --git a/src/modules/variable/mod.rs b/src/modules/variable/mod.rs index 2c1d0ef9..f0acace9 100644 --- a/src/modules/variable/mod.rs +++ b/src/modules/variable/mod.rs @@ -32,7 +32,8 @@ pub fn variable_name_keywords() -> Vec<&'static str> { // Command Modifiers "silent", "trust", // Misc - "echo", "status", "nameof", "mv", "cd", "exit", + "echo", "status", "nameof", "mv", "cd", + "exit", "len", ] } diff --git a/src/std/fs.ab b/src/std/fs.ab index 056b5273..abddddc0 100644 --- a/src/std/fs.ab +++ b/src/std/fs.ab @@ -1,4 +1,4 @@ -import { join, len, replace_regex, split } from "std/text" +import { join, replace_regex, split } from "std/text" /// Checks if a directory exists. pub fun dir_exist(path) { diff --git a/src/std/text.ab b/src/std/text.ab index b65c16c4..5529293f 100644 --- a/src/std/text.ab +++ b/src/std/text.ab @@ -91,17 +91,6 @@ pub fun chars(text: Text): [Text] { return chars } -/// Gets the length of provided text or array. -#[allow_absurd_cast] -pub fun len(value): Num { - trust { - if value is Text: - return $ echo "\$\{#{nameof value}}" $ as Num - else: - return $ echo "\$\{#{nameof value}[@]}" $ as Num - } -} - /// Checks if some text contains a value/ pub fun contains(text: Text, phrase: Text): Bool { let result = trust $ if [[ "{text}" == *"{phrase}"* ]]; then diff --git a/src/tests/stdlib/len_list.ab b/src/tests/stdlib/len_list.ab deleted file mode 100644 index 4ee01d1e..00000000 --- a/src/tests/stdlib/len_list.ab +++ /dev/null @@ -1,8 +0,0 @@ -import * from "std/text" - -// Output -// 4 - -main { - echo len([1, 2, 3, 4]) -} diff --git a/src/tests/stdlib/len_string.ab b/src/tests/stdlib/len_string.ab deleted file mode 100644 index 7fd4e567..00000000 --- a/src/tests/stdlib/len_string.ab +++ /dev/null @@ -1,8 +0,0 @@ -import * from "std/text" - -// Output -// 5 - -main { - echo len("hello") -} diff --git a/src/tests/validity/len.ab b/src/tests/validity/len.ab new file mode 100644 index 00000000..8ae479c2 --- /dev/null +++ b/src/tests/validity/len.ab @@ -0,0 +1,22 @@ +// Output +// Text literal: 5 +// Array literal: 3 +// Text variable: 6 +// Array variable: 4 +// Text reference: 6 +// Array reference: 4 + +let name = "Andrew" +let fruits = ["apple", "banana", "cherry", "date"] + +fun foo(ref text, ref arr) { + echo "Text reference: {len text}" + echo "Array reference: {len arr}" +} + +echo "Text literal: {len "Hello"}" +echo "Array literal: {len [1, 2, 3]}" +echo "Text variable: {len name}" +echo "Array variable: {len fruits}" + +foo(name, fruits)