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

Idea: Node Pattern sample-based generator #81

Closed
pirj opened this issue Jul 24, 2020 · 19 comments
Closed

Idea: Node Pattern sample-based generator #81

pirj opened this issue Jul 24, 2020 · 19 comments

Comments

@pirj
Copy link
Member

pirj commented Jul 24, 2020

Again, not a feature request, more like to start the discussion. Can't think of a better place for this discussion again.

As a prolific cop author, I personally find Node Pattern hard to write.

Please speak up if you use some tools, that visually indicate that a node pattern you're trying to craft matches a given set of source excerpts. I can think of this as:

(send {nil? (const :Greeter)} :hello str)

§ hello 'Dear'
§ Greeter.hello ''
§ hello 'world'
§ hello 1
§ hello

Think visual regexp matcher we got used to already.

I recall there are Regexp generators that build a minimal Regexp to match a given set of strings (and always returns .* haha).
Wondering if building a similar tool to automatically build node pattern given some code samples it should match, and some that it should not.

Unfortunately, I'm not aware of the principles of building such a tool. Do you have an idea of where to start?

@marcandre
Copy link
Contributor

Yes, I want to have an easy way to check a node pattern. I also was thinking of showing the compiled code (maybe side by side with the node pattern) and have it show in color for executed / not executed, to make it easier to understand non matches. I was even thinking of putting an executable in the gem (maybe rast, or else rubocop-ast)

@jonatas
Copy link
Contributor

jonatas commented Jul 27, 2020

I have worked in something like this on fast: https://jonatas.github.io/fast/similarity_tutorial/

I made expression_from part of the gem and you can also use fast -s to search by similarity but it's not generating all the inner possibilities only anonymizing all identifiers from the code sample.

Maybe instead of generating a very generic expression we can generate several about the same code.

I also implemented one very smart shortcut to find "any reference" about some wording:

# Search all references about some word
Fast.shortcut(:ref) do
  Kernel.class_eval do
    def matches_args? identifier
      search = ARGV.last
      regex = Regexp.new(search, Regexp::IGNORECASE)
      case identifier
      when Symbol, String
        regex.match?(identifier) || identifier.to_s.include?(search)
      when Parser::AST::Node
        regex.match?(identifier.to_sexp)
      end
    end
  end
  pattern = <<~FAST
    {
      ({class def sym str} #matches_args? ...)
      ({const send} {nil? _} #matches_args? ...)
    }
  FAST
  Fast::Cli.run!([pattern, '.', '--parallel'])
end

@pirj
Copy link
Member Author

pirj commented Jul 27, 2020

That's already something to start with.
Would you like to hack on it @jonatas ? I guess writing specs for some pretty basic cases would really spark the fire.

@jonatas
Copy link
Contributor

jonatas commented Jul 27, 2020

That's already something to start with.

@pirj you mean the example of the shortcut I shared "# Search all references about some word" or from the expression_from?

I'd love to contribute to the project, especially to introduce a basic CLI application to let more people know about the node pattern facilities :)

@pirj
Copy link
Member Author

pirj commented Jul 27, 2020

I meant RSpec examples of how a sample-based node pattern generator would work:

describe 'NP::G' do
  it 'generates' do
    expect(
      generate('hello.hello(1) { }', 'foo.foo(2) { }')
    ).to eq '(block (send (send nil? _) int) _ nil?)'
  end
end

or how a visual matcher will highlight matched and unmatched branches of the NP given a code sample:

describe 'NP::V' do
  it 'highlights' do
    expect(
      highlight_unmatched('(block (send (send nil? _) int) nil?)', 'hello.hello(1) { 2 }')
    ).to eq '(block (send (send nil? _) int) _ *nil?*)'
  end
end

what you like most. The latter is a slightly different feature, it just happened to have born in this ticket.

@jonatas
Copy link
Contributor

jonatas commented Aug 17, 2020

I love the idea of highlight_unmatched especially when we need to merge node patterns and add extra edge cases.

@marcandre
Copy link
Contributor

I love the idea of highlight_unmatched especially when we need to merge node patterns and add extra edge cases.

Screen Shot 2020-08-25 at 12 12 13 AM

green = match, red = no match, yellow = not reached, light blue = not something that can be matched.

@pirj
Copy link
Member Author

pirj commented Aug 25, 2020

It looks amazing!

@jonatas
Copy link
Contributor

jonatas commented Aug 27, 2020

Wow! that is very cool @marcandre! Looking ahead to test it locally :)

I was thinking about we have like a web interface like rubular.com that we could test the patterns against some code. I know that @baweaver started something on https://github.com/baweaver/ast_explorer maybe we could join the ideas to create a good playground for it :)

@marcandre
Copy link
Contributor

@jonatas Opened #105 , check it out! Yes, I'd like something like rubular! Not sure about UI... TTY? Tk? web (can parser run in Opal?)?

@jonatas
Copy link
Contributor

jonatas commented Sep 4, 2020

@marcandre that is a good point I was watching a live coding about opal right now on Ruby Kaigi with @youchan. Looks like it transpile to create the code. I think if opal compiles over opal it's possible :)´

The presentation was great and I learned a lot about how to create an AST rewriter in opal.

I think we can start with the server version and see if we can evolve to use Opal in the FE as a second version due to the challenge of transpile all the dependencies to the FE. I'm not sure what is the state of opal actually.

@marcandre
Copy link
Contributor

I put an initial version of a live debugger at https://nodepattern.herokuapp.com/

@jonatas
Copy link
Contributor

jonatas commented Sep 23, 2020

@marcandre is a coding machine 😮 👏 ❤️

I love to use it! very cool!

@baweaver
Copy link

Great job on the UI there. I'd always considered doing it through Lambda or just a Rails server because ruby > AST isn't technically executing code

@marcandre
Copy link
Contributor

marcandre commented Sep 23, 2020

Great job on the UI there.

Thanks!

I'd always considered doing it through Lambda or just a Rails server because ruby > AST isn't technically executing code

Right, but my app does match a Ruby AST against a node pattern, and NodePatterns are very permissive... #eval("insert ruby code") is a valid pattern! I disabled that on the server though...

My plan is to have a local version that can also do local searches.

@baweaver
Copy link

If you ever want to collab on any of that feel free to shoot me a message later. Interesting note on eval there.

@baweaver
Copy link

Over time I'd love to evolve it into something like Regexr but that's a beast of a task:

https://regexr.com/

@marcandre
Copy link
Contributor

Thanks for the sponsorship @baweaver 🎉

Over time I'd love to evolve it into something like Regexr but that's a beast of a task:

https://regexr.com/

Which features in particular do you think are particularly worthwhile?
I created an issue with what I'm thinking about.

@pirj
Copy link
Member Author

pirj commented Nov 25, 2021

The idea may be interesting, but probably not worth the effort. I'll close for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants