6 ms·
Forth is still interpreted. Forth's genius is that it's interpreter is so tiny that it fits as a runtime.
by terminalcommand 4y ago
Forth is still interpreted. Forth's genius is that it's interpreter is so tiny that it fits as a runtime.
- dragontamer 4y agoThere are many lisps that are not interpreted, but are instead compiled. The beauty of the lisp language is that it doesn't care about interpreted vs compiled vs whatever. Its just the true elegance of "everything is a (lisp) list" (which is probably the wrong word. The truth of the matter is that "everything is a graph" and lisp-lists are really just a universal building block that can represent arbitrary graphs). The important gadgets are: 1. Universal garbage collection (allowing you to ignore free() issues and simplify code) 2. Pointer to (two pointers). That is, car-and-cdr. That's it. Everything else flows from these two things.
- terminalcommand 4y agoThat is true, but some sort of a runtime bordering on being an interpreter needs to exist doesn't it. Because my brain cannot comprehend how you can do metaprogramming with macros etc. with static code. My opinion was that the compiled lisps precompile as much as possible (calculations etc.) into assembly and leave bits they can't compile intact, that's what make them fast.
- dragontamer 4y agoMacros just is code that transform a (list) data structure into (another different list), and then feeds (another different list) into the compiler (rather than the original (list) going into the compiler: how things happen without macros). You can see how these things happen in like, Chicken Scheme (R5RS Scheme to C compiler).
- terminalcommand 4y agoI want to look at it to understand. But my guess is that this happens at runtime, doesn't it. If this happens at runtime, this means runtime evaluates the macro, lisp function is generated, generated lisp function is compiled and used.
- dragontamer 4y ago> But my guess is that this happens at runtime, doesn't it Macros would be evaluated at compile-time (or arguably _right before_ compile time).
- terminalcommand 4y agoHow can you evaluate a complex macro at compile-time if you are doing AoT compilation? Lisp macros are not like C macros where simple substitution would suffice, they need to be evaluated. I found my exact question on stackoverflow: https://stackoverflow.com/questions/7072980/how-do-you-compile-macros-in-a-lisp-compiler https://stackoverflow.com/questions/7072980/how-do-you-compi... The answers on SO are still not clear to me, some people say the macros can be evaluated at compile time, some say that the runtime needs to include some kind of interpreted to run macros. Some say incremental compilation is key to understand how macros get evaluated at compile time. I will run experiments on this.
- erik_seaberg 4y agoA compiled Lisp program might not be able to eval a list or load a source file, for the same reason a packaged jar generally won’t contain a copy of the Java compiler. I think eval-when controls which macro definitions (if any) need to be available at runtime, rather than being expanded away while compiling functions.
- terminalcommand 4y agoFrom my conversation with lispm above, I discovered that each binary SBCL produces includes the compiler as well. This is what is meant by incremental compilation I get it. When I first heard the term incremental compilation, I thought of a process like going through the ast couple of times and evaluating expressions. This wasn't what it meant. Now it makes sense. You include the lisp compiler in each binary. For parts you cannot evaluate at compile time, you compile at runtime, just like a JIT.
- 4y ago
- pyinstallwoes 4y agoThis is wrong. Because of the way pointers are used, Forth is both compiled and not compiled. > After the fetch and store operations are redefined for the code space, the compiler, assembler, etc. are recompiled using the new definitions of fetch and store. This effectively reuses all the code of the compiler and interpreter. Then, the Forth system's code is compiled, but this version is stored in the buffer. The buffer in memory is written to disk, and ways are provided to load it temporarily into memory for testing. When the new version appears to work, it is written over the previous version.
- terminalcommand 4y agoWhat is the difference between a runtime and an interpreter? I still believe forth is interpreted. Forth uses a small set of functions in assembly as its runtime/interpeter. Your quote looks like its about bootstrapping the Forth ecosystem. After writing fetch and store operations in native assembly, you can bootstrap the forth system that is logical. This is the way Chuck Moore first used Forth anyways. He had no interpreter/compiler back than, he wrote some functions to aid him circumvent assembly and make his code portable. It is genius, but I think it's still interpreted. You can compile python to bytecode and run it in python's vm, does this mean your python code is compiled. IMHO no, it is compiled for the python vm and python vm interprets it. Same thing applies to Forth. Forth is interpreted, even if you compile and package it in a standalone executable binary.
- Jtsummers 4y agoAn interpreter interprets, typically either a byte code or textual representation version of a program or some other intermediate form (for instance, parsing to an AST and then evaluating/interpreting that versus line-by-line or token-by-token). A runtime could include an interpreter, but doesn't have to. Go includes its own runtime which handles things like scheduling goroutines and garbage collection, but it is not interpreted nor does it include an interpreter.
- terminalcommand 4y ago
- dreamcompiler 4y agoForth can be interpreted but it can also be compiled in at least two or three different ways. Forth is unique in that its interpreter can look (and perform) a lot like compiled code. Let's say you have a list of assembly language function names FOO, BAR, BAZ, etc. Each name stands for the starting memory address of that function. Each function ends with a standard RET instruction. At address PROGRAM, you store the list of 64-bit function addresses. Now a "Forth Interpreter" is just JSR-INDIRECT RP++ REPEAT where beforehand you've stashed PROGRAM in register RP. Most assemblers don't provide an indirect JSR instruction but it's easy to write a macro that serves that purpose. Is this an interpreter? The only way it differs from true compiled code is that the function calls are indirect, so it's almost as fast as regular function calls. What if you unroll the thing so the code looks like JSR FOO JSR BAR JSR BAZ ... Now all the function calls are direct. And as a bonus, all the individual functions themselves don't have to do anything more special than directly call functions. It's turtles all the way down. I've glossed over some issues like how to pass values and how many stacks you need and proactive tail-calling, but that's the gist of it. The whole "interpreter" just vanishes in a puff of smoke.
- terminalcommand 4y agoThank you, I didn't know about the jsr instruction. I always imagined forth does this somehow with jmps. Another main difference from true compiled code is that your entire program consists of subroutine calls. There are no inline instructions and a main function. I've got a lot of reading to do on this. "Indirect jsr", I'll experiment with this. Okay I'm convinced, forth is not interpreted when it's compiled to threaded code :). There is no main loop that reads instructions and dispatches them dynamically, the program flow is linear with subroutine calls stacked under each other.
- dreamcompiler 4y agoExactly. And there are many other inline instructions (e.g. for doing arithmetic etc) but I didn't show them here. Any instruction the CPU supports can be present in a Forth program. In a compiled Forth, words can be specified as a list of calls to other Forth words or as raw assembly instructions, or both.