Merge branch 'master' of git://factorcode.org/git/factor
commit
6ef4eea4ab
|
@ -62,3 +62,15 @@ IN: calendar.format.tests
|
|||
T{ duration f 0 0 0 -5 0 0 }
|
||||
}
|
||||
] [ "2008-05-26T00:37:42.12345-05:00" rfc3339>timestamp ] unit-test
|
||||
|
||||
[
|
||||
T{ timestamp
|
||||
{ year 2008 }
|
||||
{ month 10 }
|
||||
{ day 2 }
|
||||
{ hour 23 }
|
||||
{ minute 59 }
|
||||
{ second 59 }
|
||||
{ gmt-offset T{ duration f 0 0 0 0 0 0 } }
|
||||
}
|
||||
] [ "Thursday, 02-Oct-2008 23:59:59 GMT" cookie-string>timestamp ] unit-test
|
||||
|
|
|
@ -201,9 +201,13 @@ ERROR: invalid-timestamp-format ;
|
|||
: rfc822>timestamp ( str -- timestamp )
|
||||
[ (rfc822>timestamp) ] with-string-reader ;
|
||||
|
||||
: check-day-name ( str -- )
|
||||
[ day-abbreviations3 member? ] [ day-names member? ] bi or
|
||||
check-timestamp drop ;
|
||||
|
||||
: (cookie-string>timestamp-1) ( -- timestamp )
|
||||
timestamp new
|
||||
"," read-token day-abbreviations3 member? check-timestamp drop
|
||||
"," read-token check-day-name
|
||||
read1 CHAR: \s assert=
|
||||
"-" read-token checked-number >>day
|
||||
"-" read-token month-abbreviations index 1+ check-timestamp >>month
|
||||
|
@ -218,7 +222,7 @@ ERROR: invalid-timestamp-format ;
|
|||
|
||||
: (cookie-string>timestamp-2) ( -- timestamp )
|
||||
timestamp new
|
||||
read-sp day-abbreviations3 member? check-timestamp drop
|
||||
read-sp check-day-name
|
||||
read-sp month-abbreviations index 1+ check-timestamp >>month
|
||||
read-sp checked-number >>day
|
||||
":" read-token checked-number >>hour
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
USING: http help.markup help.syntax io.files io.streams.string
|
||||
io.encodings.8-bit io.encodings.binary kernel strings urls
|
||||
byte-arrays strings assocs sequences ;
|
||||
IN: http.client
|
||||
|
||||
HELP: download-failed
|
||||
{ $error-description "Thrown by " { $link http-request } " if the server returns a status code other than 200. The " { $slot "response" } " and " { $slot "body" } " slots can be inspected for the underlying cause of the problem." } ;
|
||||
|
||||
HELP: too-many-redirects
|
||||
{ $error-description "Thrown by " { $link http-request } " if the server returns a chain of than " { $link max-redirects } " redirections." } ;
|
||||
|
||||
HELP: <get-request>
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "request" request } }
|
||||
{ $description "Constructs an HTTP GET request for retrieving the URL." }
|
||||
{ $notes "The request can be passed on to " { $link http-request } ", possibly after cookies and headers are set." } ;
|
||||
|
||||
HELP: <post-request>
|
||||
{ $values { "post-data" object } { "url" "a " { $link url } " or " { $link string } } { "request" request } }
|
||||
{ $description "Constructs an HTTP POST request for submitting post data to the URL." }
|
||||
{ $notes "The request can be passed on to " { $link http-request } ", possibly after cookies and headers are set." } ;
|
||||
|
||||
HELP: download
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } }
|
||||
{ $description "Downloads the contents of the URL to a file in the " { $link current-directory } " having the same file name." }
|
||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||
|
||||
HELP: download-to
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "file" "a pathname string" } }
|
||||
{ $description "Downloads the contents of the URL to a file with the given pathname." }
|
||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||
|
||||
HELP: http-get
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "response" response } { "data" sequence } }
|
||||
{ $description "Downloads the contents of a URL." }
|
||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||
|
||||
HELP: http-post
|
||||
{ $values { "post-data" object } { "url" "a " { $link url } " or " { $link string } } { "response" response } { "data" sequence } }
|
||||
{ $description "Submits a form at a URL." }
|
||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||
|
||||
HELP: http-request
|
||||
{ $values { "request" request } { "response" response } { "data" sequence } }
|
||||
{ $description "Sends an HTTP request to an HTTP server, and reads the response." }
|
||||
{ $errors "Throws an error if the HTTP request fails." } ;
|
||||
|
||||
ARTICLE: "http.client.get" "GET requests with the HTTP client"
|
||||
"Basic usage involves passing a " { $link url } " and getting a " { $link response } " and data back:"
|
||||
{ $subsection http-get }
|
||||
"Utilities to retrieve a " { $link url } " and save the contents to a file:"
|
||||
{ $subsection download }
|
||||
{ $subsection download-to }
|
||||
"Advanced usage involves constructing a " { $link request } ", which allows " { $link "http.cookies" } " and " { $link "http.headers" } " to be set:"
|
||||
{ $subsection <get-request> }
|
||||
{ $subsection http-request } ;
|
||||
|
||||
ARTICLE: "http.client.post" "POST requests with the HTTP client"
|
||||
"As with GET requests, there is a high-level word which takes a " { $link url } " and a lower-level word which constructs an HTTP request object which can be passed to " { $link http-request } ":"
|
||||
{ $subsection http-post }
|
||||
{ $subsection <post-request> }
|
||||
"Both words take a post data parameter, which can be one of the following:"
|
||||
{ $list
|
||||
{ "a " { $link byte-array } " or " { $link string } " is sent the server without further encoding" }
|
||||
{ "an " { $link assoc } " is interpreted as a series of form parameters, which are encoded with " { $link assoc>query } }
|
||||
{ { $link f } " denotes that there is no post data" }
|
||||
} ;
|
||||
|
||||
ARTICLE: "http.client.encoding" "Character encodings and the HTTP client"
|
||||
"The " { $link http-request } ", " { $link http-get } " and " { $link http-post } " words output a sequence containing data that was sent by the server."
|
||||
$nl
|
||||
"If the server specifies a " { $snippet "content-type" } " header with a character encoding, the HTTP client decodes the data using this character encoding, and the sequence will be a string."
|
||||
$nl
|
||||
"If no encoding was specified but the MIME type is a text type, the " { $link latin1 } " encoding is assumed, and the sequence will be a string."
|
||||
$nl
|
||||
"For any other MIME type, the " { $link binary } " encoding is assumed, and thus the data is returned literally in a byte array." ;
|
||||
|
||||
ARTICLE: "http.client.errors" "HTTP client errors"
|
||||
"HTTP operations may fail for one of two reasons. The first is an I/O error resulting from a network problem; a name server lookup failure, or a refused connection. The second is a protocol-level error returned by the server. There are two such errors:"
|
||||
{ $subsection download-failed }
|
||||
{ $subsection too-many-redirects } ;
|
||||
|
||||
ARTICLE: "http.client" "HTTP client"
|
||||
"The " { $vocab-link "http.client" } " vocabulary implements an HTTP and HTTPS client on top of " { $link "http" } "."
|
||||
$nl
|
||||
"There are two primary usage patterns, data retrieval with GET requests and form submission with POST requests:"
|
||||
{ $subsection "http.client.get" }
|
||||
{ $subsection "http.client.post" }
|
||||
"More esoteric use-cases, for example HTTP methods other than the above, are accomodated by constructing an empty request object with " { $link <request> } " and filling everything in by hand."
|
||||
{ $subsection "http.client.encoding" }
|
||||
{ $subsection "http.client.errors" }
|
||||
{ $see-also "urls" } ;
|
||||
|
||||
ABOUT: "http.client"
|
|
@ -33,7 +33,7 @@ IN: http.client
|
|||
[ content-type>> "content-type" pick set-at ]
|
||||
bi
|
||||
] when*
|
||||
over cookies>> f like [ unparse-cookie "cookie" pick set-at ] when*
|
||||
over cookies>> [ unparse-cookie "cookie" pick set-at ] unless-empty
|
||||
write-header ;
|
||||
|
||||
GENERIC: >post-data ( object -- post-data )
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
USING: assocs help.markup help.syntax io.streams.string sequences strings present math kernel byte-arrays urls
|
||||
calendar ;
|
||||
IN: http
|
||||
|
||||
HELP: <request>
|
||||
{ $values { "request" request } }
|
||||
{ $description "Creates an empty request." } ;
|
||||
|
||||
HELP: request
|
||||
{ $description "An HTTP request."
|
||||
$nl
|
||||
"Instances contain the following slots:"
|
||||
{ $table
|
||||
{ { $slot "method" } { "The HTTP method as a " { $link string } ". The most frequently-used HTTP methods are " { $snippet "GET" } ", " { $snippet "HEAD" } " and " { $snippet "POST" } "." } }
|
||||
{ { $slot "url" } { "The " { $link url } " being requested" } }
|
||||
{ { $slot "version" } { "The HTTP version. Default is " { $snippet "1.1" } " and should not be changed without good reason." } }
|
||||
{ { $slot "header" } { "An assoc of HTTP header values. See " { $link "http.headers" } } }
|
||||
{ { $slot "post-data" } { "See " { $link "http.post-data" } } }
|
||||
{ { $slot "cookies" } { "A sequence of HTTP cookies. See " { $link "http.cookies" } } }
|
||||
} } ;
|
||||
|
||||
HELP: <response>
|
||||
{ $values { "response" response } }
|
||||
{ $description "Creates an empty response." } ;
|
||||
|
||||
HELP: response
|
||||
{ $class-description "An HTTP response."
|
||||
$nl
|
||||
"Instances contain the following slots:"
|
||||
{ $table
|
||||
{ { $slot "version" } { "The HTTP version. Default is " { $snippet "1.1" } " and should not be changed without good reason." } }
|
||||
{ { $slot "code" } { "HTTP status code, an " { $link integer } ". Examples are 200 for success, 404 for file not found, and so on." } }
|
||||
{ { $slot "message" } { "HTTP status message, only displayed to the user. If the status code is 200, the status message might be ``Success'', for example." } }
|
||||
{ { $slot "header" } { "An assoc of HTTP header values. See " { $link "http.headers" } } }
|
||||
{ { $slot "cookies" } { "A sequence of HTTP cookies. See " { $link "http.cookies" } } }
|
||||
{ { $slot "content-type" } { "an HTTP content type" } }
|
||||
{ { $slot "content-charset" } { "an encoding descriptor. See " { $link "io.encodings" } } }
|
||||
{ { $slot "body" } { "an HTTP response body" } }
|
||||
} } ;
|
||||
|
||||
HELP: <raw-response>
|
||||
{ $values { "response" raw-response } }
|
||||
{ $description "Creates an empty raw response." } ;
|
||||
|
||||
HELP: raw-response
|
||||
{ $class-description "A minimal HTTP response used by webapps which need full control over all output sent to the client. Most webapps can use " { $link response } " instead."
|
||||
$nl
|
||||
"Instances contain the following slots:"
|
||||
{ $table
|
||||
{ { $slot "version" } { "The HTTP version. Default is " { $snippet "1.1" } " and should not be changed without good reason." } }
|
||||
{ { $slot "code" } { "HTTP status code, an " { $link integer } ". Examples are 200 for success, 404 for file not found, and so on." } }
|
||||
{ { $slot "message" } { "HTTP status message, only displayed to the user. If the status code is 200, the status message might be ``Success'', for example." } }
|
||||
{ { $slot "body" } { "an HTTP response body" } }
|
||||
} } ;
|
||||
|
||||
HELP: <cookie>
|
||||
{ $values { "value" object } { "name" string } { "cookie" cookie } }
|
||||
{ $description "Creates a cookie with the specified name and value. The value can be any object supported by the " { $link present } " word." } ;
|
||||
|
||||
HELP: cookie
|
||||
{ $class-description
|
||||
"An HTTP cookie."
|
||||
$nl
|
||||
"Instances contain a number of slots which correspond exactly to the fields of a cookie in the cookie specification:"
|
||||
{ $table
|
||||
{ { $slot "name" } { "The cookie name, a " { $link string } } }
|
||||
{ { $slot "value" } { "The cookie value, an object supported by " { $link present } } }
|
||||
{ { $slot "comment" } { "A " { $link string } } }
|
||||
{ { $slot "path" } { "The pathname prefix where the cookie is valid, a " { $link string } } }
|
||||
{ { $slot "domain" } { "The domain name where the cookie is valid, a " { $link string } } }
|
||||
{ { $slot "expires" } { "The expiry time, a " { $link timestamp } " or " { $link f } " for a session cookie" } }
|
||||
{ { $slot "max-age" } { "The expiry duration, a " { $link duration } " or " { $link f } " for a session cookie" } }
|
||||
{ { $slot "http-only" } { "If set to a true value, JavaScript code cannot see the cookie" } }
|
||||
{ { $slot "secure" } { "If set to a true value, the cookie is only sent for " { $snippet "https" } " protocol connections" } }
|
||||
}
|
||||
"Only one of " { $snippet "expires" } " and " { $snippet "max-age" } " can be set; the latter is preferred and is supported by all modern browsers." } ;
|
||||
|
||||
HELP: delete-cookie
|
||||
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "name" string } }
|
||||
{ $description "Deletes a cookie from a request or response." }
|
||||
{ $side-effects "request/response" } ;
|
||||
|
||||
HELP: get-cookie
|
||||
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "name" string } { "cookie/f" "a " { $link cookie } " or " { $link f } } }
|
||||
{ $description "Gets a named cookie from a request or response." } ;
|
||||
|
||||
HELP: put-cookie
|
||||
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "cookie" cookie } }
|
||||
{ $description "Stores a cookie in a request or response." }
|
||||
{ $side-effects "request/response" } ;
|
||||
|
||||
HELP: <post-data>
|
||||
{ $values { "raw" byte-array } { "content-type" "a MIME type string" } { "post-data" post-data } }
|
||||
{ $description "Creates a new " { $link post-data } "." } ;
|
||||
|
||||
HELP: header
|
||||
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "key" string } { "value" string } }
|
||||
{ $description "Obtains an HTTP header value from a request or response." } ;
|
||||
|
||||
HELP: post-data
|
||||
{ $class-description "HTTP POST data passed in a POST request."
|
||||
$nl
|
||||
"Instances contain the following slots:"
|
||||
{ $table
|
||||
{ { $slot "raw" } { "The raw bytes of the POST data" } }
|
||||
{ { $slot "content" } { "The POST data. This can be in a higher-level form, such as an assoc of POST parameters, a string, or an XML document" } }
|
||||
{ { $slot "content-type" } "A MIME type" }
|
||||
} } ;
|
||||
|
||||
HELP: set-header
|
||||
{ $values { "request/response" "a " { $link request } " or a " { $link response } } { "value" object } { "key" string } }
|
||||
{ $description "Stores a value into the HTTP header of a request or response. The value can be any object supported by " { $link present } "." }
|
||||
{ $notes "This word always returns the same object that was input. This allows for a ``pipeline'' coding style, where several header parameters are set in a row." }
|
||||
{ $side-effects "request/response" } ;
|
||||
|
||||
ARTICLE: "http.cookies" "HTTP cookies"
|
||||
"Every " { $link request } " and " { $link response } " instance can contain cookies."
|
||||
$nl
|
||||
"The " { $vocab-link "furnace.sessions" } " vocabulary implements session management using cookies, thus the most common use case can be taken care of without working with cookies directly."
|
||||
$nl
|
||||
"The class of cookies:"
|
||||
{ $subsection cookie }
|
||||
"Creating cookies:"
|
||||
{ $subsection <cookie> }
|
||||
"Getting, adding, and deleting cookies in " { $link request } " and " { $link response } " objects:"
|
||||
{ $subsection get-cookie }
|
||||
{ $subsection put-cookie }
|
||||
{ $subsection delete-cookie } ;
|
||||
|
||||
ARTICLE: "http.headers" "HTTP headers"
|
||||
"Every " { $link request } " and " { $link response } " has a set of HTTP headers stored in the " { $slot "header" } " slot. Header names are normalized to lower-case when a request or response is being parsed."
|
||||
{ $subsection header }
|
||||
{ $subsection set-header } ;
|
||||
|
||||
ARTICLE: "http.post-data" "HTTP post data"
|
||||
"Every " { $link request } " where the " { $slot "method" } " slot is " { $snippet "POST" } " can contain post data."
|
||||
{ $subsection post-data }
|
||||
{ $subsection <post-data> } ;
|
||||
|
||||
ARTICLE: "http" "HTTP protocol objects"
|
||||
"The " { $vocab-link "http" } " vocabulary contains data types shared by " { $vocab-link "http.client" } " and " { $vocab-link "http.server" } "."
|
||||
$nl
|
||||
"The HTTP client sends an HTTP request to the server and receives an HTTP response back. The HTTP server receives HTTP requests from clients and sends HTTP responses back."
|
||||
$nl
|
||||
"HTTP requests:"
|
||||
{ $subsection request }
|
||||
{ $subsection <request> }
|
||||
"Requests can contain form submissions:"
|
||||
{ $subsection "http.post-data" }
|
||||
"HTTP responses:"
|
||||
{ $subsection response }
|
||||
{ $subsection <response> }
|
||||
"Raw responses only contain a status line, with no header. They are used by webapps which need full control over the HTTP response, for example " { $vocab-link "http.server.cgi" } ":"
|
||||
{ $subsection raw-response }
|
||||
{ $subsection <raw-response> }
|
||||
"Both requests and responses support some common functionality:"
|
||||
{ $subsection "http.headers" }
|
||||
{ $subsection "http.cookies" }
|
||||
{ $see-also "urls" } ;
|
||||
|
||||
ABOUT: "http"
|
|
@ -31,7 +31,7 @@ HELP: [let
|
|||
} ;
|
||||
|
||||
HELP: [let*
|
||||
{ $syntax "[let* | binding1 [ value1... ]\n binding2 [ value2... ]\n ... |\n body... ]" }
|
||||
{ $syntax "[let* | binding1 [ value1... ]\n binding2 [ value2... ]\n ... |\n body... ]" }
|
||||
{ $description "Introduces a set of lexical bindings and evaluates the body. The values are evaluated sequentially, and may refer to previous bindings from the same " { $link POSTPONE: [let* } " form; for Lisp programmers, this means that " { $link POSTPONE: [let* } " is equivalent to the Lisp " { $snippet "let*" } ", not " { $snippet "let" } "." }
|
||||
{ $examples
|
||||
{ $example
|
||||
|
|
|
@ -17,9 +17,9 @@ HELP: >url
|
|||
{ $examples
|
||||
"If we convert a string to a URL and print it out again, it will print similarly to the input string, except some normalization may have occurred:"
|
||||
{ $example
|
||||
"USING: accessors io urls ;"
|
||||
"USING: accessors prettyprint urls ;"
|
||||
"\"http://www.apple.com\" >url ."
|
||||
"URL\" www.apple.com/\""
|
||||
"URL\" http://www.apple.com/\""
|
||||
}
|
||||
"We can examine the URL object:"
|
||||
{ $example
|
||||
|
@ -27,9 +27,9 @@ HELP: >url
|
|||
"\"http://www.apple.com\" >url host>> print"
|
||||
"www.apple.com"
|
||||
}
|
||||
"A relative URL does not have a protocol or host component:"
|
||||
"A relative URL does not have a protocol, host or port:"
|
||||
{ $example
|
||||
"USING: accessors io urls ;"
|
||||
"USING: accessors prettyprint urls ;"
|
||||
"\"file.txt\" >url protocol>> ."
|
||||
"f"
|
||||
}
|
||||
|
@ -47,13 +47,12 @@ HELP: URL"
|
|||
} ;
|
||||
|
||||
HELP: assoc>query
|
||||
{ $values
|
||||
{ "hash" hashtable }
|
||||
{ "str" string } }
|
||||
{ $values { "assoc" assoc } { "str" string } }
|
||||
{ $description "Converts an assoc of query parameters into a query string, performing URL encoding." }
|
||||
{ $notes "This word is used to implement the " { $link present } " method on URLs; it is also used by the HTTP client to encode POST requests." }
|
||||
{ $examples
|
||||
{ $example
|
||||
"USING: io urls ;"
|
||||
"{ { \"from\" \"Lead\" } { \"to\" \"Gold, please\" } }"
|
||||
"assoc>query print"
|
||||
"from=Lead&to=Gold%2c+please"
|
||||
|
@ -134,7 +133,7 @@ HELP: query-param
|
|||
"USING: io urls ;"
|
||||
"URL\" http://food.com/calories?item=French+Fries\""
|
||||
"\"item\" query-param print"
|
||||
"\"French Fries\""
|
||||
"French Fries"
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -219,8 +218,8 @@ ARTICLE: "url-utilities" "URL implementation utilities"
|
|||
{ $subsection secure-protocol? }
|
||||
{ $subsection url-append-path } ;
|
||||
|
||||
ARTICLE: "urls" "URLs"
|
||||
"The " { $vocab-link "urls" } " implements a URL data type. The benefit of using a data type to prepresent URLs rather than a string is that the parsing, printing and escaping logic is encapsulated and reused, rather than re-implemented in a potentially buggy manner every time."
|
||||
ARTICLE: "urls" "URL objects"
|
||||
"The " { $vocab-link "urls" } " vocabulary implements a URL data type. The benefit of using a data type to prepresent URLs rather than a string is that the parsing, printing and escaping logic is encapsulated and reused, rather than re-implemented in a potentially buggy manner every time."
|
||||
$nl
|
||||
"URL objects are used heavily by the " { $vocab-link "http" } " and " { $vocab-link "furnace" } " vocabularies, and are also useful on their own."
|
||||
$nl
|
||||
|
|
|
@ -2,19 +2,19 @@ IN: urls.tests
|
|||
USING: urls urls.private tools.test
|
||||
arrays kernel assocs present accessors ;
|
||||
|
||||
[ "hello%20world" ] [ "hello world" url-encode ] unit-test
|
||||
[ "hello+world" ] [ "hello world" url-encode ] unit-test
|
||||
[ "hello world" ] [ "hello%20world" url-decode ] unit-test
|
||||
[ "~hello world" ] [ "%7ehello+world" url-decode ] unit-test
|
||||
[ f ] [ "%XX%XX%XX" url-decode ] unit-test
|
||||
[ f ] [ "%XX%XX%X" url-decode ] unit-test
|
||||
|
||||
[ "hello world" ] [ "hello+world" url-decode ] unit-test
|
||||
[ "hello world" ] [ "hello%20world" url-decode ] unit-test
|
||||
[ " ! " ] [ "%20%21%20" url-decode ] unit-test
|
||||
[ "hello world" ] [ "hello world%" url-decode ] unit-test
|
||||
[ "hello world" ] [ "hello world%x" url-decode ] unit-test
|
||||
[ "hello%20world" ] [ "hello world" url-encode ] unit-test
|
||||
[ "%20%21%20" ] [ " ! " url-encode ] unit-test
|
||||
[ "hello world" ] [ "hello+world" url-decode ] unit-test
|
||||
[ "hello world" ] [ "hello%20world" url-decode ] unit-test
|
||||
[ " ! " ] [ "%20%21%20" url-decode ] unit-test
|
||||
[ "hello world" ] [ "hello world%" url-decode ] unit-test
|
||||
[ "hello world" ] [ "hello world%x" url-decode ] unit-test
|
||||
[ "hello+world" ] [ "hello world" url-encode ] unit-test
|
||||
[ "+%21+" ] [ " ! " url-encode ] unit-test
|
||||
|
||||
[ "\u001234hi\u002045" ] [ "\u001234hi\u002045" url-encode url-decode ] unit-test
|
||||
|
||||
|
|
|
@ -104,8 +104,15 @@ TUPLE: url protocol username password host port path query anchor ;
|
|||
: query-param ( url key -- value )
|
||||
swap query>> at ;
|
||||
|
||||
: delete-query-param ( url key -- url )
|
||||
over query>> delete-at ;
|
||||
|
||||
: set-query-param ( url value key -- url )
|
||||
'[ [ _ _ ] dip ?set-at ] change-query ;
|
||||
over [
|
||||
'[ [ _ _ ] dip ?set-at ] change-query
|
||||
] [
|
||||
nip delete-query-param
|
||||
] if ;
|
||||
|
||||
: parse-host ( string -- host port )
|
||||
":" split1 [ url-decode ] [
|
||||
|
|
|
@ -519,7 +519,7 @@ HELP: UNION:
|
|||
HELP: INTERSECTION:
|
||||
{ $syntax "INTERSECTION: class participants... ;" }
|
||||
{ $values { "class" "a new class word to define" } { "participants" "a list of class words separated by whitespace" } }
|
||||
{ $description "Defines an intersection class. An object is an instance of a union class if it is an instance of all of its participants." } ;
|
||||
{ $description "Defines an intersection class. An object is an instance of an intersection class if it is an instance of all of its participants." } ;
|
||||
|
||||
HELP: MIXIN:
|
||||
{ $syntax "MIXIN: class" }
|
||||
|
|
|
@ -40,8 +40,7 @@ function foldl(f, initial, seq) {
|
|||
for(var i=0; i< seq.length; ++i)
|
||||
initial = f(initial, seq[i]);
|
||||
return initial;
|
||||
}
|
||||
"> main \ javascript rule (parse) remaining>> length zero?
|
||||
}"> main \ javascript rule (parse) remaining>> length zero?
|
||||
] unit-test
|
||||
|
||||
{ t } [
|
||||
|
@ -51,7 +50,6 @@ ParseState.prototype.from = function(index) {
|
|||
r.cache = this.cache;
|
||||
r.length = this.length - index;
|
||||
return r;
|
||||
}
|
||||
"> main \ javascript rule (parse) remaining>> length zero?
|
||||
}"> main \ javascript rule (parse) remaining>> length zero?
|
||||
] unit-test
|
||||
|
||||
|
|
|
@ -57,8 +57,7 @@ BEGIN
|
|||
CALL square;
|
||||
x := x + 1;
|
||||
END
|
||||
END.
|
||||
"> main \ pl0 rule (parse) remaining>> empty?
|
||||
END."> main \ pl0 rule (parse) remaining>> empty?
|
||||
] unit-test
|
||||
|
||||
{ f } [
|
||||
|
|
Loading…
Reference in New Issue