all repos — min @ 0ef917fc0b1f7327314defc1ad9ad86bfd288209

A small but practical concatenative programming language.

Added start-server and stop-server operators; updated docs.
h3rald h3rald@h3rald.com
Sat, 17 Feb 2018 22:55:08 +0100
commit

0ef917fc0b1f7327314defc1ad9ad86bfd288209

parent

85a2cc6a348e908f4ec0c850d7659f600334ce20

5 files changed, 107 insertions(+), 43 deletions(-)

jump to
M core/interpreter.nimcore/interpreter.nim

@@ -94,9 +94,9 @@

proc close*(i: In) {.extern:"min_exported_symbol_$1_2".}= i.parser.close(); -proc push*(i: In, val: MinValue) {.extern:"min_exported_symbol_$1".} +proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".} -proc apply*(i: In, op: MinOperator) {.extern:"min_exported_symbol_$1".}= +proc apply*(i: In, op: MinOperator) {.gcsafe, extern:"min_exported_symbol_$1".}= var newscope = newScopeRef(i.scope) case op.kind of minProcOp:

@@ -118,7 +118,7 @@ i.withScope(q, q.scope):

for v in q.qVal: i.push v -proc apply*(i: In, q: var MinValue) {.extern:"min_exported_symbol_$1_2".}= +proc apply*(i: In, q: var MinValue) {.gcsafe, extern:"min_exported_symbol_$1_2".}= var i2 = newMinInterpreter("<apply>") i2.trace = i.trace i2.scope = i.scope

@@ -136,7 +136,7 @@ i.trace = i2.trace

raise i.push i2.stack.newVal(i.scope) -proc push*(i: In, val: MinValue) {.extern:"min_exported_symbol_$1".}= +proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".}= if val.kind == minSymbol: i.debug(val) i.trace.add val
M lib/min_http.nimlib/min_http.nim

@@ -1,4 +1,4 @@

-import httpclient, strutils +import httpclient, asynchttpserver, asyncdispatch, strutils, uri import ../core/parser, ../core/consts,

@@ -16,31 +16,7 @@ result = newVal(newSeq[MinValue](), i.scope)

for k, v in headers: result = i.dset(result, k.newVal, v.newVal) -proc execRequest(i:In, req: MinValue): MinValue = - let cli = newCli() - var body = "".newVal - var rawHeaders, meth, url: MinValue - var headers = newHttpHeaders() - if not req.dhas("method".newVal): - raiseInvalid("Request method not specified") - if not req.dhas("url".newVal): - raiseInvalid("Request URL not specified") - if req.dhas("headers".newVal): - rawHeaders = req.dget("headers".newVal) - if not rawHeaders.isDictionary: - raiseInvalid("Headers must be specified as a dictionary") - for v in rawHeaders.qVal: - headers[v.qVal[0].getString] = v.qVal[1].getString - if req.dhas("body".newVal): - body = req.dget("body".newVal) - meth = req.dget("method".newVal) - url = req.dget("url".newVal) - let resp = cli.request(url = url.getString, httpMethod = meth.getString, body = body.getString, headers = headers) - result = newVal(newSeq[MinValue](), i.scope) - result = i.dset(result, "version".newVal, resp.version.parseFloat.newVal) - result = i.dset(result, "status".newVal, resp.status[0..2].parseInt.newVal) - result = i.dset(result, "headers".newVal, i.newVal(resp.headers)) - result = i.dset(result, "body".newVal, resp.body.newVal) +type MinServerExit = ref object of SystemError # Http

@@ -50,7 +26,31 @@

def.symbol("request") do (i: In): let vals = i.expect "dict" let req = vals[0] - i.push i.execRequest(req) + let cli = newCli() + var body = "".newVal + var rawHeaders, meth, url: MinValue + var headers = newHttpHeaders() + if not req.dhas("method".newVal): + raiseInvalid("Request method not specified") + if not req.dhas("url".newVal): + raiseInvalid("Request URL not specified") + if req.dhas("headers".newVal): + rawHeaders = req.dget("headers".newVal) + if not rawHeaders.isDictionary: + raiseInvalid("Headers must be specified as a dictionary") + for v in rawHeaders.qVal: + headers[v.qVal[0].getString] = v.qVal[1].getString + if req.dhas("body".newVal): + body = req.dget("body".newVal) + meth = req.dget("method".newVal) + url = req.dget("url".newVal) + let resp = cli.request(url = url.getString, httpMethod = meth.getString, body = body.getString, headers = headers) + var res = newVal(newSeq[MinValue](), i.scope) + res = i.dset(res, "version".newVal, resp.version.newVal) + res = i.dset(res, "status".newVal, resp.status[0..2].parseInt.newVal) + res = i.dset(res, "headers".newVal, i.newVal(resp.headers)) + res = i.dset(res, "body".newVal, resp.body.newVal) + i.push res def.symbol("get-content") do (i: In): let vals = i.expect "string"

