HN user

asrp

426 karma

github.com/asrp

blog.asrpo.com

Contact: asrp email com

Posts36
Comments87
View on HN
blog.asrpo.com 9d ago

Visualization of the OpenAI Proof of the Cycle Double Cover Conjecture

asrp
1pts1
blog.asrpo.com 10d ago

Visualization of the OpenAI Proof of the Cycle Double Cover Conjecture

asrp
3pts1
blog.asrpo.com 6y ago

Roll your own GUI automation library

asrp
3pts0
blog.asrpo.com 6y ago

Roll your own GUI automation library

asrp
2pts0
github.com 7y ago

Show HN: Nand to CPU – A circuit description language and simulator

asrp
2pts0
blog.asrpo.com 7y ago

Show HN: Nand to CPU – A circuit description language and simulator

asrp
2pts0
blog.asrpo.com 8y ago

Making a low level Linux debugger, part 3: our first program

asrp
171pts27
blog.asrpo.com 8y ago

Making a low level Linux debugger, part 2: C

asrp
102pts11
blog.asrpo.com 8y ago

Making a low level (Linux) debugger

asrp
2pts0
blog.asrpo.com 8y ago

Describing animations

asrp
2pts0
blog.asrpo.com 8y ago

Collaborative Software Trades

asrp
2pts0
github.com 8y ago

Show HN: How to make these slides (and its editor)

asrp
1pts0
blog.asrpo.com 8y ago

Combining all my GitHub projects into one

asrp
3pts0
github.com 8y ago

Show HN: Make and evolve a GUI and its toolkit simultaneously

asrp
1pts0
github.com 8y ago

Show HN: Guitktk – Make and evolve a GUI and its toolkit simultaneously

asrp
2pts1
github.com 8y ago

Show HN: An XML-like document with spreadsheet formulas for values and undo-redo

asrp
4pts2
blog.asrpo.com 8y ago

Removing polling checks in guitktk

asrp
1pts0
blog.asrpo.com 8y ago

Debugging C like it's Python

asrp
1pts0
github.com 8y ago

Show HN: Forth Lisp Python Continuum

asrp
6pts1
blog.asrpo.com 8y ago

Adding a new Python statement in python_terp

asrp
4pts0
github.com 8y ago

Show HN: A minimal Python interpreter in Python with edit-and-continue

asrp
2pts1
blog.asrpo.com 8y ago

Another tutorial for writing a Forth interpreter in assembly (Part 3)

asrp
2pts0
blog.asrpo.com 8y ago

Another tutorial for writing a Forth interpreter in assembly (Part 2)

asrp
1pts0
blog.asrpo.com 8y ago

Another tutorial for writing a Forth interpreter in assembly (Part 1)

asrp
2pts1
asrp.github.io 9y ago

The chicken or the egg problem in bootstrapping

asrp
1pts0
asrp.github.io 9y ago

Making a GUI toolkit

asrp
1pts1
asrp.github.io 9y ago

Visual explanation of the last Google Codejam 2017 question

asrp
73pts31
github.com 9y ago

Show HN: Build python ASTs in 502 lines of python without using modules

asrp
2pts1
github.com 9y ago

Show HN: Espeakui – a text-to-speech interface with mplayer-like bindings

asrp
4pts1
github.com 9y ago

Show HN: Python C extension for the eSpeak speech synthesizer

asrp
1pts1

No, that's not a problem at all. It just the notation that's a bit weird.

For example, if e is the a-edge (first edge) from the u side and v is the b-edge (second edge) from the v side then g_{u,e} = 0, g_{v,e} = a so d_e = 0 + f(x2) where f(x2) is the flow (from Kilpatrick and Jaeger's NZ8F) on the first edge next to v.

I checked the whole thing with some surface reformulations on my side and it looks right to me.

Very nice work and cool project.

However, the example in "3. Baremetal Cognition" is explained in an overly convoluted way, with many choices that IMO detracts from the point that (I think) you're trying to make. There's typos that makes it even harder to understand.

1. Use something like underscore instead of spaces and, maybe even another character like period instead of newline. You can explain after the section that you could have used space and newline instead of _ and .

2. Immediately after showing

    ldfgldftgldfdtgl
    df_
    _
    dfiff1_crank_f
you can parse it out for the reader, as something like
    "l" 'set-non-delim eval
    "gl" 'set-non-delim eval
    "tgl" 'set-non-delim eval
    "dtgl"
      "\n" 'set-non-delim eval
      "_\n"
        "_\n" 'set-non-delim eval
      'set-ignore eval
    eval
and so on. Or maybe even
    (set-non-delim "l")
    (set-non-delim "gl")
    (set-non-delim "tgl")
    (set-non-delim "dtgl")
    (set-non-delim "_\n")
    (set-ignore "_\n")
    (dtgl)
and only then you'd go through the source, character by character. Just because the source is hard to read by humans, doesn't mean we need to stick to it in an explanatory example.

3. > Delimiters have an interesting rule, and that is that the delimiter character is excluded from the tokenized word unless we have not ignored a character in the tokenization loop, in which case we collect the character as a part of the current token and keep going.

There are four "negations" in this sentence: "excluded", "unless", "not", "ignored" and two turn to explain something ostensible simple: when to end tokens added to the stack (or container). This together with whitelist, blacklist, delim, singlet needs a much cleaner naming and description.

Also set non-delimiter is an extra negation.

4. There's an error right after "Now, for the rest of the code: ". The third line contains two spaces instead of a single one. (Using suggestion 1 would have also avoided this for yourself.)

# Comment about the actual content

5. I can kind of see the rationale for this (which is also explained in the beginning). However, I don't see exactly where we'd set clear boundaries since we can alwasy stuff semantics into the initial parser. For example, instead of have `f` bound to eval, we could have set `f` to execute the entire bootstrapping sequence and then rebind `f` to eval. So the entire example would be reduced to just `f`.

I guess we'd have to argue about the initial set of functions we are allowed to are somehow primitive enough. But even `d` (set-non-delim) while it only toggles some values in an array (or list) piggybacks on the parsers inherent ability to skip characters in its semantics and `i` (set-ignore) needs inversion implemented in the parser.

6. Here we assume that one byte per character is the default starting state of the world but unicode and other encodings don't have this so you'd need some parser to be get started anyways. And in that case, is an initial parser using space and end of line as separators really unusual?

7. I don't see why (not) reading ahead would such an important property for modifiable syntax. You just need to not really ahead too much, like the entire rest of the file or stream.

8. Regarless, I think this is worth exploring but also keep in mind some of these questions while doing that.

After each outer loop iteration, A[:i] (the array up to i) is in ascending order with the max of A at A[i].

This is true the first iteration since max(A) is eventually swapped to A[1]. This is true in subsequent iterations since during the ith iteration, it inserts the next element, initially at A[i], into A[:i-1] and shift everything up with swaps with A[i] so A[:i] is sorted, with the max of A moved to A[i]. After that no more swaps happen in the ith iteration since A[i] contains the max of A.

I've put up a barebones Slack [1] and editable Wiki [2]. I might fill that with info I have in the coming weeks since I realized all I had were scattered files.

Diving into this a bit, I remembered that fonc had it's own (now defunct) wiki. [3] It seems like a lot of the important pages were unfortunately not updated though.

[1] https://join.slack.com/t/footprintsorg/shared_invite/zt-o7ch... [2] https://hackmd.io/SB4QqG7bSxmgoUvPPoSzUA [3] http://vpri.org/fonc_wiki/ [3, archive.org] https://web.archive.org/web/20110901193854/http://vpri.org/f...

The language is high-level but I wouldn't necessarily say very high level. But because I'm trying to spin up language features at runtime (like a lot of Forth does), there are a few layers on top of the language primitives.

I wish there was some framework for me to add optimizations as I go along, especially if there could be some speed gauranttee. Though in my case, I'd like to also not lose the relation to the original source (like C does when values are optimized out).

Thanks, a lot of this is new and useful to me.

Recently I've been wondering if there's enough interest for a Discord server or something. (In the spirit of STEPS, it'd be ideal to make a new collaborative thing that's really different than static text/audio/video on the web, but gotta start somewhere. :) ) Unfortunately, I have had other, higher-priority projects at the moment, so I have taken no initiative to try to build a community.

I don't really like Discord because they keep asking for phone verification and early on, they were pretty aggressively shut down alternate client attempts.

What about Mattermost? I could try to set one up though initially, we wouldn't have email notifications or a CDN. Might not be so good if the initial group is small.

Slack? Don't know how they compare to Discord but at least they don't ask for phone verification.

A subreddit? A mailing list? Some kind of fediverse thing?

If there's some possibility of migrating to our own platform, I guess it doesn't matter as much where we start.

I could try to set something up in the coming week. But interest in this HN thread will still have died by that time.

I did get Nile + Gezira to work (albeit in a very crude way by printing numbers to the console rather than hooking it up to a frame buffer). That's how I met Dan. I don't want to betray any confidences with him, but there is ongoing work with Nile.

Nice! I'm not anywhere near that. I'm still looking for a description of what it _is_ and at a very high level, how does it work internally? Something like "it's mathematical notation to describe the pixel positions/intensities implicitly via constraint equations; it uses a <something> solver for ...". What's in quote could be way off and is from memory of what I remember seeing.

I've gotten both up and running. I seem to remember the Boehm GC was pivotal in getting Maru to bootstrap and then run Nile.

I also vaguely remember something about getting the right Boehm GC version so that some of

Pymetaterp is cool! I agree: Warth's work on OMeta was impressive. In some ways, Ohm feels inferior to me, though they're both good tools with lots of potential.

Thanks! I share similar thoughts about Ohm. Having a visual editor is very nice, though I tend to use breakpoints for parser debugging [1].

Edit to add that id-objmodel [2] is another STEPS project I found to be simple and useful as an idea.

[1] See, for example, "Debugging" in https://blog.asrpo.com/adding_new_statement [2] https://www.piumarta.com/software/id-objmodel/

Is there some place STEPS fans can gather and gather our notes? There are archives of the FONC mailing list here [1].

I'm an outsider and also never got Frank to work. I was waiting for the Nile/Gezira thesis to get a high level (but hopefully also some detailed) descriptions) of how they handled graphics. I vaguely remember getting parts of idst working but for each of these projects, there were always multiple versions lying around. Sometimes in odd places.

I read Alex Warth's thesis and it's well written, in a way that makes it very easy to understand. So, of course, I had to implement my own OMeta variant [2].

Also, the VPRI website itself says it's shutting down (presumably folks moved to HARC at that time?).

Edit to add that OMeta is the language agnostic parser and compiler!

[1] https://www.mail-archive.com/fonc@vpri.org/ [2] https://github.com/asrp/pymetaterp

I've read through more of you post can came across the bottom comment (don't know how to permalink to it) which better expresses my comment above.

An optimizing linter has the problem of being destructive. It goes like this:

The programmer will write his or her program in a readable way. They'll run it through the compiler, which points out that something can be optimized, the programmer—having already gone through the process of writing the first implementation with all its constraints and other ins and outs fresh in their mind—will slap their head and mutter "of course!", and then replace the original naive implementation with one based on the notes the compiler has given. Chances are high that the result will be less comprehensible to other programmers who come along—or even to the same programmer revisiting their own code 6 months later.

Also a data point and word of warning about (lack of) optimization. My own projects (one of which was mostly hand-written in x86 assembly) have been pretty heavily stalled from speed issues, that sent me on significant detours. Since you are working with your own compiler/interpreter to implement your levels, you are directly affected by their compilation speeds as you iterate. Even with modern hardware, they can quickly become too slow to be even usable.

This is unfortunately another consequence of having too much black magic in (C) compilers. So we get the wrong intuition about how fast computers are.

I've actually never considered putting the comment first! I'll have to think about that one.

I'm sure there are many competing constraints so definitely don't do it because I'm suggesting this on a whim. :) My reasoning is that as a human reader, the comment is the more readable part, so I'd want to see it first. And for a computer, it probably doesn't care if the op code appears first or not.

You probably don't want to understand Haskell's loop fusion by comparing source and generated code.

Indeed. But even though C and Haskell are very different, I think they share a common philosophy about compilation where you can basically do whatever you want as long as it still produces the same result.

I vaguely remember looking at Python generate bytecode (with `dis.dis`) and seeing it wasn't too bad. I haven't tried it on a larger program though.

There's tcc (and more recently chibicc that I haven't had a chance to check out yet) that you're probably already aware of. Is the generated output still pretty bad.

I'll also throw my own attempt in the ring

- High level https://github.com/asrp/flpc/blob/master/lib/stage0.flpc - Low level (up to line 45) https://github.com/asrp/flpc/blob/master/precompiled/self.f

even though it's not quite optimized for this purpose and the code itself is still a bit unclean. If there was a syntax highlighter for the low level language, I'd probably highlight "[", "]" and "bind:" as a start. I can try to clarify any obscure syntax or primitive.

Some more general ideas to get aroud the issue. - Invoke optimization only when asked specifically (and apply the optimization locally). That is, optimization would need at least additional syntax in the language. - Explicitly track correspondance between source and target (at the character or token level) and also do this in each optimization pass. Maybe even keep the intermediate values of each pass so you can browse through it like a stack trace.

In my mind there's an idea maze where there are 3 major possibilities for improving the future of software:

I guess I'm trying another route even though I don't know if it fits the definition of improving the future of software.

d) Have programmers make their own compiler/interpreter and language by giving them the tools and knowledge to do that (more) easily.

This would (hopefully) avoid the black box/magic issue since the programmer would know the details of the inner workings by virtue of having written it. Though I'm most definitely very far from the goal and the questions can be asked about how to improve their target language.

Sorry if I've asked these years ago and just don't remember the answer.

The '/copy-to-ebx' after the slashes is just my way of helping the reader understand what the instruction does. I don't want the reader to have to consult the Intel manual for every instruction, even if I'm forcing the writer to do so.

Why not make the comment the instruction and the bytes the (maybe even optional?) comment in that case then?

From your first post.

The fact that C compilers are written in C contributes a lot of the complexity that makes compilers black magic to most people.

Isn't this more a symptom of C though? I'm hoping this is generally not true if you replace C with other languages (but could be very wrong). But more generally, I'm thinking you could make "the compiler's inner workings is not black magic" a constraint rather than make not writing the higher level language in the higher level language the constraint.

In my case, I tried that first route and then moved to instead having the compiler written in the higher level language but emitting output that's close enough to (my) handwritten lower level language.

I'll have to read your two part post more carefully though. Glad to see this project getting some attention, even though in an unusual fashion.

If you want to read this, I'd suggest looking at the sources in bootstrap sequence from the readme (boot.flpc, stage0.flpc, ...). Alongside, run some of the precompiled entries by hand by pasting it into the interpreter. Call `ps` once in a while to see the current state. Then for larger chunks of code invoke breakpoints by calling `debugger()` (in a `.flpc` file) or `debugger` (in a `.f` file though this will mess up source position printing beyond this point).

And of course, feel free to just ask!

It is actually not hard to read (at least in the sense of knowing what something will do when executed; getting the bigger picture takes more practice). The (base) syntax is just whitespace delimited tokens, each representing a function call (or string but we'll come to that later). So

    foo bar baz
will call the 3 functions in order

foo() bar() baz()

All functions are nullary (with side effects; these side effects determine their "true" arity). There aren't really any special characters other than whitespace so

    1 1 + print
just translates to
    1()
    1()
    +()
    print()
Function names do not have to start with a letter or be alphanumeric. I've happened to name my function so that those ending in a colon treats the next token to the right as a string instead of a function call. The [ function treats everything as strings until ] (that is a single close square bracket as a token) and puts the function in those body in a quote, effectively creating an anonymous function. So

[ foo bar baz ] bind: somename

defines a function. The equivalent in Python would be

    def somename():
        foo()
        bar()
        baz()
And you can later call somename in later functions.

Here's what I was thinking when I wrote the "incorrect assumption" line. But I think many other interpretations are also valid.

You can think of

    sum([x*x for x in range(10)])
as "desugaring" to
    sum(list_comp(lambda(x, quote(multiply(x, x))), range(10)))
which looks like Lisp if you move every open parens one token to the left and remove the commas
    (sum (list_comp (lambda x (quote (multiply x x))) (range 10)))
To evaluate this, parameters are first recursively evaluated (in order) and then the function is called on the outer value. Let's ignore the lambda for the moment.
    (sum (list_comp quoted_inner_func (range 10)))
results in the following function calls are made at execution time _in this order_
    quoted_inner_func 10 range list_comp sum
Normally, you'd have to pass the correct parameter to each function. However, in Forth, we use a global parameter stack so provided all the functions respect their inputs and output, running the above body would provide the desired result on the parameter stack!

I don't have that much to add to the other replies here. I used to never use pdb in Python for many years (not knowing or forgotten it exists) and doing perfectly well. Then I started using post-mortem debugging and finding/fixing bugs got so much faster. Basically, I'm using the computer to do the simulations I previously ran in my head (by evaluating things here and there). But that's just one point of data.

Another debugger I now recommend people (in general) to look at is Squeak Smalltalk's debugger which allows a form of "top down" programming where undefined functions are called and filled in at runtime. The debugger/editor in this post borrows a little bit from that idea in spirit. Part 3 in particular shows how it can be used even when they are no bugs.

About being off-topic, well, no-one has made another top level comment and by the time of your first post it was already 5 hours in. Some discussion is definitely better than none. So thank you for inciting that. If it burried some more on-topic discussion, that would be unfortunate though.

Thanks. The choice of gcc was somewhat arbitrary so clang could work too. I actually fiddle a bit with lldb before this.

From your description, it sounds like I'd really ought to removing optimizations (with -O0 from what's suggested here).

