ed

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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:
MMakefile | 1+
Maddress.ha | 12+++++++-----
Acursor.ha | 13+++++++++++++
Med.ha | 7++++---
Moperation.ha | 12++++++------
Mparse.ha | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mutil.ha | 2+-
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) {