Started implementing comm module
h3rald h3rald@h3rald.com
Sat, 09 Jul 2016 23:29:25 +0200
6 files changed,
131 insertions(+),
13 deletions(-)
M
core/server.nim
→
core/server.nim
@@ -1,4 +1,5 @@
import + net, asynchttpserver, asyncdispatch, httpclient,@@ -18,7 +19,7 @@ proc validUrl(req: Request, url: string): bool =
return req.url.path == url or req.url.path == url & "/" proc validMethod(req: Request, meth: string): bool = - return req.reqMethod == meth + return req.reqMethod == meth or req.reqMethod == meth.toLower proc exec(req: Request, interpreter: MinInterpreter, hosts: CritBitTree[string]): string {.gcsafe.}= let filename = "request"@@ -35,6 +36,10 @@ var matches = @["", "", ""]
template route(req, peg: expr, op: stmt): stmt {.immediate.}= if req.url.path.find(peg, matches) != -1: op + req.route peg"^\/?$": + if not req.validMethod("GET"): + raiseServer(Http405, "Method Not Allowed: " & req.reqMethod) + return "MiNiM Host '$1'" % [i.link.name] req.route peg"^\/exec\/?$": if not req.validMethod("POST"): raiseServer(Http405, "Method Not Allowed: " & req.reqMethod)@@ -42,21 +47,40 @@ return exec(req, i, hosts)
raiseServer(Http400, "Bad Request: POST "& req.url.path) -proc serve*(port: Port, address = "", interpreter: MinInterpreter) = - var hosts: CritBitTree[string] +proc init*(link: ref MinLink) {.thread.} = proc handleHttpRequest(req: Request): Future[void] {.async.} = var res: string var code: HttpCode = Http200 try: - res = req.process(interpreter, hosts) + res = req.process(link.interpreter, link.hosts) except MinServerError: let e: MinServerError = (MinServerError)getCurrentException() res = e.msg code = e.code await req.respond(code, res) - let server = newAsyncHttpServer() - asyncCheck server.serve(port, handleHttpRequest, address) + asyncCheck link.server.serve(link.port, handleHttpRequest, link.address) -proc post*(url, content: string): string = - url.postContent(content) +proc remoteExec*(i: MinInterpreter, host, content: string): string {.gcsafe.}= + if i.link.hosts.hasKey(host): + let url = "http://" & i.link.hosts[host] & "/exec" + result = url.postContent(body = content, sslContext = nil) + else: + raiseServer(Http404, "Not Found: Host '$1'" % [host]) +proc syncHosts*(i: MinInterpreter): CritBitTree[string] {.gcsafe.}= + var cmd = "" + for key, val in i.link.hosts.pairs: + cmd = cmd & """ ('$1 '$2)""" % [key, val] + cmd = "(" & cmd.strip & ") set-hosts" + for key, val in i.link.hosts.pairs: + result[key] = i.remoteExec(key, cmd) + +proc newMinLink*(name, address: string, port: int, i: var MinInterpreter): ref MinLink = + var link: ref MinLink = new MinLink + result = link + result.server = newAsyncHttpServer() + result.name = name + result.address = address + result.port = port.Port + i.link = result + result.interpreter = i
M
core/types.nim
→
core/types.nim
@@ -1,4 +1,4 @@
-import lexbase, critbits, httpclient +import lexbase, critbits, asyncdispatch, asynchttpserver type MinTokenKind* = enum@@ -80,6 +80,14 @@ filename*: string
debugging*: bool evaluating*: bool unsafe*: bool + link*: ref MinLink + MinLink* = object + hosts*: CritBitTree[string] + name*: string + address*: string + port*: Port + server*: AsyncHttpServer + interpreter*: MinInterpreter In* = var MinInterpreter Val* = var MinValue MinOperator* = proc (i: In) {.gcsafe, closure.}
M
core/utils.nim
→
core/utils.nim
@@ -195,6 +195,12 @@ b = i.pop
if not a.isString or not b.isString: raiseInvalid("Two strings are required on the stack") +proc reqTwoStringLike*(i: var MinInterpreter, a, b: var MinValue) = + a = i.pop + b = i.pop + if not a.isStringLike or not b.isStringLike: + raiseInvalid("Two symbols or strings are required on the stack") + proc reqThreeStrings*(i: var MinInterpreter, a, b, c: var MinValue) = a = i.pop b = i.pop
A
lib/min_comm.nim
@@ -0,0 +1,41 @@
+import strutils, critbits +import + ../core/types, + ../core/parser, + ../core/interpreter, + ../core/utils, + ../core/server + +# I/O + + +proc comm_module*(i: In) = + i.define("comm") + + .symbol("reg") do (i: In): + var host, address: MinValue + i.reqTwoStringLike(host, address) + i.link.hosts[host.getString] = address.getString + for host, response in i.syncHosts().pairs: + echo host, ": ", response + + .symbol("set-hosts") do (i: In): + var q: MinValue + i.reqQuotation(q) + for pair in q.qVal: + let vals = pair.qVal + if not pair.isQuotation or vals.len != 2 or not vals[0].isStringLike or not vals[1].isStringLike: + raiseInvalid("Invalid host quotation") + i.link.hosts[vals[0].getString] = vals[1].getString + i.push("OK".newVal) + + .symbol("hosts") do (i: In): + var q = newSeq[MinValue](0).newVal + for key, val in i.link.hosts.pairs: + q.qVal.add(@[key.newSym, val.newSym].newVal) + i.push q + + .symbol("host") do (i: In): + i.push i.link.name.newVal + + .finalize()
M
lib/prelude.min
→
lib/prelude.min
@@ -9,6 +9,7 @@ #stack
#sys #time #net +#comm ; Common sigils (bind) (.) sigil
M
minim.nim
→
minim.nim
@@ -1,4 +1,4 @@
-import streams, critbits, parseopt2, strutils, os +import streams, critbits, parseopt2, strutils, os, asyncdispatch import core/types, core/parser,@@ -15,11 +15,17 @@ lib/min_logic,
lib/min_time, lib/min_io, lib/min_sys, - lib/min_net + lib/min_net, + lib/min_comm const version* = "1.0.0-dev" var REPL = false var DEBUGGING = false +var PORT = 7500 +var ADDRESS = "0.0.0.0" +var SRVTHREAD: Thread[ref MinLink] +var SERVER = false +var HOSTNAME = "" const USE_LINENOISE = true@@ -38,6 +44,9 @@ filename A minim file to interpret (default: STDIN).
Options: -e, --evaluate Evaluate a minim program inline -h, --help Print this help + -a, --address Specify server address (default: 0.0.0.0) + -p, --port Specify server port (default: 7500) + -s, --server Start server remote command execution -v, --version Print the program version -i, --interactive Start MiNiM's Read Eval Print Loop"""@@ -67,6 +76,7 @@ i.stack_module
i.str_module i.sys_module i.time_module + i.comm_module i.eval PRELUDE@@ -96,8 +106,7 @@ stderr.writeLine("Error - Cannot read from "& filename)
stderr.flushFile() minimStream(stream, filename, debugging) -proc minimRepl*(debugging = false) = - var i = newMinInterpreter(debugging) +proc minimRepl*(i: var MinInterpreter) = i.stdLib() var s = newStringStream("") i.open(s, "")@@ -118,6 +127,11 @@ warn getCurrentExceptionMsg()
finally: stdout.write "-> " echo i.dump + +proc minimRepl*(debugging = false) = + var i = newMinInterpreter(debugging) + i.minimRepl + ###@@ -129,6 +143,15 @@ of cmdArgument:
file = key of cmdLongOption, cmdShortOption: case key: + of "port", "p": + PORT = val.parseInt + of "address", "a": + if val.strip.len > 0: + ADDRESS = val + of "server", "s": + if val.strip.len > 0: + HOSTNAME = val + SERVER = true of "debug", "d": DEBUGGING = true of "evaluate", "e":@@ -150,9 +173,24 @@ if s != "":
minimString(s, DEBUGGING) elif file != "": minimFile file, DEBUGGING +elif SERVER: + var i = newMinInterpreter(DEBUGGING) + if HOSTNAME == "": + HOSTNAME = ADDRESS & ":" & $PORT + var link = newMinLink(HOSTNAME, ADDRESS, PORT, i) + link.hosts[HOSTNAME] = ADDRESS & ":" & $PORT + echo "MiNiM v"&version&" - Host '", HOSTNAME,"' started on ", ADDRESS, ":", PORT + proc srv(link: ref MinLink) = + link.init() + runForever() + createThread(SRVTHREAD, srv, link) + i.minimRepl elif REPL: minimRepl DEBUGGING quit(0) else: minimFile stdin, "stdin", DEBUGGING +if SERVER: + joinThreads([SRVTHREAD]) +