Skip to content
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 "expand" command to Crystal tools #3732

Merged

Conversation

makenowjust
Copy link
Contributor

crystal tool expand expands macro for given location.

See asciinemas:

asciicast

asciicast

@bcardiff
Copy link
Member

Nice. I would like to see some specs. At least some basic one that will ensure some scenarios wont fail: nested classes with modules, top level expansions.

I bet you wish there was some kind of index per file/line so you don't need to lookup the whole hierarchy of types. For now we are good, but in the future we might need to enhance de compiler with some of those indices i think.

@bcardiff
Copy link
Member

Regarding the specs one that will result in multiple expansions should be interesting.

And json support with an array of the different levels of expansions. So we can zoom in/out. WDYT?

@makenowjust
Copy link
Contributor Author

makenowjust commented Dec 19, 2016

@bcardiff

And json support with an array of the different levels of expansions. So we can zoom in/out. WDYT?

When macros are nested, final expansion result may be too complex. In this case it is useful.

Regarding the specs one that will result in multiple expansions should be interesting.

If there is a macro in a method definition and the method definition has multiple type signatures, the macro has multiple expansion. For example:

macro foo
  puts "hello"
end

def bar(x)
  foo
end

bar 10
bar "str"

./bin/crystal tool expand -c foo.cr:6:3 foo.cr yields:

2 expansions found
expansion 1:
   foo

~> puts("hello")

expansion 2:
   foo

~> puts("hello")


I'll try to write specs. But, it is midnight in Japan, I am going to bed. Good night!

@makenowjust
Copy link
Contributor Author

tiny .vimrc for demos:

command! -buffer -nargs=0 CrystalExpand echo s:crystal_expand(expand('%'), getpos('.'))

function! s:crystal_expand(file, pos)
  let l:cmd = printf('./bin/crystal tool expand --no-color -c %s:%d:%d %s', a:file, a:pos[1], a:pos[2], a:file)
  return system(l:cmd)
endfunction

@RX14
Copy link
Contributor

RX14 commented Dec 19, 2016

Could we run the output through crystal format? Some of the generated macro code isn't particularly beautiful.

@asterite
Copy link
Member

@makenowjust Thank you for this! I think this tool can be useful, I just need to think what's the criteria to merge new tools.

Regardless of that, I find it really cool that you are hacking the compiler :-)

# in which the (start) location might be in one file and the end location in another.
@target_location.between?(loc_start, loc_end) || loc_start.filename != loc_end.filename
@target_location.between?(loc_start, loc_end) || (!@in_defs && loc_start.filename != loc_end.filename)
# When node generated by macro and macro has yielding block, it also makes `loc_start.filename != loc_end.filename` true.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Can a spec be added also for this case? The specs are at ./spec/compiler/crystal/tools/context_spec.cr and should be easy to add a sample. There are some unicode chars that act as user cursor so you can easily add specs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is just ad-hoc solution. The radical problem is yield in macro expansion sets its location, but doesn't reset it. Now, I have an idea for it. I'll make a new pull request to solve it and I am going to revert this commit after this pull request is merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fixed in #3751. I'll revert and rebase.

@makenowjust
Copy link
Contributor Author

@bcardiff Added specs just now!

@makenowjust makenowjust force-pushed the feature/crystal-tool/expand branch 2 times, most recently from 5abf7a4 to 9dfcf86 Compare December 27, 2016 02:04
@bcardiff bcardiff self-assigned this Jan 10, 2017
Copy link
Member

@bcardiff bcardiff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@makenowjust sorry for the delay. I've made some comments. Some are request for changes.

Would you be able to apply them and rebase this PR?

{% end %}
CODE

original = <<-CODE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many specs of assert_expand_result_simple could be shortened with a variation: assert_expand_single ? That could strip the line to get the original. I count 11 occurrences that could be changed with this. WDYT?

@@ -393,6 +397,11 @@ class Crystal::Command
end
end

if expand
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than adding an expand param to create_compiler and compile_no_codegen. I say to promote no_cleanup (default false) as param in this two methods.

And add no_cleanup and wants_doc to cursor_command.

The create_compiler needs hierarchy/cursor_command to know which command line options to parse. Since the expand is a cursor_command I think removing expand param make sense.

class ExpandTrace
JSON.mapping({
original: {type: String},
expandeds: {type: Array(String)},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think expandeds is a word. I would suggest expansions or progressive_expansions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I think expansions is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but we will get expansions.expansions. It is very wrong.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expanded is also used to address a plural quantity, so you can just s/expandeds/expanded :)

def self.ast_to_s(node)
s = String.build { |io| node.to_s(io, emit_doc: true) }
return s unless node.is_a?(MacroIf) || node.is_a?(MacroFor)
indent = node.location.not_nil!.column_number - 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case for changing the indentation is not stressed in the specs. If the following code that split/join the lines is removed all specs passed. Some indented if & for macros are needed in the specs.

Note: I don't fully follow why this is needed. Could the node.to_s output a nice indentation directly. This logic could be refactor later to the ToSVisitor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MacroIf and MacroFor have MacroBody, it is just sub-string of source text. For example:

begin
  {% if 1 == 1 %}
    hello
  {% end %}
end

MacroIf#to_s result of {% if 1 == 1 %}...{% end %} is:

{% if 1 == 1 %}
    hello
  {% end %}

So, re-indentation is needed. I'll add specs for it.

it "expands macro control {% if %}" do
code = <<-CODE
{% if 1 == 1 %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the other specs that use it is used in the middle of the code. Like: {% ‸if 1 == 1 %} in this case.
It is stripped before analyzing the code.

It could stay this way if you find it more comfortable, but it is a slight inconsistency.

"crystal tool expand" expands macros for given location.

https://asciinema.org/a/96590
@makenowjust
Copy link
Contributor Author

@bcardiff Updated.

@bcardiff bcardiff removed the pr:wip label Jan 13, 2017
@bcardiff bcardiff merged commit 6bd4bed into crystal-lang:master Jan 13, 2017
@bcardiff bcardiff added this to the Next milestone Jan 13, 2017
@bcardiff
Copy link
Member

🎉 🔨 🔧 Thanks again @makenowjust 🔧 🔨 🎉

@makenowjust makenowjust deleted the feature/crystal-tool/expand branch January 13, 2017 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants