Skip to content

Commit

Permalink
Custom infix operators via <infix> in spec, kill OP!
Browse files Browse the repository at this point in the history
This provides the ability to make arbitrary infix functions.  This is
for both user code and to simplify the creation of Rebol's default
infix operators as mezzanines.  It introduces <infix> as the first
case of a function "attribute", which you put at the start of a spec:

    +: func [<infix> value1 value2] [add value1 value2]

The INFIX attribute is implemented as one of the value-specific
option bits in the Rebol header, which previously is where OP!
would store its "actual type" of the function it was wrapping.  The
OP! datatype has been killed, and a new infix? test replacing
OP?.

(Note: an OP? implementation is temporarily provided, with the
traditional behavior of tolerating any type whether function or not.)

The infix mezzanines will perform slightly slower than their old
OP! counterparts would, as they are functions with Rebol code
bodies that execute vs. just a trampoline to the C code.  However
by being "just a bit" on the spec, it means infixness would be able
to be applied to any speedy mode of specialization or function
aliasing that gets implemented...which is a general need and will
have to be written to turn :append/only into a "refined" function
(for instance).  In the meantime, the mezzanines are at least
partially "compiled" with the literal NATIVE! value they wrap.
  • Loading branch information
hostilefork committed Aug 11, 2015
1 parent 367ac2d commit cfae703
Show file tree
Hide file tree
Showing 22 changed files with 293 additions and 110 deletions.
5 changes: 5 additions & 0 deletions src/boot/natives.r
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,11 @@ latin1?: native [
value [any-string! char! integer!]
]

infix?: native [
{Returns TRUE if the function gets its first argument prior to the call}
value [any-function!]
]

; Temps...

stats: native [
Expand Down
77 changes: 54 additions & 23 deletions src/boot/ops.r
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
REBOL [
System: "REBOL [R3] Language Interpreter and Run-time Environment"
Title: "Infix operator symbol definitions"
Title: "Weird Words list for bootstrap to append to lib context"
Rights: {
Copyright 2012 REBOL Technologies
REBOL is a trademark of REBOL Technologies
Expand All @@ -10,28 +10,59 @@ REBOL [
See: http://www.apache.org/licenses/LICENSE-2.0
}
Purpose: {
This table maps infix operator symbols to function names.
This contains the weird words that the lexer doesn't easily
let us make set-words out of, like /: and //: and <>:
It turns out there can be a big big difference between:
<>: func [...] [...]
and:
set (bind/new first [<>] bind? 'func) func [...] [...]
You may be able to get an assignment with the second. BUT
it could be too late for references that have already been
loaded unbound, if those references existed within the same
module that planned on defining them for export.
Hence this just lists those 7 words, and during the boot
process they are injected into the lib context prior to
running the mezzanine code in base-infix.r that wishes to
assign functionality to them.
}
]

+ add
- subtract
* multiply
/ divide
// remainder
** power
= equal?
=? same?
== strict-equal?
!= not-equal?
<> not-equal?
!== strict-not-equal?
< lesser?
<= lesser-or-equal?
> greater?
>= greater-or-equal?
& and~
| or~
and and~
or or~
xor xor~

; Pretty much everyone expects these to be legal normal words in the future
; (so '<=: 10' would be legal and normal, for instance.) TAG! naturals will
; simply be disallowed from having their first character or last character
; be a space. They also won't be able to start with <<, <=, <~ nor will they
; be able to end with >>, => or ~>

<
<=
>
>=


; The somewhat unusual looking "diamond" shape feels like it should be
; owned by TAG!...which is to say anything that starts with a less than
; and ends in a greater than should be one. Given that contention along
; with that != already exists (and is more familiar to nearly all
; programmers) suggests it should eventually be retaken for the empty tag.
; A transitional step would likely just make it illegal so people can
; convert their instances to !=

<>


; Using slash as a WORD! is very problematic in Rebol, because of its use
; in PATH!. There isn't much in the way of satisfying alternatives for
; division, *but* that could lead to an interesting possibility of having
; the dispatch of numeric types in paths do division. So 10/20 could
; evaluate to 0.5 (for instance). The alternative would be to use the
; divide operation or to make one's own infix operator that was not slash,
; but overloading slash is just not a good idea.

/
//
4 changes: 4 additions & 0 deletions src/boot/root.r
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,9 @@ unset-val ; a value of type UNSET!
empty-block ; a value that is an empty BLOCK!
noname ; noname function word

;; Tags used in function specs

infix-tag ; func is treated as "infix" (first parameter comes before it)

boot ; boot block defined in boot.r (GC'd after boot is done)

1 change: 0 additions & 1 deletion src/boot/types.r
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ action function * - - * function
routine routine * - - * function
rebcode 0 - - - * function
command function - - - * function
op function - - - * function
closure function * - - * function
function function * - - * function

Expand Down
1 change: 0 additions & 1 deletion src/boot/typespec.r
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ money ["high precision decimals with denomination (opt)" scalar]
native ["direct CPU evaluated function" function]
none ["no value represented" scalar]
object ["context of names with values" object]
op ["infix operator (special evaluation exception)" function]
pair ["two dimensional point or size" scalar]
paren ["automatically evaluating block" block]
path ["refinements to functions, objects, files" block]
Expand Down
24 changes: 16 additions & 8 deletions src/core/b-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,18 +389,14 @@ static BOOT_BLK *Boot_Block;
***********************************************************************/
{
REBVAL *word;
REBVAL *func;
REBVAL *val;

for (word = VAL_BLK(&Boot_Block->ops); NOT_END(word); word+=2) {
for (word = VAL_BLK(&Boot_Block->ops); NOT_END(word); word++) {
// Append the operator name to the lib frame:
val = Append_Frame(Lib_Context, word, 0);
// Find the related function:
func = Find_Word_Value(Lib_Context, VAL_WORD_SYM(word+1));
if (!func) Panic(RP_MISC);
*val = *func;
VAL_SET(val, REB_OP);
VAL_EXTS_DATA(val) = VAL_TYPE(func);

// leave UNSET!, functions will be filled in later...
cast(void, cast(REBUPT, val));
}
}

Expand Down Expand Up @@ -1064,6 +1060,8 @@ static REBCNT Set_Option_Word(REBCHR *str, REBCNT field)
REBOL_STATE state;
REBVAL out;

const REBYTE infix[] = "infix";

DOUT("Main init");

#ifndef NDEBUG
Expand Down Expand Up @@ -1143,6 +1141,16 @@ static REBCNT Set_Option_Word(REBCHR *str, REBCNT field)
Init_Errors(&Boot_Block->errors); // Needs system/standard/error object
PG_Boot_Phase = BOOT_ERRORS;

// We need these values around to compare to the tags we find in function
// specs. There may be a better place to put them or a better way to do
// it, but it didn't seem there was a "compare UTF8 byte array to
// arbitrary decoded REB_TAG which may or may not be REBUNI" routine.

VAL_SET(ROOT_INFIX_TAG, REB_TAG);
VAL_SERIES(ROOT_INFIX_TAG) = Append_UTF8(0, infix, LEN_BYTES(infix));
SERIES_SET_FLAG(VAL_SERIES(ROOT_INFIX_TAG), SER_LOCK);
SERIES_SET_FLAG(VAL_SERIES(ROOT_INFIX_TAG), SER_PROT);

// Special pre-made error:
assert(RE_STACK_OVERFLOW >= RE_THROW_MAX);
ser = Make_Error(RE_STACK_OVERFLOW, 0, 0, 0);
Expand Down
Loading

0 comments on commit cfae703

Please sign in to comment.