ed

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

commit 596f6d1fa4c51ecce0dd6c73fe185fcf1bdbcfa0
parent 1ed775fdc4cf5c7abaecb5cac5fa8fe36dae1b38
Author: Byron Torres <b@torresjrjr.com>
Date:   Sat, 10 Dec 2022 01:16:11 +0000

progress

Diffstat:
Maddress.ha | 21+++++++++++----------
Mbuffer.ha | 1+
Mcommand.ha | 226+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mmain.ha | 6++++--
Mparse.ha | 76+++++++++++++++++++++++++++++++++++++++++++---------------------------------
5 files changed, 194 insertions(+), 136 deletions(-)

diff --git a/address.ha b/address.ha @@ -3,6 +3,7 @@ use regex; type address = struct { addrtype: addresstype, lineoffset: int, + setcurrentline: bool, }; type addresstype = (size | currentline | lastline | rune); @@ -13,34 +14,34 @@ type lastline = void; type badaddress = !void; -fn addr_nextline(s: *session, n: size) size = { - if (len(s.buf.lines) - 1 == n) { +fn addr_nextline(buf: *buffer, n: size) size = { + if (len(buf.lines) - 1 == n) { return n; }; return n + 1; }; -fn addr_prevline(s: *session, n: size) size = { +fn addr_prevline(buf: *buffer, n: size) size = { if (n == 0 || n == 1) { return n; }; return n - 1; }; -fn addr_linenum(s: *session, n: size) (size | badaddress) = { - if (n > len(s.buf.lines)) { +fn addr_linenum(buf: *buffer, n: size) (size | badaddress) = { + if (n > len(buf.lines)) { return badaddress; }; return n; }; -fn addr_lastline(s: *session) size = { - return len(s.buf.lines) - 1z; +fn addr_lastline(buf: *buffer) size = { + return len(buf.lines) - 1z; }; -fn addr_mark(s: *session, mark: rune) (size | badaddress) = { - for (let n = 1z; n < len(s.buf.lines); n += 1) { - const l = s.buf.lines[n]; +fn addr_mark(buf: *buffer, mark: rune) (size | badaddress) = { + for (let n = 1z; n < len(buf.lines); n += 1) { + const l = buf.lines[n]; if (l.mark == mark) { return n; }; diff --git a/buffer.ha b/buffer.ha @@ -35,6 +35,7 @@ fn buf_delete(buf: *buffer, a: size, b: size) void = { }; fn buf_read(buf: *buffer, src: io::handle, a: size) (size, size) = { + // TODO: don't call this here, call it at a higher level buf_wipetrash(buf); let ls: []*line = []; diff --git a/command.ha b/command.ha @@ -4,54 +4,71 @@ use fs; use io; use os; -// Executes the session's .cmd command. -fn execute(s: *session) void = { - // first, execute the addresses - let cursor = s.buf.cursor; - let lns: []size = []; - // defer free(lns); - for (let i = 0z; i < len(s.cmd.addrs); i += 1) { - const addr = s.cmd.addrs[i]; +type error = !( + badaddress + | nofilename + | buffermodified + | unexpectedaddress + | fs::error +); + +type nofilename = !void; + +type buffermodified = !void; + +type unexpectedaddress = !void; + +fn exec_addrs(s: *session, cmd: *command) (void | error) = { + for (let i = 0z; i < len(cmd.addrs); i += 1) { + const addr = cmd.addrs[i]; let n = match (addr.addrtype) { case let n: size => - yield addr_linenum(s, n)!; + yield addr_linenum(&s.buf, n)?; case currentline => - yield cursor; + yield s.buf.cursor; case lastline => - yield addr_lastline(s); + yield addr_lastline(&s.buf); case let m: rune => - yield addr_mark(s, m)!; + yield addr_mark(&s.buf, m)?; }; n = (n: int + addr.lineoffset): size; - append(lns, n); + fmt::errorfln("DEBUG: exec_addrs() n={}", n)!; + append(cmd.linenums, n); + // fmt::errorfln("DEBUG: exec_addrs() ...[i]={}", cmd.linenums[i])!; + + if (addr.setcurrentline) { + s.buf.cursor = n; + }; }; +}; + +// Executes the session's .cmd command. +fn execute(s: *session) (void | error) = { + // TODO: move this into the cmd_* functions? + exec_addrs(s, &s.cmd)?; + defer delete(s.cmd.linenums[..]); // TODO: write finish_cmd() - // then, execute the full command switch (s.cmd.cmdtype) { case commandtype::APPEND => fmt::errorfln("TODO: append")!; case commandtype::CHANGE => fmt::errorfln("TODO: change")!; case commandtype::DELETE => - const (a, b) = get_range(lns, cursor, cursor); - cmd_delete(s, s.cmd.arg, a, b); + cmd_delete(s, &s.cmd): void; case commandtype::EDIT => - cmd_edit(s, s.cmd.arg); + cmd_edit(s, &s.cmd): void; case commandtype::EDIT_FORCED => fmt::errorfln("TODO: edit_forced")!; case commandtype::FILENAME => - if (!assert_noaddrs(s, lns)) return; - cmd_filename(s, s.cmd.arg); + cmd_filename(s, &s.cmd): void; case commandtype::GLOBAL => fmt::errorfln("TODO: global")!; case commandtype::GLOBAL_INTERACTIVE => fmt::errorfln("TODO: global_interactive")!; case commandtype::HELP => - if (!assert_noaddrs(s, lns)) return; - cmd_help(s); + cmd_help(s, &s.cmd): void; case commandtype::HELPMODE => - if (!assert_noaddrs(s, lns)) return; - cmd_helpmode(s); + cmd_helpmode(s, &s.cmd): void; case commandtype::INSERT => fmt::errorfln("TODO: insert")!; case commandtype::JOIN => @@ -63,24 +80,15 @@ fn execute(s: *session) void = { case commandtype::MOVE => fmt::errorfln("TODO: move")!; case commandtype::NUMBER => - const (a, b) = get_range(lns, cursor, cursor); - if (a > b) { - //return badaddress; - errormsg(s, "Invalid address"); - return; - }; - cmd_number(s, a, b); + cmd_number(s, &s.cmd): void; case commandtype::PRINT => - const (a, b) = get_range(lns, cursor, cursor); - cmd_print(s, a, b); + cmd_print(s, &s.cmd): void; case commandtype::PROMPT=> - if (!assert_noaddrs(s, lns)) return; - cmd_prompt(s); + cmd_prompt(s, &s.cmd): void; case commandtype::QUIT => fmt::errorfln("TODO: quit")!; case commandtype::READ => - const n = get_linenum(lns, cursor); - cmd_read(s, s.cmd.arg, n); + cmd_read(s, &s.cmd); case commandtype::SUBSTITUTE => fmt::errorfln("TODO: substitute")!; case commandtype::COPY => @@ -92,24 +100,13 @@ fn execute(s: *session) void = { case commandtype::GLOBAL_INVERSE_INTERACTIVE => fmt::errorfln("TODO: global_inverse_interactive")!; case commandtype::WRITE => - const (a, b) = get_range( - lns, - addr_linenum(s, 1)!, - addr_lastline(s), - ); - cmd_write(s, s.cmd.arg, a, b); + cmd_write(s, &s.cmd): void; case commandtype::LINE_NUMBER => - const n = get_linenum(lns, addr_lastline(s)); - cmd_linenumber(s, n); + cmd_linenumber(s, &s.cmd); case commandtype::SHELL_ESCAPE => fmt::errorfln("TODO: shell_escape")!; case commandtype::NULL => - const n = get_linenum(lns, if (len(lns) == 0) { - yield addr_nextline(s, cursor); - } else { - yield cursor; - }); - cmd_print(s, n, n); + cmd_null(s, &s.cmd): void; case commandtype::DEBUG_NUMBER => dumpbuffer(&s.buf); @@ -118,16 +115,20 @@ fn execute(s: *session) void = { }; }; -fn get_range(lns: []size, a: size, b: size) (size, size) = { - if (len(lns) == 0) { - return (a, b); +fn get_range(s: *session, lns: *[]size, a: size, b: size) ((size, size) | badaddress) = { + const (a, b) = if (len(lns) == 0) { + yield (a, b); } else if (len(lns) == 1) { - return (lns[0], lns[0]); + yield (lns[0], lns[0]); } else { - // fmt::errorfln("DEBUG: ({}, {})", lns[len(lns)-2], lns[len(lns)-1])!; - const (a, b) = (lns[len(lns)-2], lns[len(lns)-1]); - return (a, b); + yield (lns[len(lns)-2], lns[len(lns)-1]); + }; + fmt::errorfln("DEBUG: get_range() (a, b)=({}, {})", a, b)!; + if (a > b) { + errormsg(s, "Invalid address"); + return badaddress; }; + return (a, b); }; fn get_linenum(lns: []size, n: size) size = { @@ -138,69 +139,94 @@ fn get_linenum(lns: []size, n: size) size = { }; }; -fn assert_noaddrs(s: *session, lns: []size) bool = { +fn assert_noaddrs(s: *session, lns: []size) (void | unexpectedaddress) = { if (len(lns) != 0) { errormsg(s, "Unexpected address"); - return false; + return unexpectedaddress; }; - return true; }; -fn cmd_prompt(s: *session) void = { +fn cmd_prompt(s: *session, cmd: *command) (void | error) = { + assert_noaddrs(s, cmd.linenums)?; s.promptmode = !s.promptmode; }; -fn cmd_linenumber(s: *session, n: size) void = { +fn cmd_linenumber(s: *session, cmd: *command) void = { + const n = get_linenum(cmd.linenums, addr_lastline(&s.buf)); fmt::println(n)!; }; -fn cmd_help(s: *session) void = { +fn cmd_help(s: *session, cmd: *command) (void | error) = { + assert_noaddrs(s, cmd.linenums)?; if (s.lasterror != "") { fmt::println(s.lasterror)!; }; }; -fn cmd_helpmode(s: *session) void = { +fn cmd_helpmode(s: *session, cmd: *command) (void | error) = { + assert_noaddrs(s, cmd.linenums)?; s.helpmode = !s.helpmode; - if (s.helpmode) { - cmd_help(s); + if (s.helpmode && s.lasterror != "") { + fmt::println(s.lasterror)!; }; }; -fn cmd_print(s: *session, a: size, b: size) void = { +fn cmd_print(s: *session, cmd: *command) (void | error) = { + const (a, b) = get_range( + s, + &cmd.linenums, + s.buf.cursor, + s.buf.cursor, + )?; for (let n = a; n <= b; n += 1) { fmt::println(s.buf.lines[n].text)!; }; s.buf.cursor = b; }; -fn cmd_number(s: *session, a: size, b: size) void = { +fn cmd_number(s: *session, cmd: *command) (void | error) = { + const (a, b) = get_range( + s, + &cmd.linenums, + s.buf.cursor, + s.buf.cursor, + )?; + for (let n = a; n <= b; n += 1) { fmt::printfln("{}\t{}", n, s.buf.lines[n].text)!; }; s.buf.cursor = b; }; -fn cmd_filename(s: *session, filename: str) void = { - if (len(filename) != 0) { - s.buf.filename = filename; +fn cmd_filename(s: *session, cmd: *command) (void | error) = { + assert_noaddrs(s, cmd.linenums)?; + if (len(cmd.arg) != 0) { + s.buf.filename = cmd.arg; }; if (len(s.buf.filename) == 0) { - return errormsg(s, "No current filename"); + errormsg(s, "No current filename"); + return nofilename; }; fmt::println(s.buf.filename)!; }; -fn cmd_write(s: *session, filename: str, a: size, b: size) void = { - const fname = if (len(filename) != 0) { - s.buf.filename = filename; - yield filename; +fn cmd_write(s: *session, cmd: *command) (void | error) = { + const (a, b) = get_range( + s, + &cmd.linenums, + addr_linenum(&s.buf, 1)!, + addr_lastline(&s.buf), + )?; + + const fname = if (len(cmd.arg) != 0) { + s.buf.filename = cmd.arg; + yield cmd.arg; } else { yield if (len(s.buf.filename) != 0) { yield s.buf.filename; } else { errormsg(s, "No current filename"); - return; + return nofilename; }; }; @@ -215,7 +241,8 @@ fn cmd_write(s: *session, filename: str, a: size, b: size) void = { return errormsg(s, fs::strerror(err)); }; case => - return errormsg(s, fs::strerror(err)); + errormsg(s, fs::strerror(err)); + return err; }; case let h: io::file => yield h: io::handle; @@ -228,10 +255,11 @@ fn cmd_write(s: *session, filename: str, a: size, b: size) void = { }; }; -fn cmd_read(s: *session, filename: str, a: size) void = { - const fname = if (len(filename) != 0) { - s.buf.filename = filename; - yield filename; +fn cmd_read(s: *session, cmd: *command) void = { + const n = get_linenum(cmd.linenums, s.buf.cursor); + const fname = if (len(cmd.arg) != 0) { + s.buf.filename = cmd.arg; + yield cmd.arg; } else { yield if (len(s.buf.filename) != 0) { yield s.buf.filename; @@ -249,35 +277,36 @@ fn cmd_read(s: *session, filename: str, a: size) void = { }; defer io::close(h)!; - const (sz, _) = buf_read(&s.buf, h, a); + const (sz, _) = buf_read(&s.buf, h, n); if (!s.suppressmode) { fmt::println(sz)!; }; s.buf.cursor = len(s.buf.lines) - 1; }; -fn cmd_edit(s: *session, filename: str) void = { +fn cmd_edit(s: *session, cmd: *command) (void | error) = { if (s.buf.modified && !s.warned) { errormsg(s, "Warning: buffer modified"); s.warned = true; - return; + return buffermodified; }; - const fname = if (len(filename) != 0) { - s.buf.filename = filename; - yield filename; + const fname = if (len(cmd.arg) != 0) { + s.buf.filename = cmd.arg; + yield cmd.arg; } else { yield if (len(s.buf.filename) != 0) { yield s.buf.filename; } else { errormsg(s, "No current filename"); - return; + return nofilename; }; }; const h = match (os::open(fname)) { case let err: fs::error => - return errormsg(s, fs::strerror(err)); + errormsg(s, fs::strerror(err)); + return err; case let h: io::file => yield h: io::handle; }; @@ -291,7 +320,13 @@ fn cmd_edit(s: *session, filename: str) void = { s.buf.cursor = len(s.buf.lines) - 1; }; -fn cmd_delete(s: *session, filename: str, a: size, b: size) void = { +fn cmd_delete(s: *session, cmd: *command) (void | error) = { + const (a, b) = get_range( + s, + &cmd.linenums, + s.buf.cursor, + s.buf.cursor, + )?; buf_delete(&s.buf, a, b); s.buf.cursor = if (len(s.buf.lines) == 1) { yield 0; @@ -301,3 +336,12 @@ fn cmd_delete(s: *session, filename: str, a: size, b: size) void = { yield a; }; }; + +fn cmd_null(s: *session, cmd: *command) (void | error) = { + const n = get_linenum( + cmd.linenums, + addr_nextline(&s.buf, s.buf.cursor), + ); + fmt::println(s.buf.lines[n].text)!; + s.buf.cursor = n; +}; diff --git a/main.ha b/main.ha @@ -71,7 +71,8 @@ export fn main() void = { }; if (len(s.buf.filename) != 0) { - cmd_edit(&s, s.buf.filename); + s.cmd.arg = s.buf.filename; + cmd_edit(&s, &s.cmd): void; }; for (true) :repl { @@ -101,7 +102,8 @@ export fn main() void = { switch (s.mode) { case mode::COMMAND => if (parse(&s.cmd, input)) { - execute(&s); + // TODO: handle all ": void"s + execute(&s): void; }; case mode::INPUT => if (input == ".") { diff --git a/parse.ha b/parse.ha @@ -6,6 +6,7 @@ use strings; type command = struct { addrs: []address, + linenums: []size, cmdtype: commandtype, suffix: suffix, arg: str, @@ -81,38 +82,40 @@ fn scan_addrs(iter: *strings::iterator) []address = { case let r: rune => switch (r) { case ',' => + specialfirst = true; append(addrs, address { addrtype = 1z, lineoffset = 0, + setcurrentline = false, }); - specialfirst = true; case ';' => + specialfirst = true; append(addrs, address { addrtype = currentline, lineoffset = 0, + setcurrentline = true, }); - specialfirst = true; case => strings::prev(iter); }; }; for (true) { - match (scan_addr(iter)) { + let addr = match (scan_addr(iter)) { case void => - if (specialfirst) { - append(addrs, address { + yield if (specialfirst) { + yield address { addrtype = lastline, lineoffset = 0, - }); + ... + }; } else if (len(addrs) > 0) { - const prevaddr = addrs[len(addrs)-1]; - append(addrs, prevaddr); + yield addrs[len(addrs)-1]; } else { break; }; - case let addr: address => - append(addrs, addr); + case let a: address => + yield a; }; specialfirst = false; @@ -120,12 +123,17 @@ fn scan_addrs(iter: *strings::iterator) []address = { scan_blanks(iter); match (strings::next(iter)) { case void => + append(addrs, addr); break; case let r: rune => switch (r) { - case ',', ';' => - void; + case ',' => + append(addrs, addr); + case ';' => + addr.setcurrentline = true; + append(addrs, addr); case => + append(addrs, addr); strings::prev(iter); break; }; @@ -148,27 +156,28 @@ fn scan_addr(iter: *strings::iterator) (address | void) = { // fmt::errorfln("DEBUG: scan_addr() r={}", r)!; - const addrtype: (addresstype | void) = if (r == '.') { - yield currentline; - } else if (r == '$') { - yield lastline; - } else if (ascii::isdigit(r)) { - strings::prev(iter); - yield scan_uint(iter): size; - } else if (r == '\'') { - yield scan_mark(iter); - } else if (r == '/') { - // const re = scan_regex(iter, r);j - // ... - abort("TODO: /regex/"); - } else if (r == '?') { - // const re = scan_regex(iter, r);j - // ... - abort("TODO: ?regex?"); - } else { - strings::prev(iter); - yield void; - }; + const addrtype: (addresstype | void) = + if (r == '.') { + yield currentline; + } else if (r == '$') { + yield lastline; + } else if (ascii::isdigit(r)) { + strings::prev(iter); + yield scan_uint(iter): size; + } else if (r == '\'') { + yield scan_mark(iter); + } else if (r == '/') { + // const re = scan_regex(iter, r);j + // ... + abort("TODO: /regex/"); + } else if (r == '?') { + // const re = scan_regex(iter, r);j + // ... + abort("TODO: ?regex?"); + } else { + strings::prev(iter); + yield void; + }; const offs = scan_offsets(iter); @@ -186,6 +195,7 @@ fn scan_addr(iter: *strings::iterator) (address | void) = { let addr = address { addrtype = addrtype, lineoffset = 0, + ... }; for (let i = 0z; i < len(offs); i += 1) {