Skip to content

Commit d27e4b1

Browse files
committed
FEAT: new AS native function for series coercion into a compatible datatype without copying it
Example: ``` >> p: as path! b: [a b] == a/b >> append b 'c == [a b c] >> p == a/b/c ```
1 parent 47f7b46 commit d27e4b1

File tree

5 files changed

+81
-1
lines changed

5 files changed

+81
-1
lines changed

src/boot/errors.r

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Script: [
6666
no-op-arg: [:arg1 {operator is missing an argument}]
6767
invalid-data: [{data not in correct format:} :arg1]
6868
not-same-type: {values must be of the same type}
69+
not-same-class: [{cannot coerce} :arg1 {to} :arg2]
6970
not-related: [{incompatible argument for} :arg1 {of} :arg2]
7071
bad-func-def: [{invalid function definition:} :arg1]
7172
bad-func-arg: [{function argument} :arg1 {is not valid}] ; can be a number
@@ -126,7 +127,6 @@ Script: [
126127

127128
invalid-handle: {invalid handle}
128129
invalid-value-for: [{invalid value} :arg1 {for:} :arg2]
129-
130130
]
131131

132132
Math: [

src/boot/natives.r

+6
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ while: native [
342342
; string [any-string!]
343343
;]
344344

345+
as: native [
346+
{Coerce a series into a compatible datatype without copying it.}
347+
type [any-block! any-string! datatype!] "The datatype or example value"
348+
spec [any-block! any-string!] "The series to coerce"
349+
]
350+
345351
bind: native [
346352
{Binds words to the specified context.}
347353
word [block! any-word!] {A word or block (modified) (returned)}

src/core/n-data.c

+20
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,26 @@ static int Check_Char_Range(REBVAL *val, REBINT limit)
212212
}
213213

214214

215+
/***********************************************************************
216+
**
217+
*/ REBNATIVE(as)
218+
/*
219+
***********************************************************************/
220+
{
221+
REBVAL *type = D_ARG(1);
222+
REBVAL *spec = D_ARG(2);
223+
REBCNT target = IS_DATATYPE(type)? VAL_DATATYPE(type) : VAL_TYPE(type);
224+
if ((ANY_BLOCK(spec) && ANY_BLOCK_TYPE(target)) || (ANY_STR(spec) && ANY_STR_TYPE(target))) {
225+
SET_TYPE(spec, target);
226+
} else {
227+
Set_Datatype(spec, VAL_TYPE(spec));
228+
Set_Datatype(type, target);
229+
Trap2(RE_NOT_SAME_CLASS, spec, type);
230+
}
231+
return R_ARG2;
232+
}
233+
234+
215235
/***********************************************************************
216236
**
217237
*/ REBNATIVE(as_pair)

src/include/sys-value.h

+3
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,9 @@ typedef struct Reb_All {
12111211
#define ANY_EVAL_BLOCK(v) (VAL_TYPE(v) >= REB_BLOCK && VAL_TYPE(v) <= REB_PAREN)
12121212
#define ANY_OBJECT(v) (VAL_TYPE(v) >= REB_OBJECT && VAL_TYPE(v) <= REB_PORT)
12131213

1214+
#define ANY_BLOCK_TYPE(t) (t >= REB_BLOCK && t <= REB_LIT_PATH)
1215+
#define ANY_STR_TYPE(t) (t >= REB_STRING && t <= REB_TAG)
1216+
12141217
#pragma pack()
12151218

12161219
#endif // value.h

src/tests/units/series-test.r3

+51
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,57 @@ Rebol [
450450

451451
===end-group===
452452

453+
===start-group=== "AS coercion"
454+
455+
--test-- "AS datatype! any-string!"
456+
s: "hell"
457+
--assert file? f: as file! s
458+
--assert email? e: as email! s
459+
--assert url? u: as url! s
460+
--assert tag? t: as tag! s
461+
append s #"o"
462+
--assert f = %hello
463+
--assert e = to-email %hello
464+
--assert u = #[url! "hello"]
465+
--assert t = <hello>
466+
467+
--test-- "AS datatype! any-block!"
468+
b: [a b]
469+
--assert paren? pa: as paren! b
470+
--assert path? p: as path! b
471+
--assert set-path? sp: as set-path! b
472+
--assert get-path? gp: as get-path! b
473+
--assert lit-path? lp: as lit-path! b
474+
append b 'c
475+
--assert pa = quote (a b c)
476+
--assert p = quote a/b/c
477+
--assert sp = quote a/b/c:
478+
--assert gp = quote :a/b/c
479+
--assert lp = quote 'a/b/c
480+
481+
--test-- "AS example any-string!"
482+
s: "hell"
483+
--assert file? f: as %file s
484+
--assert email? e: as e@mail s
485+
--assert url? u: as #[url! ""] s
486+
--assert tag? t: as <tag> s
487+
488+
--test-- "AS with protect"
489+
b: protect [a b]
490+
--assert path? try [p: as path! b]
491+
--assert error? e: try [append b 'c]
492+
--assert e/id = 'protected
493+
--assert error? e: try [append p 'c]
494+
--assert e/id = 'protected
495+
496+
--test-- "AS coercion error"
497+
--assert error? e: try [as string! []]
498+
--assert e/id = 'not-same-class
499+
--assert error? e: try [as block! ""]
500+
--assert e/id = 'not-same-class
501+
502+
===end-group===
503+
453504
;-- VECTOR related tests moved to %vector-test.r3
454505

455506
~~~end-file~~~

0 commit comments

Comments
 (0)