commit 6dfb054aea170b36344a04e02349543241ca4d7e
parent ef9ea186acb7f6e90a631d897c04095adb8fed2c
Author: Byron Torres <b@torresjrjr.com>
Date: Sun, 12 Sep 2021 23:37:56 +0100
Add address.ha
Diffstat:
4 files changed, 107 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,6 +3,7 @@ HAREFLAGS=
source=\
ed.ha \
+ address.ha \
buffer.ha \
operation.ha \
parse.ha \
diff --git a/address.ha b/address.ha
@@ -0,0 +1,103 @@
+use strings;
+
+// These functions do not mutate. Their job is to find linenodes by addresses.
+
+// Returns the linenode at a given line number.
+fn addr_linenum(buf: *buffer, n: size) *linenode = {
+ let ln = buf.head;
+ let i = 0z;
+
+ for (i < n) {
+ match (ln.value) {
+ headnode => abort("Unreachable"),
+ tailnode => abort("Invalid range"), // TODO: return error
+ str => ln = ln.next,
+ };
+ };
+
+ return ln;
+};
+
+// Returns the linenode of the last line in the edit buffer.
+fn addr_lastline(buf: *buffer) *linenode = {
+ let ln = buf.tail.prev;
+
+ match (ln.value) {
+ tailnode => abort("Unreachable"),
+ headnode => abort("TODO"), // TODO: ???
+ str => void,
+ };
+
+ return ln;
+};
+
+// Searches for the first linenode which matches a regular expression.
+fn addr_regex(buf: *buffer, ln: *linenode, re: str, dir: bool) *linenode = {
+ if (buf.head.next == buf.tail) {
+ // buffer empty
+ abort("No match"); // TODO: return error
+ };
+
+ const startln = ln;
+ ln = ln.next;
+
+ for (true) {
+ match (ln.value) {
+ headnode => ln = buf.tail.prev,
+ tailnode => ln = buf.head.next,
+ txt: str => {
+ if (startln == ln) {
+ abort("No match"); // TODO: return error
+ };
+ switch (strings::contains(txt, re)) { // TODO: use real regex
+ false => switch (dir) {
+ true => ln = ln.next,
+ false => ln = ln.prev,
+ },
+ true => return ln,
+ };
+ },
+ };
+ };
+
+ abort("Unreachable");
+};
+
+// Searches for the first linenode which matches a regular expression.
+fn addr_mark(buf: *buffer, mark: rune) *linenode = {
+ let ln = buf.head;
+ for (true) {
+ match (ln.value) {
+ headnode => ln = ln.next,
+ tailnode => abort("Invalid address"), // TODO: return error
+ str => if (ln.mark == mark) {
+ return ln;
+ } else {
+ ln = ln.next;
+ continue;
+ },
+ };
+ };
+
+ abort("Unreachable");
+};
+
+// Searches for the first linenode which matches a regular expression.
+fn addr_offset(buf: *buffer, ln: *linenode, n: size) *linenode = {
+ for (n != 0) {
+ match (ln.value) {
+ headnode => abort("Invalid address"), // TODO: return error
+ tailnode => abort("Invalid address"), // TODO: return error
+ str => if (n > 0) {
+ ln = ln.next;
+ n -= 1;
+ } else if (n < 0) {
+ ln = ln.prev;
+ n += 1;
+ },
+ };
+ };
+
+ return ln;
+};
+
diff --git a/buffer.ha b/buffer.ha
@@ -20,6 +20,7 @@ type linenode = struct {
value: value,
prev: *linenode,
next: *linenode,
+ mark: rune,
};
// Represents what kind of linenode a linenode is, or its content text.
diff --git a/ed.ha b/ed.ha
@@ -63,6 +63,8 @@ export fn main() void = {
for (let i = 0z; i < len(ops); i += 1) :ops {
const op = ops[i];
+ // TODO: refactor ops into function pointers,
+ // and move alias to func mapping to parse_cmd().
switch (op.cmd_alias) {
"e" => op_edit(buf),
"r" => op_read(buf),