commit d7e69608322c87733dc92ebb3702673ec29815c4
parent 14af620c5e9d3556a3fd98d960ab5979e671ec67
Author: Byron Torres <b@torresjrjr.com>
Date: Mon, 13 Sep 2021 18:58:20 +0100
Add cursor.ha, update parse.ha
Diffstat:
7 files changed, 166 insertions(+), 34 deletions(-)
diff --git a/Makefile b/Makefile
@@ -5,6 +5,7 @@ source=\
ed.ha \
address.ha \
buffer.ha \
+ cursor.ha \
operation.ha \
parse.ha \
util.ha
diff --git a/address.ha b/address.ha
@@ -3,16 +3,18 @@ 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 = {
+fn addr_linenum(buf: *buffer, n: uint) *linenode = {
let ln = buf.head;
- let i = 0z;
+ let i: uint = 0;
for (i < n) {
match (ln.value) {
- headnode => abort("Unreachable"),
tailnode => abort("Invalid range"), // TODO: return error
- str => ln = ln.next,
+ headnode => ln = ln.next,
+ str => ln = ln.next,
};
+
+ i += 1;
};
return ln;
@@ -83,7 +85,7 @@ fn addr_mark(buf: *buffer, mark: rune) *linenode = {
};
// Searches for the first linenode which matches a regular expression.
-fn addr_offset(buf: *buffer, ln: *linenode, n: size) *linenode = {
+fn addr_offset(buf: *buffer, ln: *linenode, n: uint) *linenode = {
for (n != 0) {
match (ln.value) {
headnode => abort("Invalid address"), // TODO: return error
diff --git a/cursor.ha b/cursor.ha
@@ -0,0 +1,13 @@
+fn cur_linenum(buf: *buffer, args: arg...) void = {
+ if (len(args) == 0) {
+ abort("TODO");
+ };
+
+// const n = match (args[0]) {
+// n: uint => yield n,
+// * => abort("TODO"),
+// };
+ const a = args[0];
+ const n = a: uint;
+ buf.cur.node = addr_linenum(buf, n);
+};
diff --git a/ed.ha b/ed.ha
@@ -58,12 +58,13 @@ export fn main() void = {
};
defer free(rawline);
- const command = fmt::bsprint(rawline);
- const ops = parse_cmd(command);
+ const input = fmt::bsprint(rawline);
+ const cmd = parse_input(input);
+ const ops = construct_ops(cmd);
for (let i = 0z; i < len(ops); i += 1) :ops {
const op = ops[i];
- op.func(buf);
+ op.func(buf, op.args...);
};
};
};
diff --git a/operation.ha b/operation.ha
@@ -3,7 +3,7 @@ use fmt;
use fs;
// 'e' command
-fn op_edit(buf: *buffer) void = {
+fn op_edit(buf: *buffer, args: arg...) void = {
const scratch = new_buffer();
const n = match (buf.fname) {
void => abort("TODO"),
@@ -24,7 +24,7 @@ fn op_edit(buf: *buffer) void = {
};
// 'w' command
-fn op_write(buf: *buffer) void = {
+fn op_write(buf: *buffer, args: arg...) void = {
const mode = fs::mode::USER_RW | fs::mode::GROUP_R | fs::mode::OTHER_R;
const flags = [
fs::flags::WRONLY,
@@ -51,7 +51,7 @@ fn op_write(buf: *buffer) void = {
};
// 'r' command
-fn op_read(buf: *buffer) void = {
+fn op_read(buf: *buffer, args: arg...) void = {
const scratch = new_buffer();
const n = match (buf.fname) {
void => abort("TODO"),
@@ -69,7 +69,7 @@ fn op_read(buf: *buffer) void = {
};
// 'd' command
-fn op_delete(buf: *buffer) void = { // TODO
+fn op_delete(buf: *buffer, args: arg...) void = { // TODO
const h = buf.cur.node;
const t = buf.cur.node;
@@ -77,7 +77,7 @@ fn op_delete(buf: *buffer) void = { // TODO
};
// 'n' command
-fn op_numbered(buf: *buffer) void = {
+fn op_numbered(buf: *buffer, args: arg...) void = {
const a = buf.range_a;
const b = buf.range_b;
let ln = a;
@@ -100,7 +100,7 @@ fn op_numbered(buf: *buffer) void = {
};
// 'p' command
-fn op_print(buf: *buffer) void = {
+fn op_print(buf: *buffer, args: arg...) void = {
const a = buf.range_a;
const b = buf.range_b;
let ln = a;
diff --git a/parse.ha b/parse.ha
@@ -5,45 +5,160 @@
// "3,7,/a/,+2m4"
//
// str -> struct {
-// prefix_ranges: []addr, // "2" "+2" "-1"
+// prefix_ranges: []address, // "2" "+2" "-1"
// primary_command: command, // e E f q Q r w !
// secondary_command: command, // l n p
-// suffix_ranges: []addr,
+// suffix_ranges: []address,
// tertiary_commands: []command, // g G v V
// } ->
//
// Note: Whats the theory around undo? What do we track? How do we calculate an
// undo? Should commands return some history, state, or even a undo-function?
+use ascii;
use fmt;
+use strconv;
+use strings;
// Represents an intended operation by a user to the main buffer.
type op = struct {
- func: *fn(buf: *buffer) void,
+ func: *fn(buf: *buffer, args: arg...) void,
+ args: []arg
};
-// Represents what kind of operation a user intends to perform.
-type optype = enum {
- CMD, // execute a command
- CUR_LINENUM, // move cursor to a line number
- CUR_REGEX, // move cursor to a matching line
- CUR_MARK, // move cursor to a mark
+type arg = (str | uint);
+
+type command = struct {
+ addrs: []address,
+ range: (address, address),
+ main: str,
+ suffix: str,
+};
+
+type address = struct {
+ addrtype: addrtype,
+ linenum: uint,
+ re: str,
+};
+
+type addrtype = enum {
+ REGEX,
+ LINENUM,
+ MARK,
+ OFFSET,
};
// Parses command line input into intended ops.
-fn parse_cmd(cmd: str) []op = {
+fn parse_input(input: str) command = {
+ let cmd = command { ... };
+ let iter = strings::iter(input);
+
+ for (true) {
+ let r: rune = match (strings::next(&iter)) {
+ void => break,
+ r: rune => r,
+ };
+
+ if (ascii::isblank(r)) {
+ continue;
+
+ } else if (ascii::isdigit(r)) {
+ strings::push(&iter, r);
+ const n = scan_uint(&iter);
+ append(cmd.addrs, address {
+ addrtype = addrtype::LINENUM,
+ linenum = n,
+ ...
+ });
+
+ } else if (ascii::isalpha(r)) {
+ strings::push(&iter, r);
+ cmd.main = scan_command(&iter);
+
+ } else {
+ abort("TODO");
+
+ };
+ };
+
+ return cmd;
+};
+
+fn construct_ops(cmd: command) []op = {
let ops: []op = [];
- switch (cmd) {
- "e" => append(ops, op { func = &op_edit, }),
- "r" => append(ops, op { func = &op_read, }),
- "w" => append(ops, op { func = &op_write, }),
- "d" => append(ops, op { func = &op_delete, }),
- "n" => append(ops, op { func = &op_numbered, }),
- "p" => append(ops, op { func = &op_print, }),
- " p" => append(ops, op { func = &print_buffer, }), // DEBUG ONLY
- * => void, // TODO
+
+ for (let i = 0z; i < len(cmd.addrs); i += 1) {
+ const addr = cmd.addrs[i];
+ switch (addr.addrtype) {
+ addrtype::LINENUM => {
+ append(ops, op {
+ func = &cur_linenum,
+ args = [addr.linenum]
+ });
+ },
+ };
};
+
+ let op = switch (cmd.main) {
+ "e" => op { func = &op_edit, },
+ "r" => op { func = &op_read, },
+ "w" => op { func = &op_write, },
+ "d" => op { func = &op_delete, },
+ "n" => op { func = &op_numbered, },
+ "p" => op { func = &op_print, },
+ " p" => op { func = &print_buffer, }, // DEBUG ONLY
+ * => op { func = &print_buffer, }, // TODO
+ };
+ append(ops, op);
return ops;
};
+
+fn scan_uint(iter: *strings::iterator) uint = {
+ let num: []u8 = [];
+ defer free(num);
+
+ for (true) {
+ const r = match (strings::next(iter)) {
+ void => break,
+ r: rune => r,
+ };
+
+ if (ascii::isdigit(r)) {
+ append(num, r: u32: u8);
+ } else {
+ strings::push(iter, r);
+ break;
+ };
+ };
+
+ match (strconv::stou(strings::fromutf8(num))) {
+ (strconv::invalid | strconv::overflow) =>
+ abort("Invalid format string (invalid index)"),
+ u: uint => return u,
+ };
+};
+
+
+fn scan_command(iter: *strings::iterator) str = {
+ let cmdb: []u8 = [];
+ //defer free(cmdb);
+
+ for (true) {
+ const r = match (strings::next(iter)) {
+ void => break,
+ r: rune => r,
+ };
+
+ if (ascii::isalpha(r)) {
+ append(cmdb, r: u32: u8);
+ } else {
+ strings::push(iter, r);
+ break;
+ };
+ };
+
+ const cmdstr = strings::fromutf8(cmdb);
+ return cmdstr;
+};
diff --git a/util.ha b/util.ha
@@ -1,7 +1,7 @@
use fmt;
// Sequencially prints lines of a buffer.
-fn print_buffer(buf: *buffer) void = {
+fn print_buffer(buf: *buffer, args: arg...) void = {
let ln = buf.head;
for (true) {
match (ln.value) {