Skip to content

Instantly share code, notes, and snippets.

@dander
Last active December 14, 2019 08:46
Show Gist options
  • Save dander/47d10aa43d04bfcd27080ba6f3cf04ba to your computer and use it in GitHub Desktop.
Save dander/47d10aa43d04bfcd27080ba6f3cf04ba to your computer and use it in GitHub Desktop.
Searches Red source files for a parse pattern
Red [
Title: "Find pattern"
Description: "Searches Red source files for a parse pattern"
]
allow: func [any][yes]
get-files: function [
"recursively collects the full file paths for all the files in the given directory"
dir [file!] /filter "only collect files where the predicate evaluates to true" pred [function!]
][
collect [
foreach file read dir [
file: rejoin [dir file]
either dir? file [
keep get-files/filter file any [:pred :allow]
][
if any [not filter pred file] [keep file]
]
]
]
]
ends-with: function [
"create a function that returns true for a sequence which ends with any of the given values"
values [block! string! file!]
][
function [sequence] compose/deep [
foreach value [(:values)] [
if find/last/match sequence value [return true]
]
false
]
]
find-pattern: function [
"collects paths in the given data structure that refer to each instance of the given pattern"
data pattern
][
stack: copy []
rule: [
some [mark: keep pattern keep (to path! rejoin reduce [stack index? mark])
| mark: ahead block! (append stack index? mark) [into rule | skip] (take/last stack)
| skip
]]
parse data [collect rule]
]
find-all-patterns: function [
"Finds all occurrances of the given parse pattern in files under the specified directory tree"
dir pattern /filter "search files with the specied extension(s)" extensions
][
extensions: any [extensions ".red"]
sources: get-files/filter dir ends-with extensions
print ["searching" length? sources extensions "files..."]
total: 0
matches: collect [
foreach file sources [
do-events/no-wait
pats: attempt [find-pattern load file pattern]
if not empty? pats [
print [file length? pats]
total: total + length? pats
keep file keep/only pats
]
]
]
print ["found" total "matches"]
matches
]
@dander
Copy link
Author

dander commented Dec 12, 2019

This can be used to locate specific patterns in red sources. find-all-patterns/filter can be used to specify alternate file extensions to search, such as ".reds" or [".red" ".reds"]. The returned block consists of pairs of the file! and a block of path!s to the point in the file which matched (no line numbers by this point).

Examples:

Creating a function that is assigned to two words at the same time

>> result: find-all-patterns %/c/red/ [set-word! set-word! ahead ['func | 'function]] 
searching 140 .red files...
/c/red/tests/source/units/function-test.red 2
found 2 matches
== [%/c/red/tests/source/units/function-test.red [[
    a: 
    b:
] 1110...

Counting usages of print

>> result: find-all-patterns %/c/red/ ['print]
searching 140 .red files...
/c/red/bridges/android/samples/eval/eval.red 2
/c/red/bridges/java/bridge.red 18
/c/red/bridges/java/hello.red 2
...
found 264 matches

@hiiamboris
Copy link

unless block? values [values: reduce [values]]

I see this pattern a lot.. best solution so far is values: compose [(:values)]
Just wanted to share ;)

@dander
Copy link
Author

dander commented Dec 14, 2019

@hiiamboris Thanks! I hadn't seen that before. It made me realize I could take that line out altogether :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment