summaryrefslogtreecommitdiff
path: root/appl
diff options
context:
space:
mode:
authorKonstantin Kirik (snegovick) <snegovick@uprojects.org>2025-12-12 06:24:08 +0300
committerKonstantin Kirik (snegovick) <snegovick@uprojects.org>2025-12-12 06:24:08 +0300
commit06c7845f0247e77ed861b85b6c48556f6b6b120d (patch)
tree6e9eac0970f97bdae439925772f51f232af5e30f /appl
parenta40ef1434889babbd88c9d0c5913c70e96ac2774 (diff)
Reorganize files according to proper directory structure
Diffstat (limited to 'appl')
-rw-r--r--appl/cmd/mkfile14
-rw-r--r--appl/cmd/sh9.b857
-rw-r--r--appl/cmd/sh92.b215
-rw-r--r--appl/lib/mkfile19
-rw-r--r--appl/lib/sh9parser.b142
-rw-r--r--appl/lib/sh9util.b26
-rw-r--r--appl/mkfile7
7 files changed, 1280 insertions, 0 deletions
diff --git a/appl/cmd/mkfile b/appl/cmd/mkfile
new file mode 100644
index 0000000..a422ace
--- /dev/null
+++ b/appl/cmd/mkfile
@@ -0,0 +1,14 @@
+<../../mkconfig
+
+TARG=\
+ sh9.dis\
+ sh92.dis\
+
+SYSMODULES=\
+ sh9util.m\
+ sh9parser.m\
+ sys.m\
+
+DISBIN=$ROOT/dis
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/cmd/sh9.b b/appl/cmd/sh9.b
new file mode 100644
index 0000000..64d88e1
--- /dev/null
+++ b/appl/cmd/sh9.b
@@ -0,0 +1,857 @@
+implement Sh9;
+
+include "sys.m";
+ sys: Sys;
+ FD: import Sys;
+
+include "draw.m";
+ Context: import Draw;
+
+include "filepat.m";
+ filepat: Filepat;
+ nofilepat := 0; # true if load Filepat has failed.
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "env.m";
+ env: Env;
+
+include "hash.m";
+ hash: Hash;
+ HashTable: import hash;
+ HashVal: import hash;
+
+include "workdir.m";
+
+stdin: ref FD;
+stderr: ref FD;
+waitfd: ref FD;
+
+Quoted: con '\uFFF0';
+stringQuoted: con "\uFFF0";
+
+Sh9: module
+{
+ init: fn(ctxt: ref Context, argv: list of string);
+};
+
+Command: adt
+{
+ args: list of string;
+ inf, outf: string;
+ append: int;
+};
+
+Async, Seq: con iota;
+
+Pipeline: adt
+{
+ cmds: list of ref Command;
+ term: int;
+};
+
+usage()
+{
+ sys->fprint(stderr, "Usage: sh9 [-n] [-c cmd] [file]\n");
+}
+
+clean_n_chars_seek(sys: Sys, n: int, seek: int) {
+ for (i:=0; i<(n-seek); i++) {
+ sys->print("\b");
+ }
+ for (i=0; i<n; i++) {
+ sys->print(" ");
+ }
+ for (i=0; i<n; i++) {
+ sys->print("\b");
+ }
+}
+
+clean_n_chars(sys: Sys, n: int) {
+ clean_n_chars_seek(sys, n, 0);
+}
+
+init(ctxt: ref Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ env = load Env Env->PATH;
+ bufio = load Bufio Bufio->PATH;
+ hash = load Hash Hash->PATH;
+
+ n: int;
+ arg: list of string;
+ buf := array[1024] of byte;
+
+ stderr = sys->fildes(2);
+
+ waitfd = sys->open("#p/"+string sys->pctl(0, nil)+"/wait", sys->OREAD);
+ if(waitfd == nil){
+ sys->fprint(stderr, "sh9: open wait: %r\n");
+ return;
+ }
+
+ eflag := nflag := lflag := 0;
+ cmd: string;
+ if(argv != nil) {
+ argv = tl argv;
+ }
+ for(; argv != nil && len hd argv && (hd argv)[0]=='-'; argv = tl argv) {
+ case hd argv {
+ "-e" =>
+ eflag = 1;
+ "-n" =>
+ nflag = 1;
+ "-l" =>
+ lflag = 1;
+ "-c" =>
+ argv = tl argv;
+ if(len argv != 1){
+ usage();
+ return;
+ }
+ cmd = hd argv;
+ * =>
+ usage();
+ return;
+ }
+ }
+
+ if (lflag)
+ startup(ctxt);
+
+ if(eflag == 0)
+ sys->pctl(sys->FORKENV, nil);
+ if(nflag == 0)
+ sys->pctl(sys->FORKNS, nil);
+ if(cmd != nil){
+ arg = tokenize(cmd+"\n");
+ if(arg != nil)
+ runit(ctxt, parseit(arg));
+ return;
+ }
+ if(argv != nil){
+ script(ctxt, hd argv);
+ return;
+ }
+
+ cctlfd := sys->open("/dev/consctl", sys->OWRITE);
+ if(cctlfd == nil)
+ return;
+ sys->write(cctlfd, array of byte "rawon", 5);
+
+ dfd := sys->open("/dev/cons", sys->OREAD);
+ if(dfd == nil)
+ return;
+
+ #stdin = sys->fildes(0);
+
+ prompt := getusername() + "@" + sysname() + ":" + getcwd() + "; ";
+ offset : int = 0;
+ temp : int;
+ last_cmdline := array[1024] of int;
+ last_cmdline_length : int = 0;
+
+ cmd1 := 0;
+ ST_NORMAL : con 0;
+ ST_WAITCMD1 : con 1;
+ ST_WAITCMD2 : con 2;
+ state := ST_NORMAL;
+ history_len := 1;
+ history_entry_cur := 0;
+# history.entries = nil;
+# history.entries = ("", "") :: nil;
+ history := hash->new(1024);
+ history.insert("0", (0,0.0,""));
+ seek := 0;
+
+ bio := bufio->fopen(dfd, sys->OREAD);
+
+ sys->print("SH9 v0\n");
+ sys->print("%s", prompt);
+ for(;;) {
+ temp = bio.getb();
+ # check if escape
+ case state {
+ ST_NORMAL =>
+ # check if escape
+ if (temp == 27) {
+ state = ST_WAITCMD1; # is escape
+ } else if (temp == '\t') {
+ # tab complete rq
+ sys->print("tab");
+ } else if (temp == '\b') {
+ if (seek == 0) {
+ if (offset != 0) {
+ sys->print("\b");
+ sys->print(" ");
+ sys->print("\b");
+ offset --;
+ }
+ } else {
+ cur_buf := array[1024] of byte;
+ cur_buf[0:] = buf[0:offset];
+ correction := 0;
+ for (i:=0;i<offset;i++) {
+ if ((offset-i-1) == seek) {
+ correction = 1;
+ } else {
+ buf[i-correction] = cur_buf[i];
+ }
+ }
+ clean_n_chars_seek(sys, offset, seek);
+ offset --;
+ for (i=0;i<offset;i++) {
+ sys->print("%c", int(buf[i]));
+ }
+ for (i=0; i<seek; i++) {
+ sys->print("\b");
+ }
+ }
+ } else {
+ buf[offset] = byte(temp);
+ offset ++;
+ if ((offset >= len buf) || (buf[offset-1] == byte('\n'))) {
+ history_entry_cur = 0;
+ seek = 0;
+ sys->print("\n");
+ arg = tokenize(string buf[0:offset]);
+ if(arg != nil) {
+ runit(ctxt, parseit(arg));
+ history.insert(sys->sprint("%d", history_len), (0,0.0,string buf[0:offset-1]));
+ history_len++;
+ }
+ offset = 0;
+ prompt = getusername() + "@" + sysname() + ":" + getcwd() + "; ";
+ sys->print("%s", prompt);
+ } else {
+ sys->print("%c",temp);
+ }
+ }
+ ST_WAITCMD1 =>
+ if (temp == '[') {
+ state = ST_WAITCMD2;
+ cmd1 = temp;
+ }
+ ST_WAITCMD2 =>
+ state = ST_NORMAL;
+ case cmd1 {
+ '[' =>
+ case temp {
+ 65 => { #up press
+ seek = 0;
+ clean_n_chars(sys, offset);
+
+ offset = 0;
+ he := history_len;
+ if (history_entry_cur < history_len) {
+ history_entry_cur ++;
+ he -= history_entry_cur;
+ }
+ cmdline := history.find(sys->sprint("%d", he));
+ if (cmdline != nil) {
+ sys->print("%s", cmdline.s);
+ offset = len cmdline.s;
+ for (i:=0; i<len cmdline.s; i++) {
+ buf[i] = byte(cmdline.s[i]);
+ }
+ }
+ }
+ 66 => { # down key
+ seek = 0;
+ if (history_entry_cur > 0) {
+ history_entry_cur --;
+ clean_n_chars(sys, offset);
+
+ offset = 0;
+ he := history_len;
+ he -= history_entry_cur;
+ cmdline := history.find(sys->sprint("%d", he));
+ if (cmdline != nil) {
+ sys->print("%s", cmdline.s);
+ offset = len cmdline.s;
+ for (i:=0; i<len cmdline.s; i++) {
+ buf[i] = byte(cmdline.s[i]);
+ }
+ }
+ }
+ }
+ 68 => { # left key
+ if (seek > offset) {
+ seek = offset;
+ } else {
+ seek ++;
+ sys->print("\b");
+ }
+ }
+ 67 => { # right key
+ clean_n_chars_seek(sys, offset, seek);
+ for (i:=0; i<offset; i++) {
+ sys->print("%c", int(buf[i]));
+ }
+ if (seek > 0) {
+ seek --;
+ }
+ for (i=0; i<seek; i++) {
+ sys->print("\b");
+ }
+ }
+ 70 => { # end key
+ clean_n_chars_seek(sys, offset, seek);
+ seek = 0;
+ for (i:=0; i<offset; i++) {
+ sys->print("%c", int(buf[i]));
+ }
+ }
+ 72 => { # home key
+ seek = offset;
+ for (i:=0; i<seek; i++) {
+ sys->print("\b");
+ }
+ }
+ * => {
+ sys->print("no action bind for %d\n", temp);
+ }
+
+ }
+ }
+ }
+ }
+}
+
+rev(arg: list of string): list of string
+{
+ ret: list of string;
+
+ while(arg != nil){
+ ret = hd arg :: ret;
+ arg = tl arg;
+ }
+ return ret;
+}
+
+waitfor(pid: int)
+{
+ if(pid <= 0)
+ return;
+ buf := array[sys->WAITLEN] of byte;
+ status := "";
+ for(;;){
+ n := sys->read(waitfd, buf, len buf);
+ if(n < 0){
+ sys->fprint(stderr, "sh9: read wait: %r\n");
+ return;
+ }
+ status = string buf[0:n];
+ if(status[len status-1] != ':')
+ sys->fprint(stderr, "%s\n", status);
+ who := int status;
+ if(who != 0){
+ if(who == pid){
+ return;
+ }
+ }
+ }
+}
+
+mkprog(ctxt: ref Context, arg: list of string, infd, outfd: ref Sys->FD, waitpid: chan of int)
+{
+ fds := list of {0, 1, 2};
+ if(infd != nil)
+ fds = infd.fd :: fds;
+ if(outfd != nil)
+ fds = outfd.fd :: fds;
+ pid := sys->pctl(sys->NEWFD, fds);
+ console := sys->fildes(2);
+
+ if(infd != nil){
+ sys->dup(infd.fd, 0);
+ infd = nil;
+ }
+ if(outfd != nil){
+ sys->dup(outfd.fd, 1);
+ outfd = nil;
+ }
+
+ waitpid <-= pid;
+
+ if(pid < 0 || arg == nil)
+ return;
+
+ {
+ exec(ctxt, arg, console);
+ }exception{
+ "fail:*" =>
+ #sys->fprint(console, "%s:%s\n", hd arg, e.name[5:]);
+ exit;
+ "write on closed pipe" =>
+ #sys->fprint(console, "%s: %s\n", hd arg, e.name);
+ exit;
+ }
+}
+
+exec(ctxt: ref Context, args: list of string, console: ref Sys->FD)
+{
+ if (args == nil)
+ return;
+ cmd := hd args;
+ file := cmd;
+
+ if(len file<4 || file[len file-4:]!=".dis")
+ file += ".dis";
+
+ c := load Sh9 file;
+ if(c == nil) {
+ err := sys->sprint("%r");
+ if(err != "permission denied" && err != "access permission denied" && file[0]!='/' && file[0:2]!="./"){
+ c = load Sh9 "/dis/"+file;
+ if(c == nil) {
+ err = sys->sprint("%r");
+ }
+ }
+ if(c == nil){
+ sys->fprint(console, "%s: %s\n", cmd, err);
+ return;
+ }
+ }
+
+ c->init(ctxt, args);
+}
+
+script(ctxt: ref Context, src: string)
+{
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil){
+ sys->fprint(stderr, "sh9: load bufio: %r\n");
+ return;
+ }
+
+ f := bufio->open(src, Bufio->OREAD);
+ if(f == nil){
+ sys->fprint(stderr, "sh9: open %s: %r\n", src);
+ return;
+ }
+ for(;;){
+ s := f.gets('\n');
+ if(s == nil)
+ break;
+ arg := tokenize(s);
+ if(arg != nil)
+ runit(ctxt, parseit(arg));
+ }
+}
+
+sysname(): string
+{
+ fd := sys->open("#c/sysname", sys->OREAD);
+ if(fd == nil)
+ return "anon";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "anon";
+ return string buf[0:n];
+}
+
+# Lexer.
+
+tokenize(s: string): list of string
+{
+ tok: list of string;
+ token := "";
+ instring := 0;
+
+ loop:
+ for(i:=0; i<len s; i++) {
+ if(instring) {
+ if(s[i] != '\'')
+ token = addchar(token, s[i]);
+ else if(i == len s-1 || s[i+1] != '\'') {
+ if(i == len s-1 || s[i+1] == ' ' || s[i+1] == '\t' || s[i+1] == '\n'){
+ tok = token :: tok;
+ token = "";
+ }
+ instring = 0;
+ } else {
+ token[len token] = '\'';
+ i++;
+ }
+ continue;
+ }
+ case s[i] {
+ ' ' or '\t' or '\n' or '#' or
+ '\'' or '|' or '&' or ';' or
+ '>' or '<' or '\r' =>
+ if(token != "" && s[i]!='\''){
+ tok = token :: tok;
+ token = "";
+ }
+ case s[i] {
+ '#' =>
+ break loop;
+ '\'' =>
+ instring = 1;
+ '>' =>
+ ss := "";
+ ss[0] = s[i];
+ if(i<len s-1 && s[i+1]==s[i])
+ ss[1] = s[i++];
+ tok = ss :: tok;
+ '|' or '&' or ';' or '<' =>
+ ss := "";
+ ss[0] = s[i];
+ tok = ss :: tok;
+ }
+ * =>
+ token[len token] = s[i];
+ }
+ }
+ if(instring){
+ sys->fprint(stderr, "sh9: unmatched quote\n");
+ return nil;
+ }
+ return rev(tok);
+}
+
+ismeta(char: int): int
+{
+ case char {
+ '*' or '[' or '?' or
+ '#' or '\'' or '|' or
+ '&' or ';' or '>' or
+ '<' =>
+ return 1;
+ }
+ return 0;
+}
+
+addchar(token: string, char: int): string
+{
+ if(ismeta(char) && (len token==0 || token[0]!=Quoted))
+ token = stringQuoted + token;
+ token[len token] = char;
+ return token;
+}
+
+# Parser.
+
+getcommand(words: list of string): (ref Command, list of string)
+{
+ args: list of string;
+ word: string;
+ si, so: string;
+ append := 0;
+
+ gather:
+ do {
+ word = hd words;
+
+ case word {
+ ">" or ">>" =>
+ if(so != nil)
+ return (nil, nil);
+
+ words = tl words;
+
+ if(words == nil)
+ return (nil, nil);
+
+ so = hd words;
+ if(len so>0 && so[0]==Quoted)
+ so = so[1:];
+ if(word == ">>")
+ append = 1;
+ "<" =>
+ if(si != nil)
+ return (nil, nil);
+
+ words = tl words;
+
+ if(words == nil)
+ return (nil, nil);
+
+ si = hd words;
+ if(len si>0 && si[0]==Quoted)
+ si = si[1:];
+ "|" or ";" or "&" =>
+ break gather;
+ * =>
+ files := doexpand(word);
+ while(files != nil){
+ args = hd files :: args;
+ files = tl files;
+ }
+ }
+
+ words = tl words;
+ } while (words != nil);
+
+ return (ref Command(rev(args), si, so, append), words);
+}
+
+doexpand(file: string): list of string
+{
+ if(file == nil)
+ return file :: nil;
+ if(len file>0 && file[0]==Quoted)
+ return file[1:] :: nil;
+ if (nofilepat)
+ return file :: nil;
+ for(i:=0; i<len file; i++)
+ {
+ if (file[i]=='*' || file[i]=='[' || file[i]=='?'){
+ if(filepat == nil) {
+ if ((filepat = load Filepat Filepat->PATH) == nil) {
+ sys->fprint(stderr, "sh: warning: cannot load %s: %r\n",
+ Filepat->PATH);
+ nofilepat = 1;
+ return file :: nil;
+ }
+ }
+ files := filepat->expand(file);
+ if(files != nil)
+ return files;
+ break;
+ }
+ }
+ return file :: nil;
+}
+
+revc(arg: list of ref Command): list of ref Command
+{
+ ret: list of ref Command;
+ while(arg != nil) {
+ ret = hd arg :: ret;
+ arg = tl arg;
+ }
+ return ret;
+}
+
+getpipe(words: list of string): (ref Pipeline, list of string)
+{
+ cmds: list of ref Command;
+ cur: ref Command;
+ word: string;
+
+ term := Seq;
+ gather:
+ while(words != nil) {
+ word = hd words;
+
+ if(word == "|")
+ return (nil, nil);
+
+ (cur, words) = getcommand(words);
+
+ if(cur == nil)
+ return (nil, nil);
+
+ cmds = cur :: cmds;
+
+ if(words == nil)
+ break gather;
+
+ word = hd words;
+ words = tl words;
+
+ case word {
+ ";" =>
+ break gather;
+ "&" =>
+ term = Async;
+ break gather;
+ "|" =>
+ continue gather;
+ }
+ return (nil, nil);
+ }
+
+ if(word == "|")
+ return (nil, nil);
+
+ return (ref Pipeline(revc(cmds), term), words);
+}
+
+revp(arg: list of ref Pipeline): list of ref Pipeline
+{
+ ret: list of ref Pipeline;
+
+ while(arg != nil) {
+ ret = hd arg :: ret;
+ arg = tl arg;
+ }
+ return ret;
+}
+
+parseit(words: list of string): list of ref Pipeline
+{
+ ret: list of ref Pipeline;
+ cur: ref Pipeline;
+
+ while(words != nil) {
+ (cur, words) = getpipe(words);
+ if(cur == nil){
+ sys->fprint(stderr, "sh9: syntax error\n");
+ return nil;
+ }
+ ret = cur :: ret;
+ }
+ return revp(ret);
+}
+
+# Runner.
+
+runpipeline(ctx: ref Context, pipeline: ref Pipeline)
+{
+ if(pipeline.term == Async)
+ sys->pctl(sys->NEWPGRP, nil);
+ pid := startpipeline(ctx, pipeline);
+ if(pid < 0)
+ return;
+ if(pipeline.term == Seq)
+ waitfor(pid);
+}
+
+startpipeline(ctx: ref Context, pipeline: ref Pipeline): int
+{
+ pid := 0;
+ cmds := pipeline.cmds;
+ first := 1;
+ inpipe, outpipe: ref Sys->FD;
+ while(cmds != nil) {
+ last := tl cmds == nil;
+ cmd := hd cmds;
+
+ infd: ref Sys->FD;
+ if(!first)
+ infd = inpipe;
+ else if(cmd.inf != nil){
+ infd = sys->open(cmd.inf, Sys->OREAD);
+ if(infd == nil){
+ sys->fprint(stderr, "sh9: can't open %s: %r\n", cmd.inf);
+ return -1;
+ }
+ }
+
+ outfd: ref Sys->FD;
+ if(!last){
+ fds := array[2] of ref Sys->FD;
+ if(sys->pipe(fds) < 0){
+ sys->fprint(stderr, "sh9: can't make pipe: %r\n");
+ return -1;
+ }
+ outpipe = fds[0];
+ outfd = fds[1];
+ fds = nil;
+ }else if(cmd.outf != nil){
+ if(cmd.append){
+ outfd = sys->open(cmd.outf, Sys->OWRITE);
+ if(outfd != nil)
+ sys->seek(outfd, big 0, Sys->SEEKEND);
+ }
+ if(outfd == nil)
+ outfd = sys->create(cmd.outf, Sys->OWRITE, 8r666);
+ if(outfd == nil){
+ sys->fprint(stderr, "sh9: can't open %s: %r\n", cmd.outf);
+ return -1;
+ }
+ }
+
+ rpid := chan of int;
+ spawn mkprog(ctx, cmd.args, infd, outfd, rpid);
+ pid = <-rpid;
+ infd = nil;
+ outfd = nil;
+
+ inpipe = outpipe;
+ outpipe = nil;
+
+ first = 0;
+ cmds = tl cmds;
+ }
+ return pid;
+}
+
+runit(ctx: ref Context, pipes: list of ref Pipeline)
+{
+ while(pipes != nil) {
+ pipeline := hd pipes;
+ pipes = tl pipes;
+ if(pipeline.term == Seq)
+ runpipeline(ctx, pipeline);
+ else
+ spawn runpipeline(ctx, pipeline);
+ }
+}
+
+strchr(s: string, c: int): int
+{
+ ln := len s;
+ for (i := 0; i < ln; i++)
+ if (s[i] == c)
+ return i;
+ return -1;
+}
+
+# PROFILE: con "/lib/profile";
+PROFILE: con "/lib/infernoinit";
+
+startup(ctxt: ref Context)
+{
+ if (env == nil)
+ return;
+ # if (env->getenv("home") != nil)
+ # return;
+ home := gethome();
+ env->setenv("home", home);
+ escript(ctxt, PROFILE);
+ escript(ctxt, home + PROFILE);
+}
+
+escript(ctxt: ref Context, file: string)
+{
+ fd := sys->open(file, Sys->OREAD);
+ if (fd != nil)
+ script(ctxt, file);
+}
+
+gethome(): string
+{
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd == nil)
+ return "/";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "/";
+ return "/usr/" + string buf[0:n];
+}
+
+getusername(): string
+{
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd == nil)
+ return "/";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "?";
+ return string buf[0:n];
+}
+
+getcwd(): string
+{
+ gwd := load Workdir Workdir->PATH;
+ if (gwd == nil) {
+ sys->fprint(stderr, "pwd: cannot load %s: %r\n", Workdir->PATH);
+ raise "fail:bad module";
+ }
+
+ wd := gwd->init();
+ if(wd == nil) {
+ sys->fprint(stderr, "pwd: %r\n");
+ raise "fail:error";
+ }
+ return wd;
+}
diff --git a/appl/cmd/sh92.b b/appl/cmd/sh92.b
new file mode 100644
index 0000000..9748da7
--- /dev/null
+++ b/appl/cmd/sh92.b
@@ -0,0 +1,215 @@
+implement Sh92;
+
+include "sys.m";
+include "draw.m";
+include "sh9util.m";
+include "sh9parser.m";
+
+sys: Sys;
+sh9u: Sh9Util;
+sh9p: Sh9Parser;
+
+Sh92: module {
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+ModProc: adt {
+ name: string;
+ start: int;
+};
+
+ModVar: adt {
+ name: string;
+ val: string;
+};
+
+ShModule: adt {
+ global_vars: list of ref ModVar;
+ procs: list of ref ModProc;
+};
+
+GrammarNode: import sh9p;
+TokNode: import sh9p;
+mk_tok: import sh9p;
+set_last_tok: import sh9p;
+print_toks: import sh9p;
+parse_toks: import sh9p;
+
+reverse_list: import sh9u;
+to_array: import sh9u;
+
+S_UNKNOWN: con "UNK";
+S_ID: con "ID";
+S_STR: con "STR";
+S_EQ: con "EQ";
+S_DOL: con "DOL";
+S_COLON: con "COLON";
+S_SEMIC: con "SEMIC";
+S_LPAR: con "LPAR";
+S_RPAR: con "RPAR";
+S_LCURLY: con "LCURLY";
+S_RCURLY: con "RCURLY";
+S_DQSTR: con "DQSTR";
+S_SQSTR: con "SQSTR";
+S_DQTE: con "DQTE";
+S_SQTE: con "SQTE";
+S_SP: con "SP";
+S_TAB: con "TAB";
+S_EOL: con "EOL";
+
+S_STMT: con "STMT";
+S_EXPR: con "EXPR";
+S_CALL: con "CALL";
+
+tokenize(line: string, line_n: int): array of ref TokNode {
+ toks : list of ref TokNode;
+ last_tok:= ref TokNode;
+ last_tok.start = -1;
+ last_tok.line = -1;
+ last_tok.tok = "";
+ last_tok.typ = S_UNKNOWN;
+ k:=0;
+
+ for (i := 0; i < len line; i++) {
+ if (last_tok.typ == S_DQSTR) {
+ case (line[i:i+1]) {
+ "\"" => {
+ l := len last_tok.tok;
+ if ((last_tok.tok[l-1:] != "\\") || ((last_tok.tok[l-1:] == "\\") && (last_tok.tok[l-2:l-1] == "\\"))) {
+ # end of str
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ } else {
+ # escaped dqte, just continue
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ }
+ };
+ * => {
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ }
+ }
+ } else if (last_tok.typ == S_SQSTR) {
+ case (line[i:i+1]) {
+ "'" => {
+ l := len last_tok.tok;
+ if ((last_tok.tok[l-1:] != "\\") || ((last_tok.tok[l-1:] == "\\") && (last_tok.tok[l-2:l-1] == "\\"))) {
+ # end of str
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ } else {
+ # escaped sqte, just continue
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ }
+ };
+ * => {
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ }
+ }
+ } else {
+ case (line[i:i+1]) {
+ " " or "\t" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ };
+ "=" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, "=", S_EQ) :: toks;
+ };
+ ";" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, ";", S_SEMIC) :: toks;
+ };
+ "$" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, "$", S_DOL) :: toks;
+ };
+ "(" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, "(", S_LPAR) :: toks;
+ };
+ ")" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, ")", S_RPAR) :: toks;
+ };
+ "{" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, "{", S_LCURLY) :: toks;
+ };
+ "}" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, "}", S_RCURLY) :: toks;
+ };
+ "\"" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ last_tok.start = i;
+ last_tok.line = line_n;
+ last_tok.typ = S_DQSTR;
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ };
+ "'" => {
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ last_tok.start = i;
+ last_tok.line = line_n;
+ last_tok.typ = S_SQSTR;
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ };
+ * => {
+ if (last_tok.start == -1) {
+ last_tok.start = i;
+ last_tok.line = line_n;
+ last_tok.typ = S_ID;
+ }
+ last_tok.tok = last_tok.tok + line[i:i+1];
+ };
+ }
+ }
+ }
+ (last_tok, toks) = set_last_tok(last_tok, toks);
+ toks = mk_tok(i, line_n, "", S_EOL) :: toks;
+ toks = reverse_list(toks);
+ return to_array(toks);
+}
+
+stmt_assign(toks: array of ref TokNode) {
+ sys->print("ASSIGN STMT\n");
+}
+
+stmt_cmd_call(toks: array of ref TokNode) {
+ sys->print("CMD CALL\n");
+}
+
+empty(toks: array of ref TokNode) {
+ sys->print("EMPTY\n");
+}
+
+Te: adt{
+ s: string;
+};
+
+init(ctxt: ref Draw->Context, argv: list of string) {
+ sys = load Sys Sys->PATH;
+ sh9u = load Sh9Util Sh9Util->PATH;
+ sh9p = load Sh9Parser Sh9Parser->PATH;
+ sh9p->init();
+
+
+ assign_g_semic : GrammarNode = (array [] of {S_ID, S_EQ, S_EXPR, S_SEMIC}, S_UNKNOWN, stmt_assign);
+ assign_g_eol : GrammarNode = (array [] of {S_ID, S_EQ, S_EXPR, S_EOL}, S_UNKNOWN, stmt_assign);
+ sqstr_expr_g: GrammarNode = (array [] of {S_SQSTR}, S_EXPR, empty);
+ str_expr_g: GrammarNode = (array [] of {S_STR}, S_EXPR, empty);
+ cmd_call_g: GrammarNode = (array [] of {S_ID, S_EQ, S_EXPR, S_SEMIC}, S_UNKNOWN, stmt_cmd_call);
+ grammar: array of ref GrammarNode;
+ grammar = array [] of {ref assign_g_semic, ref assign_g_eol, ref sqstr_expr_g, ref str_expr_g, ref cmd_call_g};
+
+ toks1 := tokenize("AB = 'smth \"test\" ';", 0);
+ print_toks(toks1);
+ sys->print("Parse\n");
+ parse_toks(toks1, grammar);
+ sys->print("Parse done\n");
+
+ # toks2 := tokenize("echo \"smth \" \"test\";", 0);
+ # print_toks(toks2);
+ # toks3 := tokenize("if test x\"a\" = x\"b\"; then echo \"1\"; fi", 0);
+ # print_toks(toks3);
+ # toks4 := tokenize("echo 'smth2' 'test';", 0);
+ # print_toks(toks4);
+}
diff --git a/appl/lib/mkfile b/appl/lib/mkfile
new file mode 100644
index 0000000..0becc2b
--- /dev/null
+++ b/appl/lib/mkfile
@@ -0,0 +1,19 @@
+<../../mkconfig
+
+TARG=\
+ sh9util.dis\
+ sh9parser.dis\
+
+
+SYSMODULES=\
+ sh9util.m\
+ sh9parser.m\
+ sys.m\
+
+
+MODULES=\
+
+
+DISBIN=$ROOT/dis/lib
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/sh9parser.b b/appl/lib/sh9parser.b
new file mode 100644
index 0000000..ac46b40
--- /dev/null
+++ b/appl/lib/sh9parser.b
@@ -0,0 +1,142 @@
+implement Sh9Parser;
+
+include "sys.m";
+include "sh9parser.m";
+include "sh9util.m";
+
+sys: Sys;
+S_UNKNOWN: con "UNK";
+
+sh9u: Sh9Util;
+
+reverse_list: import sh9u;
+to_array: import sh9u;
+
+GrammarNode.print_expr(gn: self ref GrammarNode) {
+ lg:= len gn.expr;
+ for (i:=0; i<lg; i++) {
+ sys->print("%s ", gn.expr[i]);
+ }
+ if (gn.transform == S_UNKNOWN) {
+ sys->print("\n");
+ } else {
+ sys->print("-> %s\n", gn.transform);
+ }
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ sh9u = load Sh9Util Sh9Util->PATH;
+}
+
+mk_tok(start: int, line: int, tok: string, typ: string) : ref TokNode {
+ tok_node: TokNode;
+ tok_node.start = start;
+ tok_node.line = line;
+ tok_node.tok = tok;
+ tok_node.typ = typ;
+ return ref tok_node;
+}
+
+set_last_tok(last_tok: ref TokNode, toks: list of ref TokNode): (ref TokNode, list of ref TokNode) {
+ sys->print("last_tok: %s\n", last_tok.typ);
+ ret_tok: TokNode;
+ #ret_tok = *last_tok;
+ ret_tok.typ = last_tok.typ;
+ ret_tok.start = last_tok.start;
+ ret_tok.tok = last_tok.tok;
+ ret_tok.line = last_tok.line;
+ if (last_tok.typ != S_UNKNOWN) {
+ toks = last_tok :: toks;
+ ret_tok.typ = S_UNKNOWN;
+ ret_tok.start = -1;
+ ret_tok.tok = "";
+ ret_tok.line = -1;
+ }
+ sys->print("ret_tok: %s\n", ret_tok.typ);
+ return (ref ret_tok, toks);
+}
+
+print_toks(toks: array of ref TokNode) {
+ lt := len toks;
+ for (i := 0; i < lt; i ++) {
+ tok := toks[i];
+ sys->print("[%d/%d] %s (%s)\n", i, lt, tok.typ, tok.tok);
+ }
+}
+
+print_toks_short(toks: array of ref TokNode) {
+ lt := len toks;
+ for (i := 0; i < lt; i ++) {
+ tok := toks[i];
+ sys->print("%s ", tok.typ);
+ }
+ sys->print("\n");
+}
+
+check_grammar_node_match(toks: array of ref TokNode, gn: ref GrammarNode): int {
+ lt:= len toks;
+ lg:= len gn.expr;
+ if (lg > lt) {
+ return 0;
+ }
+ #sys->print("Checking grammar ");
+ gn.print_expr();
+ #sys->print("Against ");
+ print_toks(toks);
+ for (i:= 0; i < lg; i ++) {
+ if (toks[i].typ != gn.expr[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+replace_toks(src: array of ref TokNode, replace_start: int, replace_len: int, replace_with: array of ref TokNode): array of ref TokNode {
+ src_len:= len src;
+ new_toks: list of ref TokNode;
+ with_len:= len replace_with;
+ for (i:=0; i<replace_start; i++) {
+ new_toks = src[i] :: new_toks;
+ }
+ for (i=0; i<with_len; i++) {
+ new_toks = replace_with[i] :: new_toks;
+ }
+ for (i=replace_start + replace_len; i<src_len; i++) {
+ new_toks = src[i] :: new_toks;
+ }
+ new_toks = reverse_list(new_toks);
+ return to_array(new_toks);
+}
+
+parse_toks(toks: array of ref TokNode, g: array of ref GrammarNode): array of ref TokNode {
+ lgns := len g;
+ changed := 0;
+ ctr := 0;
+ do
+ {
+ lt := len toks;
+ sys->print("Loop %d: ", ctr);
+ print_toks_short(toks);
+ ctr ++;
+ changed = 0;
+ fast: for (i := 0; i <= lt; i ++) {
+ for (j := 0; j < lgns; j++) {
+ gj:= g[j];
+ if (check_grammar_node_match(toks[lt - i:], gj) == 1) {
+ sys->print("Something matched !\n");
+ gj.print_expr();
+ sys->print("Before replace: ");
+ print_toks_short(toks);
+ gj.callback(toks[lt-i: lt-i+len gj.expr]);
+ toks = replace_toks(toks, lt-i, len gj.expr, array[] of {mk_tok(toks[lt - i].start, toks[lt - i].line, "", gj.transform)});
+ sys->print("After replace: ");
+ changed = 1;
+ break fast;
+ }
+ }
+ }
+ } while(changed);
+ return toks;
+}
diff --git a/appl/lib/sh9util.b b/appl/lib/sh9util.b
new file mode 100644
index 0000000..7452a13
--- /dev/null
+++ b/appl/lib/sh9util.b
@@ -0,0 +1,26 @@
+implement Sh9Util;
+
+include "sh9util.m";
+
+reverse_list[T](toks: list of T): list of T
+{
+ lt := len toks;
+ out : list of T;
+ for (i := 0; i < lt; i ++) {
+ tok := hd toks;
+ toks = tl toks;
+ out = tok :: out;
+ }
+ return out;
+}
+
+to_array[T](toks: list of T): array of T {
+ lt := len toks;
+ out := array[lt] of T;
+ for (i := 0; i < lt; i ++) {
+ tok := hd toks;
+ toks = tl toks;
+ out[i] = tok;
+ }
+ return out;
+}
diff --git a/appl/mkfile b/appl/mkfile
new file mode 100644
index 0000000..1946c4f
--- /dev/null
+++ b/appl/mkfile
@@ -0,0 +1,7 @@
+<../mkconfig
+
+DIRS=\
+ lib\
+ cmd\
+
+<$ROOT/mkfiles/mksubdirs