-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add crystal-expanded & crystal-normalized emit options #5821
Comments
This feature would be very useful, use-case here: crystal-lang-tools/vscode-crystal-lang#4 |
I just got another idea, I think printing typed code would be very useful, by example: Original code: FOO_METHODS = %w(foo bar)
{% for method in FOO_METHODS %}
def {{method.id}}(baz)
end
{% end %}
foo("foo")
bar(42) Macros expanded: def foo(baz)
end
def bar(baz)
end
foo("foo")
bar(42) Typed code: def foo(baz : String) : Nil
end
def bar(baz : Int32) : Nil
end
foo("foo")
bar(42) This can be very useful for editor extensions and tools 👍 Maybe a flag like |
Another use-case is tracking macro errors, by example, I'm implementing new error pages for amber framework, and sometimes I can't see the code snippet for an error because the original file is a macro. If I can save the expanded macro code somewhere, then, I think I could show better error snippets: |
The compiler shows macro source on errors... |
@asterite Yeah, but would be nice to save expanded macros to a file, so I can read them and do nice things like debugging the expanded macro. Another suggestion is to save intermediate code with inferred types to a file (a.k.a. By example, currently to get types and do some type debugging, I need to execute So, WDYT? 😅 |
@asterite About the new amber error pages, I guess I need to improve them 😉 Currently I'm doing this:
So, I can't see expanded macro for a macro error because the location doesn't exist. 😅 |
If the error is inside a macro, you can show it in a popup. But you usually need the whole trace. Maybe someone can implement the typed output, but what's the input for that? The problem with Crystal is that to type code you need a main file, and it can be a program, a spec file, many spec files, etc. Do you specify a single main file in your editor? Will you cache the info per main file? If you are viewing some random file, which main file will you use? I said it lately a couple of times: if you want the feature of every other statically compiled language, Crystal should be compiled modularly, that is, compile a file independently from other files. But that will never happen. So I personally don't think Crystal is a language for which a good IDE will exist. But if you manage to do it, than it'll be quite a feat :-) |
@asterite Thank your for your response!
Yeah, that's why I'm doing some refactoring in Also, I added the Show raw message option, so, developers can always read the full raw error message.
Hehe, In fact, I have a https://github.com/crystal-lang-tools/vscode-crystal-lang/wiki/Settings#mainfile
No problem 😅 I still think crystal community can do some nice things to make scry and other crystal tools better and faster 💪 For now, I just need something like @bew suggested, during compilation before code generation phase, allow us to emit:
We already have emit flag. Perhaps we can add more options to it, like
WDYT? |
Could you show what output you expect from each, and how are you going to process it? |
Ok 👍 , @asterite, Given a simple file FOO_METHODS = %w(foo bar)
{% for method in FOO_METHODS %}
def {{method.id}}(baz)
p baz
end
{% end %}
foo("foo")
foo(3.141516)
bar(42) and compiled with the following command:
The compiler can generate these files:
First # ...
# ...A bunch of code above added by crystal compiler
def foo(baz)
p baz
end
def bar(baz)
p baz
end
foo("foo")
foo(3.141516)
bar(42)
# A bunch of code below added by crystal...
# ... and # ...
# ...A bunch of code above added by crystal compiler
def foo(baz)
PrettyPrint.format(baz, STDOUT, 79)
STDOUT.puts
baz
end
def bar(baz)
PrettyPrint.format(baz, STDOUT, 79)
STDOUT.puts
baz
end
foo("foo")
foo(3.141516)
bar(42)
# A bunch of code below added by crystal...
# ... and finally, # ...
# ...A bunch of code above added by crystal compiler
def foo(baz : String) : String
PrettyPrint.format(baz, STDOUT, 79)
STDOUT.puts
baz
end
def foo(baz : Float64) : Float64
PrettyPrint.format(baz, STDOUT, 79)
STDOUT.puts
baz
end
def bar(baz : Int32) : Int32
PrettyPrint.format(baz, STDOUT, 79)
STDOUT.puts
baz
end
foo("foo")
foo(3.141516)
bar(42)
# A bunch of code below added by crystal...
# ... For a project with many files I think we can merge it in one big file depending on what file is being compiled, for example, for the following project:
we can do something like:
and get something like:
or If this is a bit noise then I think we can use a
Maybe we can start saving expanded macros to a file using Another option for generating And as I said before. I know this intermediate code can be very dirty/noise, although, IMO this is still very useful for debugging and other nice things, like improving analyzing and auto-completion for a project. And finally, I know this intermediate "generated" code can change every time I do some code change, no problem with that, because still would be very useful until I edit some file 😉 So, WDYT? 😅 |
Not sure. As I said many times, the compiler doesn't work file-by-file, it slurps all the files and works on that at once. So generating instantiated code per file maybe could work, but it's not trivial. And then there's macros, which expanded don't belong to any file. And then, not sure why you would need the expansions... I mean, they are not typed, it seems they just have the method argument and return types. Plus it's a lot of work (今時間がない). I think anyone could grab the compiler's source and do it if they wanted, it's similar to other late-passes the compiler has like the type hierarchy. |
Perhaps we can output a big file with all the stuff (exanded macros, normalized and typed code) 😄
debugging generated source code is is the main use-case, see: crystal-lang-tools/vscode-crystal-lang#4
Don't worry, no problem about that, I'm glad you read my comments and sent us nice responses ✨ 🎉 I guess the crystal community (or even myself) could invest some time and understand the compiler enough to implement such features 😉 |
Oh, I just found LiteralExpander, so, now I understand the |
Both the normalizer and the literal expander operate over AST nodes and should not require semantic analysis, so they could be options to My use case is to add this and |
Also the environment variables |
From time to time I want to see the generated Crystal code at several specific stages of the Crystal compiler:
For a start, I think it would be nice to have
--emit crystal-expanded
: dump the crystal code with all macros expanded--emit crystal-normalized
: dump the crystal code after the normalization pass (which transforms all syntactic sugar to basic control structures, likeunless
->if
,until
->while
, etc..).Also at this stage, the literals would already be expanded (e.g: string interpolation to
String.build ...
)This dump would be quite hard to read I think, but would still be Crystal code (raw llvm IR is also nice, but it's another lower level)
(or with
crystal
=>cr
to reduce size of flags)Those options could be used with
--no-codegen
, and the resulting files be compiled again into binary if needed (as they are valid Crystal code).It's just ideas on how to inspect what's going on in the compiler, there are many things I didn't talked about that would be cool (e.g: after the type inference pass, or simply the list of symbols/unique string literals, ..)
Also in the long run it could help creating new stdlib (see what is needed), help during ports to new platforms (see what's used?), and in a way, inspecting into the compiler (via emit or tools, or compiler plugins) could allow to make #921 possible? (I think I'm going a bit too far for this issue, but I'd love all those things!!)
The text was updated successfully, but these errors were encountered: