diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-11-15 18:13:44 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-11-15 18:14:09 -0500 |
| commit | 290c72732f21f1cddb3a0f8ec3213e4ec321da14 (patch) | |
| tree | 7c6c939a8d563edda5afddefcc2810d550ede0f3 | |
| parent | a1884f7a85cbee5a67cf48c9e7b088fdea8b8b38 (diff) | |
Add Path.lines()
| -rw-r--r-- | CHANGES.md | 1 | ||||
| -rw-r--r-- | api/api.md | 71 | ||||
| -rw-r--r-- | api/paths.md | 28 | ||||
| -rw-r--r-- | api/paths.yaml | 25 | ||||
| -rw-r--r-- | man/man3/tomo-Path.by_line.3 | 10 | ||||
| -rw-r--r-- | src/environment.c | 1 | ||||
| -rw-r--r-- | src/stdlib/paths.c | 22 | ||||
| -rw-r--r-- | src/stdlib/paths.h | 1 |
8 files changed, 142 insertions, 17 deletions
@@ -11,6 +11,7 @@ - Syntax for text literals and inline C code has been simplified somewhat. - Syntax for tables has changed to use colons (`{k: v}`) instead of equals (`{k=v}`). +- Added `Path.lines()` - Deprecated: - Sets are no longer a separate type with separate methods. - Instead of sets, use tables with a value type of `{KeyType:Empty}`. @@ -342,6 +342,49 @@ assert [x for x in Byte(2).to(5, step=2)] == [Byte(2), Byte(4)] ``` +# CString +## CString.as_text + +```tomo +CString.as_text : func(str: CString -> Text) +``` + +Convert a C string to Text. + +Argument | Type | Description | Default +---------|------|-------------|--------- +str | `CString` | The C string. | - + +**Return:** The C string as a Text. + + +**Example:** +```tomo +assert CString("Hello").as_text() == "Hello" + +``` +## CString.join + +```tomo +CString.join : func(glue: CString, pieces: [CString] -> CString) +``` + +Join a list of C strings together with a separator. + +Argument | Type | Description | Default +---------|------|-------------|--------- +glue | `CString` | The C joiner used to between elements. | - +pieces | `[CString]` | A list of C strings to join. | - + +**Return:** A C string of the joined together bits. + + +**Example:** +```tomo +assert CString(",").join([CString("a"), CString("b")]) == CString("a,b") + +``` + # Int ## Int.abs @@ -2565,14 +2608,14 @@ path | `Path` | The path of the file. | - ```tomo # Safely handle file not being readable: if lines := (./file.txt).by_line() -for line in lines -say(line.upper()) + for line in lines + say(line.upper()) else -say("Couldn't read file!") + say("Couldn't read file!") # Assume the file is readable and error if that's not the case: for line in (/dev/stdin).by_line()! -say(line.upper()) + say(line.upper()) ``` ## Path.can_execute @@ -3019,6 +3062,26 @@ path | `Path` | The path to check. | - assert (./link).is_symlink() == yes ``` +## Path.lines + +```tomo +Path.lines : func(path: Path -> [Text]?) +``` + +Returns a list with the lines of text in a file or returns none if the file could not be opened. + +Argument | Type | Description | Default +---------|------|-------------|--------- +path | `Path` | The path of the file. | - + +**Return:** A list of the lines in a file or none if the file couldn't be read. + + +**Example:** +```tomo +lines := (./file.txt).lines()! + +``` ## Path.modified ```tomo diff --git a/api/paths.md b/api/paths.md index c69e91d9..07f0560b 100644 --- a/api/paths.md +++ b/api/paths.md @@ -108,14 +108,14 @@ path | `Path` | The path of the file. | - ```tomo # Safely handle file not being readable: if lines := (./file.txt).by_line() -for line in lines -say(line.upper()) + for line in lines + say(line.upper()) else -say("Couldn't read file!") + say("Couldn't read file!") # Assume the file is readable and error if that's not the case: for line in (/dev/stdin).by_line()! -say(line.upper()) + say(line.upper()) ``` ## Path.can_execute @@ -562,6 +562,26 @@ path | `Path` | The path to check. | - assert (./link).is_symlink() == yes ``` +## Path.lines + +```tomo +Path.lines : func(path: Path -> [Text]?) +``` + +Returns a list with the lines of text in a file or returns none if the file could not be opened. + +Argument | Type | Description | Default +---------|------|-------------|--------- +path | `Path` | The path of the file. | - + +**Return:** A list of the lines in a file or none if the file couldn't be read. + + +**Example:** +```tomo +lines := (./file.txt).lines()! + +``` ## Path.modified ```tomo diff --git a/api/paths.yaml b/api/paths.yaml index 532d9c71..8fbd18dc 100644 --- a/api/paths.yaml +++ b/api/paths.yaml @@ -107,14 +107,31 @@ Path.by_line: example: | # Safely handle file not being readable: if lines := (./file.txt).by_line() - for line in lines - say(line.upper()) + for line in lines + say(line.upper()) else - say("Couldn't read file!") + say("Couldn't read file!") # Assume the file is readable and error if that's not the case: for line in (/dev/stdin).by_line()! - say(line.upper()) + say(line.upper()) + +Path.lines: + short: return the lines in a file + description: > + Returns a list with the lines of text in a file or returns none if the file + could not be opened. + return: + type: '[Text]?' + description: > + A list of the lines in a file or none if the file couldn't be read. + args: + path: + type: 'Path' + description: > + The path of the file. + example: | + lines := (./file.txt).lines()! Path.can_execute: short: check execute permissions diff --git a/man/man3/tomo-Path.by_line.3 b/man/man3/tomo-Path.by_line.3 index ff7a737c..02a75c5a 100644 --- a/man/man3/tomo-Path.by_line.3 +++ b/man/man3/tomo-Path.by_line.3 @@ -2,7 +2,7 @@ .\" Copyright (c) 2025 Bruce Hill .\" All rights reserved. .\" -.TH Path.by_line 3 2025-05-17 "Tomo man-pages" +.TH Path.by_line 3 2025-11-15 "Tomo man-pages" .SH NAME Path.by_line \- iterate by line .SH LIBRARY @@ -31,12 +31,12 @@ An iterator that can be used to get lines from a file one at a time or none if t .EX # Safely handle file not being readable: if lines := (./file.txt).by_line() -for line in lines -say(line.upper()) + for line in lines + say(line.upper()) else -say("Couldn't read file!") + say("Couldn't read file!") # Assume the file is readable and error if that's not the case: for line in (/dev/stdin).by_line()! -say(line.upper()) + say(line.upper()) .EE diff --git a/src/environment.c b/src/environment.c index 96595ac7..8f49f86e 100644 --- a/src/environment.c +++ b/src/environment.c @@ -325,6 +325,7 @@ env_t *global_env(bool source_mapping) { {"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"}, // {"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"}, // {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, // + {"lines", "Path$lines", "func(path:Path -> [Text]?)"}, // {"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"}, // {"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"}, // {"parent", "Path$parent", "func(path:Path -> Path)"}, // diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index 810f98b1..704421c2 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -704,6 +704,28 @@ OptionalClosure_t Path$by_line(Path_t path) { } public +OptionalList_t Path$lines(Path_t path) { + const char *path_str = Path$as_c_string(path); + FILE *f = fopen(path_str, "r"); + if (f == NULL) { + if (errno == EMFILE || errno == ENFILE) { + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will + // be closed by GC finalizers. + GC_gcollect(); + f = fopen(path_str, "r"); + } + } + + if (f == NULL) return NONE_LIST; + + List_t lines = {}; + for (OptionalText_t line; (line = _next_line(&f)).tag != TEXT_NONE;) { + List$insert(&lines, &line, I(0), sizeof(line)); + } + return lines; +} + +public List_t Path$glob(Path_t path) { glob_t glob_result; int status = glob(Path$as_c_string(path), GLOB_BRACE | GLOB_TILDE, NULL, &glob_result); diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index ce6de1c8..3b1f3ce6 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -57,6 +57,7 @@ Path_t Path$sibling(Path_t path, Text_t name); Path_t Path$with_extension(Path_t path, Text_t extension, bool replace); Path_t Path$current_dir(void); Closure_t Path$by_line(Path_t path); +OptionalList_t Path$lines(Path_t path); List_t Path$glob(Path_t path); uint64_t Path$hash(const void *obj, const TypeInfo_t *); |
