ed

[hare] The standard editor
Log | Files | Refs | README | LICENSE

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:
Maddress.ha | 41++++++++++++++++++++++++++++++++++++++++-
Mcommand.ha | 5+++++
Mmain.ha | 5+++++
Mparse.ha | 49++++++++++++++++++++++++++++++++++++++++---------
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);