commit fbd53d54781074bc73ec3304b972aff4b9487103
parent 721dafa51156bc0e4004cce60c35707c131bec53
Author: Byron Torres <b@torresjrjr.com>
Date: Wed, 14 Dec 2022 03:36:35 +0000
add addr_regex(), /regex/ addresses
Diffstat:
4 files changed, 90 insertions(+), 10 deletions(-)
diff --git a/address.ha b/address.ha
@@ -6,14 +6,26 @@ type address = struct {
setcurrentline: bool,
};
-type addresstype = (size | currentline | lastline | rune);
+type addresstype = (size | currentline | lastline | rune | regexaddr);
+// The dot '.' address.
type currentline = void;
+// The dollar '$' address.
type lastline = void;
+// Regular expression search. /forward/ is true, ?backward? is false.
+type regexaddr = struct {
+ expr: str,
+ direction: bool,
+};
+
+// The address was invalid.
type invalidaddress = !void;
+// A regular expression search found no match.
+type nomatch = !void;
+
fn addr_nextline(buf: *buffer, n: size) size = {
if (len(buf.lines) - 1 == n) {
return n;
@@ -48,3 +60,30 @@ fn addr_mark(buf: *buffer, mark: rune) (size | invalidaddress) = {
};
return invalidaddress;
};
+
+fn addr_regex(
+ buf: *buffer,
+ rad: regexaddr,
+ start: size,
+) (size | nomatch | regex::error) = {
+ const length = len(buf.lines);
+ // TODO: use BRE, not ERE.
+ const re = regex::compile(rad.expr)?; defer regex::finish(&re);
+
+ for (let x = 1z; x <= length; x += 1) {
+ const n = if (rad.direction) {
+ yield (start + x) % length;
+ } else {
+ yield (start - x) % length;
+ };
+ if (n == 0) {
+ continue;
+ };
+
+ if (regex::test(&re, buf.lines[n].text)) {
+ return n;
+ };
+ };
+
+ return nomatch;
+};
diff --git a/command.ha b/command.ha
@@ -3,6 +3,7 @@ use fmt;
use fs;
use io;
use os;
+use regex;
use strings;
type command = struct {
@@ -30,6 +31,8 @@ type error = !(
| invaliddestination
| nofilename
| buffermodified
+ | nomatch
+ | regex::error
| fs::error
);
@@ -85,6 +88,8 @@ fn exec_addr(s: *session, addr: address) (size | error) = {
return addr_lastline(&s.buf);
case let m: rune =>
return addr_mark(&s.buf, m)?;
+ case let rad: regexaddr =>
+ return addr_regex(&s.buf, rad, s.buf.cursor)?;
};
};
diff --git a/main.ha b/main.ha
@@ -5,6 +5,7 @@ use fs;
use getopt;
use io;
use os;
+use regex;
use strings;
type session = struct {
@@ -136,6 +137,10 @@ fn errormsg(s: *session, err: error) error = {
yield "Unexpected address";
case invaliddestination =>
yield "Invalid destination";
+ case nomatch =>
+ yield "No match";
+ case let e: regex::error =>
+ yield regex::strerror(e);
case let e: fs::error =>
yield fs::strerror(e);
};
diff --git a/parse.ha b/parse.ha
@@ -1,6 +1,5 @@
-use fmt;
-
use ascii;
+use regex;
use strconv;
use strings;
@@ -116,13 +115,15 @@ fn scan_addr(iter: *strings::iterator) (address | void) = {
} else if (r == '\'') {
yield scan_mark(iter);
} else if (r == '/') {
- // const re = scan_regex(iter, r);j
- // ...
- abort("TODO: /regex/");
+ yield regexaddr {
+ expr = scan_regex(iter, '/'),
+ direction = true,
+ };
} else if (r == '?') {
- // const re = scan_regex(iter, r);j
- // ...
- abort("TODO: ?regex?");
+ yield regexaddr {
+ expr = scan_regex(iter, '?'),
+ direction = false,
+ };
} else {
strings::prev(iter);
yield void;
@@ -253,10 +254,39 @@ fn scan_arg(iter: *strings::iterator) str = {
return strings::trim(strings::fromrunes(rs));
};
+fn scan_regex(iter: *strings::iterator, end: rune) str = {
+ let rs: []rune = [];
+ for (true) {
+ let r = match (strings::next(iter)) {
+ case void =>
+ break;
+ case let r: rune =>
+ yield r;
+ };
+ if (r == '\\') {
+ match (strings::next(iter)) {
+ case void =>
+ break; // TODO: Error here? how?
+ case let r: rune =>
+ if (r == end) {
+ append(rs, r);
+ } else {
+ append(rs, ['\\', r]...);
+ };
+ continue;
+ };
+ } else if (r == end) {
+ break;
+ };
+ append(rs, r);
+ };
+ return strings::fromrunes(rs);
+};
+
fn scan_mark(iter: *strings::iterator) rune = {
match (strings::next(iter)) {
case void =>
- abort();
+ abort(); // TODO: aborts?
case let r: rune =>
if (ascii::isalpha(r)) { // TODO: cover all mark chars
return r;
@@ -266,6 +296,7 @@ fn scan_mark(iter: *strings::iterator) rune = {
};
};
+// TODO: rename and appropriate to "scan_size()"?
fn scan_uint(iter: *strings::iterator) uint = {
let num: []u8 = [];
defer free(num);