GithubHelp home page GithubHelp logo

metaeducation / ren-c Goto Github PK

View Code? Open in Web Editor NEW

This project forked from rebolsource/r3

126.0 28.0 27.0 100.2 MB

Library for embedding a Rebol interpreter into C codebases

License: GNU Lesser General Public License v3.0

Rebol 0.09% C 94.94% C++ 4.24% Shell 0.30% JavaScript 0.41% HTML 0.01%
homoiconic interpreter

ren-c's Introduction

Ren-C Logo

Ren-C

Ren-C is a deeply redesigned LGPL 3.0-licensed derivative of the Rebol 3 codebase. It explores solutions to some of the Rebol language's longstanding open questions, adding fundamental new evaluation abilities and API embeddings.

While Rebol 3 built for many platforms, Ren-C extends those to everything from OpenBSD to HaikuOS and WebAssembly. But the experimental nature of the project and limited resources mean there isn't support for packaging and distribution of native binaries. So the table stakes for participating is building your own native interpreter (see instructions below)

The current sole focus for deploying a prebuilt experience to users is via WebAssembly in the web browser. See the demo of the Web Console that was shown at the Rebol 2019 Conference.

(A more conservative evolution of the R3-Alpha codebase is maintained by user @Oldes, and may interest some people who don't want to run on the web.)

API

One major enabling feature of Ren-C is that it has a "user-friendly" API for C and JavaScript, which uses novel tricks to compose code as mixtures of strings and spliced Rebol values:

int x = 1020;
Value* negate = rebValue("get $negate");  // runs code, returns value

rebElide("print [", rebI(x), "+ (2 *", rebRUN(negate), "358)]");

// Would print 304--e.g. `1020 + (2 * -358)`, rebElide() returns C void.

The way this can work is described in another talk from Rebol 2019, entitled "Abusing UTF-8 For Fun and Profit"

Beyond the API and Web Build, improvements to the language itself range in the hundreds. They are ever-evolving but are tracked periodically on the Trello board and posts on the forum.

Community

The best way to get acquainted with all that is going on would be to Join The Forum! Feel free to post in the Introductions Category and ask anything you would like.

It's also possible to contact the developers via the GitHub Issues. (Ren-C inherited Rebol's thousands-strong issue database, so there's a lifetime's worth of design points to think about!)

Name

The "Ren-C" name comes from the idea that it is a C implementation of the "REadable Notation" (a name given to Rebol's file format). The codebase is able to compile as ANSI C99, despite using a wide spectrum of static analysis enhancements that apply if built as C++.

Long term, it is not intended to be the name of a language. It's simply a core that could be packaged and configured by other "branded" distributions, such as Rebol itself.

Building

The system does not require GNU Make, CMake, or any other make tools. It only needs a copy of a Ren-C executable to build itself. To do a full build, it can just invoke a C compiler using the CALL facility, with the appropriate command lines.

Several platforms are supported, including Linux, Windows, OS X, Android, and support for JavaScript via WebAssembly. Configurations for each platform are in the %configs/ directory. When the build process is run, you should be in the directory where you want the build products to go (e.g. %build/). Here is a sample of how to compile under Linux:

# You need a Ren-C-based Rebol to use in the make process
# See %tools/bootstrap-shim.r regarding what versions are usable
# Currently there are usable executables in %/prebuilt ...
# ...but that's not a permanent solution!
#
~/ren-c$ export R3_MAKE="$(pwd)/prebuilt/r3-linux-x64-8994d23"

~/ren-c$ cd build

~/ren-c/build/$ "$R3_MAKE" ../make.r \
    config: ../configs/default-config.r \
    debug: asserts \
    optimize: 2

For a list of options, run %make.r with --help.

Though it does not require other make tools, it is optional to generate a makefile target, since %make.r takes parameters like target: makefile. But there are several complicating factors related to incremental builds, due to the fact that there's a large amount of C code and header files generated from tables and scans of the source code. If you're not familiar with the source and what kinds of changes require rebuilding which parts, you should probably do full builds.

As a design goal, compiling Ren-C requires very little beyond ANSI C89. Attempts to rein in compiler dependencies have been a large amount of work, and it still supports a number of older platforms. However, if it is compiled with a C++ compiler then there is significantly more static analysis at build time, to catch errors.

(Note: The build process is far more complicated than it should be, but other priorities mean it isn't getting the attention it deserves. It would be strongly desirable if community member(s) could get involved to help streamline and document it! Since it's now all written in Rebol, that should be more possible--and maybe even a little "fun" (?))

License

When Rebol was open-sourced in 2012, it was licensed as Apache 2.0. Despite the Ren-C team's belief in Free Software Foundation's principles, contributions were made as Apache 2.0 up until 2020, to make it easier for code to be taken back to the Rebol GitHub or other branches.

Due to limited cases of such any take over an eight-year span, the Ren-C license was changed to the Apache-2-compatible LGPL 3.

The current way to explore the new features of Ren-C is using the r3 console. It is significantly enhanced from the open-sourced R3-Alpha...with much of its behavior coming from userspace Rebol code (as opposed to hardcoded C). In addition to multi-line editing and UTF-8 support, it can be "skinned" and configured in various ways, and non-C programmers can easily help contribute to enhancing it.

ren-c's People

Contributors

0branch avatar angerangel avatar blackball avatar brianhawley avatar carls avatar ccx avatar dandlee avatar draegtun avatar drkameleon avatar earl avatar gchiu avatar giuliolunati avatar hostilefork avatar iarnold avatar ingohohmann avatar jacereda avatar johnk- avatar kealist avatar kjk avatar ladislav avatar lkppo avatar mark-hi avatar meshpoint avatar nicolas42 avatar rebolbot avatar rgchris avatar spytheman avatar tectorum avatar xqlab avatar zsx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ren-c's Issues

Rebol Internal Error on startup - Linux 64Bit

> git rev-parse --short HEAD
cd3d1f5d
> make -f makefile.boot OS_ID=0.4.40
> .\r3
Rebol Internal Error
If you need to file a bug in the issue tracker, please give thorough
details on how to reproduce the problem:

    http://github.com/rebol/rebol-issues/issues

Include the following information in the report:

C Source File ../src/core/c-eval.c, Line 1119
fail() before object table initialized, code = 3006

crash on body-of lib

** Version: 2.102.0.3.40
** Platform: Windows win32-x64
** Build: 23-Mar-2017/17:13:58

body-of lib
Assertion failed!

Program: C:\Users\Graham\rebol3\r3.exe
File: ../src/core/m-gc.c, Line 780

Expression: GET_SER_FLAG(a, ARRAY_FLAG_VARLIST) || GET_SER_FLAG(a, ARRAY_FLAG_VOIDS_LEGAL)

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

assert failure by parse

>> parse [1][do [quote 1]]
src/core/u-parse.c:161: REBOOL Subparse_Throws(REBOOL*, Reb_Specific_Value*, Reb_Value*, REBSPC*, const Reb_Value*, REBSPC*, REBCNT): Assertion `IS_END(out)' failed.
Aborted (core dumped)

Crash on naive use of THEN and ELSE

This will cause a crash:

if 1 then [2]

and this

if 1 else [2]

ren-c build:

**    Version:   2.102.0.3.1                                            **
**    Platform:  Windows win32-x86                                      **
**    Build:     17-Mar-2017/23:38:29                                   **

Wrong time reported

For a time of 5:17pm, NOW reports the time with a 1 hour difference:

>> now
== 27-Feb-2017/18:17:41+11:00

I built using Ubuntu 16.04 (virtualbox on windows). Builds with these OS_IDs exhibit the problem:

  • 0.4.2
  • 0.4.3
  • 0.4.4
  • 0.4.40

Building on windows for windows did not show this problem.

Running the tests for OS_ID=0.4.2 shows these two failures:

[0.6 == round/half-down/to 0.55001 0.1] "failed"
[-0.6 == round/half-down/to -0.55001 0.1] "failed"

SWITCH Panic

t: 1 switch t [(t)]

=>

END marker or garbage/trash in VAL_TYPE()
Pointer found in freed tail capacity of series
Kind=0
Containing series for value pointer found, panicking it:
managed series was likely created during evaluator tick: 25988
series guard didn't trigger ASAN/Valgrin. trap: either not a REBSER, or you're not running ASAN/Valgrind

info? doesn't work on urls

info? http://www.rebol.com/index.html
** Script error: --anonymous-- must return value (use PROC or RETURN: )
** Where: --anonymous-- query info?
** Near: ... make port! [
[self: spec scheme actor awake state da...

info? uses query, but query on a url requires that the url be opened. Suggest check if the target is a URL and do a HEAD command instead.

Failed make -f makefile.boot

make -f makefile.boot OS_ID=0.4.4
./r3-make -qs ../src/tools/make-make.r 0.4.4
** Script error: join-of has no value

Assertion failed in Make_Error_Managed_Core

Running r3-017a1343-debug (Android5 0.13.2):

f: func [x t:][
  either 1 = x/1 [
    f x/2
  ][
    forall x [f x/1]
  ]
]

f [[1 [0]]]

../src/core/c-error.c:979: Make_Error_Managed_Core: assertion "FALSE" failed
Aborted

Function arguments to Functions be Disallowed by Default (or other mitigation?)

At its core, Rebol has the idea that any PATH! or WORD! might be a function, and it might wind up dispatching it. There is no special syntax for function calls or delimiting. Reading a line of code like:

foo baz bar/:mumble frotz

...could be pretty much anything.

>> foo: 10
>> baz: 20
>> bar: [asdf ghijk 30 lmno pqrs]
>> mumble: 3
>> frotz: 40
>> reduce [foo baz bar/:mumble frotz]
[10 20 30 40]

>> foo: func [a b c] [a + b + c]
>> reduce [foo baz bar/:mumble frotz]
[90]

>> bar: object [something: does [print "all your base!" 1000]]
>> mumble: 'something
>> reduce [foo baz bar/:mumble frotz]
all your base!
== [1060]

As per the infamous "deep lake", the goal of being so freeform is to make the language match the freedom of dialect designers. In effect, to use more or less punctuation if they want. Tools exist like parentheses, where the parentheses themselves are language elements which may-or-may not mean what you think parentheses mean in that context.

But one question about this might be "how free is too free", for instance:

check-secret: func [guess] [
    secret: "abracadabra"
    print ["guess:" guess "actual:" secret]
    return guess = secret
]

tricky: func [whatever pass] [
    print ["HAHA YOUR PASSWORD IS" pass]
    quit
]

check-secret (get 'tricky)

The output of this is HAHA YOUR PASSWORD IS abracadabra. By default, a function argument with no type limits on it can be anything... function or value. So without a type constraint that prohibits FUNCTION!, the "guess" can steal parameters that weren't intended for it by someone who only had a single value.

I do not want to bemoan the concept of "security" here for several reasons. One of those is that to my perception, Rebol is a kind of assembly...just a really weird sort of thing in that category in terms of malleability. So to criticize this aspect of its suitability as a tool for writing secure software is the tip of a large iceberg of many other things that it's not intended for. A bit like getting upset that C lets you work directly with memory addresses and "wow, imagine how many bad things can happen".

The question I wonder about is more "how confusing can this system get". Let's look at what might be thought of as a "good use" of passing a function in:

value: none
count: 0
cacher: does [
     unless value [
         print "calculating!" ;-- imagine this were slow
         value: reverse "arbadacarba"
     ]
     print "returning!"
     return value
]

check-secret (get 'cacher)

That will output:

calculating!
returning!
guess: abracadabra actual: abracadabra
returning!
== true

Because check-secret uses guess twice, we see two calls. But it only does the calculation the first time. So here we get a trick, that because guess has no special syntax to say it is-or-isn't a function call, it was able to handle being either. It's being cooperative, though--expecting that the caller wanted it to act "value-like".

But as brought up in #252, the picture gets a bit complicated when GET (or a GET-WORD!) is used. Consider this:

 something: func [arg key [word!]] [
     word: either some-condition ['arg] [key]
     return get word
 ]

Here we see a code author who tries to abstract out the place to get their value from. They'll either get it from the arg parameter, or from the word passed in. This won't work if arg is a function and the intent the caller had was for something to invoke the function and used a call of it as if it were a value.

The Problem Restated

So, passing functions as arguments to functions that expected a value has a lot of problems.

  1. The callee may expect the value to be the same every time it is examined, and if it's a function then calling it every time may return different results.
  2. The callee may not expect multiple inspections of the value to have side-effects, and if it's a function it may have side-effects such that each "inspection" changes program state.
  3. The callee may treat its argument as a word!, and then try to use GET in order to perform some level of indirection...which would subvert the idea that the caller
  4. The callee may wish to take advantage of the GET/ANY nature of GET-WORD! in order to indicate they don't mind if the value is not set, thus inadvertently triggering GET semantics when they didn't intend that.
  5. Functions that wind up in values where they are not supposed to be on accident cause very cryptic errors.

Possible Solution 1: Functions Don't Take Function Arguments By Default

In R3-Alpha, the only value type prohibited by default was the UNSET!. A function which was going to take an unset had to explicitly say that it took ANY-TYPE!.

In Ren-C, there is no UNSET! datatype...at all. Only variables in contexts can be in an unset state. So ANY-TYPE! vanished, and the new annotation was added to the generators. (under the hood, this puts a NONE! literal in the type spec... e.g. make function! [[arg [_ integer!]] [...]]...hence is not a "keyword" of the "kernel", just something the FUNC and FUNCTION generators use).

So this offers up the possibility that the new "all types" type, known as ANY-VALUE!, could be required in order to signal a function can also accept functions. The default would be an acceptance of "anything but"...based on the idea that every other type is not executable by default.

When @earl weighed in on the topic long ago, he said he probably made more functions-that-take-functions than most. The casual convenience of not having to mark them as such helped for quick-and-dirty programming. This isn't a defense of the idea that one can easily write a function that didn't expect a function argument will have any chance of handling it intelligently. Just that "well, what else can it handle intelligently anyway? so why not let the people who want type signatures pay the cost, they really should be putting type signatures on anyway if they want the routines to be any good".

But above I've outlined how the reactions to GET and GET-WORD!, and how basic series operations work, that distinguish this. If you know something is not a function, there actually are a fair number of things you can do with it. e.g. the category division is a bit more meaningful than most.

Question: How's a Function's Parameters Different from Any Object or Word?

So basically, let's say I have a function foo: func [bar] [...] and I know bar is not a function, but it might be a block or an object, and I write something like bar/:baz, or maybe :bar/:baz. All the same issues seem to apply, bar might be a block with a function in it or not. Maybe it's a MAP! with a function in it. There's only one level of protection provided by guarding the function arguments from being functions...and it's going to need to be overrideable anyway.

So given that, is it worth the inconsistency? In practice, how many times do people actually try passing functions as arguments when they weren't expected?

Related Idea: Create an effectively "OPT-WORD!" (operator ?)

A motivator of this line of thought was seeing an increase in the use of GET-WORD!s to fetch values conditionally, as it is a historical way to bypass the UNSET!ness.

Another possibility would be to invent something new. Having in the past looked for a better usage of it than a HELP synonym...what if ? was an operator that quoted its word or path argument, and then would evaluate it if it were a function... if it were any other value type be that value...and be a void if the word was unset. e.g.

foo: ()
bar: does [10]
baz: 20
? foo ;-- generate no error, evaluates to no value/void
? bar ;-- 10, ran the function
? baz ;-- 20, fetched the value

It doesn't solve every part of the general problem. But the problem was always around...it's only that it starts to get noticeably worse the more GET-WORD!s and GET-PATH!s you find yourself actually handling unset things as a more common case.

any [:foo | if set? 'bar [bar] | :baz] => any [? foo | ? bar | ? baz]

It at least helps bridge it so that you can separate out "optionality" from "get" semantics.

One disadvantage is that if ? isn't part of the type, e.g. ?foo for OPT-WORD! and ?what/ever as OPT-PATH!, it would have to be processed as a keyword in dialects or escaped, and it would need to be variadic. Hm...actually, it can't actually work for functions in the sense that it would need to evaluate the arguments to know how far to skip to pass the function's arguments, were the word unset. So only arity-0 functions could work with it. :-/ Leaving it here to still think about it or if it inspires other thoughts.

Other Ideas or Thoughts?

This doesn't really answer whether it's worth it for functions to not include FUNCTION! as legal arguments by default. Perhaps MAKE FUNCTION! the low-level primitive would always be explicit about it, and the FUNC and FUNCTION generators would be the ones who chose.

It seems fairly innocuous to have you need to say any-value!, in the scheme of things...so it's at least a little bit of fair warning. I dunno.

Comments welcome.

echo crashes on %|

to file! "|"
== %|

echo %| ; crash

** Version: 2.102.0.3.40 **
** Platform: Windows win32-x64 **
** Build: 27-Mar-2017/14:13:02 **
** Commit: c5ab948 **

Specialized function FRAME!s expose fields

Due to the way specialization works, the frame that is pushed for a specialized function will need to have enough fields to run the underlying code--even though that's more fields than the user-visible function spec contains. This exposes fields that should not be visible, e.g.

foo: make frame! :add
foo/value1: 10
foo/value2: 20
do foo ;-- gives 30

add20: specialize 'add [value2: 20]
bar: make frame! :add20
bar/value1: 10
do bar ;-- also gives 30

mumble: make frame! :add20
mumble/value1: 10
mumble/value2: 100000 ;-- not hidden, no error
do bar ;-- gives 30; overwrites meaningless frame value

Some mechanism should be involved to hide the specialized-out fields from both binding and the user's view, when considered from the perspective of the aggregate function.

Mechanically, making a smaller frame for the specialization and then proxying it into a larger frame would be costly. A better mechanism would look at the specialized frame values themselves, and for any slots that were not void omit them from view and binding, based on which level of execution the frame was on.

This points to a greater difficulty, which is the problem that over its lifetime a single frame can be used for several different functions, each with its own "view". Hence even the question FUNCTION-OF on a frame is ambiguous. It is thus likely up to a FRAME! value to codify not only the context of the frame it is speaking about, but also which layer of execution within that frame...considering each nested level to be distinct despite the reuse.

Problem with trace

In ren-c on win64:

>> trace on
    <-- trace == &void
    --> --anonymous--
         1: source:
         2: copy : function! [value /part limit /deep /types kinds]
             3: ""
        --> copy
        <-- copy == ""
         4: code:
         5: copy : function! [value /part limit /deep /types kinds]
             6: []
        --> copy
        <-- copy == []
     ......................................   

types-of doesn't seem to be working

types-of doesn't seem to be working:

>> types-of :if
Assertion failed!

Program: C:\XX\r3-a4154a6-debug-cpp.exe
File: ../src/core/s-mold.c, Line 835

Expression: !TYPE_CHECK(value, REB_MAX_VOID) || VAL_KEY_SPELLING(value) != NULL

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Another:

>> types-of :print
Assertion failed!

[...]
File: ../src/core/s-mold.c, Line 826

Expression: !TYPE_CHECK(value, REB_0)

On windows10-64-bit

echo fails with an assertion

** Version: 2.102.0.3.40
** Platform: Windows win32-x64
** Build: 24-Mar-2017/19:08:06
system/commit is "0a833354fe57e43b02e2decd8306edf21ee7f60e"

echo %foo.txt
Assertion failed!

Program: C:\Users\Graham\rebol3\r3-0a83335-debug.exe
File: ../src/include/reb-device.h, Line 241

Expression: req->device == RDI_FILE

Including rebol file into host-repl.r (via do, load or import) produces a ASAN/Valgrind error

This is the error produce when starting ./r3 binary:

C Source File ../src/os/host-main.c, Line 896
unmanaged series was likely created during evaluator tick: 24329
series guard didn't trigger ASAN/Valgrind trap
:
either not a REBSER, or you're not running ASAN/Valgrind

Here is diff of my change:

diff --git a/src/os/host-repl.r b/src/os/host-repl.r
index 0dee170..e77bc44 100644
--- a/src/os/host-repl.r
+++ b/src/os/host-repl.r
@@ -223,3 +223,5 @@ upgrade: procedure [
 ][
     fail "Automatic upgrade checking is currently not supported."
 ]
+
+do %/Users/barry/.rebol/repl-skin.reb

File is hardcoded at moment and does exist. Get error regardless of content in file.

to-file _

to-file _ gets %_
I think that's wrong.
If a script expects filename in system/options/args/1 but you pass none, then %_ is opened. :-/

recursion with variadic function

f : func [:look [block! <...>]] [ apply 'f [look: make varargs! take look] ] f [x]

SER_AT_RAW asked 1 on width=255
Kind=38

f: func [:look [block! <...>]] [ apply 'f [look: make varargs! take look] ] g: func [x] [f [x]] g 1

Relative item used with SPECIFIED
Kind=4

f: func [:look [block! <...>]] [ first look apply 'f [look: make varargs! take look] ] g: func [x] [f [x]] g 1

sys-rebval.h:908: KNOWN: assertion "IS_SPECIFIC(value)" failed

TO-WORD tag

Wish direct conversion to-word <a> instead of to-word to-string <a>

PREP step fails @271c5b5

r3-make -qs ../src/tools/make-boot-mod-header.r MODULES="Crypt"
** access error: script not found: %../src/tools/make-boot-mod-header.r

if-take-empty

if take [ ] _
=>
../src/core/c-error.c:198: Trapped_Helper_Halted
: assertion "SER_LEN(GC_Manuals) >= s->manuals_l
en" failed

Unable to print error

whenever hit an error in script, fails to print it -- only print
managed series was likely created during evaluator tick ....

load incorrect integer leads to assertion

>> trap [load "2x"]
r3: ../src/include/sys-value.h:163: VAL_TYPE_Debug: Assertion `v->header.bits & NODE_FLAG_CELL' failed.
fish: โ€œr3โ€ terminated by signal SIGABRT (Abort)

Return Results vs. Errors for SELECT, PICK, or PATH! accesses

With the rise of the new "non-value" status of void results, they offer a tool that did not previously exist for routines like SELECT or PICK. That tool is the ability to return a signal that is guaranteed to not represent a value held by the target.

The following is the behavior of R3-Alpha and Red 0.6.0:

>> block: reduce [() none]
>> type? pick block 1
== unset!
>> type? pick block 2
== none!
>> type? pick block 3
== none!

In Ren-C, you simply can't make that block. The reduce fails on the (), or anything that evaluates to nothing. _(This has seemed to arise as the preferred default, as opposed to vaporizing such slots or just putting NONE! there. Beyond trying to make up some refinement for handling such cases that are purposeful, see notes on _reduce-each* as a generalized alternative.)*

With voids illegal in blocks, you could instead do a design which "avoids lying" yet still tolerates the access of out of bound values. Using the _ as a literal NONE! from Ren-C:

 >> block: [_]
 >> type-of pick block 1
 == none!
 >> type-of pick block 2
 == _
 >> void? pick block 2
 == true

That feels "more rigorous". Though there are a couple of issues to address.

But isn't NONE! "friendlier"?

While some people were big proponents of the importance of literal NONE! values in blocks in R3-Alpha, most people didn't like them as literals very much. They had bad properties, like often molding as the word NONE. Molded accurately as #[none], they were ugly.

Whereas NONE!s in blocks were unfriendly, UNSET!s were downright evil. Hard to introspect and work with, with many core routines not really bulletproofed to handle them with proper errors. So they were even more rare in literal form.

Despite UNSET!s being rarer...NONE!s could be handled normally in IFs and assignments. So you could write things like while [x: take block] [do stuff with x] If you hit an UNSET! in the block, that would be bad...and if you hit a literal NONE! or a logic literal FALSE! you'd probably do the wrong thing.

  • People concerned about incorrectly ending an enumeration on FALSE could write while [not none? x: take block] [...].
  • Adding concern discerning literal NONE! from the tail would write while [all [not tail? block | x: take block]] [...]
  • People concerned about literal UNSET! too could say while [all [not tail? block | set/any 'x take block]] [...]

When you look at how circuitous that is, it makes the features described in #252 and orthogonality with void? seem rather nice. Using void to signal, you get full coverage with just:

 while [not void? x: take block] [...]

And retaking value? to mean "not void?" it gets about as good as it can get:

 while [value? x: take block] [...]

In short, the motivating factors which once made NONE! seemingly desirable as a return value to indicate "not there" have vanished. Where NONE? was an insufficient test before to know you were actually at the tail, VOID? is now certain. Plus, a slew of other behaviors of letting voids "opt out" makes them an actively useful result...which means you might not even do a value?/void? test because it might be usable directly:

 if value? x: take block1 [append block2 x]

Could be rewritten as simply...:

 append block2 take block1 

How Cognizant is the User of the "Failure"?

The ease of handling of void results in Ren-C and their "out of band" character makes them ideal return results, if the way you want to signal "no result" is purely by the result. But what if no one checks the result?

TAKE is a good example of something that has a side-effect, and that someone might use without checking the result. Scanning across some code and seeing blah blah | take block | blah blah it could be an easy mistake to make to have that take fail, and say nothing. This may be a sign that TAKE is a candidate for "error by default, but no error if you use an /OPT refinement". It's not that onerous, especially compared with the past:

  while [value? x: take/opt block] [...]

This would be assuming that many cases of TAKE would be used in some broader iteration where it was already known that there was a value to take in the block.

But that's something with side effects which people might not check. What about a read-only operation, like a SELECT or a PICK? Since the operation has no side-effects, the only reason you would do it would be to get a value that you use or check somehow. Is there anything to worry about?

The one thing to worry about would be if there's a piping that doesn't check. For instance, the current handling behavior of APPEND with voids is to just throw them out, e.g. append copy [a b c] () is [a b c]. But this means append data select block key could fail and be unchecked.

If "why didn't it add anything, I said to append!" is a problem, there's already a few failure modes in that example. Like "what if you selected something and it was an empty block". This might point to the idea that append/only should error if someone tries to opt-out of the append, e.g. the "raw" append operation always demands the block grow...while the APPEND is more open-minded.

There may be a way to build a sort of "language culture" around the idea that SELECT can return nothing, while GET presumes a value will be returned unless you say /OPT. Meaning that if someone writes a routine like get-foo-thing it would be understood as different from select-foo-thing... even though both took in a key, the GET presumes success while SELECT doesn't.

But this is small potatoes compared to the big question which is...

What about Paths, which may run Functions?

In R3-Alpha and Red 0.6.0:

block: reduce [
    func [x] [print x]
    func [y] [print reverse copy y]
]

block/1 "Hello!"
block/2 "World!"
block/3 "Ignored?"

That just outputs:

Hello!
!dlroW

If blocks and maps are to be considered places where functions might be run from, as much so as other places, then this becomes a pretty inconsistent-feeling situation. A word and a path seems like a thing that should give you some kind of error or warning if it didn't find anything, because it's hard to know what you meant otherwise. "Just kind of skipping things" is bad.

The distinction here is because "selecting" by a path isn't the same as SELECT... because if a SELECT finds a function it doesn't run it:

 >> select object [foo: does [print "Hi"]] 'foo
 == make function! [[][print "Hi"]]

Paths are variadic foundational parts of the evaluator that do run functions they find. And they're weird enough already, without saying that they'll handle the thing they find at the end of the path differently when they find it. words that look up to functions run them, get-words that look up to functions do not. paths that look up to functions run them, get-paths that look up to functions do not.

Hence I think block/3 has to give an error here, and this same rationale applies for MAP!. So this does paint a picture in which path access must use GET-PATH! if you consider the possibility that the thing might not be there. Or you can use the operators that don't do function dispatch, like SELECT, if you prefer, though these will not necessarily correspond to path dispatch depending on the type. (e.g. block/1 is a "PICK" not a select)

value: map/key ;-- error if key not in map

value: :map/key ;-- won't error if key not in map, value will be unset 

value: select map 'key ;--- same, but won't run function and not "always pathy"

Assert failure on MacOS

Haven't found a simple test case yet, but It failed to run tools/make-natives.r on MacOS, with the assert error:

IS_END() called on garbage
Rebol Internal Error

The assert happened at here, and the reason it's failing is because the D_OUT is invalidated by SINK on this line, and Scan_Array is erroring out because it detects a syntax error.

Breaking it into two statements fixed the issue:

diff --git a/src/core/l-scan.c b/src/core/l-scan.c
index ca7b91a..39b44cd 100755
--- a/src/core/l-scan.c
+++ b/src/core/l-scan.c
@@ -2101,7 +2101,8 @@ REBNATIVE(transcode)
     // If the source data bytes are "1" then it will be the block [1]
     // if the source data is "[1]" then it will be the block [[1]]
 
-    Init_Block(D_OUT, Scan_Array(&scan_state, 0));
+    REBARR *array = Scan_Array(&scan_state, 0);
+    Init_Block(D_OUT, array);
 
     // Add a value to the tail of the result, representing the input
     // with position advanced past the content consumed by the scan.

The reason why we didn't see this on Linux/Window is probably the difference in sequence of parameter evaluation, i.e. Scan_Array is called before SINK?

But I think a better fix is to remove such traps, like this:

diff --git a/src/include/sys-series.h b/src/include/sys-series.h
index cb1a79d..c1e702d 100644
--- a/src/include/sys-series.h
+++ b/src/include/sys-series.h
@@ -629,8 +629,12 @@ inline static REBYTE *VAL_RAW_DATA_AT(const RELVAL *v) {
     return SER_AT_RAW(SER_WIDE(VAL_SERIES(v)), VAL_SERIES(v), VAL_INDEX(v));
 }
 
-#define Init_Any_Series_At(v,t,s,i) \
-    Init_Any_Series_At_Core(SINK(v), (t), (s), (i), SPECIFIED)
+inline static void Init_Any_Series_At(RELVAL *out,
+    enum Reb_Kind type,
+    REBSER *series,
+    REBCNT index) {
+    Init_Any_Series_At_Core(SINK(out), type, series, index, SPECIFIED);
+}
 
 #define Init_Any_Series(v,t,s) \
     Init_Any_Series_At((v), (t), (s), 0)

Missing rule for DO in PARSE dialect causes panic

>> parse [(print now)] [do]
18-Mar-2017/11:05:03+11:00
VAL_TYPE() called on END marker
Pointer found in freed tail capacity of series
Kind=0
Containing series for value pointer found, panicking it:
managed series was likely created during evaluator tick: 83970

Ren-c build:

**    Version:   2.102.0.3.1                                            **
**    Platform:  Windows win32-x86                                      **
**    Build:     17-Mar-2017/23:38:29                                   **

Non-debug compilation fails (Android)

../src/os/host-main.c: In function 'main':
../src/os/host-main.c:686:12: error: 'SIGINT' undeclared (first use in this function)
signal(SIGINT, Handle_Signal);
^
../src/os/host-main.c:686:12: note: each undeclared identifier is reported only once for each function it appears in
../src/os/host-main.c:690:12: error: 'SIGTERM' undeclared (first use in this function)
signal(SIGTERM, Handle_Signal);
^
../src/os/host-main.c:694:12: error: 'SIGHUP' undeclared (first use in this function)
signal(SIGHUP, Handle_Signal);
^
make: *** [objs/host-main.o] Error 1

NOTE: affects only Android, with -DNDEBUG

ls/r and ls/l error

Version: 2.102.0.3.40 Platform: Windows win32-x64 Build: 22-Mar-2017/5:49:03 Commit: ed8f62f

ls/l
.travis.yml 4250 2-Mar-2017/16:07:12.005+ file
** Script error: printf must return value (use PROC or RETURN: )
** Where: printf either for-each ls
** Near: printf [indent 16 -8 #" " 24 #" " 6] info ?? if all [r | dir...

ls/r
.travis.yml 4250 2-Mar-2017/16:07:12.005+ file
** Script error: printf must return value (use PROC or RETURN: )
** Where: printf either for-each ls
** Near: printf [indent 16 -8 #" " 24 #" " 6] info ?? if all [r | dir...

reword fails with edge case

>> reword "$a" [a  "foo"] 
== "foo"

under r3-g25033f8.exe

but now r3-de87088-debug-cpp.exe. It seems a trailing character is needed.

>> reword "$a" [a "foo"]
== "$a"
>> reword " $a" [a "foo"] 
== " $a"
>> reword " $a " [a "foo"]
== " foo "
>> reword "$a " [a "foo"] 
== "foo "

WHILE [] in a parse rule has trouble responding to Ctrl-C

The Ctrl-C handling lives in Do_Core()...but empty block short-circuits ever calling Do_Core(). Hence tight loops can't break.

This issue does not arise in R3-Alpha because UNTIL [] requires a result, as opposed to treating void results of the loop as an "opt out".

Pick map -> assertion

pick make map! [1 1] 1

../src/include/sys-array.h:335: VAL_ARRAY: assertion "ANY_ARRAY(v)" failed

Free_Series() error in foreach

foreach [:x] [ ] [ ]

Trying to Free_Series() on a series managed by GC.
managed series was likely created during evaluator tick: 26065
series guard didn't trigger ASAN/Valgrind trap:
either not a REBSER, or you're not running ASAN/Valgrind

END marker or garbage/trash in VAL_TYPE() in parse

>> o: make object! [a: 1]
== make object! [
    [self: a]
    [
        a: 1
    ]
]

>> parse "a" [o/a: skip]     
END marker or garbage/trash in VAL_TYPE()
REBVAL init on tick #28226 at ../src/core/c-path.c:195
Kind=50
No containing series for value...panicking to make stack dump:
managed series was likely created during evaluator tick: 0
series guard didn't trigger ASAN/Valgrind trap
:
either not a REBSER, or you're not running ASAN/Valgrind

series in console are locked

>> b: []
== []

>> append b 1
** Access error: series is source or permanently locked, can't modify
** Where: append
** Near: append b 1 ??

though it may make sense to make function source be locked, having series in the console being locked makes the console a lot less usefull.

Parse and remove

In Ren-c (Version: 2.102.0.3.40 | Platform: Windows win32-x64 | Build: 22-Mar-2017/5:49:03):

>> parse  "a<bbb>" [ some [remove ["<" thru ">"] | skip]]
== true

>> parse  "<bbb>a" [ some [remove ["<" thru ">"] | skip]]
== false

A few Discussion Issues for Ren-C's "unset => void" as Opt-Out

(Starting as a GitHub issue for purposes of commenting, and will gather any comments to use in a write-up...)

UNSET! is Dead. Long Live UNSET

As part of a large-scale reframing of the issue of unset-ness in general, Ren-C moved to a model in which there are many new things to know about them:

  • There is no "UNSET! datatype". type-of do [] isn't a DATATYPE! at all...it's a blank value, e.g. blank? type-of () is TRUE. This is not to be confused with blank? ()...which is false.

  • An evaluation which produces an entity with no type is said to be "void"--considered the absence of a value (as opposed to a value whose type is void! or unset! or what-have-you).

  • Because voids are not Rebol values, they may not be stored in a BLOCK! or other ANY-ARRAY!. Operations like APPEND, REDUCE, INSERT etc. are thus free to define meanings for what to do when they see them. This is on a case-by-case basis, e.g. join [a b c] () is [a b c], while reduce [a () b] is an error by default. (A construct like reduce-each could give more fine-grained control for handling this and other cases, e.g. collect [reduce-each x [1 + 2 () 3 + 4] [keep/only either set? 'x [:x] ['it-was-void]]]. => [3 it-was-void 7]

  • When a context has a value for a word, it is said that "the variable is set". If it does not have a value for a word, it is said that "the variable is not set" or "the variable is unset". This is a terminology shift--and because of it the unset? routine is deprecated, because proper usage would be unset? 'some-var and not unset? :some-var. It will be revived with the correct meaning in the future, but for now not set? 'some-var is to be used...as UNSET? is gradually replaced with VOID? in legacy modules.

  • I'll restate it for emphasis, that void? is not a test for a void! type. There is no void! or unset!. There is no construction syntax for a void! or unset!, because it is not a value...you can't write do [x: #[unset!]] anymore because the source block could not hold a non-value in that second spot of the block. void? is a standalone routine you only call on a transient evaluative result.

Note: This worldview makes, I think, much more sense:

set 'x value
assert [set? 'x]

unset 'x
assert [unset? 'x]

"Setness" and "unsetness" does much better as a property of a variable, than a value. Being liberated from having variables that are set to UNSET! (e.g. the interpreter source had code like SET_UNSET(value))...and all the confusions that go with that, is very nice.

By pushing void into this new role, users work with it in many more places where BLANK! ("none!") might have been used previously. As the default return result of conditional expressions and loops, with a pervasive distinct meaning for "opting out"... there are many much more elegant ways of doing things. It takes over many cases of what BLANK! used to be for--(which helps clarify the roles of BLANK! specifically as a purposeful positional placeholder that is used in blocks and should -not- be omitted).

>> compose [a (if false ['b]) c]
== [a #[none] c] ;-- R3-Alpha/Red 0.6.0
== [a c] ;-- Ren-C

>> compose [a (switch 1 [2 ['b] 3 ['c]]) d]
== [a #[none] d] ;-- R3-Alpha/Red 0.6.0
== [a d] ;-- Ren-C

>> rejoin ["a" (while [1 > 2] ["b"]) "c"]
== "anonec" ;-- R3-Alpha/Red 0.6.0
== "ac" ;-- Ren-C

The more places that void is not allowed to make sense for, the more features open up. For instance: you cannot pass a void value as a refinement argument, so they can be used to revoke refinements:

 >> condition: true
 >> append/dup copy [a b c] [d e] if condition [2]
 [a b c d e d e]

 >> condition: false
 >> append/dup copy [a b c] [d e] if condition [2]
 [a b c d e]

Note: There is one portability pitfall to be aware of, in the sense of "creates problems that can be hard to find". It's not terribly common, but is sort of along these lines:

>> all [true (if 1 > 2 [true]) true]
== #[none] ;-- R3-Alpha/Red 0.6.0
== true ;-- Ren-C

So here we see that returning void made Ren-C consider the condition to opt-out. e.g. "if 1 < 2 then I do not have a vote to contribute to this ALL". While R3-Alpha and Red saw the whole expression as being logically false. I strongly believe the Ren-C interpretation is the more powerful and useful one, and it fits perfectly with the other findings.)

Also, there is room here for tools like if? ... which would return TRUE if the condition was taken and FALSE if it was not, ignoring the result of the block. This is potentially more generically useful in some places, allowing one to not worry about what falls out the bottom of the block.

Familiarity Breeds Factoring

These new features are all fine and well to use with inline IF or WHILE or SWITCH or CASE. But if you can do something inline, it isn't long before people want to factor out the expression. If you can write all [... case [...] ...] it's quite natural to want to change that to expr: case [...] | all [... :expr ...]

But when it comes to assigning "voids", R3-Alpha, Rebol2, and Red 0.6.0 make it a bit difficult. This produces an error in all three:

 >> code: []
 >> result: do code
 ** Script error: result: needs a value

To work around this, one must use a function call to SET with a refinement, /ANY. (In Ren-C the refinement is called /OPT which ties into saying it's an "optional set"..as well as tying it to the OPT primitive that converts BLANK! to void and passes through all other values.)

>> code: []
>> set/any 'result do code
>> if unset? :result [print "It was unset"]
It was unset

There's already a mismatch here, between set 'result (...) and result: (...) when compared with get 'result and :result, demonstrated if we use a plain GET.

>> code: []
>> set/any 'result do code
>> if unset? get 'result [print "It was unset"]
** Script error: result has no value

In other words, :result is effectively GET/ANY while result: is just SET, as opposed to SET/ANY.

One reasoning could be because "getting" has two entry points via words...one that is the plain undecorated use of a word (e.g. result vs :result) while "setting" has only one hook (result:). But note that it's not appropriate to think of the leading : as /ANY, and without as just GET...because GET doesn't call functions the way an undecorated word does! So :result is the only real entry point to GET, and it's a GET/ANY.

After considerable reflection, and trying things multiple ways...in Ren-C result: acts as SET/OPT, and :result acts as GET/OPT. The unrefined GET and SET operations give errors if passed a void. This has a number of benefits, here are a few:

  • As explained, it provides parity in the definitions for setting and getting.

  • Instead of those who can handle "opted-out" variables (or are willing to just have a "downstream" error) being forced to use the ugly set/opt 'foo (...), the people for whom assurances have extra value can optionally use the prettier set 'foo (...). Ren-C offers even more choices, like foo: ensure (...) and that would work as ensure foo: (...) as well.

Note: One consideration on wanting to shift the burden for "it would be nice to check" onto those who think it's valuable is the idea that really, checking the return result of a function for just void is awfully specific. If you call a function and expect it to be an integer, but it comes back a BLANK!, then are you any "safer"? Quite arguably you are less safe, because when you go to use the assigned result and it's a BLANK! then you won't get any errors, such as with x: some-function | append data x. Where the void will complain, the BLANK! wouldn't. This shows how the previous checking was very much an illusion, and it gets in the way of much more interesting code when everything is piped together. ensure integer! x: (...) is an example of expanding the concepts.

  • Convenient means of unsetting: foo: ()

  • Cleaner expressivity of conditional sets. For instance, if one wishes to conditionally set or unset a key in a map one can write map/key: if condition [code] and it will be removed should the condition be false. This is more convenient than either condition [map/key: (code)] [unset 'map/key].

  • Avoids separate test for presence and lookup in a map...e.g. unless void? value: :map/key [do stuff with value]

Note: This builds on the idea that allowing any legal value to be in the mapped-to range of a map is an important feature. Regardless of the legal kinds of keys that a map permits, having the full range of values ... including BLANK! in that range... is useful. There's simply a lot that can be done. See #253 for more on how this has cleaned things up.

To make a long story short, this has held up very, very well. But there are some thinking points that have come along in the process which should be pointed out:

Thinking Point 1: Get-Function vs Optional Get

With ERROR! "disarmed", the only "live" types which remained that needed GET-WORD! to inspect their values in R3-Alpha were UNSET! and ANY-FUNCTION!. Ren-C has pared ANY-FUNCTION! down to just one universal type, FUNCTION!...with the best qualities of functions and closures all together in one.

But I've come to think it's not a good idea to use :some-func if we expect some-func to be defined. Instead, use get 'some-func. So the schematic is:

  • Want a value vs a function call...variable shouldn't be unset, think it might be a function: get 'foo

  • Want to invoke value if it's a function or get something back otherwise, shouldn't be unset: foo

  • Variable may be unset, and would like to evaluate to a void if so...may or may not be a function: :foo

This will help avoid the accidental tolerance of unset variables when all you wanted was to suppress the function call.

Note that in the matrix of possibilities this leaves something out: "The variable may be unset, in which case I want a void back and no error...but if it's a function I want to run it, and if it's not I want the value." So basically if set? 'foo [foo]. This isn't necessarily an uncommon desire, but seeing whether it needed its own operation would have to come from looking at practice. If an operation did take a single 'foo argument, the operation would either have to be variadic (likely bad) or enforce that foo was arity 0, so perhaps the if set? form is the best idea.

Thinking Point 2: Typos

One thing that happens as a result of this shift is that people use :foo a lot more often than they did before. Using a GET-WORD! instead of a plain WORD! indicates "I know it may not be set...but that's okay, because this is a spot where I want to use a void to signal an opt-out"

extra: if need-extra-stuff ["...something extra"]
print ["main message" :extra]

And that's pretty elegant...but there's now a danger of making typos that go uncaught. The reason is because "references" to words get bound before they ever "see" a "declaration":

>> foo: does [print variable] ;-- variable word is already bound here
>> variable: 10 ;-- is re-using the "definition" that the does already made
>> foo
10

Since everything is bound...no matter how it's spelled, then you can get in trouble once you consider a variable with no content as being "interesting" and not just an error.

The good-ish news here is that it's not a new problem...and it is something the module system has to handle. And among the features that it does handle in its current condition, this is one of the things it can do. Though usually the problem it was worried about was not reading, but writing: using stray variable names and creating new globals with each stray name. So more like this:

>> foo: does [varable: variable + 10] ;-- typo...
>> variable: 10
>> foo
>> print variable
10 ;-- "wait, it's supposed to be 20..."

So it's a mirror of that, just from reading things that shouldn't have a binding but get one anyway since the user context is so permissive by default.

I guess the moral of the story is that if you're not using modules, you might stick with using NONE! to carry conditions when you put them into variables. That way you can use a normal word fetch (that would fail if undefined) and then use OPT to convert the NONE! into a void:

extra: either need-extra-stuff ["...something extra"] [none]
print ["main message" (opt extra)]

There are other ways to write this in Ren-C

extra: to-value if need-extra-stuff ["...something extra"]

extra: either need-extra-stuff ["...something extra"] _

extra: all [need-extra-stuff | "...something extra"]

The last one is the best, and it shows a case of how by thinking about the difference between what NONE!/"BLANK!" is for the problem can be seen in a new light. (ALL and ANY cannot return a void or a false).

Thinking Point 2.1: Unused Refinement Args are Not Set

I said if you're not using modules then typos could screw you up. And you might be the kind of person who says fine, you'll keep not-using-modules and go with the all [...] instead of if ... [...] pattern, and then use opt on references. Basically you'll enjoy the new inline forms when putting IF/SWITCH/CASE/WHILE* into something that can handle optionality...but when you factor, you'll stick to the convention of making sure your variables are assigned NONE! and then un-none-i-fy them. Because you didn't really want to put NONE!s in blocks before, and you're not going to start now.

Note: You might want to start thinking about it, because "NONE! is a lot more FUN!" when its literal form is just _. Before it was a terror, looking like a word when it shouldn't... you really don't like writing #[none] very much, and # never seemed right. But with _, you start having more ideas for it in dialects, and you do want to compose it into blocks and such.

That's fine when it's your code and you're making the rules, but refinement arguments are now voids whenever the refinement isn't used. That's a property of MAKE FUNCTION! itself... not FUNC or FUNCTION.

So again, without module protection on bindings, if you write something like:

foo: func [bar /ref1 baz /ref2 mumble] [
     zapf: any [:baz | :mumblle | ...] ;-- typo, uncaught! might miss mumble
     ...
 ]

By default there's a nice guard in here that keeps the average coder from accidentally using a refinement argument that wasn't supplied, because they're not set if their corresponding refinement is none. Ordinary word access will fail on them. But if you were accustomed to Rebol2/R3-Alpha's choice to make refinements NONE! by default, then you don't want to have to test ref1 or ref2 before accessing their arguments because that's "redundant"...and you use GET-WORD!, and there could be an issue.

I could just repeat "well, reading something you shouldn't isn't that different from writing something you shouldn't, you're just used to the old problem and not the new one, you need modules anyway so look at all the other benefits". And I will say that. But I gave an alternative before so it seems there should be some alternatives here.

One is defaults, which I have said is a likely addition. This covers a lot of cases, even if you just want a default of BLANK!:

x: 100
foo: func [bar /ref1 baz (x * 100) /ref2 mumble (_)] [
     zapf: any [baz | mumble | ...] ;-- typo gets caught, mumble undefined
     ...
 ]

The leading idea on defaults is they would come from the generator, e.g. this would actually be doing:

make function! [[bar /ref1 baz /ref2 mumble] [
    baz: any [:baz 10000]
    mumble: any [:mumble _] 
    zapf: any [baz | mumblle | ...]
]]

So the expressions are evaluated once at function generation time and then get grafted into the body as code (probably use a tweaked default which only acts on unset variables).

Feel free to add comments here or in chat

help on all lib words errors

** Version: 2.102.0.3.40 **
** Platform: Windows win32-x64 **
** Build: 23-Mar-2017/17:13:58 **

for-each [key val] lib [help :val]

make typeset! [path! set-path! get-path! lit-path! group! block!] is a typeset
true is a logic
false is a logic
** Script error: type-name does not allow function! for its value argument
** Where: delimit either either print if help for-each
** Near: "is" type-name :word ??

Assert failure caused by protect

>> a: func [i][protect 'i]
== make function! [[i return:][
    return: make function! [
        ["Returns a value from a function." value [<opt> any-value!]]
        [exit/from/with (context-of 'return) :value]
    ]
    (protect 'i)
]]

>> a 0
r3-core: /home/zsx/r3-dev/src/core/c-context.c:116: Expand_Context_Keylist_Core: Assertion `NOT_SER_FLAG(keylist, ARRAY_FLAG_PARAMLIST)' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff7552a10 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff7552a10 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff755413a in abort () from /usr/lib/libc.so.6
#2  0x00007ffff754b607 in __assert_fail_base () from /usr/lib/libc.so.6
#3  0x00007ffff754b6b2 in __assert_fail () from /usr/lib/libc.so.6
#4  0x000000000042177d in Expand_Context_Keylist_Core (context=0x7ffff742fc28, delta=0) at /home/zsx/r3-dev/src/core/c-context.c:116
#5  0x00000000004af0d8 in Ensure_Keylist_Unique_Invalidated (context=0x7ffff742fc28) at /home/zsx/r3-dev/make/../src/include/sys-context.h:345
#6  0x00000000004afd25 in Protect_Key (context=0x7ffff742fc28, index=1, flags=9) at /home/zsx/r3-dev/src/core/n-protect.c:42
#7  0x00000000004b01b6 in Protect_Word_Value (word=0x8a5988, flags=9) at /home/zsx/r3-dev/src/core/n-protect.c:152
#8  0x00000000004b059d in Protect_Unprotect_Core (frame_=0x7fffffffd040, flags=9) at /home/zsx/r3-dev/src/core/n-protect.c:204
#9  0x00000000004b0c78 in N_protect (frame_=0x7fffffffd040) at /home/zsx/r3-dev/src/core/n-protect.c:304
#10 0x000000000042ab14 in Do_Core (f=0x7fffffffd040) at /home/zsx/r3-dev/src/core/c-eval.c:1251
#11 0x0000000000436906 in Do_Array_At_Core (out=0x7fffffffdbc0, opt_first=0x0, array=0x7ffff742e508, index=0, specifier=0x7ffff742fc28, flags=16)
    at /home/zsx/r3-dev/make/../src/include/sys-do.h:544
#12 0x000000000043699d in Do_At_Throws (out=0x7fffffffdbc0, array=0x7ffff742e508, index=0, specifier=0x7ffff742fc28) at /home/zsx/r3-dev/make/../src/include/sys-do.h:851
#13 0x000000000043a23e in Returner_Dispatcher (f=0x7fffffffd3e0) at /home/zsx/r3-dev/src/core/c-function.c:1788
#14 0x000000000042ab14 in Do_Core (f=0x7fffffffd3e0) at /home/zsx/r3-dev/src/core/c-eval.c:1251
#15 0x000000000059c182 in Do_Array_At_Core (out=0x7fffffffdbc0, opt_first=0x0, array=0x7ffff742f1d8, index=0, specifier=0x0, flags=16)
    at /home/zsx/r3-dev/make/../src/include/sys-do.h:544
#16 0x000000000059c5b1 in Do_At_Throws (out=0x7fffffffdbc0, array=0x7ffff742f1d8, index=0, specifier=0x0) at /home/zsx/r3-dev/make/../src/include/sys-do.h:851
#17 0x000000000059cae2 in Do_Code (exit_status=0x7fffffffdbf4, out=0x7fffffffdbc0, code=0x7fffffffd7b0, at_breakpoint=FALSE) at /home/zsx/r3-dev/src/os/host-main.c:286
#18 0x000000000059cff9 in Host_Repl (exit_status=0x7fffffffdbf4, out=0x7fffffffdbc0, at_breakpoint=FALSE) at /home/zsx/r3-dev/src/os/host-main.c:418
#19 0x000000000059df5a in main (argc=1, argv_ansi=0x7fffffffded8) at /home/zsx/r3-dev/src/os/host-main.c:1026

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.