second commit

This commit is contained in:
Sekulum Forka 2021-05-10 22:22:20 +02:00
parent b662afe779
commit 36d2b8e03a
3 changed files with 179 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
fsh

13
fsh.nimble Normal file
View File

@ -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"

165
src/fsh.nim Normal file
View File

@ -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