@@ -64,6 +64,68 @@ let file = vals[0]

let url = vals[1] let cli = newCli() cli.downloadFile(url.getString, file.getString) - discard + + def.symbol("start-server") do (ii: In): + let vals = ii.expect "dict" + let cfg = vals[0] + if not cfg.dhas("port".newVal): + raiseInvalid("Port not specified.") + if not cfg.dhas("handler".newVal): + raiseInvalid("Handler quotation not specified.") + let port = cfg.dget("port".newVal) + var qhandler = cfg.dget("handler".newVal) + var address = "".newVal + if cfg.dhas("address".newVal): + address = cfg.dget("address".newVal) + if not qhandler.isQuotation: + raiseInvalid("Handler is not a quotation.") + if not port.isInt: + raiseInvalid("Port is not an integer.") + var server = newAsyncHttpServer() + var i {.threadvar.}: MinInterpreter + i = ii + proc handler(req: Request) {.async, gcsafe.} = + var qreq = newSeq[MinValue]().newVal(i.scope) + qreq = i.dset(qreq, "url".newVal, newVal($req.url)) + qreq = i.dset(qreq, "headers".newVal, i.newVal(req.headers)) + qreq = i.dset(qreq, "method".newVal, newVal($req.reqMethod)) + qreq = i.dset(qreq, "hostname".newVal, newVal($req.hostname)) + qreq = i.dset(qreq, "version".newVal, newVal("$1.$2" % [$req.protocol.major, $req.protocol.minor])) + qreq = i.dset(qreq, "body".newVal, newVal($req.body)) + i.push qreq + i.dequote qhandler + let qres = i.pop + var body = "".newVal + var rawHeaders = newSeq[MinValue]().newVal(i.scope) + var v = "1.1".newVal + var status = 200.newVal + if not qres.isDictionary(): + raiseInvalid("Response is not a dictionary.") + if qres.dhas("status".newVal): + status = qres.dget("status".newVal) + if not status.isInt and status.intVal < 600: + raiseInvalid("Invalid status code: $1." % $status) + if qres.dhas("body".newVal): + body = qres.dget("body".newVal) + if not body.isString: + raiseInvalid("Response body is not a string.") + if qres.dhas("version".newVal): + v = qres.dget("version".newVal) + if qres.dhas("headers".newVal): + rawHeaders = qres.dget("headers".newVal) + if not rawHeaders.isDictionary(): + raiseInvalid("Response headers are not in a dictionary.") + var headers = newHttpHeaders() + for v in rawHeaders.qVal: + headers[v.qVal[0].getString] = v.qVal[1].getString + await req.respond(status.intVal.HttpCode, body.getString, headers) + try: + waitFor server.serve(port = port.intVal.Port, callback = handler, address = address.getString) + except MinServerExit: + server.close() + + def.symbol("stop-server") do (i: In): + raise MinServerExit() + def.finalize("http")
M min.vimmin.vim

@@ -11,7 +11,7 @@

setl iskeyword=@,36-39,+,-,*,.,/,:,~,!,48-57,60-65,94-95,192-255 setl iskeyword+=^ -syntax keyword minDefaultSymbol ! != $ & ' * + # - % ^ -> . .. / § : = # < <= == => =~ > >= @ -inf ROOT accept acos aes all? and any? append apply args asin ask atan atime bind bool boolean? call call! capitalize case cd ceil chmod choose clear-stack cleave close column-print concat confirm connect cons cos cosh cp cpu crypto ctime d2r datetime ddel debug decode define defined? delete dequote dequote-and dequote-or dget dhas? dkeys dictionary? dip dir? dirname div download dpick dprint dprint! dset dsort dup dvalues e encode env? error eval even? exists? exit expect fappend fatal find file? filename filter first flatten float float? floor foreach fperms fread from-json format-error fs fsize fstats ftype fwrite get get-content gets get-env get-stack hardlink harvest hidden? http id if import in? indent indexof inf info insert int integer? interpolate interval io join keep last length linrec listen ln load load-symbol log2 log10 logic loglevel loglevel? lowercase ls ls-r map map-reduce match math md5 mkdir mod module module-symbols module-sigils mtime mv nan newline net nip not notice now num number? odd? opts os over parse partition password pi pick pop popd pow pred prepend print print! prompt publish puts puts! put-env q quotation? quote quote-bind quote-define quote-map r2d random raise read recv recv-line reduce regex reject remove remove-symbol repeat replace request rest reverse rm rmdir round run save-symbol scope scope? seal search send seq set set-stack sha1 sha224 sha256 sha384 sha512 shorten sigils sin sinh sip size sleep slice socket sort source split spread sqrt stack startup stored-symbols str string string? strip succ sum swap swons symbols symlink symlink? sys system take tan tanh tap tap! tau tformat time timeinfo times timestamp titleize to-json to-timestamp try trunc unless unseal unzip uppercase version warn when which while with xor zip +syntax keyword minDefaultSymbol ! != $ & ' * + # - % ^ -> . .. / § : = # < <= == => =~ > >= @ -inf ROOT accept acos aes all? and any? append apply args asin ask atan atime bind bool boolean? call call! capitalize case cd ceil chmod choose clear-stack cleave close column-print concat confirm connect cons cos cosh cp cpu crypto ctime d2r datetime ddel debug decode define defined? delete dequote dequote-and dequote-or dget dhas? dkeys dictionary? dip dir? dirname div download dpick dprint dprint! dset dsort dup dvalues e encode env? error eval even? exists? exit expect fappend fatal find file? filename filter first flatten float float? floor foreach fperms fread from-json format-error fs fsize fstats ftype fwrite get get-content gets get-env get-stack hardlink harvest hidden? http id if import in? indent indexof inf info insert int integer? interpolate interval io join keep last length linrec listen ln load load-symbol log2 log10 logic loglevel loglevel? lowercase ls ls-r map map-reduce match math md5 mkdir mod module module-symbols module-sigils mtime mv nan newline net nip not notice now num number? odd? opts os over parse partition password pi pick pop popd pow pred prepend print print! prompt publish puts puts! put-env q quotation? quote quote-bind quote-define quote-map r2d random raise read recv recv-line reduce regex reject remove remove-symbol repeat replace request rest reverse rm rmdir round run save-symbol scope scope? seal search send seq set set-stack sha1 sha224 sha256 sha384 sha512 shorten sigils sin sinh sip size sleep slice socket sort source split spread sqrt stack start-server startup stop-server stored-symbols str string string? strip succ sum swap swons symbols symlink symlink? sys system take tan tanh tap tap! tau tformat time timeinfo times timestamp titleize to-json to-timestamp try trunc unless unseal unzip uppercase version warn when which while with xor zip syntax match minDefaultSigil ;\<[/:@'~!?$%&$=<>#^*#+]; contained syntax match minQuote ;\<['];
M site/contents/reference.mdsite/contents/reference.md

@@ -95,21 +95,23 @@ : A request dictionary, representing an HTTP request to be performed through the operators exposed by the {#link-module||http#}:

( ("url" "http://httpbin.org/put") - ("methdd" "PUT") - ("headers" ( - ("Accept-Language" "it-id") - ("Host" "httpbin.org"))) - ("body" "test body") + ("method" "PUT") + ("version" "1.1") ;optional + ("hostname" "h3rald.com") ;optional + ("headers" ( ;optional + ("Accept-Language" "it-id") + ("Host" "httpbin.org"))) + ("body" "test body") ;optional ) {{res}} : A response dictionary, representing an HTTP response returned by some of the operators exposed by the {#link-module||http#}: ( - ("version" 1.1) - ("status" 200) - ("headers" + ("version" "1.1") ;optional + ("status" 200) ;optional + ("headers" ;optional (("Content-Type" "application/json"))) - ("body" + ("body" ;optional "{\"test\": \"This is a test\"}") ) {{t}}
M tests/http.mintests/http.min

@@ -34,7 +34,7 @@ ("$1/post" (url) => % %url)

("POST" %method) ((("test" "post")) to-json %body) (request) - ) tap /headers /content-length "341" == + ) tap /headers /content-type "application/json" == ) assert (