Skip to content

Commit

Permalink
Builtin lines function part II - Functionality (#566)
Browse files Browse the repository at this point in the history
  • Loading branch information
hdwalters authored Nov 16, 2024
1 parent 7219c34 commit 6a08f86
Show file tree
Hide file tree
Showing 16 changed files with 313 additions and 17 deletions.
80 changes: 80 additions & 0 deletions src/modules/builtin/lines.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use heraclitus_compiler::prelude::*;
use crate::docs::module::DocumentationModule;
use crate::modules::expression::expr::Expr;
use crate::modules::types::{Type, Typed};
use crate::translate::module::TranslateModule;
use crate::utils::metadata::{ParserMetadata, TranslateMetadata};

#[derive(Debug, Clone)]
pub struct LinesInvocation {
path: Box<Option<Expr>>,
}

impl Typed for LinesInvocation {
fn get_type(&self) -> Type {
Type::Array(Box::new(Type::Text))
}
}

impl SyntaxModule<ParserMetadata> for LinesInvocation {
syntax_name!("Lines Invocation");

fn new() -> Self {
LinesInvocation {
path: Box::new(None)
}
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
token(meta, "lines")?;
token(meta, "(")?;
let tok = meta.get_current_token();
let mut path = Expr::new();
syntax(meta, &mut path)?;
token(meta, ")")?;
if path.get_type() != Type::Text {
let msg = format!("Expected value of type 'Text' but got '{}'", path.get_type());
return error!(meta, tok, msg);
}
self.path = Box::new(Some(path));
Ok(())
}
}

impl TranslateModule for LinesInvocation {
fn translate(&self, meta: &mut TranslateMetadata) -> String {
let name = format!("__AMBER_ARRAY_{}", meta.gen_array_id());
let temp = format!("__AMBER_LINE_{}", meta.gen_value_id());
let path = (*self.path).as_ref()
.map(|p| p.translate_eval(meta, false))
.unwrap_or_default();
let quote = meta.gen_quote();
let dollar = meta.gen_dollar();
let indent = TranslateMetadata::single_indent();
let block = [
format!("{name}=()"),
format!("while IFS= read -r {temp}; do"),
format!("{indent}{name}+=(\"${temp}\")"),
format!("done <{path}"),
].join("\n");
meta.stmt_queue.push_back(block);
format!("{quote}{dollar}{{{name}[@]}}{quote}")
}
}

impl LinesInvocation {
pub fn surround_iter(&self, meta: &mut TranslateMetadata, name: &str) -> (String, String) {
let path = (*self.path).as_ref()
.map(|p| p.translate(meta))
.unwrap_or_default();
let prefix = format!("while IFS= read -r {name}; do");
let suffix = format!("done <{path}");
(prefix, suffix)
}
}

impl DocumentationModule for LinesInvocation {
fn document(&self, _meta: &ParserMetadata) -> String {
"".to_string()
}
}
1 change: 1 addition & 0 deletions src/modules/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pub mod mv;
pub mod nameof;
pub mod exit;
pub mod len;
pub mod lines;
8 changes: 8 additions & 0 deletions src/modules/expression/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use super::parentheses::Parentheses;
use crate::modules::variable::get::VariableGet;
use super::ternop::ternary::Ternary;
use crate::modules::function::invocation::FunctionInvocation;
use crate::modules::builtin::lines::LinesInvocation;
use crate::modules::builtin::nameof::Nameof;
use crate::{document_expression, parse_expr, parse_expr_group, translate_expression};

Expand All @@ -72,6 +73,7 @@ pub enum ExprType {
Neq(Neq),
Not(Not),
Ternary(Ternary),
LinesInvocation(LinesInvocation),
FunctionInvocation(FunctionInvocation),
Command(Command),
Array(Array),
Expand Down Expand Up @@ -150,6 +152,8 @@ impl SyntaxModule<ParserMetadata> for Expr {
// Literals
Parentheses, Bool, Number, Text,
Array, Null, Status, Nameof,
// Builtin invocation
LinesInvocation,
// Function invocation
FunctionInvocation, Command,
// Variable access
Expand Down Expand Up @@ -179,6 +183,8 @@ impl TranslateModule for Expr {
// Literals
Parentheses, Bool, Number, Text,
Array, Null, Status,
// Builtin invocation
LinesInvocation,
// Function invocation
FunctionInvocation, Command,
// Variable access
Expand All @@ -205,6 +211,8 @@ impl DocumentationModule for Expr {
// Literals
Parentheses, Bool, Number, Text,
Array, Null, Status,
// Builtin invocation
LinesInvocation,
// Function invocation
FunctionInvocation, Command,
// Variable access
Expand Down
34 changes: 21 additions & 13 deletions src/modules/loops/iter_loop.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use heraclitus_compiler::prelude::*;
use crate::docs::module::DocumentationModule;
use crate::modules::expression::expr::Expr;
use crate::modules::expression::expr::{Expr, ExprType};
use crate::modules::types::{Typed, Type};
use crate::modules::variable::variable_name_extensions;
use crate::translate::module::TranslateModule;
Expand Down Expand Up @@ -71,35 +71,43 @@ impl SyntaxModule<ParserMetadata> for IterLoop {

impl TranslateModule for IterLoop {
fn translate(&self, meta: &mut TranslateMetadata) -> String {
let name = &self.iter_name;
let expr = self.iter_expr.translate(meta);
let (prefix, suffix) = self.surround_iter(meta);
match self.iter_index.as_ref() {
Some(index) => {
// Create an indentation for the index increment
meta.increase_indent();
let indent = meta.gen_indent();
meta.decrease_indent();
let indent = TranslateMetadata::single_indent();
[
format!("{index}=0;"),
format!("for {name} in {expr}"),
"do".to_string(),
prefix,
self.block.translate(meta),
format!("{indent}(( {index}++ )) || true"),
"done".to_string(),
suffix,
].join("\n")
},
None => {
[
format!("for {name} in {expr}"),
"do".to_string(),
prefix,
self.block.translate(meta),
"done".to_string(),
suffix,
].join("\n")
},
}
}
}

impl IterLoop {
fn surround_iter(&self, meta: &mut TranslateMetadata) -> (String, String) {
let name = &self.iter_name;
if let Some(ExprType::LinesInvocation(value)) = &self.iter_expr.value {
value.surround_iter(meta, name)
} else {
let expr = self.iter_expr.translate(meta);
let prefix = format!("for {name} in {expr}; do");
let suffix = String::from("done");
(prefix, suffix)
}
}
}

impl DocumentationModule for IterLoop {
fn document(&self, _meta: &ParserMetadata) -> String {
"".to_string()
Expand Down
2 changes: 1 addition & 1 deletion src/std/text.ab
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fun split(text: Text, delimiter: Text): [Text] {
}

/// Splits a `text` into an array of substrings based on newline characters.
pub fun lines(text: Text): [Text] {
pub fun split_lines(text: Text): [Text] {
return split(text, "\n")
}

Expand Down
4 changes: 2 additions & 2 deletions src/tests/stdlib/lines.ab → src/tests/stdlib/split_lines.ab
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { lines } from "std/text"
import { split_lines } from "std/text"

// Output
// line: hello
// line: world

main {
for line in lines("hello\nworld") {
for line in split_lines("hello\nworld") {
echo "line: " + line
}
}
20 changes: 20 additions & 0 deletions src/tests/validity/lines_add_file_file.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four' >{tmpdir}/numbers1.txt $
trust $ echo -e '\nfive\n six ' >{tmpdir}/numbers2.txt $

// Inefficient for large files.
let lines = lines("{tmpdir}/numbers1.txt") + lines("{tmpdir}/numbers2.txt")
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
19 changes: 19 additions & 0 deletions src/tests/validity/lines_add_file_text.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four' >{tmpdir}/numbers1.txt $

// Inefficient for large files.
let lines = lines("{tmpdir}/numbers1.txt") + ["", "five", " six "]
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
19 changes: 19 additions & 0 deletions src/tests/validity/lines_add_text_file.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e '\nfive\n six ' >{tmpdir}/numbers2.txt $

// Inefficient for large files.
let lines = ["one", "two three four"] + lines("{tmpdir}/numbers2.txt")
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
21 changes: 21 additions & 0 deletions src/tests/validity/lines_append.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four' >{tmpdir}/numbers1.txt $
trust $ echo -e '\nfive\n six ' >{tmpdir}/numbers2.txt $

// Inefficient for large files.
let lines = lines("{tmpdir}/numbers1.txt")
lines += lines("{tmpdir}/numbers2.txt")
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
25 changes: 25 additions & 0 deletions src/tests/validity/lines_append_ref.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

fun append_ref(ref inner: [Text], path: Text): Null {
inner += lines(path)
}

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four' >{tmpdir}/numbers1.txt $
trust $ echo -e '\nfive\n six ' >{tmpdir}/numbers2.txt $

// Inefficient for large files.
let lines = lines("{tmpdir}/numbers1.txt")
append_ref(lines, "{tmpdir}/numbers2.txt")
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
26 changes: 26 additions & 0 deletions src/tests/validity/lines_iter.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]
// 0 [one]
// 1 [two three four]
// 2 []
// 3 [five]
// 4 [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four\n\nfive\n six ' >{tmpdir}/numbers.txt $

// Efficient for large files.
for line in lines("{tmpdir}/numbers.txt") {
echo "[{line}]"
}
for index, line in lines("{tmpdir}/numbers.txt") {
echo "{index} [{line}]"
}

trust $ rm -rf {tmpdir} $
}
19 changes: 19 additions & 0 deletions src/tests/validity/lines_let.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four\n\nfive\n six ' >{tmpdir}/numbers.txt $

// Inefficient for large files.
let lines = lines("{tmpdir}/numbers.txt")
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
20 changes: 20 additions & 0 deletions src/tests/validity/lines_set.ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Output
// [one]
// [two three four]
// []
// [five]
// [ six ]

main {
let tmpdir = trust $ mktemp -d $
trust $ echo -e 'one\ntwo three four\n\nfive\n six ' >{tmpdir}/numbers.txt $

// Inefficient for large files.
let lines = ["a", "b", "c"]
lines = lines("{tmpdir}/numbers.txt")
for line in lines {
echo "[{line}]"
}

trust $ rm -rf {tmpdir} $
}
Loading

0 comments on commit 6a08f86

Please sign in to comment.