commit f3608da092b501d946629acec907fbc3edebfcfe
parent 718d4ae9c7a5a520f58c2155a6c7b64e1a68a30a
Author: Byron Torres <b@torresjrjr.com>
Date: Sun, 18 Dec 2022 02:30:38 +0000
parse progress
Diffstat:
M | command.ha | | | 6 | ++++-- |
M | main.ha | | | 8 | +++++++- |
M | parse.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;
+ };
};