From 36d2b8e03a6fdef9f8250010450531158f9822c0 Mon Sep 17 00:00:00 2001 From: Sekulum Forka Date: Mon, 10 May 2021 22:22:20 +0200 Subject: [PATCH] second commit --- .gitignore | 1 + fsh.nimble | 13 +++++ src/fsh.nim | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 .gitignore create mode 100644 fsh.nimble create mode 100644 src/fsh.nim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a66ca1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fsh diff --git a/fsh.nimble b/fsh.nimble new file mode 100644 index 0000000..b023315 --- /dev/null +++ b/fsh.nimble @@ -0,0 +1,13 @@ +# Package + +version = "0.1.0" +author = "Sekulum Forka" +description = "A shell inspired by tcl" +license = "AGPL-3.0-only" +srcDir = "src" +bin = @["fsh"] + + +# Dependencies + +requires "nim >= 1.4.4" diff --git a/src/fsh.nim b/src/fsh.nim new file mode 100644 index 0000000..871f4f2 --- /dev/null +++ b/src/fsh.nim @@ -0,0 +1,165 @@ +# fsh - Forka shell +# 2021 - Seculum Forka +# This program is licensed under the agpl v3.0 only license + +# imports +import streams +import strutils +import osproc + +# forward declarations +proc eval(cmd: string): string + +# skipSpaces reads the stream until a non-space character is found +proc skipSpaces(strm: Stream): int = + while not strm.atEnd(): + if strm.peekChar() == ' ': + discard strm.readChar() + result.inc + else: break + +# readLiterals parses the string passed in as a literal, preserving all braces except the first and last ones +proc readLiteral(strm: Stream): string = + var numbraces=1 + while not strm.atEnd() and numbraces > 0: + let c = strm.readChar() + case c: + of '{': + numbraces.inc + of '}': + numbraces = numbraces - 1 + of '\\': + if strm.peekChar notin {'{', '}'}: result.add(strm.readChar) + result.add(strm.readChar) + else: + result.add(c) + +# a forward declaration. Fuck this shit +proc readSubstitution(strm: Stream): string + +# readInterpelation reads the interpelation in etween [ and ] +proc readInterpelation(strm: Stream): string = + while not strm.atEnd(): + let c = strm.readChar() + case c: + of '{': + result.add('{') + result.add(strm.readLiteral) + result.add('}') + of '[': + result.add('[') + result.add(strm.readInterpelation) + result.add(']') + of '"': + result.add("\"") + result.add(strm.readSubstitution) + result.add("\"") + of '\\': + result.add("\\") + result.add(strm.readChar()) + of ']': + break + else: result.add(c) + +# readSubstitution reads a substitutionbetween "quotes" +proc readSubstitution(strm: Stream): string = + while not strm.atEnd(): + let c = strm.readChar() + case c: + of '{': + result.add('{') + result.add(strm.readLiteral) + result.add('}') + of '[': + result.add('[') + result.add(strm.readInterpelation) + result.add(']') + of '"': + break + of '\\': + result.add("\\") + result.add(strm.readChar()) + else: result.add(c) + +# readCommand reads a command from stream +proc readCommand(strm: Stream): string = + while not strm.atEnd(): + let c = strm.readChar() + case c: + of '{': + result.add('{') + result.add(strm.readLiteral) + result.add('}') + of '[': + result.add('[') + result.add(strm.readInterpelation) + result.add(']') + of '"': + result.add("\"") + result.add(strm.readSubstitution) + result.add("\"") + of '\\': + result.add("\\") + result.add(strm.readChar()) + of '\n', '\r', ';': break + else: result.add(c) + +# substitute does a substitution on the stream passed in +proc substitute(strm: Stream, delim=";\p"): string = + while not strm.atEnd(): + let c = strm.readChar() + if c in delim: break + case c: + of '{': + result.add(strm.readLiteral) + of '}': + stderr.write("Extra closing brace") + of '[': + result.add(strm.readInterpelation.eval) + of ']': + stderr.write("Extra closing bracket") + of '\\': + result.add(strm.readChar()) + else: + result.add(c) + +# parseCommand parses the string given into a list representing a command +proc parseCommand(cmd: string): seq[string] = + var newitem = "" + let strm = newStringStream(cmd) + while true: + if strm.atEnd(): + result.add(newitem) + break + let c = strm.readChar() + case c: + of '{': + newitem.add(strm.readLiteral) + of '[': + newitem.add(strm.readInterpelation.eval) + of '"': + newitem.add(strm.readSubstitution.newStringStream.substitute) + of ' ': + result.add(newitem) + newitem = "" + discard strm.skipSpaces() + else: newitem.add(c) + +# executes executes the command. For now it involves catting the command and printing it +proc execute(cmd: seq[string]): string = + let progname=cmd[0] + let args = cmd[1..cmd.high] + let ps = startProcess(progname, args=args, options={poUsePath}) + result=ps.outputStream.readAll + ps.close + + +# eval evaluates the given string +proc eval(cmd: string): string = + let parsed = cmd.parseCommand + result = parsed.execute + +when isMainModule: + let stdinstrm = stdin.newFileStream + while not stdinstrm.atEnd(): + stdinstrm.readCommand.eval.echo