mirror of https://github.com/adamdruppe/arsd.git
more docs
This commit is contained in:
parent
5fb4b0e6d2
commit
a76dd66945
5
http.d
5
http.d
|
@ -1,3 +1,8 @@
|
||||||
|
/++
|
||||||
|
Old version of my http implementation.
|
||||||
|
|
||||||
|
I no longer work on this, use http2.d instead.
|
||||||
|
+/
|
||||||
module arsd.http;
|
module arsd.http;
|
||||||
|
|
||||||
import std.socket;
|
import std.socket;
|
||||||
|
|
123
http2.d
123
http2.d
|
@ -1,7 +1,37 @@
|
||||||
/// HTTP client lib
|
// Copyright 2013-2017, Adam D. Ruppe.
|
||||||
// Copyright 2013, Adam D. Ruppe.
|
/++
|
||||||
|
This is version 2 of my http/1.1 client implementation.
|
||||||
|
|
||||||
|
|
||||||
|
It has no dependencies for basic operation, but does require OpenSSL
|
||||||
|
libraries (or compatible) to be support HTTPS. Compile with
|
||||||
|
`-version=with_openssl` to enable such support.
|
||||||
|
|
||||||
|
http2.d, despite its name, does NOT implement HTTP/2.0, but this
|
||||||
|
shouldn't matter for 99.9% of usage, since all servers will continue
|
||||||
|
to support HTTP/1.1 for a very long time.
|
||||||
|
|
||||||
|
+/
|
||||||
module arsd.http2;
|
module arsd.http2;
|
||||||
|
|
||||||
|
/++
|
||||||
|
Demonstrates core functionality, using the [HttpClient],
|
||||||
|
[HttpRequest] (returned by [HttpClient.navigateTo|client.navigateTo]),
|
||||||
|
and [HttpResponse] (returned by [HttpRequest.waitForCompletion|request.waitForCompletion]).
|
||||||
|
|
||||||
|
+/
|
||||||
|
unittest {
|
||||||
|
import arsd.http2;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
auto client = new HttpClient();
|
||||||
|
auto request = client.navigateTo(Uri("http://dlang.org/"));
|
||||||
|
auto response = request.waitForCompletion();
|
||||||
|
|
||||||
|
string returnedHtml = response.contentText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: multipart encoded file uploads needs implementation
|
// FIXME: multipart encoded file uploads needs implementation
|
||||||
// future: do web client api stuff
|
// future: do web client api stuff
|
||||||
|
|
||||||
|
@ -89,15 +119,32 @@ struct HttpResponse {
|
||||||
|
|
||||||
string statusLine; ///
|
string statusLine; ///
|
||||||
|
|
||||||
string contentType; ///
|
string contentType; /// The content type header
|
||||||
|
|
||||||
string[string] cookies; ///
|
string[string] cookies; /// Names and values of cookies set in the response.
|
||||||
|
|
||||||
string[] headers; ///
|
string[] headers; /// Array of all headers returned.
|
||||||
string[string] headersHash; ///
|
string[string] headersHash; ///
|
||||||
|
|
||||||
ubyte[] content; ///
|
ubyte[] content; /// The raw content returned in the response body.
|
||||||
string contentText; ///
|
string contentText; /// [content], but casted to string (for convenience)
|
||||||
|
|
||||||
|
/++
|
||||||
|
returns `new Document(this.contentText)`. Requires [arsd.dom].
|
||||||
|
+/
|
||||||
|
auto contentDom()() {
|
||||||
|
import arsd.dom;
|
||||||
|
return new Document(this.contentText);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
returns `var.fromJson(this.contentText)`. Requires [arsd.jsvar].
|
||||||
|
+/
|
||||||
|
auto contentJson()() {
|
||||||
|
import arsd.jsvar;
|
||||||
|
return var.fromJson(this.contentText);
|
||||||
|
}
|
||||||
|
|
||||||
HttpRequestParameters requestParameters; ///
|
HttpRequestParameters requestParameters; ///
|
||||||
|
|
||||||
|
@ -1140,14 +1187,14 @@ interface ICache {
|
||||||
HttpResponse* getCachedResponse(HttpRequestParameters request);
|
HttpResponse* getCachedResponse(HttpRequestParameters request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides caching behavior similar to a real web browser
|
// / Provides caching behavior similar to a real web browser
|
||||||
class HttpCache : ICache {
|
class HttpCache : ICache {
|
||||||
HttpResponse* getCachedResponse(HttpRequestParameters request) {
|
HttpResponse* getCachedResponse(HttpRequestParameters request) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives simple maximum age caching, ignoring the actual http headers
|
// / Gives simple maximum age caching, ignoring the actual http headers
|
||||||
class SimpleCache : ICache {
|
class SimpleCache : ICache {
|
||||||
HttpResponse* getCachedResponse(HttpRequestParameters request) {
|
HttpResponse* getCachedResponse(HttpRequestParameters request) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1318,7 +1365,42 @@ version(use_openssl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
An experimental component for working with REST apis. Note that it
|
||||||
|
is a zero-argument template, so to create one, use `new HttpApiClient!()(args..)`
|
||||||
|
or you will get "HttpApiClient is used as a type" compile errors.
|
||||||
|
|
||||||
|
This will probably not work for you yet, and I might change it significantly.
|
||||||
|
|
||||||
|
Requires [arsd.jsvar].
|
||||||
|
|
||||||
|
|
||||||
|
Here's a snippet to create a pull request on GitHub to Phobos:
|
||||||
|
|
||||||
|
---
|
||||||
|
auto github = new HttpApiClient!()("https://api.github.com/", "your personal api token here");
|
||||||
|
|
||||||
|
// create the arguments object
|
||||||
|
// see: https://developer.github.com/v3/pulls/#create-a-pull-request
|
||||||
|
var args = var.emptyObject;
|
||||||
|
args.title = "My Pull Request";
|
||||||
|
args.head = "yourusername:" ~ branchName;
|
||||||
|
args.base = "master";
|
||||||
|
// note it is ["body"] instead of .body because `body` is a D keyword
|
||||||
|
args["body"] = "My cool PR is opened by the API!";
|
||||||
|
args.maintainer_can_modify = true;
|
||||||
|
|
||||||
|
// this translates to `repos/dlang/phobos/pulls` and sends a POST request,
|
||||||
|
// containing `args` as json, then immediately grabs the json result and extracts
|
||||||
|
// the value `html_url` from it. `prUrl` is typed `var`, from arsd.jsvar.
|
||||||
|
auto prUrl = github.rest.repos.dlang.phobos.pulls.POST(args).result.html_url;
|
||||||
|
|
||||||
|
writeln("Created: ", prUrl);
|
||||||
|
---
|
||||||
|
|
||||||
|
Why use this instead of just building the URL? Well, of course you can! This just makes
|
||||||
|
it a bit more convenient than string concatenation and manages a few headers for you.
|
||||||
|
|
||||||
|
Subtypes could potentially add static type checks too.
|
||||||
+/
|
+/
|
||||||
class HttpApiClient() {
|
class HttpApiClient() {
|
||||||
import arsd.jsvar;
|
import arsd.jsvar;
|
||||||
|
@ -1331,7 +1413,13 @@ class HttpApiClient() {
|
||||||
string oauth2Token;
|
string oauth2Token;
|
||||||
string submittedContentType;
|
string submittedContentType;
|
||||||
|
|
||||||
///
|
/++
|
||||||
|
Params:
|
||||||
|
|
||||||
|
urlBase = The base url for the api. Tends to be something like `https://api.example.com/v2/` or similar.
|
||||||
|
oauth2Token = the authorization token for the service. You'll have to get it from somewhere else.
|
||||||
|
submittedContentType = the content-type of POST, PUT, etc. bodies.
|
||||||
|
+/
|
||||||
this(string urlBase, string oauth2Token, string submittedContentType = "application/json") {
|
this(string urlBase, string oauth2Token, string submittedContentType = "application/json") {
|
||||||
httpClient = new HttpClient();
|
httpClient = new HttpClient();
|
||||||
|
|
||||||
|
@ -1374,6 +1462,7 @@ class HttpApiClient() {
|
||||||
alias request this;
|
alias request this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
HttpRequestWrapper request(string uri, HttpVerb requestMethod = HttpVerb.GET, ubyte[] bodyBytes = null) {
|
HttpRequestWrapper request(string uri, HttpVerb requestMethod = HttpVerb.GET, ubyte[] bodyBytes = null) {
|
||||||
if(uri[0] == '/')
|
if(uri[0] == '/')
|
||||||
uri = uri[1 .. $];
|
uri = uri[1 .. $];
|
||||||
|
@ -1389,6 +1478,7 @@ class HttpApiClient() {
|
||||||
return HttpRequestWrapper(this, req);
|
return HttpRequestWrapper(this, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
var throwOnError(HttpResponse res) {
|
var throwOnError(HttpResponse res) {
|
||||||
if(res.code < 200 || res.code >= 300)
|
if(res.code < 200 || res.code >= 300)
|
||||||
throw new Exception(res.codeText);
|
throw new Exception(res.codeText);
|
||||||
|
@ -1401,6 +1491,7 @@ class HttpApiClient() {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
@property RestBuilder rest() {
|
@property RestBuilder rest() {
|
||||||
return RestBuilder(this, null, null);
|
return RestBuilder(this, null, null);
|
||||||
}
|
}
|
||||||
|
@ -1409,6 +1500,7 @@ class HttpApiClient() {
|
||||||
// gives: "/room/Tech%20Team/history"
|
// gives: "/room/Tech%20Team/history"
|
||||||
//
|
//
|
||||||
// hipchat.rest.room["Tech Team"].history("page", "12)
|
// hipchat.rest.room["Tech Team"].history("page", "12)
|
||||||
|
///
|
||||||
static struct RestBuilder {
|
static struct RestBuilder {
|
||||||
HttpApiClientType apiClient;
|
HttpApiClientType apiClient;
|
||||||
string[] pathParts;
|
string[] pathParts;
|
||||||
|
@ -1419,24 +1511,30 @@ class HttpApiClient() {
|
||||||
this.queryParts = queryParts;
|
this.queryParts = queryParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
RestBuilder opDispatch(string str)() {
|
RestBuilder opDispatch(string str)() {
|
||||||
return RestBuilder(apiClient, pathParts ~ str, queryParts);
|
return RestBuilder(apiClient, pathParts ~ str, queryParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
RestBuilder opIndex(string str) {
|
RestBuilder opIndex(string str) {
|
||||||
return RestBuilder(apiClient, pathParts ~ str, queryParts);
|
return RestBuilder(apiClient, pathParts ~ str, queryParts);
|
||||||
}
|
}
|
||||||
|
///
|
||||||
RestBuilder opIndex(var str) {
|
RestBuilder opIndex(var str) {
|
||||||
return RestBuilder(apiClient, pathParts ~ str.get!string, queryParts);
|
return RestBuilder(apiClient, pathParts ~ str.get!string, queryParts);
|
||||||
}
|
}
|
||||||
|
///
|
||||||
RestBuilder opIndex(int i) {
|
RestBuilder opIndex(int i) {
|
||||||
return RestBuilder(apiClient, pathParts ~ to!string(i), queryParts);
|
return RestBuilder(apiClient, pathParts ~ to!string(i), queryParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
RestBuilder opCall(T)(string name, T value) {
|
RestBuilder opCall(T)(string name, T value) {
|
||||||
return RestBuilder(apiClient, pathParts, queryParts ~ [name, to!string(value)]);
|
return RestBuilder(apiClient, pathParts, queryParts ~ [name, to!string(value)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
string toUri() {
|
string toUri() {
|
||||||
import std.uri;
|
import std.uri;
|
||||||
string result;
|
string result;
|
||||||
|
@ -1456,12 +1554,17 @@ class HttpApiClient() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
final HttpRequestWrapper GET() { return _EXECUTE(HttpVerb.GET, this.toUri(), null); }
|
final HttpRequestWrapper GET() { return _EXECUTE(HttpVerb.GET, this.toUri(), null); }
|
||||||
|
/// ditto
|
||||||
final HttpRequestWrapper DELETE() { return _EXECUTE(HttpVerb.DELETE, this.toUri(), null); }
|
final HttpRequestWrapper DELETE() { return _EXECUTE(HttpVerb.DELETE, this.toUri(), null); }
|
||||||
|
|
||||||
// need to be able to send: JSON, urlencoded, multipart/form-data, and raw stuff.
|
// need to be able to send: JSON, urlencoded, multipart/form-data, and raw stuff.
|
||||||
|
/// ditto
|
||||||
final HttpRequestWrapper POST(T...)(T t) { return _EXECUTE(HttpVerb.POST, this.toUri(), toBytes(t)); }
|
final HttpRequestWrapper POST(T...)(T t) { return _EXECUTE(HttpVerb.POST, this.toUri(), toBytes(t)); }
|
||||||
|
/// ditto
|
||||||
final HttpRequestWrapper PATCH(T...)(T t) { return _EXECUTE(HttpVerb.PATCH, this.toUri(), toBytes(t)); }
|
final HttpRequestWrapper PATCH(T...)(T t) { return _EXECUTE(HttpVerb.PATCH, this.toUri(), toBytes(t)); }
|
||||||
|
/// ditto
|
||||||
final HttpRequestWrapper PUT(T...)(T t) { return _EXECUTE(HttpVerb.PUT, this.toUri(), toBytes(t)); }
|
final HttpRequestWrapper PUT(T...)(T t) { return _EXECUTE(HttpVerb.PUT, this.toUri(), toBytes(t)); }
|
||||||
|
|
||||||
private ubyte[] toBytes(T...)(T t) {
|
private ubyte[] toBytes(T...)(T t) {
|
||||||
|
|
1
jsvar.d
1
jsvar.d
|
@ -498,6 +498,7 @@ private var _op(alias _this, alias this2, string op, T)(T t) if(op != "~") {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
struct var {
|
struct var {
|
||||||
public this(T)(T t) {
|
public this(T)(T t) {
|
||||||
static if(is(T == var))
|
static if(is(T == var))
|
||||||
|
|
165
script.d
165
script.d
|
@ -1,23 +1,13 @@
|
||||||
/**
|
/++
|
||||||
|
A small script interpreter that is easily embedded inside and has easy two-way interop with the host D program.
|
||||||
|
The script language it implements is based on a hybrid of D and Javascript.
|
||||||
|
|
||||||
FIXME: prettier stack trace when sent to D
|
The interpreter is slightly buggy and poorly documented, but the basic functionality works well and much of
|
||||||
|
your existing knowledge from Javascript will carry over, making it hopefully easy to use right out of the box.
|
||||||
|
See the [#examples] to quickly get the feel of the script language as well as the interop.
|
||||||
|
|
||||||
FIXME: interpolated string: "$foo" or "#{expr}" or something.
|
|
||||||
FIXME: support more escape things in strings like \n, \t etc.
|
|
||||||
|
|
||||||
FIXME: add easy to use premade packages for the global object.
|
Script_features:
|
||||||
|
|
||||||
FIXME: maybe simplify the json!q{ } thing a bit.
|
|
||||||
|
|
||||||
FIXME: the debugger statement from javascript might be cool to throw in too.
|
|
||||||
|
|
||||||
FIXME: add continuations or something too
|
|
||||||
|
|
||||||
FIXME: Also ability to get source code for function something so you can mixin.
|
|
||||||
|
|
||||||
Script features:
|
|
||||||
|
|
||||||
FIXME: add COM support on Windows
|
|
||||||
|
|
||||||
OVERVIEW
|
OVERVIEW
|
||||||
* easy interop with D thanks to arsd.jsvar. When interpreting, pass a var object to use as globals.
|
* easy interop with D thanks to arsd.jsvar. When interpreting, pass a var object to use as globals.
|
||||||
|
@ -31,6 +21,7 @@
|
||||||
* string literals come in "foo" or 'foo', like Javascript, or `raw string` like D. Also come as “nested “double quotes” are an option!”
|
* string literals come in "foo" or 'foo', like Javascript, or `raw string` like D. Also come as “nested “double quotes” are an option!”
|
||||||
* mixin aka eval (does it at runtime, so more like eval than mixin, but I want it to look like D)
|
* mixin aka eval (does it at runtime, so more like eval than mixin, but I want it to look like D)
|
||||||
* scope guards, like in D
|
* scope guards, like in D
|
||||||
|
* Built-in assert() which prints its source and its arguments
|
||||||
* try/catch/finally/throw
|
* try/catch/finally/throw
|
||||||
You can use try as an expression without any following catch to return the exception:
|
You can use try as an expression without any following catch to return the exception:
|
||||||
|
|
||||||
|
@ -133,12 +124,45 @@
|
||||||
|
|
||||||
FIXME:
|
FIXME:
|
||||||
* make sure superclass ctors are called
|
* make sure superclass ctors are called
|
||||||
|
|
||||||
|
FIXME: prettier stack trace when sent to D
|
||||||
|
|
||||||
|
FIXME: interpolated string: "$foo" or "#{expr}" or something.
|
||||||
|
FIXME: support more escape things in strings like \n, \t etc.
|
||||||
|
|
||||||
|
FIXME: add easy to use premade packages for the global object.
|
||||||
|
|
||||||
|
FIXME: maybe simplify the json!q{ } thing a bit.
|
||||||
|
|
||||||
|
FIXME: the debugger statement from javascript might be cool to throw in too.
|
||||||
|
|
||||||
|
FIXME: add continuations or something too
|
||||||
|
|
||||||
|
FIXME: Also ability to get source code for function something so you can mixin.
|
||||||
|
FIXME: add COM support on Windows
|
||||||
|
|
||||||
|
|
||||||
Might be nice:
|
Might be nice:
|
||||||
varargs
|
varargs
|
||||||
lambdas
|
lambdas - maybe without function keyword and the x => foo syntax from D.
|
||||||
*/
|
+/
|
||||||
module arsd.script;
|
module arsd.script;
|
||||||
|
|
||||||
|
/++
|
||||||
|
|
||||||
|
+/
|
||||||
|
unittest {
|
||||||
|
var globals = var.emptyObject;
|
||||||
|
q{
|
||||||
|
function foo() {
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = foo() + 12;
|
||||||
|
assert(a == 25);
|
||||||
|
}.interpret(globals);
|
||||||
|
}
|
||||||
|
|
||||||
public import arsd.jsvar;
|
public import arsd.jsvar;
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
@ -149,24 +173,29 @@ import std.json;
|
||||||
import std.array;
|
import std.array;
|
||||||
import std.range;
|
import std.range;
|
||||||
|
|
||||||
/***************************************
|
/* **************************************
|
||||||
script to follow
|
script to follow
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
|
/// Thrown on script syntax errors and the sort.
|
||||||
class ScriptCompileException : Exception {
|
class ScriptCompileException : Exception {
|
||||||
this(string msg, int lineNumber, string file = __FILE__, size_t line = __LINE__) {
|
this(string msg, int lineNumber, string file = __FILE__, size_t line = __LINE__) {
|
||||||
super(to!string(lineNumber) ~ ": " ~ msg, file, line);
|
super(to!string(lineNumber) ~ ": " ~ msg, file, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Thrown on things like interpretation failures.
|
||||||
class ScriptRuntimeException : Exception {
|
class ScriptRuntimeException : Exception {
|
||||||
this(string msg, int lineNumber, string file = __FILE__, size_t line = __LINE__) {
|
this(string msg, int lineNumber, string file = __FILE__, size_t line = __LINE__) {
|
||||||
super(to!string(lineNumber) ~ ": " ~ msg, file, line);
|
super(to!string(lineNumber) ~ ": " ~ msg, file, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This represents an exception thrown by `throw x;` inside the script as it is interpreted.
|
||||||
class ScriptException : Exception {
|
class ScriptException : Exception {
|
||||||
|
///
|
||||||
var payload;
|
var payload;
|
||||||
|
///
|
||||||
int lineNumber;
|
int lineNumber;
|
||||||
this(var payload, int lineNumber, string file = __FILE__, size_t line = __LINE__) {
|
this(var payload, int lineNumber, string file = __FILE__, size_t line = __LINE__) {
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
|
@ -570,6 +599,10 @@ class Expression {
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string toInterpretedString(PrototypeObject sc) {
|
||||||
|
return toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MixinExpression : Expression {
|
class MixinExpression : Expression {
|
||||||
|
@ -899,6 +932,10 @@ class BinaryExpression : Expression {
|
||||||
return e1.toString() ~ " " ~ op ~ " " ~ e2.toString();
|
return e1.toString() ~ " " ~ op ~ " " ~ e2.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string toInterpretedString(PrototypeObject sc) {
|
||||||
|
return e1.toInterpretedString(sc) ~ " " ~ op ~ " " ~ e2.toInterpretedString(sc);
|
||||||
|
}
|
||||||
|
|
||||||
this(string op, Expression e1, Expression e2) {
|
this(string op, Expression e1, Expression e2) {
|
||||||
this.op = op;
|
this.op = op;
|
||||||
this.e1 = e1;
|
this.e1 = e1;
|
||||||
|
@ -1004,6 +1041,10 @@ class VariableExpression : Expression {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string toInterpretedString(PrototypeObject sc) {
|
||||||
|
return getVar(sc).get!string;
|
||||||
|
}
|
||||||
|
|
||||||
ref var getVar(PrototypeObject sc, bool recurse = true) {
|
ref var getVar(PrototypeObject sc, bool recurse = true) {
|
||||||
return sc._getMember(identifier, true /* FIXME: recurse?? */, true);
|
return sc._getMember(identifier, true /* FIXME: recurse?? */, true);
|
||||||
}
|
}
|
||||||
|
@ -1491,6 +1532,25 @@ class ParentheticalExpression : Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AssertKeyword : Expression {
|
||||||
|
ScriptToken token;
|
||||||
|
this(ScriptToken token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
override string toString() {
|
||||||
|
return "assert";
|
||||||
|
}
|
||||||
|
|
||||||
|
override InterpretResult interpret(PrototypeObject sc) {
|
||||||
|
if(AssertKeywordObject is null)
|
||||||
|
AssertKeywordObject = new PrototypeObject();
|
||||||
|
var dummy;
|
||||||
|
dummy._object = AssertKeywordObject;
|
||||||
|
return InterpretResult(dummy, sc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PrototypeObject AssertKeywordObject;
|
||||||
PrototypeObject DefaultArgumentDummyObject;
|
PrototypeObject DefaultArgumentDummyObject;
|
||||||
|
|
||||||
class CallExpression : Expression {
|
class CallExpression : Expression {
|
||||||
|
@ -1513,6 +1573,22 @@ class CallExpression : Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
override InterpretResult interpret(PrototypeObject sc) {
|
override InterpretResult interpret(PrototypeObject sc) {
|
||||||
|
if(auto asrt = cast(AssertKeyword) func) {
|
||||||
|
auto assertExpression = arguments[0];
|
||||||
|
Expression assertString;
|
||||||
|
if(arguments.length > 1)
|
||||||
|
assertString = arguments[1];
|
||||||
|
|
||||||
|
var v = assertExpression.interpret(sc).value;
|
||||||
|
|
||||||
|
if(!v)
|
||||||
|
throw new ScriptException(
|
||||||
|
var(this.toString() ~ " failed, got: " ~ assertExpression.toInterpretedString(sc)),
|
||||||
|
asrt.token.lineNumber);
|
||||||
|
|
||||||
|
return InterpretResult(v, sc);
|
||||||
|
}
|
||||||
|
|
||||||
auto f = func.interpret(sc).value;
|
auto f = func.interpret(sc).value;
|
||||||
bool isMacro = (f.payloadType == var.Type.Object && ((cast(MacroPrototype) f._payload._object) !is null));
|
bool isMacro = (f.payloadType == var.Type.Object && ((cast(MacroPrototype) f._payload._object) !is null));
|
||||||
var[] args;
|
var[] args;
|
||||||
|
@ -1535,8 +1611,9 @@ class CallExpression : Expression {
|
||||||
var _this;
|
var _this;
|
||||||
if(auto dve = cast(DotVarExpression) func) {
|
if(auto dve = cast(DotVarExpression) func) {
|
||||||
_this = dve.e1.interpret(sc).value;
|
_this = dve.e1.interpret(sc).value;
|
||||||
} else if(auto ide = cast(IndexExpression) func)
|
} else if(auto ide = cast(IndexExpression) func) {
|
||||||
_this = ide.interpret(sc).value;
|
_this = ide.interpret(sc).value;
|
||||||
|
}
|
||||||
|
|
||||||
return InterpretResult(f.apply(_this, args), sc);
|
return InterpretResult(f.apply(_this, args), sc);
|
||||||
}
|
}
|
||||||
|
@ -2265,6 +2342,13 @@ Expression parseStatement(MyTokenStreamHere)(ref MyTokenStreamHere tokens, strin
|
||||||
case ScriptToken.Type.keyword:
|
case ScriptToken.Type.keyword:
|
||||||
case ScriptToken.Type.symbol:
|
case ScriptToken.Type.symbol:
|
||||||
switch(token.str) {
|
switch(token.str) {
|
||||||
|
// assert
|
||||||
|
case "assert":
|
||||||
|
tokens.popFront();
|
||||||
|
|
||||||
|
return parseFunctionCall(tokens, new AssertKeyword(token));
|
||||||
|
|
||||||
|
break;
|
||||||
// declarations
|
// declarations
|
||||||
case "var":
|
case "var":
|
||||||
return parseVariableDeclaration(tokens, ";");
|
return parseVariableDeclaration(tokens, ";");
|
||||||
|
@ -2480,18 +2564,57 @@ var interpret(string code, PrototypeObject variables, string scriptFilename = nu
|
||||||
return interpretStream(lexScript(repeat(code, 1), scriptFilename), variables);
|
return interpretStream(lexScript(repeat(code, 1), scriptFilename), variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
This is likely your main entry point to the interpreter. It will interpret the script code
|
||||||
|
given, with the given global variable object (which will be modified by the script, meaning
|
||||||
|
you can pass it to subsequent calls to `interpret` to store context), and return the result
|
||||||
|
of the last expression given.
|
||||||
|
|
||||||
|
---
|
||||||
|
var globals = var.emptyObject; // the global object must be an object of some type
|
||||||
|
globals.x = 10;
|
||||||
|
globals.y = 15;
|
||||||
|
// you can also set global functions through this same style, etc
|
||||||
|
|
||||||
|
var result = interpret(`x + y`, globals);
|
||||||
|
assert(result == 25);
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
$(TIP
|
||||||
|
If you want to just call a script function, interpret the definition of it,
|
||||||
|
then just call it through the `globals` object you passed to it.
|
||||||
|
|
||||||
|
---
|
||||||
|
var globals = var.emptyObject;
|
||||||
|
interpret(`function foo(name) { return "hello, " ~ name ~ "!"; }`, globals);
|
||||||
|
var result = globals.foo()("world");
|
||||||
|
assert(result == "hello, world!");
|
||||||
|
---
|
||||||
|
)
|
||||||
|
|
||||||
|
Params:
|
||||||
|
code = the script source code you want to interpret
|
||||||
|
scriptFilename = the filename of the script, if you want to provide it. Gives nicer error messages if you provide one.
|
||||||
|
variables = The global object of the script context. It will be modified by the user script.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the result of the last expression evaluated by the script engine
|
||||||
|
+/
|
||||||
var interpret(string code, var variables = null, string scriptFilename = null) {
|
var interpret(string code, var variables = null, string scriptFilename = null) {
|
||||||
return interpretStream(
|
return interpretStream(
|
||||||
lexScript(repeat(code, 1), scriptFilename),
|
lexScript(repeat(code, 1), scriptFilename),
|
||||||
(variables.payloadType() == var.Type.Object && variables._payload._object !is null) ? variables._payload._object : new PrototypeObject());
|
(variables.payloadType() == var.Type.Object && variables._payload._object !is null) ? variables._payload._object : new PrototypeObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
var interpretFile(File file, var globals) {
|
var interpretFile(File file, var globals) {
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
return interpretStream(lexScript(file.byLine.map!((a) => a.idup), file.name),
|
return interpretStream(lexScript(file.byLine.map!((a) => a.idup), file.name),
|
||||||
(globals.payloadType() == var.Type.Object && globals._payload._object !is null) ? globals._payload._object : new PrototypeObject());
|
(globals.payloadType() == var.Type.Object && globals._payload._object !is null) ? globals._payload._object : new PrototypeObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
void repl(var globals) {
|
void repl(var globals) {
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
|
|
Loading…
Reference in New Issue