Comments (6)
In addition to scoping variables, we also need to scope macros and type definitions. This will be interesting.
from passerine.
This is happening in the big-refactor
branch.
from passerine.
Regarding macros I just saw Unseemly and thought it might be a really good place to take some inspiration from as it offers "typed macros" and seeing it in practice kind of sparked my interest in that idea (syntax aside
from passerine.
Another way to do types macros is to leverage a two-level lambda calculus with a kind-based macro system. I'll give Unseemly a look, I wonder if its using a similar system under the hood.
Update: Holy cow, Unseemly is pretty cool! I'm a huge fan of how the language is bootstrapped from first principles, the examples are pretty incredible. I like the way it does syntactic extension:
extend_syntax
Expr ::=also forall T . '{
[
lit ,{ DefaultToken }, = 'if'
cond := ( ,{ Expr<Bool> }, )
lit ,{ DefaultToken }, = 'then'
then_e := ( ,{ Expr<T> }, )
lit ,{ DefaultToken }, = 'else'
else_e := ( ,{ Expr<T> }, )
]
}' conditional -> .{
'[Expr | match ,[cond], {
+[True]+ => ,[then_e],
+[False]+ => ,[else_e], } ]' }. ;
in
...
There's this really nice mapping between patterns and expressions. I'd love to figure out a better syntax for this idea, and explore the ergonomics a bit.
The link you shared kinda started a rabbit hole, here are some links I've found interesting:
- http://composition.al/blog/2017/07/31/my-first-fifteen-compilers/
- https://en.wikipedia.org/wiki/Catamorphism
- https://github.com/nanopass/
- https://www.youtube.com/watch?v=Os7FE3J-U5Q
- https://www.nand2tetris.org/
- https://www.cis.upenn.edu/~bcpierce/tapl/
- https://github.com/paulstansifer/unseemly/blob/master/src/examples/worked_example.unseemly
I highly recommend the youtube video.
from passerine.
I propose to move expand
and infer
before desugar
.
There are two benefits gained by moving infer and expand up in the pipeline.
First and foremost, errors derived from macro expansion or ill-typed expressions can be reported in the same way the user sees them (that is, with the original source location and specific error messages).
Secondly, the compilation can fail without doing further passes if the program is syntactically incorrect or ill-typed.
This is how GHC (Haskell compiler) does it. [reference]
Probably the most important phase in the frontend is the type checker, which is located at compiler/GHC/Tc/. GHC type checks programs in their original Haskell form before the desugarer converts them into Core code. This complicates the type checker as it has to handle the much more verbose Haskell AST, but it improves error messages, as those message are based on the same structure that the user sees.
PS: I am not sure where to place hoist
from passerine.
First and foremost, errors derived from macro expansion or ill-typed expressions can be reported in the same way the user sees them (that is, with the original source location and specific error messages).
Passerine reports the original location of all error messages because it uses a span-based error reporting system. In essence, every artifact produced by the compiler keeps track of set of spans of source code used to produce it. This allows for accurate error reporting everywhere in the compiler, including at runtime.
Secondly, the compilation can fail without doing further passes if the program is syntactically incorrect or ill-typed.
This, however, is a good thing! My only concern is that operating on the parse tree (for type inference, specifically) may add extra redundancy to those passes. For example, there are a few constructs like:
3 . double . print
-- the same as
print (double 3)
If type inference were done before desugaring, we'd have to handle both these cases separately, even they both are just different ways to write function calls.
PS: I am not sure where to place
hoist
Hoist is a fairly simple step that consistently renames all variables that refer to the same value to the same name, and builds a set of which variables are captured by each function/scope. This step needs to happen before infer
, because type inference expects 'normalized' values, so to speak. Macro expansion doesn't need normalized values, but moving infer
before expansion makes it easier to manage hygiene - instead of mangling symbols, we simply have to introduce a new unique symbol.
I guess the main idea is that desugar does not convert the parse tree to a small core language, it just reduces the tree to a normalized representation that is easier to work with in later passes. Some compilers would include the equivalent of passerine's desugar
step in the parser - doing this and removing the desugar step might be the best course of action.
from passerine.
Related Issues (20)
- More Rust idioms HOT 7
- Efficient parsing of UTF-8 HOT 3
- Intern strings in the Lexer HOT 3
- Commit Convention? HOT 1
- cargo run doesn't pretty-print errors
- VM should be Send HOT 1
- Small error in the README? HOT 9
- modulo vs remainder HOT 3
- Some questions regarding suitability for embedded systems. HOT 8
- Decimal floating point HOT 11
- Tracking Issue: Vaporization Checklist HOT 2
- Github Sponsorship HOT 4
- A few Exciting Announcements HOT 4
- Add `match` expressions HOT 2
- The installation shell command on Passerine's website doesn't work. HOT 10
- Document hoisting semantics HOT 1
- Document effect system
- Alternative Backends (Minivm, Wasm, LLVM) HOT 3
- Change README code example
- Remaining tasks from `big-refactor`
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from passerine.