Spry interpreter

This commit is contained in:
Ehmry - 2019-01-03 19:46:33 +01:00
parent 8ae2353cab
commit 340ce658d3
3 changed files with 174 additions and 11 deletions

View File

@ -6,7 +6,7 @@ description = "Sets of named blobs"
license = "AGPLv3"
srcDir = "src"
requires "nim >= 0.18.0", "base58", "cbor >= 0.5.1", "siphash", "nimcrypto"
requires "nim >= 0.18.0", "base58", "cbor >= 0.5.1", "siphash", "nimcrypto", "spryvm"
bin = @["blobset"]
skipFiles = @["blobset.nim"]

View File

@ -5,6 +5,19 @@ import std/nre, std/os, std/strutils, std/tables, std/parseopt, std/streams, std
import cbor
import ./blobsets, ./blobsets/filestores
# Basic Spry
import spryvm/spryvm
# Spry extra modules, as much as possible!
import spryvm/sprycore, spryvm/spryextend, spryvm/sprymath, spryvm/spryos, spryvm/spryio,
spryvm/spryoo, spryvm/sprystring, spryvm/sprymodules, spryvm/spryreflect, spryvm/sprymemfile,
spryvm/spryblock,
./blobsets/spryblobs
import os, strutils
when defined(readLine):
import rdstdin, linenoise
#when defined(genode):
# import dagfsclient
@ -676,6 +689,90 @@ proc replMain() =
outStream.write "\n"
flush outStream
const Prompt = ">>> "
proc getLine(prompt: string): string =
# Using line editing
when defined(readLine):
result = readLineFromStdin(prompt)
else:
# Primitive fallback
stdout.write(prompt)
result = stdin.readline()
proc spryMain() =
let spry = newInterpreter()
spry.addCore()
spry.addExtend()
spry.addMath()
spry.addOS()
spry.addIO()
spry.addOO()
spry.addString()
spry.addModules()
spry.addReflect()
spry.addMemfile()
spry.addBlock()
spry.addBlobSets()
var
lines, stashed, fileLines = newSeq[string]()
suspended: bool = true
echo "Welcome to interactive Spry!"
echo "An empty line will evaluate previous lines, so hit enter twice."
# We collect lines until an empty line is entered, easy way to enter
# multiline code.
while true:
var line: string
if suspended:
line = getLine(Prompt)
else:
if fileLines.len == 0:
quit 0
# Read a line, eh, would be nice with removeFirst or popFirst...
line = fileLines[0]
fileLines.delete(0)
# Logic for pausing
if line.strip() == "# pause":
var enter = getLine(" <enter = eval, s = suspend>")
if enter.strip() == "s":
stdout.write(" <suspended, c = continue>\n")
stashed = lines
lines = newSeq[string]()
suspended = true
continue
else:
stdout.write(line & "\n")
# Logic to start the script again
if suspended and line.strip() == "c":
lines = stashed
suspended = false
continue
# Finally time to eval
if line.strip().len() == 0:
let code = lines.join("\n")
lines = newSeq[string]()
try:
# Let the interpreter eval the code. We need to eval whatever we
# get (ispry acting as a func). The surrounding block is just because we only
# want to pass one Node.
var result = spry.evalRoot("[" & code & "]")
#discard spry.setBinding(newEvalWord("@"), result)
var output = $result
# Print any result
if output.isNil:
output = if suspended: "nil" else: ""
stdout.write(output & "\n")
except:
echo "Oops, sorry about that: " & getCurrentExceptionMsg() & "\n"
echo getStackTrace()
else:
lines.add(line)
proc main() =
var cmd = ""
for kind, key, val in getopt():
@ -683,15 +780,11 @@ proc main() =
cmd = key
break
case normalize(cmd)
of "":
quit("no subcommand specified")
of "repl":
replMain()
of "dump":
dumpMain()
of "ingest":
ingestMain()
else:
quit("no such subcommand ")
of "": quit("no subcommand specified")
of "repl": replMain()
of "dump": dumpMain()
of "ingest": ingestMain()
of "spry": spryMain()
else: quit("no such subcommand ")
main()

View File

@ -0,0 +1,70 @@
import spryvm/spryvm
import ../blobsets, ./filestores
import std/strutils, std/tables, std/os
type BlobIdSpryNode* = ref object of Value
id*: BlobId
type BlobSetSpryNode* = ref object of Value
set*: BlobSet
method eval*(self: BlobIdSpryNode|BlobSetSpryNode; spry: Interpreter): Node = self
method `$`*(self: BlobIdSpryNode): string = $self.id
method `$`*(self: BlobSetSpryNode): string = "<BlobSet>"
type BlobStoreNode* = ref object of Value
## Object for store interface and caches
store: BlobStore
blobs: Table[string, tuple[id: BlobId, size: BiggestInt]]
sets: Table[string, BlobSet]
proc insertPath(store: BlobStore; bs: BlobSet; kind: PathComponent; path: string): BlobSet =
result = bs
try:
case kind
of pcFile, pcLinkToFile:
var path = normalizedPath path
let (id, size) = store.ingestFile(path)
path.removePrefix(getCurrentDir())
path.removePrefix("/")
result = result.insert(path, id, size)
writeLine(stdout, id, align($size, 11), " ", path)
of pcDir, pcLinkToDir:
for kind, subPath in path.walkDir:
result = store.insertPath(result, kind, subPath)
except:
let e = getCurrentException()
writeLine(stderr, "failed to ingest '", path, "', ", e.msg)
proc getSet(env: BlobStoreNode; path: string): BlobSet =
result = env.sets.getOrDefault(path)
if result.isNil:
result = newBlobSet()
result = env.store.insertPath(result, path.getFileInfo.kind, path)
if not result.isEmpty:
env.sets[path] = result
proc ingestBlobs(env: BlobStoreNode; path: string): BlobSetSpryNode =
let bs = env.getSet(path)
BlobSetSpryNode(set: bs)
# Spry Blobset module
proc addBlobSets*(spry: Interpreter) =
nimMeth "ingestBlobs":
var env = BlobStoreNode(evalArgInfix(spry))
let path = StringVal(evalArg(spry)).value
result = ingestBlobs(env, path)
nimFunc "newBlobStore":
let path = StringVal(evalArg(spry)).value
BlobStoreNode(
store: newNullStore(),
blobs: initTable[string, tuple[id: BlobId, size: BiggestInt]](),
sets: initTable[string, BlobSet]())
nimFunc "toBlobId":
let str = StringVal(evalArg(spry)).value
BlobIdSpryNode(id: str.toBlobId)