ed

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

commit f3608da092b501d946629acec907fbc3edebfcfe
parent 718d4ae9c7a5a520f58c2155a6c7b64e1a68a30a
Author: Byron Torres <b@torresjrjr.com>
Date:   Sun, 18 Dec 2022 02:30:38 +0000

parse progress

Diffstat:
Mcommand.ha | 6++++--
Mmain.ha | 8+++++++-
Mparse.ha | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
3 files changed, 237 insertions(+), 41 deletions(-)

diff --git a/command.ha b/command.ha @@ -10,15 +10,17 @@ type command = struct { addrs: []address, linenums: []size, cmdfn: commandfn, - suffix: suffix, + printmode: printmode, arg: str, + arg2: str, + arg3: str, input: []str, subcmds: []command, }; type commandfn = *fn(_: *session, _: *command) (void | error); -type suffix = enum { +type printmode = enum { NONE, LIST, NUMBER, diff --git a/main.ha b/main.ha @@ -103,7 +103,13 @@ export fn main() void = { switch (s.mode) { case mode::COMMAND => - if (parse(&cmd, input)) { + const execnow = match (parse(&cmd, input)) { + case let e: parseerror => + continue; // TODO: print error + case let b: bool => + yield b; + }; + if (execnow) { // TODO: handle all ": void"s execute(&s, &cmd): void; }; diff --git a/parse.ha b/parse.ha @@ -3,20 +3,170 @@ use regex; use strconv; use strings; -// Parses inputted commands. Returns true on terminal input. -fn parse(cmd: *command, input: str) bool = { - if (len(input) != 1) { - void; - }; +type parseerror = ( + unknowncommand + | unexpectedsuffix + | trailingcharacters + | expectedargument + | expectedmark + | invaliddelimiter + | expecteddelimiter +); + +type unknowncommand = !rune; + +type unexpectedsuffix = !rune; + +type trailingcharacters = !void; + +type expectedargument = !void; + +type expectedmark = !void; + +type invaliddelimiter = !void; + +type expecteddelimiter = !void; +// Parses inputted commands. Returns true when command is ready. +fn parse(cmd: *command, input: str) (bool | parseerror) = { const iter = strings::iter(input); cmd.addrs = scan_addrs(&iter); - cmd.cmdfn = scan_cmdfn(&iter); - cmd.arg = scan_arg(&iter); -// debug("parse(): cmd.arg={}", cmd.arg); + cmd.cmdfn = scan_cmdfn(&iter)?; + + switch (cmd.cmdfn) { + case // [ ] + &cmd_null, + => + return true; + + case // (q|Q) + &cmd_quit, + &cmd_quit_forced, + => + scan_end_assert(&iter)?; + return true; + + case // .[ <file>] + &cmd_edit, + &cmd_edit_forced, + &cmd_filename, + &cmd_read, + &cmd_write, + => + if (scan_blanks(&iter) == 0) { + match (strings::next(&iter)) { + case let r: rune => + return r: unexpectedsuffix; + case void => + return true; + }; + } else { + cmd.arg = scan_rest(&iter); + return true; + }; + + case // k<x> + &cmd_mark, + => + match (strings::next(&iter)) { + case let r: rune => + cmd.arg = strings::fromrunes([r]); + case void => + return expectedmark; + }; + scan_end_assert(&iter)?; + return true; + + case // !<shellcmd> + &cmd_shellescape, + => + cmd.arg = scan_rest(&iter); + return true; + + case // .[s] where 's' is '(l|n|p)' + &cmd_append, + &cmd_change, + &cmd_delete, + &cmd_help, + &cmd_helpmode, + &cmd_insert, + &cmd_join, + &cmd_list, + &cmd_number, + &cmd_print, + &cmd_prompt, + &cmd_undo, + &cmd_linenumber, + => + cmd.printmode = scan_suffix(&iter); + scan_end_assert(&iter)?; + return true; + + case // .[s][ ]<addr> + &cmd_move, + &cmd_copy, + => + cmd.printmode = scan_suffix(&iter); + cmd.arg = scan_rest(&iter); + return true; + + case // ./<regex>[/] + &cmd_global_manual, + &cmd_invglobal_manual, + => + const delim = match (strings::next(&iter)) { + case void => + return expectedargument; + case let r: rune => + yield if (r == ' ') { + return invaliddelimiter; + } else { + yield r; + }; + }; + cmd.arg = scan_item(&iter, delim); + strings::next(&iter); // scan delimiter if exists + scan_end_assert(&iter)?; + return true; + + case // s/<regex>/[<replace>[/[<flags>]]] + &cmd_substitute, + => + const delim = match (strings::next(&iter)) { + case void => + return expectedargument; + case let r: rune => + yield if (r == ' ') { + return invaliddelimiter; + } else { + yield r; + }; + }; + cmd.arg = scan_item(&iter, delim); + match (strings::next(&iter)) { + case rune => void; + case void => + return expecteddelimiter; + }; + cmd.arg2 = scan_item(&iter, delim); + match (strings::next(&iter)) { + case rune => void; + case void => + return true; + }; + cmd.arg3 = scan_rest(&iter); // TODO: scan properly here? + return true; + + case // ./regex/CMDLIST + &cmd_global, + &cmd_invglobal, + => + abort("TODO: parse: global, invglobal"); - return true; + case => + abort(); + }; }; fn scan_addrs(iter: *strings::iterator) []address = { @@ -115,15 +265,19 @@ fn scan_addr(iter: *strings::iterator) (address | void) = { } else if (r == '\'') { yield scan_mark(iter); } else if (r == '/') { - yield regexaddr { - expr = scan_regex(iter, '/'), + const rad = regexaddr { + expr = scan_item(iter, '/'), direction = true, }; + strings::next(iter); + yield rad; } else if (r == '?') { - yield regexaddr { - expr = scan_regex(iter, '?'), + const rad = regexaddr { + expr = scan_item(iter, '?'), direction = false, }; + strings::next(iter); + yield rad; } else { strings::prev(iter); yield void; @@ -196,7 +350,8 @@ fn scan_offset(iter: *strings::iterator) int = { }; }; -fn scan_cmdfn(iter: *strings::iterator) commandfn = { +fn scan_cmdfn(iter: *strings::iterator) (commandfn | unknowncommand)= { + scan_blanks(iter); let r = match (strings::next(iter)) { case void => return &cmd_null; @@ -205,43 +360,62 @@ fn scan_cmdfn(iter: *strings::iterator) commandfn = { }; switch (r) { - case '=' => return &cmd_linenumber; -// case '!' => return &cmd_shellescape; -// case 'E' => return &cmd_edit_forced; -// case 'G' => return &cmd_global_manual; - case 'H' => return &cmd_helpmode; - case 'P' => return &cmd_prompt; -// case 'Q' => return &cmd_quit_forced; -// case 'V' => return &cmd_invglobal_manual; -// case 'a' => return &cmd_append; -// case 'c' => return &cmd_change; + case 'a' => return &cmd_append; + case 'c' => return &cmd_change; case 'd' => return &cmd_delete; case 'e' => return &cmd_edit; + case 'E' => return &cmd_edit_forced; case 'f' => return &cmd_filename; -// case 'g' => return &cmd_global; + case 'g' => return &cmd_global; + case 'G' => return &cmd_global_manual; case 'h' => return &cmd_help; -// case 'i' => return &cmd_insert; + case 'H' => return &cmd_helpmode; + case 'i' => return &cmd_insert; case 'j' => return &cmd_join; case 'k' => return &cmd_mark; case 'l' => return &cmd_list; case 'm' => return &cmd_move; case 'n' => return &cmd_number; case 'p' => return &cmd_print; -// case 'q' => return &cmd_quit; + case 'P' => return &cmd_prompt; + case 'q' => return &cmd_quit; + case 'Q' => return &cmd_quit_forced; case 'r' => return &cmd_read; -// case 's' => return &cmd_substitute; + case 's' => return &cmd_substitute; case 't' => return &cmd_copy; -// case 'u' => return &cmd_undo; -// case 'v' => return &cmd_invglobal; + case 'u' => return &cmd_undo; + case 'v' => return &cmd_invglobal; + case 'V' => return &cmd_invglobal_manual; case 'w' => return &cmd_write; - - case 'b' => return &cmd_dumpbuffer; - case => abort("Invalid command (unimplemented?)"); + case '=' => return &cmd_linenumber; + case '!' => return &cmd_shellescape; +// case 'b' => return &cmd_dumpbuffer; + case => return r: unknowncommand; }; }; +fn scan_suffix(iter: *strings::iterator) printmode = { + let r = match (strings::next(iter)) { + case void => + return printmode::NONE; + case let r: rune => + yield r; + }; -fn scan_arg(iter: *strings::iterator) str = { + switch (r) { + case 'l' => + return printmode::LIST; + case 'n' => + return printmode::NUMBER; + case 'p' => + return printmode::PRINT; + case => + strings::prev(iter); + return printmode::NONE; + }; +}; + +fn scan_rest(iter: *strings::iterator) str = { // TODO: just use [[strings::iterstr]]? let rs: []rune = []; for (true) { @@ -255,7 +429,7 @@ fn scan_arg(iter: *strings::iterator) str = { return strings::trim(strings::fromrunes(rs)); }; -fn scan_regex(iter: *strings::iterator, end: rune) str = { +fn scan_item(iter: *strings::iterator, end: rune) str = { let rs: []rune = []; for (true) { let r = match (strings::next(iter)) { @@ -277,6 +451,7 @@ fn scan_regex(iter: *strings::iterator, end: rune) str = { continue; }; } else if (r == end) { + strings::prev(iter); break; }; append(rs, r); @@ -329,16 +504,29 @@ fn scan_uint(iter: *strings::iterator) uint = { }; }; -fn scan_blanks(iter: *strings::iterator) void = { +fn scan_blanks(iter: *strings::iterator) size = { + let sz = 0z; // runes, not bytes for (true) { match (strings::next(iter)) { case void => - return; + break; case let r: rune => if (!ascii::isblank(r)) { strings::prev(iter); - return; + break; }; + sz += 1; }; }; + return sz; +}; + +fn scan_end_assert(iter: *strings::iterator) (void | trailingcharacters) = { + scan_blanks(iter); + match (strings::next(iter)) { + case rune => + return trailingcharacters; + case void => + return void; + }; };