This commit is contained in:
nocturn9x 2022-01-06 16:53:50 +01:00
commit fec05a1291
1 changed files with 2 additions and 2 deletions

View File

@ -21,13 +21,13 @@ Clox is fine as a toy language, but its limitations are a little too strict for
- Classes support multiple inheritance. Methods are resolved starting from the first parent until the last one, in the order in which they are listed in the class declaration
- Proper exception support, with `try`/`except` handlers (with `finally` and `else` support as well), and obviously `raise`, has been added. The VM will keep a list of active exception hooks which are created with the `BeginTry` instruction. When an exception is raised, the VM will traverse this list backwards to find any matching exception hook according to a set of rules (i.e. taking superclasses and multiple catching into account) and execute their associated code if they do. If the VM can't find any matching handler and it gets to the top of the list, it then writes the message to stderr and exits. Some niceties like `except (exc1, exc2, exc2)` and `exc SomeExc as excName` (`except (SomeExc, Exc2) as name` is also valid) have also been blatantly stolen from python's exception handling system
- JAPL will have an iterator protocol, hence a `foreach` loop has been added to iterate over collections and sequence types
- Closures are now different from regular functions. JAPL will compile a function to be a closure only if it makes use of values that are outside its own scope. In this case, as the book rightly suggests: _"[...] The next easiest approach, then, would be to take any local variable that gets closed over and have it always live on the heap. When the local variable declaration in the surrounding function is executed, the VM would allocate memory for it dynamically. That way it could live as long as needed."_
- Closures are now different from regular functions. JAPL will compile a function to be a closure only if it makes use of stack variables that are outside its own scope (which means that most functions _won't_ be closures). In this case, as the book rightly suggests: _"[...] The next easiest approach, then, would be to take any local variable that gets closed over and have it always live on the heap. When the local variable declaration in the surrounding function is executed, the VM would allocate memory for it dynamically. That way it could live as long as needed."_
- Builtin collections similar to Python's have been added: lists, tuples, sets and dictionaries. A notable quirk though is that since brackets are used both for set and dictionary literals and for block statements, and due to the latter's precedence being higher, a bare `{};` creates an empty block scope and leaves a dangling semicolon instead of creating a dictionary object and discarding it immediately. Set and dictionary literals can only be defined where an expression is expected (which is fine, because they _are_ expressions), so something like `var a = {1, 2, 3};` is perfectly fine and not ambiguous because the parser only expects expression as values for variable declarations, and block statements are, well, statements
- JAPL supports `yield` statements and expressions, allowing on-the-fly value generation for improved iteration performance and highly-efficient O(1) algorithms, the most basic being infinite counters
- Some handy operators have been added: `is` (and its opppsite `isnot`) checks if two objects refer to the same value, `A of B` returns `true` if A is a subclass of B and `A as B` will call `B(A)` allowing for simple casting, like `"55" as Integer;`, which pushes the Integer `55` onto the stack
- The keyword `this` has been removed and instead JAPL passes the instance's value as the first argument to a bound method (commonly named `self`)
- JAPL's type system is much more flexible than lox's: everything is an object, including builtins, hence no more `only instances have properties` errors, because all entities in JAPL are essentially an instance of some type, all the way up to `BaseObject` which is the base of itself and is merely an implementation detail
- When optimizations are enabled (i.e. always unless explicitly disabled, if you like slow code I guess) the compiler will emit optimized instructions for a few edge cases. For example, it will emit a specialized `PopN` instruction that pops n values off the stack when compiling local scopes because those usually pop a lot of values when discarding local variables. Another small optimization is used when compiling if statements, which will cause the compiler to emit an `JumpIfFalsePop` instruction instead of separate `JumpIfFalse` and a `Pop` instructions. JAPL will also reuse existing entries in the constant table by default, so don't be surprised if two separate `LoadConstant` instructions point to the same constant index: it just means the compiler had already seen that constant and just reused it
- When optimizations are enabled (i.e. always unless explicitly disabled, if you like slow code I guess) the compiler will emit optimized instructions for a few edge cases. For example, it will emit a specialized `PopN` instruction that pops n values off the stack when compiling local scopes because those usually pop a lot of values when discarding local variables. Another small optimization is used when compiling if statements, which will cause the compiler to emit a `JumpIfFalsePop` instruction instead of separate `JumpIfFalse` and a `Pop` instructions. JAPL will also reuse existing entries in the constant table by default, so don't be surprised if two separate `LoadConstant` instructions point to the same constant index: it just means the compiler had already seen that constant and just reused it
- JAPL now has long jump instructions, which use 24-bit operands instead of 16-bit ones to allow to jump even further if one wants to jump more than 65535 instructions into the code (which is not unlikely in real-world scenarios)
- Private attributes are not very useful without an import system and modules, a topic that clox doesn't touch at all (probably because it's not that interesting implementation-wise and is only a recipe for trouble in a beginner's book), so JAPL fixes that by having a pretty Python-esque import system with things like `import name;`, `import module.submodule;`, `from module import someComponent;`, `import someModule as someName;`, `import a, b, c;` and basically all variations of the above. Imports in JAPL are "proper", i.e. they don't just copy-paste code like C's `#include` or some other lox implementations: they create a separate namespace and populate it with whatever the imported module decides to export (i.e. all declarations marked as `public`)
- Inline comments in JAPL start with an hashtag and that's the only kind of comment that exists. This is because `//` is used as the binary operator for integer (aka floor) division