For variables, local variables can be optimized out (something I recall seeing in gdb without -O0) but all global variables are still kept, right? (At least, the ELF has names and addresses.)

Thanks for the clarification. I never really know how much (accuracy) to put in a post. I'm assuming that part of it is what turned some folk away from learning complexity in the first place. (I have intuition but not a firm grasp of what turns people away either.)

The implicit default of "worst-case running time" (in the context of algorithm) may actually be the source of errors like the one made in the video. For what its worth, I actually think the lim sup definition is easier because its fewer things to memorize.

This is a good video but it gets the definition of omega and theta wrong, or at least their definition is very non-standard.

By default, O, omega and theta all refer to the worst case running time.

    - O(n) means worst_case_run_time <= C n
    - omega(n) means worst_case_run_time >= C n
    - theta(n) means C1 n <= worst_case_run_time <= C2 n
If you want to talk about something other than worst case, you usually just say it in words, like "the average complexity of this algorithm is O(n^2)".

(I can't remember when someone has ever looked at "best case".)

To rephrase

- "This algorithm is O(something)" means I have an upper bound on the running time on the worst inputs.

- "This algorithm is omega(something)" means I have a lower bound on the running time on the worst inputs.

- "This algorithm is theta(something)" means I have both an upper and lower bound on the running time on the worst inputs (that differ only by a constant multiple.

In particular, a lower bound does not mean "best case" (they aren't even in the same part of the sentence).

(There's also some other detail from the video like the bit representation of string length that needs a footnote but that's much less important, I think.)

I was previous linked to your Cells library because I'm trying to do something very similar [1]. Matrix seem to be in a similar vein (at least from the sample usage here).

Could you tell me if any of your libraries allow live editing of formulas? (Adding new ones, editing existing ones and deleting some.) If yes, do the new input (cI) need to be declare manually?

I've also seen these slides [2] in your repo. Can you say a bit about the solution to "The Glitch"? As you've noted there, topological sort isn't enough if there are conditionals.

I also couldn't tell what "Glitches and RoboCup Sim" was about from just the PDF.

Finally, when using a cells-like library, do you get "discontinuous jumps" when cells are no longer suitable (or the network becomes too complicated) and fallback to imperative callbacks? Or do you manage to used cells for everything?

[1] https://github.com/asrp/persistent_doc https://github.com/asrp/guitktk

[2] https://github.com/kennytilton/cells/blob/master/Lisp-NYC-20...

What is your use case for this?

I'm using it in guitktk [1]

Also, you say you have "spreadsheet formulas", but is it possible to apply a formula to a range of nodes, like you can in an actual spreadsheet?

You can apply to all children of a node like len(`foo). In a different version, if you wanted to do that for an entire subtree, you could have something like len(list((`foo).dfs()))

[1] https://blog.asrpo.com/gui_toolkit

Here's some extra information about what running each piece gets you. Assuming flpc.c is already compiled,

./flpc with no arguments drops into the FlpcForth REPL. But absolutely nothing is defined in that case, of course. You can try to paste in some source from files in precompiled/*.

The sample sources in precompiled/ are all for parsing (some of) its own source and/or generating its own source. Pass one of them as argument to ./flpc and it will run and drop into the REPL at the end.

In the REPL, "ps" is probably the most useful command to get information about current state.

There’s no FlpcPython REPL yet (it can parse FlpcPython but doesn’t know what to do with the resulting AST). So right now, to write FlpcPython, I suggest writing to a file, say foo.flpc, and running

    python compiler.py lib/stage{0,1{a,c,d},3{a,b}}.flpc foo.flpc > foo.f
    ./flpc foo.f
This uses the external Python compiler which will eventually be removed.