commit 9508bae0bfd364cc1e60305b9c310dc836db18c9
parent af82d6b3db88a3af3281be29992904dd92d3d313
Author: Byron Torres <b@torresjrjr.com>
Date: Sat, 6 Jan 2024 11:22:39 +0000
progress; parse input stream; struct{} style
Diffstat:
M | address.ha | | | 4 | ++-- |
M | buffer.ha | | | 6 | +++--- |
M | command.ha | | | 47 | +++++++++++++++++++++++++---------------------- |
M | main.ha | | | 75 | ++++++++++++++++++++------------------------------------------------------- |
M | parse.ha | | | 124 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
5 files changed, 138 insertions(+), 118 deletions(-)
diff --git a/address.ha b/address.ha
@@ -1,6 +1,6 @@
use regex;
-type Address = struct {
+type Address = struct{
addrform: AddressForm,
lineoffset: int,
setcurrentline: bool,
@@ -15,7 +15,7 @@ type CurrentLine = void;
type LastLine = void;
// Regular expression search. /forward/ is true, ?backward? is false.
-type RegexAddr = struct {
+type RegexAddr = struct{
expr: str,
direction: bool,
};
diff --git a/buffer.ha b/buffer.ha
@@ -4,7 +4,7 @@ use fmt;
use io;
use strings;
-type Buffer = struct {
+type Buffer = struct{
filename: str,
lines: []*Line,
trash: []*Line,
@@ -12,7 +12,7 @@ type Buffer = struct {
modified: bool,
};
-type Line = struct {
+type Line = struct{
text: str,
mark: rune,
globalmark: bool,
@@ -65,7 +65,7 @@ fn buf_read(buf: *Buffer, src: io::handle, a: size) ((size, size) | io::error |
sz += len(bytes) + 1; // TODO: handle newlines better
const text = strings::fromutf8(bytes)?;
- append(ls, alloc(Line { text = text, ... }));
+ append(ls, alloc(Line{ text = text, ... }));
};
insert(buf.lines[a + 1], ls...);
diff --git a/command.ha b/command.ha
@@ -9,7 +9,7 @@ use regex;
use strings;
use memio;
-type Command = struct {
+type Command = struct{
addrs: []Address,
linenums: []size,
cmdname: rune,
@@ -17,13 +17,13 @@ type Command = struct {
arg1: str,
arg2: str,
arg3: str,
- input: []str,
+ textinput: []str,
subcmds: []Command,
};
type CommandFn = *fn(*Session, *Command) (void | Error);
-type PrintMode = enum {
+type PrintMode = enum{
NONE,
LIST,
NUMBER,
@@ -62,7 +62,7 @@ fn command_finish(cmd: *Command) void = {
debug("command_finish(): free(cmd.arg3)");
free(cmd.arg3);
debug("command_finish(): delete(cmd.input[..])");
- delete(cmd.input[..]);
+ delete(cmd.textinput[..]);
debug("command_finish(): END");
// TODO: free other fields?
// TODO: make a separate "fn command_clear()" ? probably not
@@ -107,13 +107,13 @@ fn lookupcmd(name: rune) CommandFn = {
fn cmd_append(s: *Session, cmd: *Command) (void | Error) = {
const n = get_linenum(cmd.linenums, s.buf.cursor);
- for (let i = 0z; i < len(cmd.input); i += 1) {
- const l = alloc(Line { text = cmd.input[i], ... });
+ for (let i = 0z; i < len(cmd.textinput); i += 1) {
+ const l = alloc(Line{ text = cmd.textinput[i], ... });
debug("cmd_append(): l.text={}", l.text);
buf_insert(s.buf, n + 1 + i, l);
};
- s.buf.cursor = n + len(cmd.input);
+ s.buf.cursor = n + len(cmd.textinput);
};
fn cmd_change(s: *Session, cmd: *Command) (void | Error) = {
@@ -132,20 +132,20 @@ fn cmd_change(s: *Session, cmd: *Command) (void | Error) = {
buf_delete(s.buf, a, b);
- for (let i = 0z; i < len(cmd.input); i += 1) {
- const l = alloc(Line { text = cmd.input[i], ... });
+ for (let i = 0z; i < len(cmd.textinput); i += 1) {
+ const l = alloc(Line{ text = cmd.textinput[i], ... });
debug("cmd_append(): l.text={}", l.text);
buf_insert(s.buf, a + i, l);
};
- s.buf.cursor = if (len(cmd.input) == 0) {
+ s.buf.cursor = if (len(cmd.textinput) == 0) {
yield if (len(s.buf.lines) == a) {
yield a - 1;
} else {
yield a;
};
} else {
- yield a + len(cmd.input) - 1;
+ yield a + len(cmd.textinput) - 1;
};
};
@@ -206,6 +206,7 @@ fn cmd_edit_forced(s: *Session, cmd: *Command) (void | Error) = void;
fn cmd_filename(s: *Session, cmd: *Command) (void | Error) = {
assert_noaddrs(s, cmd.linenums)?;
+
if (cmd.arg1 != "") {
//debug("cmd_filename(): cmd.arg1 != 0");
//free(s.buf.filename);
@@ -213,9 +214,11 @@ fn cmd_filename(s: *Session, cmd: *Command) (void | Error) = {
debug("cmd_filename(): s.buf.filename = dup(cmd.arg1)");
s.buf.filename = strings::dup(cmd.arg1);
};
+
if (s.buf.filename == "") {
return NoFilename;
};
+
fmt::println(s.buf.filename)!;
};
@@ -264,7 +267,7 @@ fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = {
fmt::println(line.text)!;
s.buf.cursor = n;
- let cmd = Command { ... };
+ let cmd = Command{ ... };
// const rawinput = match (bufio::read_line(os::stdin)?) {
// case let bs: []u8 =>
@@ -276,7 +279,7 @@ fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = {
// const input = strings::fromutf8(rawinput)?;
const input = scanline(s)?;
- parse(&cmd, input)?;
+ let cmd = parse(s.input)!;
execute(s, &cmd)?;
// TODO: test if line was modified.
@@ -305,17 +308,17 @@ fn cmd_insert(s: *Session, cmd: *Command) (void | Error) = {
const n = get_linenum(cmd.linenums, s.buf.cursor);
const n = if (n == 0) 1z else n;
- for (let i = 0z; i < len(cmd.input); i += 1) {
- const l = alloc(Line { text = cmd.input[i], ... });
+ for (let i = 0z; i < len(cmd.textinput); i += 1) {
+ const l = alloc(Line{ text = cmd.textinput[i], ... });
debug("cmd_append(): l.text={}", l.text);
buf_insert(s.buf, n + i, l);
};
- s.buf.cursor = if (len(cmd.input) == 0) {
- yield n;
- } else {
- yield n + len(cmd.input) - 1;
- };
+ s.buf.cursor =
+ if (len(cmd.textinput) == 0)
+ n
+ else
+ n + len(cmd.textinput) - 1;
};
fn cmd_join(s: *Session, cmd: *Command) (void | Error) = {
@@ -342,7 +345,7 @@ fn cmd_join(s: *Session, cmd: *Command) (void | Error) = {
};
const newtext = strings::concat(ls...);
- const newline = alloc(Line {
+ const newline = alloc(Line{
text = newtext,
mark = mark,
...
@@ -544,7 +547,7 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = {
};
memio::concat(&new, strings::sub(old, rbound, strings::end))!;
- let newline = alloc(Line {
+ let newline = alloc(Line{
text = strings::dup(memio::string(&new)!),
...
});
diff --git a/main.ha b/main.ha
@@ -9,7 +9,7 @@ use regex;
use strings;
use types;
-type Session = struct {
+type Session = struct{
input: *bufio::scanner,
buf: *Buffer,
mode: InputMode,
@@ -22,11 +22,6 @@ type Session = struct {
prev_shcmd: (str | void),
};
-type InputMode = enum {
- COMMAND,
- TEXT,
-};
-
type Error = !(...InteractionError | ParseError | CmdError);
def help: [_]getopt::help = [
@@ -43,14 +38,14 @@ export fn main() void = {
const input = bufio::newscanner(os::stdin, types::SIZE_MAX);
defer bufio::finish(&input);
- const buffer = Buffer {
- lines = *alloc([ alloc(Line { ... }) ]),
+ const buffer = Buffer{
+ lines = *alloc([ alloc(Line{ ... }) ]),
trash = *alloc([]: []*Line),
...
};
defer buffer_finish(&buffer);
- let s = Session {
+ let s = Session{
input = &input,
buf = &buffer,
prompt = "*",
@@ -71,11 +66,10 @@ export fn main() void = {
};
};
- if (len(main_cmd.args) > 1) {
+ if (len(main_cmd.args) > 1)
exit_usage();
- };
- if (len(main_cmd.args) == 1) {
+ if (len(main_cmd.args) == 1)
switch (main_cmd.args[0]) {
case "-" =>
fmt::fatal("Invalid filename '-'");
@@ -84,60 +78,31 @@ export fn main() void = {
case =>
s.buf.filename = strings::dup(main_cmd.args[0]);
};
- };
if (len(s.buf.filename) != 0) {
- let cmd = Command { ... };
+ let cmd = Command{ ... };
cmd.arg1 = strings::dup(s.buf.filename);
cmd_edit(&s, &cmd): void; // TODO: handle error?
};
- let cmd = Command { ... };
-
for (true) :repl {
- if (s.promptmode && s.mode == InputMode::COMMAND) {
+ if (s.promptmode)
fmt::error(s.prompt)!;
- };
- const inputstr = match (bufio::scan_line(s.input)) {
- case let s: const str =>
- yield s;
- case io::EOF =>
- fmt::println()!;
- break;
- case let err: io::error =>
- fmt::fatal(io::strerror(err));
+ const cmd = match (parse(&input)) {
+ case let cmd: Command =>
+ yield cmd;
+ case let err: ParseError =>
+ errormsg(&s, strerror(err));
+ continue;
};
- switch (s.mode) {
- case InputMode::COMMAND =>
- const execnow = match (parse(&cmd, inputstr)) {
- case let err: ParseError =>
- errormsg(&s, strerror(err));
- continue;
- case let b: bool =>
- yield b;
- };
- if (execnow) {
- // TODO: handle all ": void"s
- execute(&s, &cmd): void;
- debug("main(): before command_finish()");
- command_finish(&cmd);
- debug("main(): after command_finish()");
- } else {
- s.mode = InputMode::TEXT;
- continue;
- };
- case InputMode::TEXT =>
- if (inputstr == ".") {
- execute(&s, &cmd): void;
- command_finish(&cmd);
- s.mode = InputMode::COMMAND;
- continue;
- };
- debug("repl: input={}", inputstr);
- append(cmd.input, strings::dup(inputstr));
- };
+ // TODO: handle all ": void"s
+ execute(&s, &cmd): void;
+
+ debug("main(): before command_finish()");
+ command_finish(&cmd);
+ debug("main(): after command_finish()");
};
// dumpbuffer(&s.buf);
diff --git a/parse.ha b/parse.ha
@@ -1,9 +1,15 @@
use ascii;
+use bufio;
use io;
use regex;
use strconv;
use strings;
+type InputMode = enum{
+ COMMAND,
+ TEXT,
+};
+
type ParseError = !(
UnknownCommand
| UnexpectedSuffix
@@ -29,73 +35,119 @@ 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);
+fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = {
+ let inputline = match (bufio::scan_line(input)?) {
+ case let s: const str =>
+ yield s;
+ case io::EOF =>
+ return Command{ cmdname = 'q', ... };
+ };
+ let iter = strings::iter(inputline);
+ let cmd = Command{ ... };
+
cmd.addrs = scan_addrs(&iter);
cmd.cmdname = scan_cmdname(&iter)?;
+ parse_cmdargs(&cmd, &iter)?;
switch (cmd.cmdname) {
+ case => void;
+ case 'a', 'c', 'i' =>
+ for (true) {
+ let inputline = match (bufio::scan_line(input)?) {
+ case let s: const str =>
+ yield s;
+ case io::EOF =>
+ break;
+ };
+
+ if (inputline == ".")
+ break;
+
+ append(cmd.textinput, inputline);
+ };
+ case 'q', 'v' =>
+ if (!strings::hassuffix(cmd.arg2, '\\'))
+ yield;
+
+ for (true) {
+ let inputstr = match (bufio::scan_line(input)?) {
+ case let s: const str =>
+ yield s;
+ case io::EOF =>
+ break;
+ };
+
+ // TODO: parse and append to .subcmds ?
+ append(cmd.textinput, inputstr);
+ };
+ };
+
+ return cmd;
+};
+
+fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) = {
+ switch (cmd.cmdname) {
// [ ]
case '\x00' =>
return true;
// (q|Q)
case 'q', 'Q' =>
- scan_end_assert(&iter)?;
+ scan_end_assert(iter)?;
return true;
// .[ <file>]
case 'e', 'E', 'f', 'r', 'w' =>
- if (scan_blanks(&iter) == 0) {
- match (strings::next(&iter)) {
+ if (scan_blanks(iter) == 0) {
+ match (strings::next(iter)) {
case let r: rune =>
return r: UnexpectedSuffix;
case void =>
return true;
};
} else {
- cmd.arg1 = scan_rest(&iter);
+ cmd.arg1 = scan_rest(iter);
return true;
};
// k<x>
case 'k' =>
- match (strings::next(&iter)) {
+ match (strings::next(iter)) {
case let r: rune =>
cmd.arg1 = strings::fromrunes([r]);
case void =>
return ExpectedMark;
};
- scan_end_assert(&iter)?;
+ scan_end_assert(iter)?;
return true;
// !<shellcmd>
case '!' =>
- cmd.arg1 = scan_rest(&iter);
+ cmd.arg1 = scan_rest(iter);
return true;
// .[s] where 's' is '(l|n|p)'
case 'd', 'h', 'H', 'j', 'l', 'n', 'p', 'P', 'u', '=' =>
- cmd.printmode = scan_suffix(&iter);
- scan_end_assert(&iter)?;
+ cmd.printmode = scan_suffix(iter);
+ scan_end_assert(iter)?;
return true;
// .[s]
case 'a', 'c', 'i' =>
- cmd.printmode = scan_suffix(&iter);
- scan_end_assert(&iter)?;
+ cmd.printmode = scan_suffix(iter);
+ scan_end_assert(iter)?;
return false;
// .[s][ ]<addr>
case 'm', 't' =>
- cmd.printmode = scan_suffix(&iter);
- cmd.arg1 = scan_rest(&iter);
+ cmd.printmode = scan_suffix(iter);
+ cmd.arg1 = scan_rest(iter);
return true;
// ./<regex>[/] where delimiter '/' is arbitrary
case 'G', 'V' =>
- const delim = match (strings::next(&iter)) {
+ const delim = match (strings::next(iter)) {
case void =>
return ExpectedArgument;
case let r: rune =>
@@ -105,14 +157,14 @@ fn parse(cmd: *Command, input: str) (bool | ParseError) = {
yield r;
};
};
- cmd.arg1 = scan_item(&iter, delim);
- strings::next(&iter); // scan delimiter if exists
- scan_end_assert(&iter)?;
+ cmd.arg1 = scan_item(iter, delim);
+ strings::next(iter); // scan delimiter if exists
+ scan_end_assert(iter)?;
return true;
// ./<regex>/<cmdlist...>
case 'g', 'v' =>
- const delim = match (strings::next(&iter)) {
+ const delim = match (strings::next(iter)) {
case void =>
return ExpectedArgument;
case let r: rune =>
@@ -122,17 +174,17 @@ fn parse(cmd: *Command, input: str) (bool | ParseError) = {
yield r;
};
};
- cmd.arg1 = scan_item(&iter, delim);
- strings::next(&iter); // scan delimiter if exists
- cmd.arg2 = scan_rest(&iter);
- if (strings::prev(&iter) as rune == '\\') {
+ cmd.arg1 = scan_item(iter, delim);
+ strings::next(iter); // scan delimiter if exists
+ cmd.arg2 = scan_rest(iter); // TODO: append to .subcmds?
+ if (strings::prev(iter) as rune == '\\') {
return false;
};
return true;
// s/<regex>/[<replace>[/[<flags>]]]
case 's' =>
- const delim = match (strings::next(&iter)) {
+ const delim = match (strings::next(iter)) {
case void =>
return ExpectedArgument;
case let r: rune =>
@@ -142,19 +194,19 @@ fn parse(cmd: *Command, input: str) (bool | ParseError) = {
yield r;
};
};
- cmd.arg1 = scan_item(&iter, delim);
- match (strings::next(&iter)) {
+ cmd.arg1 = 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)) {
+ 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?
+ cmd.arg3 = scan_rest(iter); // TODO: scan properly here?
return true;
case =>
@@ -174,14 +226,14 @@ fn scan_addrs(iter: *strings::iterator) []Address = {
switch (r) {
case ',' =>
specialfirst = true;
- append(addrs, Address {
+ append(addrs, Address{
addrform = 1z,
lineoffset = 0,
setcurrentline = false,
});
case ';' =>
specialfirst = true;
- append(addrs, Address {
+ append(addrs, Address{
addrform = CurrentLine,
lineoffset = 0,
setcurrentline = true,
@@ -195,7 +247,7 @@ fn scan_addrs(iter: *strings::iterator) []Address = {
let addr = match (scan_addr(iter)) {
case void =>
yield if (specialfirst) {
- yield Address {
+ yield Address{
addrform = LastLine,
lineoffset = 0,
...
@@ -258,14 +310,14 @@ fn scan_addr(iter: *strings::iterator) (Address | void) = {
} else if (r == '\'') {
yield scan_mark(iter);
} else if (r == '/') {
- const rad = RegexAddr {
+ const rad = RegexAddr{
expr = scan_item(iter, '/'),
direction = true,
};
strings::next(iter);
yield rad;
} else if (r == '?') {
- const rad = RegexAddr {
+ const rad = RegexAddr{
expr = scan_item(iter, '?'),
direction = false,
};
@@ -289,7 +341,7 @@ fn scan_addr(iter: *strings::iterator) (Address | void) = {
yield addrform as AddressForm;
};
- let addr = Address {
+ let addr = Address{
addrform = addrform,
lineoffset = 0,
...