├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README-en.md ├── README.md ├── assets ├── cpp.c ├── curve.svg ├── error_reporting.png ├── extra-semicolon.cpp ├── large.cpp ├── large_num_mul.svg ├── list_qsort.svg ├── manage_sys.svg ├── parse_num.svg ├── phone_number.svg ├── polyline.svg ├── rangefor.cpp ├── switch.c ├── test.cpp └── wc.c ├── build.rs ├── gallery.md └── src ├── ast.rs ├── cli.rs ├── display ├── d2.rs ├── dot.rs ├── mod.rs └── tikz.rs ├── dump.rs ├── error.rs ├── graph.rs ├── lib.rs ├── main.rs └── parser.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | name: CI build 4 | 5 | jobs: 6 | build: 7 | name: Build for ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | include: 12 | - os: ubuntu-latest 13 | artifact_name: cxx2flow 14 | asset_name: cxx2flow-linux-amd64 15 | - os: windows-latest 16 | artifact_name: cxx2flow.exe 17 | asset_name: cxx2flow-windows-amd64.exe 18 | # - os: macos-latest 19 | # artifact_name: cxx2flow 20 | # asset_name: cxx2flow-macos-amd64 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: Swatinem/rust-cache@v1 24 | - uses: actions-rs/cargo@v1 25 | with: 26 | command: build 27 | args: --release 28 | - uses: actions/upload-artifact@v2 29 | with: 30 | name: ${{ matrix.asset_name }} 31 | path: target/release/${{ matrix.artifact_name }} 32 | - name: Release 33 | uses: svenstaro/upload-release-action@v2 34 | if: startsWith(github.ref, 'refs/tags/') 35 | with: 36 | repo_token: ${{ secrets.GITHUB_TOKEN }} 37 | file: target/release/${{ matrix.artifact_name }} 38 | asset_name: ${{ matrix.asset_name }} 39 | tag: ${{ github.ref }} 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | debug/ 6 | target/ 7 | 8 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 9 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | # MSVC Windows builds of rustc generate these, which store debugging information 15 | *.pdb 16 | *.png 17 | *.dot 18 | !assets/test.svg 19 | .DS_Store 20 | ## Core latex/pdflatex auxiliary files: 21 | *.aux 22 | *.lof 23 | *.log 24 | *.lot 25 | *.fls 26 | *.out 27 | *.toc 28 | *.fmt 29 | *.fot 30 | *.cb 31 | *.cb2 32 | .*.lb 33 | 34 | ## Intermediate documents: 35 | *.dvi 36 | *.xdv 37 | *-converted-to.* 38 | # these rules might exclude image files for figures etc. 39 | # *.ps 40 | # *.eps 41 | # *.pdf 42 | 43 | ## Generated if empty string is given at "Please type another file name for output:" 44 | .pdf 45 | 46 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 47 | *.bbl 48 | *.bcf 49 | *.blg 50 | *-blx.aux 51 | *-blx.bib 52 | *.run.xml 53 | 54 | ## Build tool auxiliary files: 55 | *.fdb_latexmk 56 | *.synctex 57 | *.synctex(busy) 58 | *.synctex.gz 59 | *.synctex.gz(busy) 60 | *.pdfsync 61 | 62 | ## Build tool directories for auxiliary files 63 | # latexrun 64 | latex.out/ 65 | 66 | ## Auxiliary and intermediate files from other packages: 67 | # algorithms 68 | *.alg 69 | *.loa 70 | 71 | # achemso 72 | acs-*.bib 73 | 74 | # amsthm 75 | *.thm 76 | 77 | # beamer 78 | *.nav 79 | *.pre 80 | *.snm 81 | *.vrb 82 | 83 | # changes 84 | *.soc 85 | 86 | # comment 87 | *.cut 88 | 89 | # cprotect 90 | *.cpt 91 | 92 | # elsarticle (documentclass of Elsevier journals) 93 | *.spl 94 | 95 | # endnotes 96 | *.ent 97 | 98 | # fixme 99 | *.lox 100 | 101 | # feynmf/feynmp 102 | *.mf 103 | *.mp 104 | *.t[1-9] 105 | *.t[1-9][0-9] 106 | *.tfm 107 | 108 | #(r)(e)ledmac/(r)(e)ledpar 109 | *.end 110 | *.?end 111 | *.[1-9] 112 | *.[1-9][0-9] 113 | *.[1-9][0-9][0-9] 114 | *.[1-9]R 115 | *.[1-9][0-9]R 116 | *.[1-9][0-9][0-9]R 117 | *.eledsec[1-9] 118 | *.eledsec[1-9]R 119 | *.eledsec[1-9][0-9] 120 | *.eledsec[1-9][0-9]R 121 | *.eledsec[1-9][0-9][0-9] 122 | *.eledsec[1-9][0-9][0-9]R 123 | 124 | # glossaries 125 | *.acn 126 | *.acr 127 | *.glg 128 | *.glo 129 | *.gls 130 | *.glsdefs 131 | *.lzo 132 | *.lzs 133 | 134 | # uncomment this for glossaries-extra (will ignore makeindex's style files!) 135 | # *.ist 136 | 137 | # gnuplottex 138 | *-gnuplottex-* 139 | 140 | # gregoriotex 141 | *.gaux 142 | *.glog 143 | *.gtex 144 | 145 | # htlatex 146 | *.4ct 147 | *.4tc 148 | *.idv 149 | *.lg 150 | *.trc 151 | *.xref 152 | 153 | # hyperref 154 | *.brf 155 | 156 | # knitr 157 | *-concordance.tex 158 | # TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files 159 | # *.tikz 160 | *-tikzDictionary 161 | 162 | # listings 163 | *.lol 164 | 165 | # luatexja-ruby 166 | *.ltjruby 167 | 168 | # makeidx 169 | *.idx 170 | *.ilg 171 | *.ind 172 | 173 | # minitoc 174 | *.maf 175 | *.mlf 176 | *.mlt 177 | *.mtc[0-9]* 178 | *.slf[0-9]* 179 | *.slt[0-9]* 180 | *.stc[0-9]* 181 | 182 | # minted 183 | _minted* 184 | *.pyg 185 | 186 | # morewrites 187 | *.mw 188 | 189 | # newpax 190 | *.newpax 191 | 192 | # nomencl 193 | *.nlg 194 | *.nlo 195 | *.nls 196 | 197 | # pax 198 | *.pax 199 | 200 | # pdfpcnotes 201 | *.pdfpc 202 | 203 | # sagetex 204 | *.sagetex.sage 205 | *.sagetex.py 206 | *.sagetex.scmd 207 | 208 | # scrwfile 209 | *.wrt 210 | 211 | # sympy 212 | *.sout 213 | *.sympy 214 | sympy-plots-for-*.tex/ 215 | 216 | # pdfcomment 217 | *.upa 218 | *.upb 219 | 220 | # pythontex 221 | *.pytxcode 222 | pythontex-files-*/ 223 | 224 | # tcolorbox 225 | *.listing 226 | 227 | # thmtools 228 | *.loe 229 | 230 | # TikZ & PGF 231 | *.dpth 232 | *.md5 233 | *.auxlock 234 | 235 | # todonotes 236 | *.tdo 237 | 238 | # vhistory 239 | *.hst 240 | *.ver 241 | 242 | # easy-todo 243 | *.lod 244 | 245 | # xcolor 246 | *.xcp 247 | 248 | # xmpincl 249 | *.xmpi 250 | 251 | # xindy 252 | *.xdy 253 | 254 | # xypic precompiled matrices and outlines 255 | *.xyc 256 | *.xyd 257 | 258 | # endfloat 259 | *.ttt 260 | *.fff 261 | 262 | # Latexian 263 | TSWLatexianTemp* 264 | 265 | ## Editors: 266 | # WinEdt 267 | *.bak 268 | *.sav 269 | 270 | # Texpad 271 | .texpadtmp 272 | 273 | # LyX 274 | *.lyx~ 275 | 276 | # Kile 277 | *.backup 278 | 279 | # gummi 280 | .*.swp 281 | 282 | # KBibTeX 283 | *~[0-9]* 284 | 285 | # TeXnicCenter 286 | *.tps 287 | 288 | # auto folder when using emacs and auctex 289 | ./auto/* 290 | *.el 291 | 292 | # expex forward references with \gathertags 293 | *-tags.tex 294 | 295 | # standalone packages 296 | *.sta 297 | 298 | # Makeindex log files 299 | *.lpz 300 | 301 | # xwatermark package 302 | *.xwm 303 | 304 | # REVTeX puts footnotes in the bibliography by default, unless the nofootinbib 305 | # option is specified. Footnotes are the stored in a file with suffix Notes.bib. 306 | # Uncomment the next line to have this generated file ignored. 307 | #*Notes.bib 308 | *.pdf 309 | .idea 310 | !error_reporting.png 311 | 312 | # Logs 313 | logs 314 | *.log 315 | npm-debug.log* 316 | yarn-debug.log* 317 | yarn-error.log* 318 | lerna-debug.log* 319 | .pnpm-debug.log* 320 | 321 | # Diagnostic reports (https://nodejs.org/api/report.html) 322 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 323 | 324 | # Runtime data 325 | pids 326 | *.pid 327 | *.seed 328 | *.pid.lock 329 | 330 | # Directory for instrumented libs generated by jscoverage/JSCover 331 | lib-cov 332 | 333 | # Coverage directory used by tools like istanbul 334 | coverage 335 | *.lcov 336 | 337 | # nyc test coverage 338 | .nyc_output 339 | 340 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 341 | .grunt 342 | 343 | # Bower dependency directory (https://bower.io/) 344 | bower_components 345 | 346 | # node-waf configuration 347 | .lock-wscript 348 | 349 | # Compiled binary addons (https://nodejs.org/api/addons.html) 350 | build/Release 351 | 352 | # Dependency directories 353 | node_modules/ 354 | jspm_packages/ 355 | 356 | # Snowpack dependency directory (https://snowpack.dev/) 357 | web_modules/ 358 | 359 | # TypeScript cache 360 | *.tsbuildinfo 361 | 362 | # Optional npm cache directory 363 | .npm 364 | 365 | # Optional eslint cache 366 | .eslintcache 367 | 368 | # Optional stylelint cache 369 | .stylelintcache 370 | 371 | # Microbundle cache 372 | .rpt2_cache/ 373 | .rts2_cache_cjs/ 374 | .rts2_cache_es/ 375 | .rts2_cache_umd/ 376 | 377 | # Optional REPL history 378 | .node_repl_history 379 | 380 | # Output of 'npm pack' 381 | *.tgz 382 | 383 | # Yarn Integrity file 384 | .yarn-integrity 385 | 386 | # dotenv environment variable files 387 | .env 388 | .env.development.local 389 | .env.test.local 390 | .env.production.local 391 | .env.local 392 | 393 | # parcel-bundler cache (https://parceljs.org/) 394 | .cache 395 | .parcel-cache 396 | 397 | # Next.js build output 398 | .next 399 | out 400 | 401 | # Nuxt.js build / generate output 402 | .nuxt 403 | dist 404 | 405 | # Gatsby files 406 | .cache/ 407 | # Comment in the public line in if your project uses Gatsby and not Next.js 408 | # https://nextjs.org/blog/next-9-1#public-directory-support 409 | # public 410 | 411 | # vuepress build output 412 | .vuepress/dist 413 | 414 | # vuepress v2.x temp and cache directory 415 | .temp 416 | .cache 417 | 418 | # Serverless directories 419 | .serverless/ 420 | 421 | # FuseBox cache 422 | .fusebox/ 423 | 424 | # DynamoDB Local files 425 | .dynamodb/ 426 | 427 | # TernJS port file 428 | .tern-port 429 | 430 | # Stores VSCode versions used for testing VSCode extensions 431 | .vscode-test 432 | 433 | # yarn v2 434 | .yarn/cache 435 | .yarn/unplugged 436 | .yarn/build-state.yml 437 | .yarn/install-state.gz 438 | .pnp.* 439 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.22.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.6.14" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is_terminal_polyfill", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.4" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.2" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 64 | dependencies = [ 65 | "windows-sys", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "3.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" 73 | dependencies = [ 74 | "anstyle", 75 | "windows-sys", 76 | ] 77 | 78 | [[package]] 79 | name = "anyhow" 80 | version = "1.0.86" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 83 | 84 | [[package]] 85 | name = "backtrace" 86 | version = "0.3.72" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" 89 | dependencies = [ 90 | "addr2line", 91 | "cc", 92 | "cfg-if", 93 | "libc", 94 | "miniz_oxide", 95 | "object", 96 | "rustc-demangle", 97 | ] 98 | 99 | [[package]] 100 | name = "backtrace-ext" 101 | version = "0.2.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" 104 | dependencies = [ 105 | "backtrace", 106 | ] 107 | 108 | [[package]] 109 | name = "bitflags" 110 | version = "2.4.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 113 | 114 | [[package]] 115 | name = "camino" 116 | version = "1.1.7" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" 119 | dependencies = [ 120 | "serde", 121 | ] 122 | 123 | [[package]] 124 | name = "cargo-platform" 125 | version = "0.1.8" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" 128 | dependencies = [ 129 | "serde", 130 | ] 131 | 132 | [[package]] 133 | name = "cargo_metadata" 134 | version = "0.18.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" 137 | dependencies = [ 138 | "camino", 139 | "cargo-platform", 140 | "semver", 141 | "serde", 142 | "serde_json", 143 | "thiserror", 144 | ] 145 | 146 | [[package]] 147 | name = "cc" 148 | version = "1.0.99" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" 151 | dependencies = [ 152 | "jobserver", 153 | "libc", 154 | "once_cell", 155 | ] 156 | 157 | [[package]] 158 | name = "cfg-if" 159 | version = "1.0.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 162 | 163 | [[package]] 164 | name = "clap" 165 | version = "4.5.6" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" 168 | dependencies = [ 169 | "clap_builder", 170 | "clap_derive", 171 | ] 172 | 173 | [[package]] 174 | name = "clap_builder" 175 | version = "4.5.6" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" 178 | dependencies = [ 179 | "anstream", 180 | "anstyle", 181 | "clap_lex", 182 | "strsim", 183 | "terminal_size", 184 | ] 185 | 186 | [[package]] 187 | name = "clap_derive" 188 | version = "4.5.5" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" 191 | dependencies = [ 192 | "heck", 193 | "proc-macro2", 194 | "quote", 195 | "syn", 196 | ] 197 | 198 | [[package]] 199 | name = "clap_lex" 200 | version = "0.7.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" 203 | 204 | [[package]] 205 | name = "colorchoice" 206 | version = "1.0.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 209 | 210 | [[package]] 211 | name = "colored" 212 | version = "2.1.0" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 215 | dependencies = [ 216 | "lazy_static", 217 | "windows-sys", 218 | ] 219 | 220 | [[package]] 221 | name = "cxx2flow" 222 | version = "0.6.2" 223 | dependencies = [ 224 | "anyhow", 225 | "clap", 226 | "colored", 227 | "enum_dispatch", 228 | "hash-chain", 229 | "itertools", 230 | "log", 231 | "miette", 232 | "once_cell", 233 | "petgraph", 234 | "thiserror", 235 | "tree-sitter", 236 | "tree-sitter-cpp", 237 | "vergen", 238 | ] 239 | 240 | [[package]] 241 | name = "deranged" 242 | version = "0.3.11" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 245 | dependencies = [ 246 | "powerfmt", 247 | ] 248 | 249 | [[package]] 250 | name = "either" 251 | version = "1.6.1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 254 | 255 | [[package]] 256 | name = "enum_dispatch" 257 | version = "0.3.13" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" 260 | dependencies = [ 261 | "once_cell", 262 | "proc-macro2", 263 | "quote", 264 | "syn", 265 | ] 266 | 267 | [[package]] 268 | name = "equivalent" 269 | version = "1.0.1" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 272 | 273 | [[package]] 274 | name = "errno" 275 | version = "0.3.6" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" 278 | dependencies = [ 279 | "libc", 280 | "windows-sys", 281 | ] 282 | 283 | [[package]] 284 | name = "fixedbitset" 285 | version = "0.4.0" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" 288 | 289 | [[package]] 290 | name = "form_urlencoded" 291 | version = "1.2.1" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 294 | dependencies = [ 295 | "percent-encoding", 296 | ] 297 | 298 | [[package]] 299 | name = "gimli" 300 | version = "0.29.0" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 303 | 304 | [[package]] 305 | name = "git2" 306 | version = "0.18.3" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" 309 | dependencies = [ 310 | "bitflags", 311 | "libc", 312 | "libgit2-sys", 313 | "log", 314 | "url", 315 | ] 316 | 317 | [[package]] 318 | name = "hash-chain" 319 | version = "0.3.2" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "4ae1e9c6caefb8e1fb1be731642c99a7d5803d2282ee890f78dce35aeee988b2" 322 | 323 | [[package]] 324 | name = "hashbrown" 325 | version = "0.14.2" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" 328 | 329 | [[package]] 330 | name = "heck" 331 | version = "0.5.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 334 | 335 | [[package]] 336 | name = "idna" 337 | version = "0.5.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 340 | dependencies = [ 341 | "unicode-bidi", 342 | "unicode-normalization", 343 | ] 344 | 345 | [[package]] 346 | name = "indexmap" 347 | version = "2.1.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" 350 | dependencies = [ 351 | "equivalent", 352 | "hashbrown", 353 | ] 354 | 355 | [[package]] 356 | name = "is_ci" 357 | version = "1.2.0" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 360 | 361 | [[package]] 362 | name = "is_terminal_polyfill" 363 | version = "1.70.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" 366 | 367 | [[package]] 368 | name = "itertools" 369 | version = "0.13.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 372 | dependencies = [ 373 | "either", 374 | ] 375 | 376 | [[package]] 377 | name = "itoa" 378 | version = "1.0.11" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 381 | 382 | [[package]] 383 | name = "jobserver" 384 | version = "0.1.31" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 387 | dependencies = [ 388 | "libc", 389 | ] 390 | 391 | [[package]] 392 | name = "lazy_static" 393 | version = "1.4.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 396 | 397 | [[package]] 398 | name = "libc" 399 | version = "0.2.150" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" 402 | 403 | [[package]] 404 | name = "libgit2-sys" 405 | version = "0.16.2+1.7.2" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" 408 | dependencies = [ 409 | "cc", 410 | "libc", 411 | "libz-sys", 412 | "pkg-config", 413 | ] 414 | 415 | [[package]] 416 | name = "libz-sys" 417 | version = "1.1.18" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" 420 | dependencies = [ 421 | "cc", 422 | "libc", 423 | "pkg-config", 424 | "vcpkg", 425 | ] 426 | 427 | [[package]] 428 | name = "linux-raw-sys" 429 | version = "0.4.11" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" 432 | 433 | [[package]] 434 | name = "log" 435 | version = "0.4.21" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 438 | 439 | [[package]] 440 | name = "memchr" 441 | version = "2.7.2" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 444 | 445 | [[package]] 446 | name = "miette" 447 | version = "7.2.0" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" 450 | dependencies = [ 451 | "backtrace", 452 | "backtrace-ext", 453 | "cfg-if", 454 | "miette-derive", 455 | "owo-colors", 456 | "supports-color", 457 | "supports-hyperlinks", 458 | "supports-unicode", 459 | "terminal_size", 460 | "textwrap", 461 | "thiserror", 462 | "unicode-width", 463 | ] 464 | 465 | [[package]] 466 | name = "miette-derive" 467 | version = "7.2.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" 470 | dependencies = [ 471 | "proc-macro2", 472 | "quote", 473 | "syn", 474 | ] 475 | 476 | [[package]] 477 | name = "miniz_oxide" 478 | version = "0.7.3" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" 481 | dependencies = [ 482 | "adler", 483 | ] 484 | 485 | [[package]] 486 | name = "num-conv" 487 | version = "0.1.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 490 | 491 | [[package]] 492 | name = "num_threads" 493 | version = "0.1.7" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 496 | dependencies = [ 497 | "libc", 498 | ] 499 | 500 | [[package]] 501 | name = "object" 502 | version = "0.35.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" 505 | dependencies = [ 506 | "memchr", 507 | ] 508 | 509 | [[package]] 510 | name = "once_cell" 511 | version = "1.19.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 514 | 515 | [[package]] 516 | name = "owo-colors" 517 | version = "4.0.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" 520 | 521 | [[package]] 522 | name = "percent-encoding" 523 | version = "2.3.1" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 526 | 527 | [[package]] 528 | name = "petgraph" 529 | version = "0.6.5" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" 532 | dependencies = [ 533 | "fixedbitset", 534 | "indexmap", 535 | ] 536 | 537 | [[package]] 538 | name = "pkg-config" 539 | version = "0.3.30" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 542 | 543 | [[package]] 544 | name = "powerfmt" 545 | version = "0.2.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 548 | 549 | [[package]] 550 | name = "proc-macro2" 551 | version = "1.0.85" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" 554 | dependencies = [ 555 | "unicode-ident", 556 | ] 557 | 558 | [[package]] 559 | name = "quote" 560 | version = "1.0.36" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 563 | dependencies = [ 564 | "proc-macro2", 565 | ] 566 | 567 | [[package]] 568 | name = "regex" 569 | version = "1.10.5" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" 572 | dependencies = [ 573 | "aho-corasick", 574 | "memchr", 575 | "regex-automata", 576 | "regex-syntax", 577 | ] 578 | 579 | [[package]] 580 | name = "regex-automata" 581 | version = "0.4.7" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 584 | dependencies = [ 585 | "aho-corasick", 586 | "memchr", 587 | "regex-syntax", 588 | ] 589 | 590 | [[package]] 591 | name = "regex-syntax" 592 | version = "0.8.4" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 595 | 596 | [[package]] 597 | name = "rustc-demangle" 598 | version = "0.1.24" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 601 | 602 | [[package]] 603 | name = "rustc_version" 604 | version = "0.4.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 607 | dependencies = [ 608 | "semver", 609 | ] 610 | 611 | [[package]] 612 | name = "rustix" 613 | version = "0.38.21" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" 616 | dependencies = [ 617 | "bitflags", 618 | "errno", 619 | "libc", 620 | "linux-raw-sys", 621 | "windows-sys", 622 | ] 623 | 624 | [[package]] 625 | name = "rustversion" 626 | version = "1.0.17" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 629 | 630 | [[package]] 631 | name = "ryu" 632 | version = "1.0.18" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 635 | 636 | [[package]] 637 | name = "semver" 638 | version = "1.0.23" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 641 | dependencies = [ 642 | "serde", 643 | ] 644 | 645 | [[package]] 646 | name = "serde" 647 | version = "1.0.203" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" 650 | dependencies = [ 651 | "serde_derive", 652 | ] 653 | 654 | [[package]] 655 | name = "serde_derive" 656 | version = "1.0.203" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" 659 | dependencies = [ 660 | "proc-macro2", 661 | "quote", 662 | "syn", 663 | ] 664 | 665 | [[package]] 666 | name = "serde_json" 667 | version = "1.0.117" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" 670 | dependencies = [ 671 | "itoa", 672 | "ryu", 673 | "serde", 674 | ] 675 | 676 | [[package]] 677 | name = "smawk" 678 | version = "0.3.1" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" 681 | 682 | [[package]] 683 | name = "strsim" 684 | version = "0.11.1" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 687 | 688 | [[package]] 689 | name = "supports-color" 690 | version = "3.0.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" 693 | dependencies = [ 694 | "is_ci", 695 | ] 696 | 697 | [[package]] 698 | name = "supports-hyperlinks" 699 | version = "3.0.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" 702 | 703 | [[package]] 704 | name = "supports-unicode" 705 | version = "3.0.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 708 | 709 | [[package]] 710 | name = "syn" 711 | version = "2.0.66" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 714 | dependencies = [ 715 | "proc-macro2", 716 | "quote", 717 | "unicode-ident", 718 | ] 719 | 720 | [[package]] 721 | name = "terminal_size" 722 | version = "0.3.0" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" 725 | dependencies = [ 726 | "rustix", 727 | "windows-sys", 728 | ] 729 | 730 | [[package]] 731 | name = "textwrap" 732 | version = "0.16.1" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" 735 | dependencies = [ 736 | "smawk", 737 | "unicode-linebreak", 738 | "unicode-width", 739 | ] 740 | 741 | [[package]] 742 | name = "thiserror" 743 | version = "1.0.61" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" 746 | dependencies = [ 747 | "thiserror-impl", 748 | ] 749 | 750 | [[package]] 751 | name = "thiserror-impl" 752 | version = "1.0.61" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" 755 | dependencies = [ 756 | "proc-macro2", 757 | "quote", 758 | "syn", 759 | ] 760 | 761 | [[package]] 762 | name = "time" 763 | version = "0.3.36" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 766 | dependencies = [ 767 | "deranged", 768 | "itoa", 769 | "libc", 770 | "num-conv", 771 | "num_threads", 772 | "powerfmt", 773 | "serde", 774 | "time-core", 775 | "time-macros", 776 | ] 777 | 778 | [[package]] 779 | name = "time-core" 780 | version = "0.1.2" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 783 | 784 | [[package]] 785 | name = "time-macros" 786 | version = "0.2.18" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 789 | dependencies = [ 790 | "num-conv", 791 | "time-core", 792 | ] 793 | 794 | [[package]] 795 | name = "tinyvec" 796 | version = "1.6.0" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 799 | dependencies = [ 800 | "tinyvec_macros", 801 | ] 802 | 803 | [[package]] 804 | name = "tinyvec_macros" 805 | version = "0.1.1" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 808 | 809 | [[package]] 810 | name = "tree-sitter" 811 | version = "0.22.6" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca" 814 | dependencies = [ 815 | "cc", 816 | "regex", 817 | ] 818 | 819 | [[package]] 820 | name = "tree-sitter-cpp" 821 | version = "0.22.2" 822 | source = "git+https://github.com/tree-sitter/tree-sitter-cpp?rev=7ce8946cae4bb25adebe5b50394f702beb007026#7ce8946cae4bb25adebe5b50394f702beb007026" 823 | dependencies = [ 824 | "cc", 825 | "tree-sitter", 826 | ] 827 | 828 | [[package]] 829 | name = "unicode-bidi" 830 | version = "0.3.15" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 833 | 834 | [[package]] 835 | name = "unicode-ident" 836 | version = "1.0.5" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 839 | 840 | [[package]] 841 | name = "unicode-linebreak" 842 | version = "0.1.5" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 845 | 846 | [[package]] 847 | name = "unicode-normalization" 848 | version = "0.1.23" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 851 | dependencies = [ 852 | "tinyvec", 853 | ] 854 | 855 | [[package]] 856 | name = "unicode-width" 857 | version = "0.1.13" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 860 | 861 | [[package]] 862 | name = "url" 863 | version = "2.5.0" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 866 | dependencies = [ 867 | "form_urlencoded", 868 | "idna", 869 | "percent-encoding", 870 | ] 871 | 872 | [[package]] 873 | name = "utf8parse" 874 | version = "0.2.1" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 877 | 878 | [[package]] 879 | name = "vcpkg" 880 | version = "0.2.15" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 883 | 884 | [[package]] 885 | name = "vergen" 886 | version = "8.3.1" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" 889 | dependencies = [ 890 | "anyhow", 891 | "cargo_metadata", 892 | "cfg-if", 893 | "git2", 894 | "regex", 895 | "rustc_version", 896 | "rustversion", 897 | "time", 898 | ] 899 | 900 | [[package]] 901 | name = "windows-sys" 902 | version = "0.48.0" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 905 | dependencies = [ 906 | "windows-targets", 907 | ] 908 | 909 | [[package]] 910 | name = "windows-targets" 911 | version = "0.48.5" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 914 | dependencies = [ 915 | "windows_aarch64_gnullvm", 916 | "windows_aarch64_msvc", 917 | "windows_i686_gnu", 918 | "windows_i686_msvc", 919 | "windows_x86_64_gnu", 920 | "windows_x86_64_gnullvm", 921 | "windows_x86_64_msvc", 922 | ] 923 | 924 | [[package]] 925 | name = "windows_aarch64_gnullvm" 926 | version = "0.48.5" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 929 | 930 | [[package]] 931 | name = "windows_aarch64_msvc" 932 | version = "0.48.5" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 935 | 936 | [[package]] 937 | name = "windows_i686_gnu" 938 | version = "0.48.5" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 941 | 942 | [[package]] 943 | name = "windows_i686_msvc" 944 | version = "0.48.5" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 947 | 948 | [[package]] 949 | name = "windows_x86_64_gnu" 950 | version = "0.48.5" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 953 | 954 | [[package]] 955 | name = "windows_x86_64_gnullvm" 956 | version = "0.48.5" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 959 | 960 | [[package]] 961 | name = "windows_x86_64_msvc" 962 | version = "0.48.5" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 965 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cxx2flow" 3 | version = "0.6.2" 4 | edition = "2021" 5 | authors = ["mgt "] 6 | description = "Convert your C/C++ code to control flow chart" 7 | license = "MIT" 8 | repository = "https://github.com/Enter-tainer/cxx2flow" 9 | include = ["src/**/*", "LICENSE", "README.md", "build.rs"] 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [[bin]] 14 | name = "cxx2flow" 15 | path = "src/main.rs" 16 | 17 | [lib] 18 | name = "cxx2flow" 19 | path = "src/lib.rs" 20 | crate-type = ["lib"] 21 | 22 | [dependencies] 23 | thiserror = "1.0" 24 | clap = { version = "4.5.6", features = ["derive", "wrap_help"] } 25 | log = "0.4.21" 26 | tree-sitter = "0.22.6" 27 | tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "7ce8946cae4bb25adebe5b50394f702beb007026" } 28 | petgraph = "0.6.5" 29 | itertools = "0.13.0" 30 | hash-chain = "0.3.2" 31 | once_cell = "1.19.0" 32 | miette = { version = "7.2.0", features = ["fancy"] } 33 | enum_dispatch = "0.3.13" 34 | colored = "2.1.0" 35 | [build-dependencies] 36 | anyhow = "1.0" 37 | vergen = { version = "8.3.1", features = [ 38 | "build", 39 | "cargo", 40 | "git", 41 | "git2", 42 | "rustc", 43 | ] } 44 | [profile.release] 45 | lto = "fat" 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | # cxx2flow 2 | 3 | [简体中文](README.md) | [English](README-en.md) 4 | 5 | Turn your C/C++ code into flowchart 6 | 7 | ## Demo 8 | 9 | For more demo please refer to [GALLERY](gallery.md) 10 | 11 | Two different styles: 12 | | | | 13 | |:-:|:-:| 14 | | polyline | smooth | 15 | |![ployline](assets/polyline.svg)|![curve](assets/curve.svg)| 16 | 17 | ```cpp 18 | inline int read() { //快读 19 | char c = getchar(); 20 | int x = 0, f = 1; 21 | while (c < '0' || c > '9') { 22 | if (c == '-') f = -1; 23 | c = getchar(); 24 | } 25 | while (c >= '0' && c <= '9') { 26 | x = x * 10 + c - '0'; 27 | c = getchar(); 28 | } 29 | return x * f; 30 | } 31 | ``` 32 | 33 | ### Error reporting 34 | 35 | ![error reporting](assets/error_reporting.png) 36 | 37 | ## Installation 38 | 39 | ### Compile from source 40 | 41 | ```bash 42 | cargo install cxx2flow 43 | ``` 44 | 45 | ### Prebuilt binary 46 | 47 | It is recommended to download prebuilt binary from [Github Release](https://github.com/Enter-tainer/cxx2flow/releases). 48 | 49 | ### GUI version 50 | 51 | For those who are not familiar with command line, I recommend the GUI version of cxx2flow. https://github.com/Enter-tainer/cxx2flow-gui/releases 52 | 53 | ![gui](https://github.com/Enter-tainer/cxx2flow-gui/raw/master/assets/2022-05-01-16-37-32.png) 54 | 55 | 56 | ## Usage 57 | 58 | To compile the generated dot file, you need graphviz. You can also copy the output to online graphviz services such as http://magjac.com/graphviz-visual-editor/ . 59 | 60 | ``` 61 | Convert your C/C++ code to control flow chart 62 | 63 | Usage: cxx2flow [OPTIONS] [INPUT] [FUNCTION] 64 | 65 | Arguments: 66 | [INPUT] Sets the path of the input file. e.g. test.cpp 67 | If not specified, cxx2flow will read from stdin. 68 | [FUNCTION] The function you want to convert. e.g. main [default: main] 69 | 70 | Options: 71 | -o, --output Sets the output file. 72 | If not specified, result will be directed to stdout. 73 | e.g. graph.dot 74 | -c, --curly Sets the style of the flow chart. 75 | If specified, output flow chart will have curly connection line. 76 | --cpp Use C preprocessor. 77 | -t, --tikz Use tikz backend. 78 | -d, --dump-ast Dump AST(For debug purpose only). 79 | -h, --help Print help information 80 | -V, --version Print version information 81 | 82 | Note that you need to manually compile the dot file using graphviz to get SVG or PNG files. 83 | 84 | EXAMPLES: 85 | cat main.cpp | cxx2flow | dot -Tsvg -o test.svg 86 | cxx2flow test.cpp | dot -Tpng -o test.png 87 | cxx2flow main.cpp my_custom_func | dot -Tsvg -o test.svg 88 | 89 | Please give me star if this application helps you! 90 | 如果这个应用有帮助到你,请给我点一个 star! 91 | https://github.com/Enter-tainer/cxx2flow 92 | ``` 93 | 94 | ## Limitations 95 | 96 | - The support of preprocessor is based on `cpp`, and is disabled by default. `--cpp` flag is needed to enable it. It will fail if `cpp` does not exist in `PATH`. 97 | - Supported control flow keyword: while,for,if,break,continue,break,return,switch, goto, do-while。 98 | - Very basic support for range based loop in C++ 11. 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cxx2flow 2 | 3 | [简体中文](README.md) | [English](README-en.md) 4 | 5 | 将 C/C++ 代码转换为流程图 6 | 7 | ## 效果 8 | 9 | 更多效果图请参考 [GALLERY](gallery.md) 10 | 11 | 两种样式: 12 | | | | 13 | |:-:|:-:| 14 | | 折线 | 平滑 | 15 | |![ployline](assets/polyline.svg)|![curve](assets/curve.svg)| 16 | 17 | ```cpp 18 | inline int read() { //快读 19 | char c = getchar(); 20 | int x = 0, f = 1; 21 | while (c < '0' || c > '9') { 22 | if (c == '-') f = -1; 23 | c = getchar(); 24 | } 25 | while (c >= '0' && c <= '9') { 26 | x = x * 10 + c - '0'; 27 | c = getchar(); 28 | } 29 | return x * f; 30 | } 31 | ``` 32 | 33 | ### 错误报告 34 | 35 | ![error reporting](assets/error_reporting.png) 36 | 37 | ## 安装 38 | 39 | ### 自行编译 40 | 41 | ```bash 42 | cargo install cxx2flow 43 | ``` 44 | 45 | ### 下载预构建二进制 46 | 47 | 推荐从右侧的 [Github Release](https://github.com/Enter-tainer/cxx2flow/releases) 下载对应平台的二进制文件。 48 | 49 | 也可以到 [GitHub Actions](https://github.com/Enter-tainer/cxx2flow/actions?query=branch%3Amaster+is%3Asuccess+event%3Apush+actor%3AEnter-tainer) 或 [Nightly.link](https://nightly.link/Enter-tainer/cxx2flow/workflows/build/master) 下载最新构建的二进制,包含 Linux, Windows 和 MacOS 版本。 50 | 51 | ### 使用 GUI 版本 52 | 53 | 对于没有命令行使用经验的用户,推荐下载使用基于 tauri 编写的 GUI 版本。 https://github.com/Enter-tainer/cxx2flow-gui/releases 54 | 55 | ![gui](https://github.com/Enter-tainer/cxx2flow-gui/raw/master/assets/2022-05-01-16-37-32.png) 56 | 57 | ## 使用 58 | 59 | 为了编译生成的 dot 文件,你需要安装 graphviz,并将其添加到 PATH 中。也可以将生成的结果复制进在线的 graphviz 服务中,如 http://magjac.com/graphviz-visual-editor/ 。 60 | 61 | ``` 62 | Convert your C/C++ code to control flow chart 63 | 64 | Usage: cxx2flow [OPTIONS] [INPUT] [FUNCTION] 65 | 66 | Arguments: 67 | [INPUT] Sets the path of the input file. e.g. test.cpp 68 | If not specified, cxx2flow will read from stdin. 69 | [FUNCTION] The function you want to convert. e.g. main [default: main] 70 | 71 | Options: 72 | -o, --output Sets the output file. 73 | If not specified, result will be directed to stdout. 74 | e.g. graph.dot 75 | -c, --curly Sets the style of the flow chart. 76 | If specified, output flow chart will have curly connection line. 77 | --cpp Use C preprocessor. 78 | -t, --tikz Use tikz backend. 79 | -d, --dump-ast Dump AST(For debug purpose only). 80 | -h, --help Print help information 81 | -V, --version Print version information 82 | 83 | Note that you need to manually compile the dot file using graphviz to get SVG or PNG files. 84 | 85 | EXAMPLES: 86 | cat main.cpp | cxx2flow | dot -Tsvg -o test.svg 87 | cxx2flow test.cpp | dot -Tpng -o test.png 88 | cxx2flow main.cpp my_custom_func | dot -Tsvg -o test.svg 89 | 90 | Please give me star if this application helps you! 91 | 如果这个应用有帮助到你,请给我点一个 star! 92 | https://github.com/Enter-tainer/cxx2flow 93 | ``` 94 | 95 | ## 限制 96 | 97 | - 对于预处理器的支持基于 `cpp` ,默认关闭,需要使用 `--cpp` 参数手动启用。如果 `PATH` 中不存在 `cpp` 则会失败。 98 | - 支持的控制流语句有:while,for,if,break,continue,break,return,switch, goto, do-while。 99 | - 对 range for 有基本支持。部分情况下,受到 tree-sitter-cpp 能力限制,会出现一些问题 100 | -------------------------------------------------------------------------------- /assets/cpp.c: -------------------------------------------------------------------------------- 1 | #define TEST 2 | 3 | int main() { 4 | #ifdef TEST 5 | int a = 0; 6 | #else 7 | int a = 1; 8 | #endif 9 | } 10 | -------------------------------------------------------------------------------- /assets/curve.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | D0 14 | 15 | begin 16 | 17 | 18 | 19 | D4 20 | 21 | char c = getchar(); 22 | 23 | 24 | 25 | D0->D4 26 | 27 | 28 | 29 | 30 | 31 | D1 32 | 33 | end 34 | 35 | 36 | 37 | D6 38 | 39 | int x = 0, f = 1; 40 | 41 | 42 | 43 | D4->D6 44 | 45 | 46 | 47 | 48 | 49 | D8 50 | 51 | (c < '0' || c > '9')? 52 | 53 | 54 | 55 | D6->D8 56 | 57 | 58 | 59 | 60 | 61 | D13 62 | 63 | (c == '-')? 64 | 65 | 66 | 67 | D8:s->D13:n 68 | 69 | 70 | Y 71 | 72 | 73 | 74 | D20 75 | 76 | (c >= '0' && c <= '9')? 77 | 78 | 79 | 80 | D8:e->D20:n 81 | 82 | 83 | N 84 | 85 | 86 | 87 | D16 88 | 89 | f = -1; 90 | 91 | 92 | 93 | D13:s->D16:n 94 | 95 | 96 | Y 97 | 98 | 99 | 100 | D18 101 | 102 | c = getchar(); 103 | 104 | 105 | 106 | D13:e->D18:n 107 | 108 | 109 | N 110 | 111 | 112 | 113 | D16->D18 114 | 115 | 116 | 117 | 118 | 119 | D18->D8 120 | 121 | 122 | 123 | 124 | 125 | D25 126 | 127 | x = x * 10 + c - '0'; 128 | 129 | 130 | 131 | D20:s->D25:n 132 | 133 | 134 | Y 135 | 136 | 137 | 138 | D29 139 | 140 | return x * f; 141 | 142 | 143 | 144 | D20:e->D29:n 145 | 146 | 147 | N 148 | 149 | 150 | 151 | D27 152 | 153 | c = getchar(); 154 | 155 | 156 | 157 | D25->D27 158 | 159 | 160 | 161 | 162 | 163 | D27->D20 164 | 165 | 166 | 167 | 168 | 169 | D29->D1 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /assets/error_reporting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Enter-tainer/cxx2flow/1bc135017284856e73a5115f4b79ae2bbb3fca93/assets/error_reporting.png -------------------------------------------------------------------------------- /assets/extra-semicolon.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | int t; 3 | double ans, x; 4 | printf("Please input your salary:"); 5 | scanf("%lf", &x); 6 | printf("Please choose a method\n1:with if; 2:with swtch:"); 7 | scanf("%d", &t); 8 | if (t == 1) 9 | ans = useif(x); 10 | else if (t == 2) 11 | ans = usesw(x); 12 | else { 13 | printf("Invalid input!\n"); 14 | return 0; 15 | }; 16 | if (t == 1) 17 | printf("calculate by if, the tax is:%.2lf\n", ans); 18 | else 19 | printf("calculate by switch, the tax is:%.2lf\n", ans); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /assets/large.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | typedef struct node node; 4 | typedef struct single_list single_list; 5 | 6 | struct node { 7 | void* data; 8 | node* next; 9 | }; 10 | 11 | struct single_list { 12 | node* head; 13 | int size; 14 | }; 15 | 16 | single_list init_list() { 17 | single_list x; 18 | x.size = 0; 19 | x.head = malloc(sizeof(node)); 20 | x.head->next = x.head->data = NULL; 21 | return x; 22 | } 23 | 24 | single_list push_back_list(single_list x, void* data) { 25 | node* t = x.head; 26 | while (t->next) 27 | t = t->next; 28 | t->next = malloc(sizeof(node)); 29 | t->next->next = NULL; 30 | t->next->data = data; 31 | x.size++; 32 | return x; 33 | } 34 | 35 | single_list push_front_list(single_list x, void* data) { 36 | node* t = x.head; 37 | node* n = malloc(sizeof(node)); 38 | n->data = data; 39 | n->next = t->next; 40 | t->next = n; 41 | x.size++; 42 | return x; 43 | } 44 | 45 | single_list insert_list(single_list x, void* data, int pos) { 46 | // after insert, data stays in the pos postion 47 | node* t = x.head; 48 | for (int i = 0; i < pos && t->next; ++i, t = t->next) 49 | ; 50 | node* n = malloc(sizeof(node)); 51 | n->data = data; 52 | n->next = t->next; 53 | t->next = n; 54 | x.size++; 55 | return x; 56 | } 57 | 58 | void* in_access_list(single_list x, int pos) { 59 | node* t = x.head; 60 | for (int i = 0; i <= pos && t->next; ++i, t = t->next) 61 | ; 62 | return t->data; 63 | } 64 | 65 | #define access_list(type) (type*)in_access_list 66 | 67 | int search_list(single_list x, void* data, 68 | int (*eq)(const void* a, const void* b)) { 69 | node* t = x.head->next; 70 | for (int i = 0; t->next; ++i, t = t->next) 71 | if (eq(t->data, data)) 72 | return i; 73 | return -1; 74 | } 75 | 76 | #define from_heap(type) \ 77 | type* from_heap_##type(type x) { \ 78 | type* t = malloc(sizeof(type)); \ 79 | *t = x; \ 80 | return t; \ 81 | } 82 | 83 | single_list delete_node(single_list x, int pos) { 84 | node* t = x.head; 85 | for (int i = 0; i < pos && t->next; ++i, t = t->next) 86 | ; 87 | node* d = t->next; 88 | t->next = d->next; 89 | free(d->data); 90 | d->next = d->data = NULL; 91 | free(d); 92 | x.size--; 93 | return x; 94 | } 95 | 96 | single_list pop_front_list(single_list x) { return delete_node(x, 0); } 97 | 98 | single_list pop_back_list(single_list x) { return delete_node(x, x.size - 1); } 99 | 100 | void drop_nodes(node* x) { 101 | if (x->next) { 102 | drop_nodes(x->next); 103 | } 104 | free(x->data); 105 | x->data = x->next = NULL; 106 | free(x); 107 | } 108 | 109 | void drop_list(single_list x) { drop_nodes(x.head); } 110 | 111 | int iseq(const int* a, const int* b) { return *a == *b; } 112 | 113 | from_heap(int); 114 | 115 | typedef struct student { 116 | int id; 117 | char name[16]; 118 | int eng; 119 | int mat; 120 | int phy; 121 | int cpl; 122 | } student; 123 | 124 | int get_choice() { 125 | printf("\t\t1. Input data\n"); 126 | printf("\t\t2. Ouput data\n"); 127 | printf("\t\t3. Modify data of someone\n"); 128 | printf("\t\t4. Ouput avg mark\n"); 129 | printf("\t\t5. Output data & avg mark & sum\n"); 130 | printf("\t\t6. Sort\n"); 131 | printf("\t\t7. Exit\n"); 132 | int c; 133 | scanf("%d", &c); 134 | return c; 135 | } 136 | 137 | int isleq(const student* a, const student* b) { 138 | int sum1, sum2; 139 | sum1 = a->cpl + a->eng + a->phy + a->mat; 140 | sum2 = b->cpl + b->eng + b->phy + b->mat; 141 | return sum1 <= sum2; 142 | } 143 | 144 | node *sort_node(node *s) { 145 | if (s == NULL || s->next == NULL) 146 | return s; 147 | node* head, *leq = NULL, *g = NULL; 148 | node* p = s->next; 149 | head = s; 150 | s->next = NULL; 151 | for (; p; ) { 152 | node* t = p->next; 153 | student* stu = p->data, *piv = head->data; 154 | if (isleq(stu, piv)) { 155 | p->next = leq; 156 | leq = p; 157 | } else { 158 | p->next = g; 159 | g = p; 160 | } 161 | p = t; 162 | } 163 | leq = sort_node(leq); 164 | g = sort_node(g); 165 | node* res = head; 166 | if (leq) { 167 | res = leq; 168 | p = leq; 169 | while (p->next) 170 | p = p->next; 171 | p->next = head; 172 | } 173 | head->next = g; 174 | return res; 175 | } 176 | 177 | single_list sort_list(single_list x) { 178 | x.head->next = sort_node(x.head->next); 179 | return x; 180 | } 181 | 182 | int main() { 183 | single_list s = init_list(); 184 | int choice = 0; 185 | while ((choice = get_choice()) != 7) { 186 | if (choice == 1) { 187 | printf("Input n and , one student per " 188 | "line\n"); 189 | int n; 190 | scanf("%d", &n); 191 | while (n--) { 192 | student* stu = malloc(sizeof(student)); 193 | scanf("%d %s %d %d %d %d", &stu->id, &stu->name, &stu->eng, &stu->mat, 194 | &stu->phy, &stu->cpl); 195 | s = push_back_list(s, stu); 196 | } 197 | 198 | } else if (choice == 2) { 199 | int idx = 0; 200 | for (node* i = s.head->next; i; i = i->next, idx++) { 201 | student* stu = i->data; 202 | printf("%d: %d %s %d %d %d %d\n", idx, stu->id, stu->name, stu->eng, 203 | stu->mat, stu->phy, stu->cpl); 204 | } 205 | } else if (choice == 3) { 206 | printf("Input student idx and \n"); 207 | student stu, *tbmdf; 208 | int idx; 209 | scanf("%d %d %s %d %d %d %d", &idx, &stu.id, &stu.name, &stu.eng, 210 | &stu.mat, &stu.phy, &stu.cpl); 211 | tbmdf = access_list(student)(s, idx); 212 | *tbmdf = stu; 213 | } else if (choice == 4) { 214 | double sum = 0; 215 | for (node* i = s.head->next; i; i = i->next) { 216 | student* stu = i->data; 217 | sum += stu->cpl + stu->mat + stu->eng + stu->phy; 218 | } 219 | printf("avg: %.2lf\n", sum / 4.0 / s.size); 220 | } else if (choice == 5) { 221 | for (node* i = s.head->next; i; i = i->next) { 222 | double sum = 0; 223 | student* stu = i->data; 224 | sum = stu->cpl + stu->mat + stu->eng + stu->phy; 225 | printf("%d %s sum: %lf avg: %lf\n", stu->id, stu->name, sum, sum / 4); 226 | } 227 | } else if (choice == 6) { 228 | s = sort_list(s); 229 | } 230 | } 231 | drop_list(s); 232 | } 233 | 234 | mint mul(const mint* a, const mint* b) { 235 | const mint* c = a; 236 | if (a->len > b->len) 237 | a = b, b = c; 238 | mint t; 239 | memset(&t, 0, sizeof(t)); 240 | t.len = 1; 241 | for (int i = 0; i < a->len; i++) { 242 | for (int j = 0; j < b->len; ++j) { 243 | t.d[i + j] += a->d[i] * b->d[j]; 244 | } 245 | for (int j = 0; j < t.len; ++j) { 246 | if (t.d[j] >= 10) { 247 | int c = t.d[j] / 10; 248 | t.d[j] %= 10; 249 | t.d[j + 1] += c; 250 | if (j == t.len - 1) 251 | ++t.len; 252 | } 253 | } 254 | } 255 | for (int j = 0; j < t.len; ++j) { 256 | if (t.d[j] >= 10) { 257 | int c = t.d[j] / 10; 258 | t.d[j] %= 10; 259 | t.d[j + 1] += c; 260 | if (j == t.len - 1) 261 | ++t.len; 262 | } 263 | } 264 | for (int i = 499; i >= 1; --i) { 265 | if (t.d[i]) { 266 | t.len = 1 + i; 267 | break; 268 | } 269 | } 270 | return t; 271 | } 272 | 273 | token_result_t parse_num(sds source_code, int ptr, int line, int column, int *skips) { 274 | int v_int = 0; 275 | double v_double = 0; 276 | bool is_double = false, is_long_lit = false; 277 | int len = sdslen(source_code); 278 | int i = ptr, double_cnt = -1; 279 | int base = 10; 280 | if (source_code[i] == '0') { 281 | if (i + 1 < len) { 282 | if (tolower(source_code[i + 1]) == 'x') { 283 | base = 16; 284 | i += 2; 285 | } else if (source_code[i + 1] == '.' || is_valid_symbol_to_end_identifier(source_code[i + 1])) { 286 | 287 | } else if (isdigit(source_code[i + 1])) { 288 | base = 8; 289 | i++; 290 | } else { 291 | sds msg = sdscatprintf(sdsempty() ,"expecting hex or oct lit, but get '%c'", source_code[i + 1]); 292 | return err_token(msg); 293 | } 294 | } 295 | } 296 | 297 | for (; i < len; ++i) { 298 | bool valid = false; 299 | if (base == 10) { 300 | valid = isdigit(source_code[i]); 301 | } else if (base == 8) { 302 | valid = isoctdigit(source_code[i]); 303 | } else { 304 | valid = ishexdigit(source_code[i]); 305 | } 306 | if (valid) { 307 | if (is_double) { 308 | v_double += pow(base, double_cnt--) * (char2int(source_code[i])); 309 | } else { 310 | v_int *= base; 311 | v_int += char2int(source_code[i]); 312 | } 313 | } else { 314 | if (source_code[i] == '.') { 315 | if (base == 8 || base == 16) { 316 | sds msg = sdscatprintf(sdsempty(), "float in oct and hex is not supported"); 317 | return err_token(msg); 318 | } 319 | is_double = true; 320 | v_double = v_int; 321 | } else if (isblank(source_code[i]) || source_code[i] == 0 || is_valid_symbol_to_end_identifier(source_code[i])) { 322 | break; 323 | } else if (source_code[i] == 'L') { 324 | if (is_double) { 325 | sds msg = sdscatprintf(sdsempty(), "unexpected 'L'"); 326 | } else { 327 | is_long_lit = true; 328 | } 329 | } else { 330 | sds err_msg = sdscatprintf(sdsempty(), "unexpected '%c', at line %d, column %d", source_code[i], line, 331 | column); 332 | *skips = i - ptr; 333 | return err_token(err_msg); 334 | } 335 | } 336 | } 337 | *skips = i - ptr; 338 | if (is_double) { 339 | struct Token res = { 340 | .column_num = column, 341 | .line_num = line, 342 | .type = FloatLit, 343 | .v.f = v_double, 344 | }; 345 | return ok_token(res); 346 | } else { 347 | struct Token res = { 348 | .column_num = column, 349 | .line_num = line, 350 | .type = is_long_lit ? LongLit: IntLit, 351 | .v.v = v_int, 352 | }; 353 | return ok_token(res); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /assets/large_num_mul.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | D0 14 | 15 | begin 16 | 17 | 18 | 19 | D4 20 | 21 | const mint* c = a; 22 | 23 | 24 | 25 | D0->D4 26 | 27 | 28 | 29 | 30 | 31 | D1 32 | 33 | end 34 | 35 | 36 | 37 | D6 38 | 39 | (a->len > b->len)? 40 | 41 | 42 | 43 | D4->D6 44 | 45 | 46 | 47 | 48 | 49 | D9 50 | 51 | a = b, b = c; 52 | 53 | 54 | 55 | D6:s->D9:n 56 | 57 | 58 | Y 59 | 60 | 61 | 62 | D11 63 | 64 | mint t; 65 | 66 | 67 | 68 | D6:e->D11:n 69 | 70 | 71 | N 72 | 73 | 74 | 75 | D9->D11 76 | 77 | 78 | 79 | 80 | 81 | D13 82 | 83 | memset(&t, 0, sizeof(t)); 84 | 85 | 86 | 87 | D11->D13 88 | 89 | 90 | 91 | 92 | 93 | D15 94 | 95 | t.len = 1; 96 | 97 | 98 | 99 | D13->D15 100 | 101 | 102 | 103 | 104 | 105 | D20 106 | 107 | int i = 0; 108 | 109 | 110 | 111 | D15->D20 112 | 113 | 114 | 115 | 116 | 117 | D19 118 | 119 | i < a->len? 120 | 121 | 122 | 123 | D27 124 | 125 | int j = 0; 126 | 127 | 128 | 129 | D19:s->D27:n 130 | 131 | 132 | Y 133 | 134 | 135 | 136 | D59 137 | 138 | int j = 0; 139 | 140 | 141 | 142 | D19:e->D59:n 143 | 144 | 145 | N 146 | 147 | 148 | 149 | D20->D19 150 | 151 | 152 | 153 | 154 | 155 | D21 156 | 157 | i++ 158 | 159 | 160 | 161 | D21->D19 162 | 163 | 164 | 165 | 166 | 167 | D26 168 | 169 | j < b->len? 170 | 171 | 172 | 173 | D31 174 | 175 | t.d[i + j] += a->d[i] * b->d[j]; 176 | 177 | 178 | 179 | D26:s->D31:n 180 | 181 | 182 | Y 183 | 184 | 185 | 186 | D36 187 | 188 | int j = 0; 189 | 190 | 191 | 192 | D26:e->D36:n 193 | 194 | 195 | N 196 | 197 | 198 | 199 | D27->D26 200 | 201 | 202 | 203 | 204 | 205 | D28 206 | 207 | ++j 208 | 209 | 210 | 211 | D28->D26 212 | 213 | 214 | 215 | 216 | 217 | D31->D28 218 | 219 | 220 | 221 | 222 | 223 | D35 224 | 225 | j < t.len? 226 | 227 | 228 | 229 | D35:e->D21:n 230 | 231 | 232 | N 233 | 234 | 235 | 236 | D40 237 | 238 | (t.d[j] >= 10)? 239 | 240 | 241 | 242 | D35:s->D40:n 243 | 244 | 245 | Y 246 | 247 | 248 | 249 | D36->D35 250 | 251 | 252 | 253 | 254 | 255 | D37 256 | 257 | ++j 258 | 259 | 260 | 261 | D37->D35 262 | 263 | 264 | 265 | 266 | 267 | D40:e->D37:n 268 | 269 | 270 | N 271 | 272 | 273 | 274 | D45 275 | 276 | int c = t.d[j] / 10; 277 | 278 | 279 | 280 | D40:s->D45:n 281 | 282 | 283 | Y 284 | 285 | 286 | 287 | D47 288 | 289 | t.d[j] %= 10; 290 | 291 | 292 | 293 | D45->D47 294 | 295 | 296 | 297 | 298 | 299 | D49 300 | 301 | t.d[j + 1] += c; 302 | 303 | 304 | 305 | D47->D49 306 | 307 | 308 | 309 | 310 | 311 | D51 312 | 313 | (j == t.len - 1)? 314 | 315 | 316 | 317 | D49->D51 318 | 319 | 320 | 321 | 322 | 323 | D51:e->D37:n 324 | 325 | 326 | N 327 | 328 | 329 | 330 | D54 331 | 332 | ++t.len; 333 | 334 | 335 | 336 | D51:s->D54:n 337 | 338 | 339 | Y 340 | 341 | 342 | 343 | D54->D37 344 | 345 | 346 | 347 | 348 | 349 | D58 350 | 351 | j < t.len? 352 | 353 | 354 | 355 | D63 356 | 357 | (t.d[j] >= 10)? 358 | 359 | 360 | 361 | D58:s->D63:n 362 | 363 | 364 | Y 365 | 366 | 367 | 368 | D82 369 | 370 | int i = 499; 371 | 372 | 373 | 374 | D58:e->D82:n 375 | 376 | 377 | N 378 | 379 | 380 | 381 | D59->D58 382 | 383 | 384 | 385 | 386 | 387 | D60 388 | 389 | ++j 390 | 391 | 392 | 393 | D60->D58 394 | 395 | 396 | 397 | 398 | 399 | D63:e->D60:n 400 | 401 | 402 | N 403 | 404 | 405 | 406 | D68 407 | 408 | int c = t.d[j] / 10; 409 | 410 | 411 | 412 | D63:s->D68:n 413 | 414 | 415 | Y 416 | 417 | 418 | 419 | D70 420 | 421 | t.d[j] %= 10; 422 | 423 | 424 | 425 | D68->D70 426 | 427 | 428 | 429 | 430 | 431 | D72 432 | 433 | t.d[j + 1] += c; 434 | 435 | 436 | 437 | D70->D72 438 | 439 | 440 | 441 | 442 | 443 | D74 444 | 445 | (j == t.len - 1)? 446 | 447 | 448 | 449 | D72->D74 450 | 451 | 452 | 453 | 454 | 455 | D74:e->D60:n 456 | 457 | 458 | N 459 | 460 | 461 | 462 | D77 463 | 464 | ++t.len; 465 | 466 | 467 | 468 | D74:s->D77:n 469 | 470 | 471 | Y 472 | 473 | 474 | 475 | D77->D60 476 | 477 | 478 | 479 | 480 | 481 | D81 482 | 483 | i >= 1? 484 | 485 | 486 | 487 | D86 488 | 489 | (t.d[i])? 490 | 491 | 492 | 493 | D81:s->D86:n 494 | 495 | 496 | Y 497 | 498 | 499 | 500 | D95 501 | 502 | return t; 503 | 504 | 505 | 506 | D81:e->D95:n 507 | 508 | 509 | N 510 | 511 | 512 | 513 | D82->D81 514 | 515 | 516 | 517 | 518 | 519 | D83 520 | 521 | --i 522 | 523 | 524 | 525 | D83->D81 526 | 527 | 528 | 529 | 530 | 531 | D86:e->D83:n 532 | 533 | 534 | N 535 | 536 | 537 | 538 | D91 539 | 540 | t.len = 1 + i; 541 | 542 | 543 | 544 | D86:s->D91:n 545 | 546 | 547 | Y 548 | 549 | 550 | 551 | D93 552 | 553 | break 554 | 555 | 556 | 557 | D91->D93 558 | 559 | 560 | 561 | 562 | 563 | D93->D95 564 | 565 | 566 | 567 | 568 | 569 | D95->D1 570 | 571 | 572 | 573 | 574 | 575 | -------------------------------------------------------------------------------- /assets/list_qsort.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | D0 14 | 15 | begin 16 | 17 | 18 | 19 | D4 20 | 21 | (s == NULL || s->next == NULL)? 22 | 23 | 24 | 25 | D0->D4 26 | 27 | 28 | 29 | 30 | 31 | D1 32 | 33 | end 34 | 35 | 36 | 37 | D7 38 | 39 | return s; 40 | 41 | 42 | 43 | D4:s->D7:n 44 | 45 | 46 | Y 47 | 48 | 49 | 50 | D9 51 | 52 | node* head, *leq = NULL, *g = NULL; 53 | 54 | 55 | 56 | D4:e->D9:n 57 | 58 | 59 | N 60 | 61 | 62 | 63 | D7->D1 64 | 65 | 66 | 67 | 68 | 69 | D11 70 | 71 | node* p = s->next; 72 | 73 | 74 | 75 | D9->D11 76 | 77 | 78 | 79 | 80 | 81 | D13 82 | 83 | head    = s; 84 | 85 | 86 | 87 | D11->D13 88 | 89 | 90 | 91 | 92 | 93 | D15 94 | 95 | s->next = NULL; 96 | 97 | 98 | 99 | D13->D15 100 | 101 | 102 | 103 | 104 | 105 | D19 106 | 107 | p? 108 | 109 | 110 | 111 | D15->D19 112 | 113 | 114 | 115 | 116 | 117 | D24 118 | 119 | node* t      = p->next; 120 | 121 | 122 | 123 | D19:s->D24:n 124 | 125 | 126 | Y 127 | 128 | 129 | 130 | D45 131 | 132 | leq = sort_node(leq); 133 | 134 | 135 | 136 | D19:e->D45:n 137 | 138 | 139 | N 140 | 141 | 142 | 143 | D26 144 | 145 | student* stu = p->data, *piv = head->data; 146 | 147 | 148 | 149 | D24->D26 150 | 151 | 152 | 153 | 154 | 155 | D28 156 | 157 | (isleq(stu, piv))? 158 | 159 | 160 | 161 | D26->D28 162 | 163 | 164 | 165 | 166 | 167 | D33 168 | 169 | p->next = leq; 170 | 171 | 172 | 173 | D28:s->D33:n 174 | 175 | 176 | Y 177 | 178 | 179 | 180 | D39 181 | 182 | p->next = g; 183 | 184 | 185 | 186 | D28:e->D39:n 187 | 188 | 189 | N 190 | 191 | 192 | 193 | D35 194 | 195 | leq     = p; 196 | 197 | 198 | 199 | D33->D35 200 | 201 | 202 | 203 | 204 | 205 | D43 206 | 207 | p = t; 208 | 209 | 210 | 211 | D35->D43 212 | 213 | 214 | 215 | 216 | 217 | D41 218 | 219 | g       = p; 220 | 221 | 222 | 223 | D39->D41 224 | 225 | 226 | 227 | 228 | 229 | D41->D43 230 | 231 | 232 | 233 | 234 | 235 | D43->D19 236 | 237 | 238 | 239 | 240 | 241 | D47 242 | 243 | g   = sort_node(g); 244 | 245 | 246 | 247 | D45->D47 248 | 249 | 250 | 251 | 252 | 253 | D49 254 | 255 | node* res = head; 256 | 257 | 258 | 259 | D47->D49 260 | 261 | 262 | 263 | 264 | 265 | D51 266 | 267 | (leq)? 268 | 269 | 270 | 271 | D49->D51 272 | 273 | 274 | 275 | 276 | 277 | D56 278 | 279 | res = leq; 280 | 281 | 282 | 283 | D51:s->D56:n 284 | 285 | 286 | Y 287 | 288 | 289 | 290 | D67 291 | 292 | head->next = g; 293 | 294 | 295 | 296 | D51:e->D67:n 297 | 298 | 299 | N 300 | 301 | 302 | 303 | D58 304 | 305 | p = leq; 306 | 307 | 308 | 309 | D56->D58 310 | 311 | 312 | 313 | 314 | 315 | D60 316 | 317 | (p->next)? 318 | 319 | 320 | 321 | D58->D60 322 | 323 | 324 | 325 | 326 | 327 | D63 328 | 329 | p = p->next; 330 | 331 | 332 | 333 | D60:s->D63:n 334 | 335 | 336 | Y 337 | 338 | 339 | 340 | D65 341 | 342 | p->next = head; 343 | 344 | 345 | 346 | D60:e->D65:n 347 | 348 | 349 | N 350 | 351 | 352 | 353 | D63->D60 354 | 355 | 356 | 357 | 358 | 359 | D65->D67 360 | 361 | 362 | 363 | 364 | 365 | D69 366 | 367 | return res; 368 | 369 | 370 | 371 | D67->D69 372 | 373 | 374 | 375 | 376 | 377 | D69->D1 378 | 379 | 380 | 381 | 382 | 383 | -------------------------------------------------------------------------------- /assets/phone_number.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | D0 14 | 15 | begin 16 | 17 | 18 | 19 | D4 20 | 21 | char str[100]; 22 | 23 | 24 | 25 | D0->D4 26 | 27 | 28 | 29 | 30 | 31 | D1 32 | 33 | end 34 | 35 | 36 | 37 | D6 38 | 39 | scanf("%s", str); 40 | 41 | 42 | 43 | D4->D6 44 | 45 | 46 | 47 | 48 | 49 | D8 50 | 51 | int len = strlen(str); 52 | 53 | 54 | 55 | D6->D8 56 | 57 | 58 | 59 | 60 | 61 | D10 62 | 63 | (len == 11 && str[0] == '1')? 64 | 65 | 66 | 67 | D8->D10 68 | 69 | 70 | 71 | 72 | 73 | D21 74 | 75 | (str[1]) == '3'? 76 | 77 | 78 | 79 | D10:s->D21:n 80 | 81 | 82 | Y 83 | 84 | 85 | 86 | D40 87 | 88 | printf("not a phone number"); 89 | 90 | 91 | 92 | D10:e->D40:n 93 | 94 | 95 | N 96 | 97 | 98 | 99 | D22 100 | 101 | (str[1]) == '4'? 102 | 103 | 104 | 105 | D21:e->D22:n 106 | 107 | 108 | N 109 | 110 | 111 | 112 | D30 113 | 114 | printf("is a phone number"); 115 | 116 | 117 | 118 | D21:s->D30:n 119 | 120 | 121 | Y 122 | 123 | 124 | 125 | D23 126 | 127 | (str[1]) == '5'? 128 | 129 | 130 | 131 | D22:e->D23:n 132 | 133 | 134 | N 135 | 136 | 137 | 138 | D22:s->D30:n 139 | 140 | 141 | Y 142 | 143 | 144 | 145 | D24 146 | 147 | (str[1]) == '7'? 148 | 149 | 150 | 151 | D23:e->D24:n 152 | 153 | 154 | N 155 | 156 | 157 | 158 | D23:s->D30:n 159 | 160 | 161 | Y 162 | 163 | 164 | 165 | D25 166 | 167 | (str[1]) == '8'? 168 | 169 | 170 | 171 | D24:e->D25:n 172 | 173 | 174 | N 175 | 176 | 177 | 178 | D24:s->D30:n 179 | 180 | 181 | Y 182 | 183 | 184 | 185 | D25:s->D30:n 186 | 187 | 188 | Y 189 | 190 | 191 | 192 | D34 193 | 194 | printf("not a phone number"); 195 | 196 | 197 | 198 | D25:e->D34:n 199 | 200 | 201 | N 202 | 203 | 204 | 205 | D32 206 | 207 | break 208 | 209 | 210 | 211 | D30->D32 212 | 213 | 214 | 215 | 216 | 217 | D32->D1 218 | 219 | 220 | 221 | 222 | 223 | D36 224 | 225 | break 226 | 227 | 228 | 229 | D34->D36 230 | 231 | 232 | 233 | 234 | 235 | D36->D1 236 | 237 | 238 | 239 | 240 | 241 | D40->D1 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /assets/polyline.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | D0 14 | 15 | begin 16 | 17 | 18 | 19 | D4 20 | 21 | char c = getchar(); 22 | 23 | 24 | 25 | D0->D4 26 | 27 | 28 | 29 | 30 | 31 | D1 32 | 33 | end 34 | 35 | 36 | 37 | D6 38 | 39 | int x = 0, f = 1; 40 | 41 | 42 | 43 | D4->D6 44 | 45 | 46 | 47 | 48 | 49 | D8 50 | 51 | (c < '0' || c > '9')? 52 | 53 | 54 | 55 | D6->D8 56 | 57 | 58 | 59 | 60 | 61 | D13 62 | 63 | (c == '-')? 64 | 65 | 66 | 67 | D8:s->D13:n 68 | 69 | 70 | Y 71 | 72 | 73 | 74 | D20 75 | 76 | (c >= '0' && c <= '9')? 77 | 78 | 79 | 80 | D8:e->D20:n 81 | 82 | 83 | N 84 | 85 | 86 | 87 | D16 88 | 89 | f = -1; 90 | 91 | 92 | 93 | D13:s->D16:n 94 | 95 | 96 | Y 97 | 98 | 99 | 100 | D18 101 | 102 | c = getchar(); 103 | 104 | 105 | 106 | D13:e->D18:n 107 | 108 | 109 | N 110 | 111 | 112 | 113 | D16->D18 114 | 115 | 116 | 117 | 118 | 119 | D18->D8 120 | 121 | 122 | 123 | 124 | 125 | D25 126 | 127 | x = x * 10 + c - '0'; 128 | 129 | 130 | 131 | D20:s->D25:n 132 | 133 | 134 | Y 135 | 136 | 137 | 138 | D29 139 | 140 | return x * f; 141 | 142 | 143 | 144 | D20:e->D29:n 145 | 146 | 147 | N 148 | 149 | 150 | 151 | D27 152 | 153 | c = getchar(); 154 | 155 | 156 | 157 | D25->D27 158 | 159 | 160 | 161 | 162 | 163 | D27->D20 164 | 165 | 166 | 167 | 168 | 169 | D29->D1 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /assets/rangefor.cpp: -------------------------------------------------------------------------------- 1 | void basic() { 2 | for (auto i : vec) { 3 | i = i + 1; 4 | } 5 | } 6 | 7 | void auto_ref() { 8 | for (auto & i : map) { 9 | a; 10 | } 11 | } 12 | 13 | void const_auto_rvref() { 14 | for (const auto &&i : vec) { 15 | i - 1; 16 | } 17 | } 18 | 19 | void auto_destruct() { 20 | for (auto [x, y] : vec) { 21 | x + y; 22 | } 23 | } 24 | 25 | void int_init() { 26 | for (int i : vec) { 27 | i; 28 | } 29 | } -------------------------------------------------------------------------------- /assets/switch.c: -------------------------------------------------------------------------------- 1 | // decide if a number is a phone number 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | char str[100]; 8 | scanf("%s", str); 9 | int len = strlen(str); 10 | if (len == 11 && str[0] == '1') { 11 | switch (str[1]) { 12 | case '3': 13 | case '4': 14 | case '5': 15 | case '7': 16 | case '8': 17 | printf("is a phone number"); 18 | break; 19 | 20 | default: 21 | printf("not a phone number"); 22 | break; 23 | } 24 | } else { 25 | printf("not a phone number"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /assets/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int logn = 21; 4 | const int maxn = 2000001; 5 | int f[maxn][logn + 1], Logn[maxn + 1]; 6 | inline int read() { //快读 7 | char c = getchar(); 8 | int x = 0, f = 1; 9 | while (c < '0' || c > '9') { 10 | if (c == '-') f = -1; 11 | c = getchar(); 12 | } 13 | while (c >= '0' && c <= '9') { 14 | x = x * 10 + c - '0'; 15 | c = getchar(); 16 | } 17 | return x * f; 18 | } 19 | void pre() { //准备工作,初始化 20 | Logn[1] = 0; 21 | Logn[2] = 1; 22 | for (int i = 3; i < maxn; i++) { 23 | Logn[i] = Logn[i / 2] + 1; 24 | } 25 | } 26 | 27 | void test0() { 28 | while (c) { 29 | d; 30 | if (e) { 31 | continue; 32 | } 33 | } 34 | } 35 | 36 | void test1 { 37 | while (a) { 38 | while (b) { 39 | if (f) { 40 | break; 41 | } 42 | } 43 | } 44 | } 45 | 46 | void test2() { 47 | if (a) { 48 | while (b) { 49 | e; 50 | } 51 | } else { 52 | f; 53 | } 54 | } 55 | 56 | void test3() { 57 | while (a) { 58 | while (b) { 59 | for (c; d; e) { 60 | f; 61 | if (g) { 62 | break; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | void test4() { 69 | while (g) { 70 | if (h) { 71 | if(i) { 72 | if (j) { 73 | 74 | } else { 75 | if (k) { 76 | while (l) { 77 | wtf; 78 | } 79 | } 80 | } 81 | } 82 | } else { 83 | n; 84 | } 85 | } 86 | o; 87 | } 88 | void test5() { 89 | while (p) { 90 | if (q) { 91 | r; 92 | } else { 93 | s; 94 | } 95 | } 96 | 97 | } 98 | 99 | void test6() { 100 | if (c) { 101 | } else 102 | for (r; s; t) { 103 | u; 104 | if (a) { 105 | break; 106 | } 107 | if (v) { 108 | continue; 109 | } 110 | } 111 | } 112 | 113 | void test7() { 114 | if (a) 115 | b; 116 | else if (c) 117 | d; 118 | else while (e) 119 | for (f; g; h) 120 | do 121 | if (i) 122 | continue; 123 | while(j); 124 | } 125 | 126 | void test8() { 127 | l1 : l2: switch (c) { 128 | case 0: 129 | break; 130 | case 1: 131 | default: 132 | d; 133 | } 134 | } 135 | 136 | void test9() { 137 | switch (c) { 138 | case 0: 139 | case 1: 140 | a + b; 141 | break; 142 | case 2: 143 | case 3: 144 | c + d; 145 | case 4: 146 | switch (f) { 147 | case 1: 148 | l; 149 | break; 150 | case 7: 151 | t; 152 | } 153 | break; 154 | default: 155 | e; 156 | } 157 | } 158 | 159 | void test10() { 160 | switch (a) { 161 | case 1: 162 | switch (b) { 163 | case 1: 164 | break; 165 | default: 166 | break; 167 | } 168 | break; 169 | default: 170 | break; 171 | } 172 | } 173 | 174 | void test11() { 175 | struct grades_list list = {NULL, NULL, 0}; 176 | struct grades *g; 177 | int is_exit = 0; 178 | while (is_exit == 0) { 179 | int choice, num, i, sub_choice; 180 | char ID[16]; 181 | scanf("%d", &choice); 182 | switch (choice) { 183 | case 0: 184 | clean_info(&list); 185 | is_exit = 1; 186 | break; 187 | case 1: /* 输入 */ 188 | scanf("%d", &num); 189 | for (i = 0; i < num; ++i) { 190 | get_grades(&list); 191 | } 192 | sort_grades(&list); 193 | break; 194 | case 2: /* 输出 */ 195 | for_each(&list, print_basic_info); 196 | break; 197 | case 3: /* 修改 */ 198 | scanf("%s%d", ID, &sub_choice); 199 | g = find_grades(&list, ID); 200 | if (g == NULL) 201 | break; 202 | switch (sub_choice) { 203 | case 1: 204 | scanf("%d", &(g->english)); 205 | break; 206 | case 2: 207 | scanf("%d", &(g->math)); 208 | break; 209 | case 3: 210 | scanf("%d", &(g->physics)); 211 | break; 212 | case 4: 213 | scanf("%d", &(g->c_lang)); 214 | break; 215 | default: 216 | break; 217 | } 218 | count_grades(g); 219 | break; 220 | case 4: /* 统计平均 */ 221 | for_each(&list, print_average); 222 | break; 223 | case 5: /* 输出总成绩及平均成绩 */ 224 | for_each(&list, print_sum_and_average); 225 | break; 226 | default: 227 | break; 228 | } 229 | } 230 | return 0; 231 | } 232 | 233 | void incomplete_switch() { 234 | // should error 235 | switch (c) { default } 236 | } 237 | 238 | int main() { 239 | int n = read(), m = read(); 240 | for (int i = 1; i <= n; i++) f[i][0] = read(); 241 | pre(); 242 | for (int j = 1; j <= logn; j++) 243 | for (int i = 1; i + (1 << j) - 1 <= n; i++) 244 | f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); // ST表具体实现 245 | for (int i = 1; i <= m; i++) { 246 | int x = read(), y = read(); 247 | int s = Logn[y - x + 1]; 248 | printf("%d\n", max(f[x][s], f[y - (1 << s) + 1][s])); 249 | } 250 | return 0; 251 | } 252 | -------------------------------------------------------------------------------- /assets/wc.c: -------------------------------------------------------------------------------- 1 | #include 2 | int char_count, word_count, line_count = 1; 3 | char ch; 4 | int state; // the global state of the finite state machine 5 | enum { kchn, keng, kendl, kspc }; // four states 6 | 7 | char fill_ch() { 8 | // read a char from stdin 9 | ++char_count; 10 | ch = getchar(); 11 | return ch; 12 | } 13 | 14 | int is_chn(char s) { return s & 0x80; } // return true if s is a chinese char 15 | 16 | int is_spc(char s) { 17 | return s == ' ' || s == '\n' || s == '\t'; 18 | } // return true if s is space 19 | 20 | int is_newline(char s) { return s == '\n'; } // return true if s is a newline 21 | 22 | int is_eng(char s) { 23 | return ('0' <= s && s <= '9' || 'a' <= s && s <= 'z' || 24 | 'A' <= s && s <= 'Z'); // return true if s is number or alpha 25 | } 26 | 27 | int is_eof(char s) { return s == EOF; } // return true if s is EOF 28 | 29 | char peek_next() { 30 | // first get a char from stdin then put it back 31 | // so as to peek the next char in stdin 32 | ch = getchar(); 33 | ungetc(ch, stdin); 34 | return ch; 35 | } 36 | 37 | void set_state() { 38 | // set global state after peeking the next char 39 | char cc = peek_next(); 40 | if (is_eng(cc)) 41 | state = keng; 42 | else if (is_newline(cc)) 43 | state = kendl; 44 | else if (is_spc(cc)) 45 | state = kspc; 46 | else if (is_chn(cc)) 47 | state = kchn; 48 | } 49 | 50 | int main() { 51 | set_state(); 52 | while (ch != EOF) { 53 | // if ch is not EOF, keep running the FSM 54 | switch (state) { 55 | case kchn: { 56 | fill_ch(); 57 | fill_ch(); 58 | // in this case, I assume that Chinese char is encoded with GB2312. 59 | // if it is encoded with UTF-8, you need to add an additional fill_ch() 60 | ++word_count; 61 | --char_count; 62 | // two char can represent a chinese char, if it is UTF-8, it should be 63 | // char_cout -= 2 64 | set_state(); 65 | break; 66 | } 67 | case keng: { 68 | while (is_eng(ch)) { 69 | fill_ch(); 70 | } 71 | ungetc(ch, stdin); 72 | --char_count; 73 | ++word_count; 74 | set_state(); 75 | break; 76 | } 77 | case kendl: { 78 | ++line_count; 79 | --char_count; 80 | fill_ch(); 81 | set_state(); 82 | break; 83 | } 84 | case kspc: { 85 | fill_ch(); 86 | --char_count; 87 | set_state(); 88 | break; 89 | } 90 | } 91 | } 92 | printf("%d lines, %d chars, %d words", line_count, char_count, word_count); 93 | } 94 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use vergen::EmitBuilder; 3 | 4 | fn main() -> Result<()> { 5 | // Emit the instructions 6 | EmitBuilder::builder() 7 | .all_cargo() 8 | .build_timestamp() 9 | .git_sha(false) 10 | .git_describe(true, true, None) 11 | .all_rustc() 12 | .emit()?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /gallery.md: -------------------------------------------------------------------------------- 1 | # Gallery 2 | 3 | ## 链表快排 4 | 5 | ![list_qsort](assets/list_qsort.svg) 6 | 7 | ## 电话号码判定 8 | 9 | ![phone_number](assets/phone_number.svg) 10 | 11 | ## 学生管理系统 12 | 13 | ![manage_sys](assets/manage_sys.svg) 14 | 15 | ## 高精度乘法 16 | 17 | ![mul](assets/large_num_mul.svg) 18 | 19 | ## 解析数值 20 | 21 | ![parse_num](assets/parse_num.svg) 22 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, ops::Range, rc::Rc}; 2 | 3 | #[derive(Debug)] 4 | pub enum AstNode { 5 | Dummy, 6 | /// Children 7 | Compound(Vec>>), 8 | /// Content 9 | Stat(String), 10 | /// Content 11 | Continue(String), 12 | /// Content 13 | Break(String), 14 | /// Content 15 | Return(String), 16 | /// Condition, Children1, Children2 17 | If { 18 | cond: String, 19 | body: Rc>, 20 | otherwise: Option>>, 21 | }, 22 | /// Condition, Children 23 | While { 24 | cond: String, 25 | body: Rc>, 26 | }, 27 | /// Condition, Children 28 | DoWhile { 29 | cond: String, 30 | body: Rc>, 31 | }, 32 | /// Init, Condition, Update, Children 33 | For { 34 | init: String, 35 | cond: String, 36 | upd: String, 37 | body: Rc>, 38 | }, 39 | /// Condition, Children, Body 40 | Switch { 41 | cond: String, 42 | cases: Vec, 43 | body: Rc>, 44 | }, 45 | /// Label Name 46 | Goto(String), 47 | } 48 | #[derive(Debug)] 49 | pub struct Ast { 50 | pub node: AstNode, 51 | pub range: Range, 52 | pub label: Option>, 53 | } 54 | 55 | impl Ast { 56 | pub fn new(node: AstNode, range: Range, label: Option>) -> Ast { 57 | Ast { node, range, label } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use once_cell::sync::Lazy; 3 | 4 | static NONE: &str = "None"; 5 | static LONG_VERSION: Lazy = Lazy::new(|| { 6 | format!( 7 | " 8 | Version: {} 9 | Build Timestamp: {} 10 | Build Git Describe: {} 11 | Commit SHA: {} 12 | Commit Date: {} 13 | Commit Branch: {} 14 | Cargo Target Triple: {} 15 | ", 16 | env!("CARGO_PKG_VERSION"), 17 | env!("VERGEN_BUILD_TIMESTAMP"), 18 | env!("VERGEN_GIT_DESCRIBE"), 19 | option_env!("VERGEN_GIT_SHA").unwrap_or(NONE), 20 | option_env!("VERGEN_GIT_COMMIT_TIMESTAMP").unwrap_or(NONE), 21 | option_env!("VERGEN_GIT_BRANCH").unwrap_or(NONE), 22 | env!("VERGEN_CARGO_TARGET_TRIPLE"), 23 | ) 24 | }); 25 | #[derive(Parser, Debug)] 26 | #[clap(about, version, long_version(LONG_VERSION.as_str()) ,author, after_help("Note that you need to manually compile the dot file using graphviz to get SVG or PNG files. 27 | 28 | EXAMPLES: 29 | cat main.cpp | cxx2flow | dot -Tsvg -o test.svg 30 | cxx2flow test.cpp | dot -Tpng -o test.png 31 | cxx2flow main.cpp my_custom_func | dot -Tsvg -o test.svg 32 | 33 | Please give me star if this application helps you! 34 | 如果这个应用有帮助到你,请给我点一个 star! 35 | https://github.com/Enter-tainer/cxx2flow 36 | "))] 37 | pub struct Args { 38 | #[clap( 39 | short, 40 | long, 41 | help( 42 | "Sets the output file. 43 | If not specified, result will be directed to stdout. 44 | e.g. graph.dot" 45 | ) 46 | )] 47 | pub output: Option, 48 | 49 | #[clap( 50 | short, 51 | long, 52 | help( 53 | "Sets the style of the flow chart. 54 | If specified, output flow chart will have curly connection line." 55 | ) 56 | )] 57 | pub curly: bool, 58 | 59 | #[clap(long, help("Use C preprocessor."))] 60 | pub cpp: bool, 61 | 62 | #[clap(short, long, help("Use tikz backend."))] 63 | pub tikz: bool, 64 | 65 | #[clap(short, long, help("Use d2 backend."))] 66 | pub d2: bool, 67 | 68 | #[clap(long, help("Dump AST(For debug purpose only)."))] 69 | pub dump_ast: bool, 70 | 71 | #[clap(help( 72 | "Sets the path of the input file. e.g. test.cpp 73 | If not specified, cxx2flow will read from stdin." 74 | ))] 75 | pub input: Option, 76 | 77 | #[clap( 78 | default_value("main"), 79 | help("The function you want to convert. e.g. main") 80 | )] 81 | pub function: String, 82 | } 83 | -------------------------------------------------------------------------------- /src/display/d2.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Result}; 2 | use crate::graph::{Graph, GraphNodeType}; 3 | use petgraph::{ 4 | visit::IntoNodeReferences, 5 | visit::{EdgeRef, IntoEdgeReferences}, 6 | }; 7 | 8 | use super::GraphDisplay; 9 | #[derive(Debug, Default)] 10 | pub struct D2 {} 11 | 12 | impl D2 { 13 | pub fn new() -> Self { 14 | D2 {} 15 | } 16 | } 17 | 18 | impl GraphDisplay for D2 { 19 | fn generate_from_graph(&self, graph: &Graph) -> Result { 20 | let mut res = String::new(); 21 | for (id, i) in graph.node_references() { 22 | match i { 23 | GraphNodeType::Begin => res.push_str(format!("D{}: begin\n", id.index()).as_str()), 24 | GraphNodeType::End => res.push_str(format!("D{}: end\n", id.index()).as_str()), 25 | GraphNodeType::Node(str) => res.push_str( 26 | format!( 27 | "D{}: \"{}\"\n", 28 | id.index(), 29 | str.replace('\"', "\\\"").replace('\n', "\\n") 30 | ) 31 | .as_str(), 32 | ), 33 | GraphNodeType::Choice(str) => { 34 | res.push_str( 35 | format!("D{}: \"{}\"\n", id.index(), str.replace('\"', "\\\"")).as_str(), 36 | ); 37 | res.push_str(format!("D{}.shape: diamond\n", id.index()).as_str()); 38 | } 39 | GraphNodeType::Dummy => { 40 | return Err(Error::UnexpectedDummyGraphNode { 41 | graph: graph.clone(), 42 | }) 43 | } 44 | } 45 | } 46 | for i in graph.edge_references() { 47 | match i.weight() { 48 | crate::graph::EdgeType::Normal => res.push_str( 49 | format!("D{} -> D{}\n", i.source().index(), i.target().index()).as_str(), 50 | ), 51 | crate::graph::EdgeType::Branch(t) => res.push_str( 52 | format!( 53 | "D{} -> D{}: {}\n", 54 | i.source().index(), 55 | i.target().index(), 56 | if *t { "Y" } else { "N" } 57 | ) 58 | .as_str(), 59 | ), 60 | }; 61 | } 62 | Ok(res) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/display/dot.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Result}; 2 | use crate::graph::{Graph, GraphNodeType}; 3 | use petgraph::{ 4 | visit::IntoNodeReferences, 5 | visit::{EdgeRef, IntoEdgeReferences}, 6 | }; 7 | 8 | use super::GraphDisplay; 9 | 10 | pub struct Dot { 11 | curly: bool, 12 | } 13 | 14 | impl Dot { 15 | pub fn new(curly: bool) -> Self { 16 | Dot { curly } 17 | } 18 | } 19 | 20 | impl GraphDisplay for Dot { 21 | fn generate_from_graph(&self, graph: &Graph) -> Result { 22 | let mut res = "digraph {\n".to_string(); 23 | if !self.curly { 24 | res.push_str("graph [splines=polyline];\n"); 25 | } 26 | for (id, i) in graph.node_references() { 27 | match i { 28 | GraphNodeType::Begin => res.push_str( 29 | format!( 30 | "D{} [shape=box, style=rounded, label=\"begin\"];\n", 31 | id.index() 32 | ) 33 | .as_str(), 34 | ), 35 | GraphNodeType::End => res.push_str( 36 | format!( 37 | "{{rank = sink; D{} [shape=box, style=rounded, label=\"end\"];}}\n", 38 | id.index() 39 | ) 40 | .as_str(), 41 | ), 42 | GraphNodeType::Node(str) => res.push_str( 43 | format!( 44 | "D{} [shape=box, label=\"{}\"];\n", 45 | id.index(), 46 | str.replace('\"', "\\\"") 47 | ) 48 | .as_str(), 49 | ), 50 | GraphNodeType::Choice(str) => res.push_str( 51 | format!( 52 | "D{} [shape=diamond, label=\"{}?\"];\n", 53 | id.index(), 54 | str.replace('\"', "\\\"") 55 | ) 56 | .as_str(), 57 | ), 58 | GraphNodeType::Dummy => { 59 | return Err(Error::UnexpectedDummyGraphNode { 60 | graph: graph.clone(), 61 | }) 62 | } // GraphNodeType::Dummy => {} // all dummy node will be eliminated 63 | } 64 | } 65 | 66 | for i in graph.edge_references() { 67 | match i.weight() { 68 | crate::graph::EdgeType::Normal => res.push_str( 69 | format!("D{} -> D{};\n", i.source().index(), i.target().index()).as_str(), 70 | ), 71 | crate::graph::EdgeType::Branch(t) => res.push_str( 72 | format!( 73 | "D{}:{} -> D{}:n [xlabel={}];\n", 74 | i.source().index(), 75 | if *t { "s" } else { "e" }, 76 | i.target().index(), 77 | if *t { "Y" } else { "N" } 78 | ) 79 | .as_str(), 80 | ), 81 | }; 82 | } 83 | res.push_str("}\n"); 84 | Ok(res) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/display/mod.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use crate::{error::Result, graph::Graph}; 4 | 5 | use self::d2::D2; 6 | use self::dot::Dot; 7 | use self::tikz::Tikz; 8 | 9 | pub mod d2; 10 | pub mod dot; 11 | pub mod tikz; 12 | #[enum_dispatch] 13 | pub enum GraphDisplayBackend { 14 | Dot, 15 | Tikz, 16 | D2, 17 | } 18 | #[enum_dispatch(GraphDisplayBackend)] 19 | pub trait GraphDisplay { 20 | fn generate_from_graph(&self, graph: &Graph) -> Result; 21 | } 22 | -------------------------------------------------------------------------------- /src/display/tikz.rs: -------------------------------------------------------------------------------- 1 | use petgraph::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences}; 2 | 3 | use crate::error::{Error, Result}; 4 | use crate::graph::{Graph, GraphNodeType}; 5 | 6 | use super::GraphDisplay; 7 | #[derive(Debug, Default)] 8 | pub struct Tikz {} 9 | 10 | impl Tikz { 11 | pub fn new() -> Self { 12 | Tikz {} 13 | } 14 | } 15 | 16 | impl GraphDisplay for Tikz { 17 | fn generate_from_graph(&self, graph: &Graph) -> Result { 18 | let mut res = r#" 19 | \documentclass[tikz,border=10pt]{standalone} 20 | \usepackage{ctex} 21 | \usetikzlibrary{graphdrawing} 22 | \usetikzlibrary{shapes} 23 | \usepackage{spverbatim} 24 | \usepackage{varwidth} 25 | \usetikzlibrary{graphs} 26 | \usegdlibrary{layered} 27 | \usepackage[T1]{fontenc}% NOT OT1! 28 | \usepackage{lmodern}% Latin Modern fonts, 29 | % a modern variant of Computer Modern fonts 30 | \let\ttdefault\rmdefault 31 | \tikzstyle{block} = [% 32 | draw,thick,fill=blue!0, 33 | inner sep=0.3cm, 34 | text centered, minimum height=1em, 35 | execute at begin node={\begin{varwidth}{8em}}, 36 | execute at end node={\end{varwidth}}] 37 | \begin{document} 38 | \tikz [layered layout, sibling distance=3cm] { 39 | "# 40 | .to_string(); 41 | for (id, i) in graph.node_references() { 42 | match i { 43 | GraphNodeType::Begin => res.push_str( 44 | format!( 45 | "\\node[draw] (D{}) [rounded rectangle, block] {{ Begin }};\n", 46 | id.index() 47 | ) 48 | .as_str(), 49 | ), 50 | GraphNodeType::End => res.push_str( 51 | format!( 52 | "\\node[draw] (D{}) [rounded rectangle, block] {{ End }};\n", 53 | id.index() 54 | ) 55 | .as_str(), 56 | ), 57 | GraphNodeType::Node(str) => res.push_str( 58 | format!( 59 | "\\node[draw] (D{}) [rectangle, block] {{ \\spverb${}$ }};\n", 60 | id.index(), 61 | str.replace('%', "\\%") 62 | ) 63 | .replace('\n', " ") 64 | .as_str(), 65 | ), 66 | GraphNodeType::Choice(str) => res.push_str( 67 | format!( 68 | "\\node[draw] (D{}) [diamond, aspect=2, block] {{ \\spverb${}$ }};\n", 69 | id.index(), 70 | str.replace('%', "\\%") 71 | ) 72 | .replace('\n', " ") 73 | .as_str(), 74 | ), 75 | GraphNodeType::Dummy => { 76 | return Err(Error::UnexpectedDummyGraphNode { 77 | graph: graph.clone(), 78 | }) 79 | } // all dummy node will be eliminated 80 | } 81 | } 82 | for i in graph.edge_references() { 83 | match i.weight() { 84 | crate::graph::EdgeType::Normal => res.push_str( 85 | format!( 86 | "\\draw (D{}) edge[->] (D{});\n", 87 | i.source().index(), 88 | i.target().index() 89 | ) 90 | .as_str(), 91 | ), 92 | crate::graph::EdgeType::Branch(t) => res.push_str( 93 | format!( 94 | "\\draw (D{}) edge[->, below] node {{ {} }} (D{});\n", 95 | i.source().index(), 96 | i.target().index(), 97 | if *t { "Y" } else { "N" } 98 | ) 99 | .as_str(), 100 | ), 101 | } 102 | } 103 | res.push_str( 104 | r#" 105 | } 106 | \end{document} 107 | "#, 108 | ); 109 | Ok(res) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/dump.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use colored::*; 4 | use itertools::Itertools; 5 | use tree_sitter::{Node, TreeCursor}; 6 | 7 | pub fn useful_children<'a, 'tree>( 8 | node: &'a Node<'tree>, 9 | cursor: &'a mut TreeCursor<'tree>, 10 | ) -> impl Iterator> + 'a { 11 | node.children(cursor).enumerate().filter_map(move |(i, n)| { 12 | if n.is_named() || node.field_name_for_child(i as u32).is_some() { 13 | Some(n) 14 | } else { 15 | None 16 | } 17 | }) 18 | } 19 | 20 | #[allow(dead_code)] 21 | fn dump_node_internal( 22 | node: &Node, 23 | prefix: &str, 24 | content: &[u8], 25 | field_name: Option<&str>, 26 | is_last: bool, 27 | is_init: bool, 28 | ) { 29 | let node_text = node.utf8_text(content).unwrap(); 30 | let start = node.start_position(); 31 | let end = node.end_position(); 32 | let kind = node.kind(); 33 | println!( 34 | "{}{}{}: `{}` {} - {}{}", 35 | prefix, 36 | if is_init { 37 | "" 38 | } else if is_last { 39 | "└──" 40 | } else { 41 | "├──" 42 | }, 43 | match field_name { 44 | Some(name) => name.bold().yellow(), 45 | None => "[ANON]".normal(), 46 | }, 47 | kind.bold(), 48 | start, 49 | end, 50 | if node.child_count() == 0 || !node_text.contains('\n') { 51 | format!(" {} {}", "->".cyan(), node_text.bold()).bold() 52 | } else { 53 | "".to_owned().normal() 54 | } 55 | ); 56 | let node_to_idx: HashMap<_, _> = { 57 | let mut cursor = node.walk(); 58 | node.children(&mut cursor) 59 | .enumerate() 60 | .map(|(x, y)| (y, x)) 61 | .collect() 62 | }; 63 | let nodes: Vec<_> = { 64 | let mut cursor = node.walk(); 65 | // useful_children(node, &mut cursor).collect_vec() 66 | node.children(&mut cursor).collect_vec() 67 | }; 68 | let prefix = format!("{}{} ", prefix, if is_last { " " } else { "│" }); 69 | for (pos, i) in nodes.into_iter().with_position() { 70 | match pos { 71 | itertools::Position::First | itertools::Position::Middle => { 72 | dump_node_internal( 73 | &i, 74 | &prefix, 75 | content, 76 | node.field_name_for_child(node_to_idx[&i] as u32), 77 | false, 78 | false, 79 | ); 80 | } 81 | itertools::Position::Last | itertools::Position::Only => { 82 | dump_node_internal( 83 | &i, 84 | &prefix, 85 | content, 86 | node.field_name_for_child(node_to_idx[&i] as u32), 87 | true, 88 | false, 89 | ); 90 | } 91 | } 92 | } 93 | } 94 | 95 | #[allow(dead_code)] 96 | pub fn dump_node(node: &Node, content: &[u8]) { 97 | dump_node_internal(node, "", content, None, true, true); 98 | } 99 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use miette::{Diagnostic, NamedSource, SourceSpan}; 2 | use petgraph::graph::NodeIndex; 3 | use thiserror::Error; 4 | 5 | use crate::graph::Graph; 6 | 7 | #[derive(Debug, Error, Diagnostic)] 8 | pub enum Error { 9 | #[error("transparent")] 10 | #[diagnostic( 11 | code(cxx2flow::tree_sitter), 12 | help("error with tree_sitter parsing library") 13 | )] 14 | TreeSitter(#[from] tree_sitter::LanguageError), 15 | 16 | #[error("transparent")] 17 | #[diagnostic(code(cxx2flow::io), help("error with reading/writing file"))] 18 | Io(#[from] std::io::Error), 19 | 20 | #[error("transparent")] 21 | #[diagnostic(code(cxx2flow::utf8), help("error with UTF-8 decoding"))] 22 | UTF8(#[from] std::str::Utf8Error), 23 | 24 | #[error("transparent")] 25 | #[diagnostic(code(cxx2flow::from_utf8), help("error with UTF-8 decoding"))] 26 | FromUTF8(#[from] std::string::FromUtf8Error), 27 | 28 | #[error("transparent")] 29 | #[diagnostic( 30 | code(cxx2flow::hashchain_insert_failed), 31 | help("hashchain insert failed") 32 | )] 33 | InsertFailed(#[from] hash_chain::Error), 34 | 35 | #[error("target function not found")] 36 | #[diagnostic( 37 | code(cxx2flow::target_function_not_found), 38 | help("maybe you have a typo, or source code is incomplete, \nplease check your input") 39 | )] 40 | FunctionNotFound { 41 | #[source_code] 42 | src: String, 43 | #[label("this is the name of your target function")] 44 | range: SourceSpan, 45 | }, 46 | 47 | #[error("declarator not found")] 48 | #[diagnostic( 49 | code(cxx2flow::declarator_not_found), 50 | help("maybe source code is incomplete, \nplease check your input") 51 | )] 52 | DeclaratorNotFound, 53 | 54 | #[error("child not found")] 55 | #[diagnostic( 56 | code(cxx2flow::child_not_found), 57 | help("maybe source code is incomplete, \nplease check your input") 58 | )] 59 | ChildNotFound, 60 | 61 | #[error("treesitter parse failed")] 62 | #[diagnostic(code(cxx2flow::treesitter_parse_failed))] 63 | TreesitterParseFailed, 64 | 65 | #[diagnostic( 66 | code(cxx2flow::garbage_token), 67 | help("garbage token found in AST\nthis might be a bug, please report it to the author") 68 | )] 69 | #[error("garbage token {0}")] 70 | GarbageToken(&'static str), 71 | 72 | #[diagnostic( 73 | code(cxx2flow::unexpected_continue), 74 | help("maybe you have a continue in a wrong place(e.g. out of a loop)") 75 | )] 76 | #[error("unexpected continue")] 77 | UnexpectedContinue { 78 | #[source_code] 79 | src: NamedSource, 80 | #[label("unexpected continue statement here")] 81 | range: SourceSpan, 82 | }, 83 | 84 | #[diagnostic( 85 | code(cxx2flow::unexpected_break), 86 | help("maybe you have a break in a wrong place(e.g. out of a loop/switch)") 87 | )] 88 | #[error("unexpected break")] 89 | UnexpectedBreak { 90 | #[source_code] 91 | src: NamedSource, 92 | #[label("unexpected break statement here")] 93 | range: SourceSpan, 94 | }, 95 | 96 | #[diagnostic(code(cxx2flow::unexpected_dummy_graph), help("dummy node found in the flow graph\nthis might be a bug, please report it to the author"))] 97 | #[error("unexpected dummy graph node {:?}", petgraph::dot::Dot::new(.graph))] 98 | UnexpectedDummyGraphNode { graph: Graph }, 99 | 100 | #[error("unexpected dummy ast node")] 101 | #[diagnostic( 102 | code(cxx2flow::unexpected_dummy_ast), 103 | help("dummy node found in the ast\nthis might be a bug, please report it to the author") 104 | )] 105 | UnexpectedDummyAstNode { 106 | #[source_code] 107 | src: NamedSource, 108 | #[label("dummy ast node here")] 109 | range: SourceSpan, 110 | }, 111 | 112 | #[error("unexpected outgoing edge: {node_index:?}, neighbors: {neighbors:?}, graph: {:?}", petgraph::dot::Dot::new(.graph))] 113 | #[diagnostic(code(cxx2flow::unexpected_outgoing_nodes), help("usually, every dummy node only has one outgoing edge, but this node has zero or more than one outgoing edges\nthis might be a bug, please report it to the author"))] 114 | UnexpectedOutgoingEdges { 115 | node_index: NodeIndex, 116 | neighbors: Vec, 117 | graph: Graph, 118 | }, 119 | } 120 | 121 | pub type Result = std::result::Result; 122 | -------------------------------------------------------------------------------- /src/graph.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::{Ast, AstNode}; 2 | use crate::error::{Error, Result}; 3 | use hash_chain::ChainMap; 4 | use itertools::{Itertools, Position}; 5 | use miette::NamedSource; 6 | use petgraph::stable_graph::{NodeIndex, StableDiGraph}; 7 | use petgraph::visit::{EdgeRef, IntoNodeReferences}; 8 | use petgraph::EdgeDirection; 9 | use std::collections::HashMap; 10 | use std::{cell::RefCell, rc::Rc}; 11 | #[derive(Debug, PartialEq, Eq, Clone)] 12 | pub enum GraphNodeType { 13 | /// Dummy nodes will be removed eventually 14 | Dummy, 15 | Begin, 16 | End, 17 | Node(String), 18 | Choice(String), 19 | } 20 | 21 | #[derive(Debug, Clone, Copy)] 22 | pub enum EdgeType { 23 | Normal, 24 | Branch(bool), 25 | } 26 | 27 | pub type Graph = StableDiGraph; 28 | 29 | struct GraphContext { 30 | pub graph: Graph, 31 | pub break_target: Option, 32 | pub continue_target: Option, 33 | pub goto_target: ChainMap, 34 | #[allow(dead_code)] 35 | pub global_begin: NodeIndex, 36 | pub global_end: NodeIndex, 37 | pub local_source: NodeIndex, 38 | pub local_sink: NodeIndex, 39 | } 40 | 41 | impl GraphContext { 42 | fn new() -> GraphContext { 43 | let mut graph = Graph::new(); 44 | let begin = graph.add_node(GraphNodeType::Begin); 45 | let end = graph.add_node(GraphNodeType::End); 46 | GraphContext { 47 | graph, 48 | break_target: None, 49 | continue_target: None, 50 | goto_target: ChainMap::new(HashMap::new()), 51 | global_begin: begin, 52 | global_end: end, 53 | local_source: begin, 54 | local_sink: end, 55 | } 56 | } 57 | } 58 | 59 | fn build_graph(ast: &Ast, context: &mut GraphContext, source: &str, file_name: &str) -> Result<()> { 60 | // local_source -> [...current parsing...] -> local_sink 61 | let local_source = context.local_source; 62 | let local_sink = context.local_sink; 63 | let break_target = context.break_target; 64 | let continue_target = context.continue_target; 65 | if let Some(labels) = &ast.label { 66 | for i in labels { 67 | if let Some(v) = context.goto_target.get(i) { 68 | context.graph.add_edge(*v, local_source, EdgeType::Normal); 69 | } else { 70 | let v = context.graph.add_node(GraphNodeType::Dummy); 71 | context.goto_target.insert_at(0, i.clone(), v)?; 72 | // 0 is the global hashmap, goto labels should be put in hashmap 0 73 | context.graph.add_edge(v, local_source, EdgeType::Normal); 74 | } 75 | } 76 | } 77 | match &ast.node { 78 | AstNode::Dummy => { 79 | return Err(Error::UnexpectedDummyAstNode { 80 | src: NamedSource::new(file_name, source.to_string()), 81 | range: ast.range.clone().into(), 82 | }) 83 | } 84 | AstNode::Compound(v) => { 85 | let mut sub_source = context.graph.add_node(GraphNodeType::Dummy); 86 | let mut sub_sink = context.graph.add_node(GraphNodeType::Dummy); 87 | context 88 | .graph 89 | .add_edge(local_source, sub_source, EdgeType::Normal); 90 | // what if v.is_empty()?? 91 | if v.is_empty() { 92 | context 93 | .graph 94 | .add_edge(sub_source, sub_sink, EdgeType::Normal); 95 | } else { 96 | for (pos, i) in v.iter().with_position() { 97 | context.local_source = sub_source; 98 | context.local_sink = sub_sink; 99 | build_graph(&i.borrow(), context, source, file_name)?; 100 | match pos { 101 | itertools::Position::First | itertools::Position::Middle => { 102 | sub_source = sub_sink; 103 | sub_sink = context.graph.add_node(GraphNodeType::Dummy); 104 | } 105 | _ => {} 106 | } 107 | } 108 | } 109 | context 110 | .graph 111 | .add_edge(sub_sink, local_sink, EdgeType::Normal); 112 | context.local_source = local_source; 113 | context.local_sink = local_sink; 114 | } 115 | AstNode::Stat(s) => { 116 | // local_source -> current -> local_sink 117 | let current = context.graph.add_node(GraphNodeType::Node(s.clone())); 118 | context 119 | .graph 120 | .add_edge(local_source, current, EdgeType::Normal); 121 | context 122 | .graph 123 | .add_edge(current, local_sink, EdgeType::Normal); 124 | } 125 | AstNode::Continue(s) => { 126 | // local_source -> current -> continue_target 127 | let current = context.graph.add_node(GraphNodeType::Node(s.clone())); 128 | context 129 | .graph 130 | .add_edge(local_source, current, EdgeType::Normal); 131 | context.graph.add_edge( 132 | current, 133 | context.continue_target.ok_or(Error::UnexpectedContinue { 134 | src: NamedSource::new(file_name, source.to_string()), 135 | range: ast.range.clone().into(), 136 | })?, 137 | EdgeType::Normal, 138 | ); 139 | } 140 | AstNode::Break(s) => { 141 | // local_source -> current -> break_target 142 | let current = context.graph.add_node(GraphNodeType::Node(s.clone())); 143 | context 144 | .graph 145 | .add_edge(local_source, current, EdgeType::Normal); 146 | context.graph.add_edge( 147 | current, 148 | context.break_target.ok_or(Error::UnexpectedBreak { 149 | src: NamedSource::new(file_name, source.to_string()), 150 | range: ast.range.clone().into(), 151 | })?, 152 | EdgeType::Normal, 153 | ); 154 | } 155 | AstNode::Return(s) => { 156 | // local_source -> current -> global_end 157 | let current = context.graph.add_node(GraphNodeType::Node(s.clone())); 158 | context 159 | .graph 160 | .add_edge(local_source, current, EdgeType::Normal); 161 | context 162 | .graph 163 | .add_edge(current, context.global_end, EdgeType::Normal); 164 | } 165 | AstNode::If { 166 | cond, 167 | body, 168 | otherwise, 169 | } => { 170 | // local_source -> cond -> ---Y--> sub_source -> [...body...] -> sub_sink---------------v 171 | // ---N--> sub_source1 -> Option<[...otherwise...]> -> sub_sink -> local_sink 172 | let cond = context.graph.add_node(GraphNodeType::Choice(cond.clone())); 173 | let sub_source = context.graph.add_node(GraphNodeType::Dummy); 174 | let sub_sink = context.graph.add_node(GraphNodeType::Dummy); 175 | context.graph.add_edge(local_source, cond, EdgeType::Normal); 176 | context 177 | .graph 178 | .add_edge(cond, sub_source, EdgeType::Branch(true)); 179 | context 180 | .graph 181 | .add_edge(sub_sink, local_sink, EdgeType::Normal); 182 | context.local_source = sub_source; 183 | context.local_sink = sub_sink; 184 | // context must be restored after calling this function 185 | // only graph should be changed 186 | // so it is OK to process the other branch directly 187 | build_graph(&body.borrow(), context, source, file_name)?; 188 | // restore context 189 | context.local_source = local_source; 190 | context.local_sink = local_sink; 191 | 192 | if let Some(t) = otherwise { 193 | let sub_source1 = context.graph.add_node(GraphNodeType::Dummy); 194 | context 195 | .graph 196 | .add_edge(cond, sub_source1, EdgeType::Branch(false)); 197 | context.local_source = sub_source1; 198 | context.local_sink = sub_sink; 199 | build_graph(&t.borrow(), context, source, file_name)?; 200 | context.local_source = local_source; 201 | context.local_sink = local_sink; 202 | } else { 203 | context 204 | .graph 205 | .add_edge(cond, local_sink, EdgeType::Branch(false)); 206 | } 207 | } 208 | AstNode::While { cond, body } => { 209 | // local_src -> cond ---Y--> sub_source -> [...body...] -> sub_sink 210 | // | \ / 211 | // | N \_______________________________________/ 212 | // v <<< 213 | // local_sink 214 | // continue: jump to cond 215 | // break: jump to local_sink 216 | let cond = context.graph.add_node(GraphNodeType::Choice(cond.clone())); 217 | let sub_source = context.graph.add_node(GraphNodeType::Dummy); 218 | let sub_sink = context.graph.add_node(GraphNodeType::Dummy); 219 | context.graph.add_edge(local_source, cond, EdgeType::Normal); 220 | context 221 | .graph 222 | .add_edge(cond, sub_source, EdgeType::Branch(true)); 223 | context 224 | .graph 225 | .add_edge(cond, local_sink, EdgeType::Branch(false)); 226 | context.graph.add_edge(sub_sink, cond, EdgeType::Normal); 227 | context.continue_target = Some(cond); 228 | context.break_target = Some(local_sink); 229 | context.local_source = sub_source; 230 | context.local_sink = sub_sink; 231 | build_graph(&body.borrow(), context, source, file_name)?; 232 | context.continue_target = continue_target; 233 | context.break_target = break_target; 234 | context.local_source = local_source; 235 | context.local_sink = local_sink; 236 | } 237 | AstNode::DoWhile { cond, body } => { 238 | // local_src -> sub_source -> [...body...] -> sub_sink -> cond ---N--> local_sink 239 | // \ / 240 | // <-----------------Y----------------< 241 | // continue: jump to cond 242 | // break: jump to local_sink 243 | let sub_source = context.graph.add_node(GraphNodeType::Dummy); 244 | let sub_sink = context.graph.add_node(GraphNodeType::Dummy); 245 | let cond = context.graph.add_node(GraphNodeType::Choice(cond.clone())); 246 | context 247 | .graph 248 | .add_edge(local_source, sub_source, EdgeType::Normal); 249 | context.graph.add_edge(sub_sink, cond, EdgeType::Normal); 250 | context 251 | .graph 252 | .add_edge(cond, sub_source, EdgeType::Branch(true)); 253 | context 254 | .graph 255 | .add_edge(cond, local_sink, EdgeType::Branch(false)); 256 | context.continue_target = Some(cond); 257 | context.break_target = Some(local_sink); 258 | context.local_source = sub_source; 259 | context.local_sink = sub_sink; 260 | build_graph(&body.borrow(), context, source, file_name)?; 261 | context.continue_target = continue_target; 262 | context.break_target = break_target; 263 | context.local_source = local_source; 264 | context.local_sink = local_sink; 265 | } 266 | AstNode::For { 267 | init, 268 | cond, 269 | upd, 270 | body, 271 | } => { 272 | // local_source -> init -> cond ---Y--> sub_source -> [...body...] -> sub_sink -> upd 273 | // | \ / 274 | // | \----N--> local_sink / 275 | // |___________________________________________________/ 276 | // <<< 277 | // continue: jump to sub_sink 278 | // break: jump to local_sink 279 | let sub_source = context.graph.add_node(GraphNodeType::Dummy); 280 | let sub_sink = context.graph.add_node(GraphNodeType::Dummy); 281 | let cond = context.graph.add_node(GraphNodeType::Choice(cond.clone())); 282 | let init = context.graph.add_node(GraphNodeType::Node(init.clone())); 283 | let upd = context.graph.add_node(GraphNodeType::Node(upd.clone())); 284 | context.graph.add_edge(local_source, init, EdgeType::Normal); 285 | context.graph.add_edge(init, cond, EdgeType::Normal); 286 | context 287 | .graph 288 | .add_edge(cond, sub_source, EdgeType::Branch(true)); 289 | context 290 | .graph 291 | .add_edge(cond, local_sink, EdgeType::Branch(false)); 292 | context.graph.add_edge(sub_sink, upd, EdgeType::Normal); 293 | context.graph.add_edge(upd, cond, EdgeType::Normal); 294 | context.continue_target = Some(upd); 295 | context.break_target = Some(local_sink); 296 | context.local_source = sub_source; 297 | context.local_sink = sub_sink; 298 | build_graph(&body.borrow(), context, source, file_name)?; 299 | context.continue_target = continue_target; 300 | context.break_target = break_target; 301 | context.local_source = local_source; 302 | context.local_sink = local_sink; 303 | } 304 | AstNode::Switch { cond, body, cases } => { 305 | // local_src -> cond == case[0] ---Y-> goto case[0] 306 | // ---N-> cond == case[1] .... 307 | // ---N--> goto default 308 | // sub_src -> [..body..] -> sub_sink -> local_sink 309 | // continue: None 310 | // break: local_sink 311 | let case_goto_targets: HashMap = cases 312 | .iter() 313 | .map(|c| (c.clone(), context.graph.add_node(GraphNodeType::Dummy))) 314 | .collect(); 315 | let table_start = generate_jump_table( 316 | cond, 317 | &mut context.graph, 318 | &mut cases.iter().filter(|x| *x != "default").with_position(), 319 | &case_goto_targets, 320 | &cases.iter().any(|x| x == "default"), 321 | &local_sink, 322 | ); 323 | context 324 | .graph 325 | .add_edge(local_source, table_start, EdgeType::Normal); 326 | let sub_source = context.graph.add_node(GraphNodeType::Dummy); 327 | let sub_sink = context.graph.add_node(GraphNodeType::Dummy); 328 | context.goto_target.new_child_with(case_goto_targets); 329 | context.local_source = sub_source; 330 | context.local_sink = sub_sink; 331 | context.break_target = Some(local_sink); 332 | context.continue_target = None; 333 | context 334 | .graph 335 | .add_edge(sub_sink, local_sink, EdgeType::Normal); 336 | build_graph(&body.borrow(), context, source, file_name)?; 337 | context.local_source = local_source; 338 | context.local_sink = local_sink; 339 | context.break_target = break_target; 340 | context.continue_target = continue_target; 341 | context.goto_target.remove_child(); 342 | } 343 | AstNode::Goto(t) => { 344 | // local_source -> goto_target 345 | if let Some(target) = context.goto_target.get(t) { 346 | context 347 | .graph 348 | .add_edge(local_source, *target, EdgeType::Normal); 349 | } else { 350 | let v = context.graph.add_node(GraphNodeType::Dummy); 351 | context.goto_target.insert_at(0, t.clone(), v)?; 352 | context.graph.add_edge(local_source, v, EdgeType::Normal); 353 | } 354 | } 355 | } 356 | Ok(()) 357 | } 358 | 359 | fn generate_jump_table<'a, I, R>( 360 | cond: &str, 361 | graph: &mut Graph, 362 | iter: &mut I, 363 | case_goto_targets: &HashMap, 364 | has_default: &bool, 365 | sink: &NodeIndex, 366 | ) -> NodeIndex 367 | where 368 | I: Itertools, 369 | R: AsRef, 370 | { 371 | if let Some((pos, i)) = iter.next() { 372 | // dbg!(i); 373 | let cur = graph.add_node(GraphNodeType::Choice(format!("{} == {}", cond, i.as_ref()))); 374 | graph.add_edge(cur, case_goto_targets[i.as_ref()], EdgeType::Branch(true)); 375 | match pos { 376 | itertools::Position::First | itertools::Position::Middle => { 377 | let idx = 378 | generate_jump_table(cond, graph, iter, case_goto_targets, has_default, sink); 379 | graph.add_edge(cur, idx, EdgeType::Branch(false)); 380 | } 381 | itertools::Position::Last | itertools::Position::Only => { 382 | if *has_default { 383 | graph.add_edge(cur, case_goto_targets["default"], EdgeType::Branch(false)); 384 | } else { 385 | graph.add_edge(cur, *sink, EdgeType::Branch(false)); 386 | } 387 | } 388 | }; 389 | cur 390 | } else { 391 | let cur = graph.add_node(GraphNodeType::Dummy); 392 | if *has_default { 393 | graph.add_edge(cur, case_goto_targets["default"], EdgeType::Normal); 394 | } else { 395 | graph.add_edge(cur, *sink, EdgeType::Normal); 396 | } 397 | cur 398 | } 399 | } 400 | 401 | fn remove_zero_in_degree_nodes(graph: &mut Graph, _source: &str) -> bool { 402 | let nodes = graph 403 | .node_indices() 404 | .filter(|i| -> bool { 405 | *graph.node_weight(*i).unwrap() == GraphNodeType::Dummy 406 | && graph.edges_directed(*i, EdgeDirection::Incoming).count() == 0 407 | }) 408 | .collect_vec(); 409 | nodes 410 | .iter() 411 | .map(|x| graph.remove_node(*x)) 412 | .any(|x| x.is_some()) 413 | } 414 | 415 | // remove the first node which predicate(node) == True 416 | // return Ok(true) if successfully remove a node 417 | // return Ok(false) if no node is available 418 | // return Err if there are more than one predecessors 419 | fn remove_single_node(graph: &mut Graph, _source: &str, predicate: F) -> Result 420 | where 421 | F: Fn(NodeIndex, &GraphNodeType) -> bool, 422 | { 423 | // take first dummy node 424 | if let Some(node_index) = graph 425 | .node_references() 426 | .filter(|(x, t)| predicate(*x, t)) 427 | .map(|(x, _)| x) 428 | .take(1) 429 | .next() 430 | { 431 | let incoming_edges = graph 432 | .edges_directed(node_index, EdgeDirection::Incoming) 433 | .map(|x| (x.source(), *x.weight())) 434 | .collect_vec(); 435 | let neighbors = graph 436 | .neighbors_directed(node_index, EdgeDirection::Outgoing) 437 | .collect_vec(); 438 | if neighbors.len() != 1 { 439 | return Err(Error::UnexpectedOutgoingEdges { 440 | node_index, 441 | neighbors, 442 | graph: graph.clone(), 443 | }); 444 | } 445 | let next_node = neighbors[0]; 446 | for (src, edge_type) in incoming_edges { 447 | // add edge: i.src -> next_node 448 | graph.add_edge(src, next_node, edge_type); 449 | } 450 | graph.remove_node(node_index); 451 | Ok(true) 452 | } else { 453 | Ok(false) 454 | } 455 | } 456 | 457 | pub fn from_ast(ast: Rc>, source: &str, file_name: &str) -> Result { 458 | let mut ctx = GraphContext::new(); 459 | build_graph(&ast.borrow(), &mut ctx, source, file_name)?; 460 | // dbg!(petgraph::dot::Dot::new(&ctx.graph)); 461 | while remove_zero_in_degree_nodes(&mut ctx.graph, source) {} 462 | while remove_single_node(&mut ctx.graph, source, |_, t| *t == GraphNodeType::Dummy)? {} 463 | let remove_empty_nodes: fn(NodeIndex, &GraphNodeType) -> bool = |_, t| match t { 464 | GraphNodeType::Node(t) => t.is_empty() || t.trim() == ";", 465 | _ => false, 466 | }; 467 | while remove_single_node(&mut ctx.graph, source, remove_empty_nodes)? {} 468 | Ok(ctx.graph) 469 | } 470 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ast; 2 | pub mod cli; 3 | pub mod display; 4 | pub mod dump; 5 | pub mod error; 6 | mod graph; 7 | mod parser; 8 | use display::{GraphDisplay, GraphDisplayBackend}; 9 | use error::Result; 10 | pub fn generate( 11 | content: &[u8], 12 | file_name: &str, 13 | function_name: Option, 14 | backend: GraphDisplayBackend, 15 | ) -> Result { 16 | let ast = parser::parse(content, file_name, function_name)?; 17 | // dbg!(&ast); 18 | let graph = graph::from_ast(ast, &String::from_utf8(content.to_vec())?, file_name)?; 19 | // dbg!(&graph); 20 | backend.generate_from_graph(&graph) 21 | } 22 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use cxx2flow::{ 2 | cli::Args, 3 | display::{d2::D2, dot::Dot, tikz::Tikz}, 4 | dump, 5 | error::Error, 6 | }; 7 | use std::{ 8 | io::{Read, Write}, 9 | process::{self, Stdio}, 10 | }; 11 | use tree_sitter::Parser; 12 | 13 | use itertools::Itertools; 14 | 15 | use cxx2flow::generate; 16 | use miette::IntoDiagnostic; 17 | 18 | fn main() -> miette::Result<()> { 19 | miette::set_panic_hook(); 20 | let args = ::parse(); 21 | let mut content: Vec = Vec::new(); 22 | match args.input { 23 | Some(ref file_name) => { 24 | content = std::fs::read(file_name).into_diagnostic()?; 25 | } 26 | None => { 27 | std::io::stdin() 28 | .read_to_end(&mut content) 29 | .into_diagnostic()?; 30 | } 31 | }; 32 | let content = if args.cpp { 33 | let mut cpp = process::Command::new("cpp") 34 | .stdin(Stdio::piped()) 35 | .stdout(Stdio::piped()) 36 | .spawn() 37 | .into_diagnostic()?; 38 | if let Some(mut child_stdin) = cpp.stdin.take() { 39 | child_stdin.write_all(&content).into_diagnostic()?; 40 | } 41 | cpp.wait_with_output().into_diagnostic()?.stdout 42 | } else { 43 | content 44 | }; 45 | let content = Itertools::intersperse( 46 | String::from_utf8(content) 47 | .unwrap() 48 | .lines() 49 | .filter(|x| !x.starts_with('#')), 50 | "\n", 51 | ) 52 | .collect::() 53 | .into_bytes(); 54 | if args.dump_ast { 55 | let mut parser = Parser::new(); 56 | let language = tree_sitter_cpp::language(); 57 | parser 58 | .set_language(&language) 59 | .map_err(|_| Error::TreesitterParseFailed)?; 60 | let tree = parser 61 | .parse(&content, None) 62 | .ok_or(Error::TreesitterParseFailed)?; 63 | dump::dump_node(&tree.root_node(), &content); 64 | return Ok(()); 65 | } 66 | let backend = if args.tikz { 67 | Tikz::new().into() 68 | } else if args.d2 { 69 | D2::new().into() 70 | } else { 71 | Dot::new(args.curly).into() 72 | }; 73 | let res = generate( 74 | &content, 75 | &args.input.unwrap_or_else(|| "stdin".to_owned()), 76 | Some(args.function), 77 | backend, 78 | )?; 79 | if let Some(output) = args.output { 80 | std::fs::write(output, res).into_diagnostic()?; 81 | } else { 82 | print!("{}", res); 83 | } 84 | Ok(()) 85 | } 86 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use crate::ast::{Ast, AstNode}; 4 | #[allow(unused_imports)] 5 | use crate::dump::dump_node; 6 | use crate::error::{Error, Result}; 7 | use tree_sitter::{Node, Parser, TreeCursor}; 8 | 9 | fn filter_ast<'a>(node: Node<'a>, kind: &str) -> Option> { 10 | if node.kind() == kind { 11 | return Some(node); 12 | } 13 | let mut cursor = node.walk(); 14 | if cursor.goto_first_child() { 15 | loop { 16 | if let Some(v) = filter_ast(cursor.node(), kind) { 17 | return Some(v); 18 | } 19 | if !cursor.goto_next_sibling() { 20 | break; 21 | } 22 | } 23 | } 24 | None 25 | } 26 | 27 | pub fn parse( 28 | content: &[u8], 29 | _file_name: &str, 30 | function_name: Option, 31 | ) -> Result>> { 32 | let mut parser = Parser::new(); 33 | let language = tree_sitter_cpp::language(); 34 | parser.set_language(&language)?; 35 | let tree = parser 36 | .parse(content, None) 37 | .ok_or(Error::TreesitterParseFailed)?; 38 | let mut cursor = tree.walk(); 39 | cursor.goto_first_child(); 40 | let mut functions: Vec = Vec::new(); 41 | loop { 42 | let node = cursor.node(); 43 | let node = filter_ast(node, "function_definition"); 44 | if let Some(node) = node { 45 | functions.push(node); 46 | } 47 | if !cursor.goto_next_sibling() { 48 | break; 49 | } 50 | } 51 | let target_function = function_name.unwrap_or_else(|| "main".to_string()); 52 | for i in functions { 53 | cursor.reset(i); 54 | let stats = cursor 55 | .node() 56 | .child_by_field_name("body") 57 | .ok_or(Error::ChildNotFound)?; 58 | let node = cursor 59 | .node() 60 | .child_by_field_name("declarator") 61 | .ok_or(Error::DeclaratorNotFound)?; 62 | let func_name = filter_ast(node, "identifier"); 63 | if func_name.is_none() { 64 | continue; 65 | } 66 | let func_name = func_name.unwrap().utf8_text(content)?; 67 | if func_name != target_function { 68 | continue; 69 | } 70 | let res = parse_stat(stats, content)?; 71 | remove_dummy(res.clone()); 72 | return Ok(res); 73 | } 74 | Err(Error::FunctionNotFound { 75 | src: target_function.clone(), 76 | range: (0..target_function.len()).into(), 77 | }) 78 | } 79 | 80 | fn remove_dummy(ast: Rc>) { 81 | match &mut ast.borrow_mut().node { 82 | AstNode::If { 83 | body, otherwise, .. 84 | } => { 85 | remove_dummy(body.clone()); 86 | if let Some(otherwise) = otherwise { 87 | remove_dummy(otherwise.clone()); 88 | } 89 | } 90 | AstNode::While { body, .. } 91 | | AstNode::DoWhile { body, .. } 92 | | AstNode::For { body, .. } 93 | | AstNode::Switch { body, .. } => { 94 | remove_dummy(body.clone()); 95 | } 96 | AstNode::Compound(v) => { 97 | v.retain(|x| !matches!(x.borrow().node, AstNode::Dummy)); 98 | v.iter().for_each(|x| { 99 | remove_dummy(x.clone()); 100 | }); 101 | } 102 | _ => {} 103 | } 104 | } 105 | 106 | fn parse_stat(stat: Node, content: &[u8]) -> Result>> { 107 | match stat.kind() { 108 | "compound_statement" => { 109 | let mut cursor = stat.walk(); 110 | let mut vec = Vec::new(); 111 | if !cursor.goto_first_child() { 112 | return Ok(Rc::new(RefCell::new(Ast::new( 113 | AstNode::Compound(Vec::new()), 114 | stat.byte_range(), 115 | None, 116 | )))); 117 | } 118 | loop { 119 | let node = cursor.node(); 120 | let ast = parse_stat(node, content)?; 121 | vec.push(ast); 122 | if !cursor.goto_next_sibling() { 123 | break; 124 | } 125 | } 126 | Ok(Rc::new(RefCell::new(Ast::new( 127 | AstNode::Compound(vec), 128 | stat.byte_range(), 129 | None, 130 | )))) 131 | } 132 | "labeled_statement" => { 133 | let mut label_vec = Vec::new(); 134 | let mut cursor = stat.walk(); 135 | loop { 136 | let node = cursor.node(); 137 | let label_str = node 138 | .child_by_field_name("label") 139 | .ok_or(Error::ChildNotFound)? 140 | .utf8_text(content)?; 141 | label_vec.push(label_str.to_owned()); 142 | cursor.goto_first_child(); 143 | while cursor.goto_next_sibling() {} 144 | if cursor.node().kind() != "labeled_statement" { 145 | break; 146 | } 147 | } 148 | let ast = parse_stat(cursor.node(), content)?; 149 | ast.borrow_mut().label = Some(label_vec); 150 | Ok(ast) 151 | } 152 | _ => { 153 | let res = parse_single_stat(stat, content); 154 | match res { 155 | Ok(res) => Ok(res), 156 | Err(msg) => { 157 | if !matches!(msg, Error::GarbageToken(_)) { 158 | Err(msg) 159 | } else { 160 | Ok(Rc::new(RefCell::new(Ast::new( 161 | AstNode::Dummy, 162 | stat.byte_range(), 163 | None, 164 | )))) 165 | } 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | fn parse_single_stat(stat: Node, content: &[u8]) -> Result>> { 173 | match stat.kind() { 174 | "continue_statement" => Ok(Rc::new(RefCell::new(Ast::new( 175 | AstNode::Continue("continue".to_string()), 176 | stat.byte_range(), 177 | None, 178 | )))), 179 | "break_statement" => Ok(Rc::new(RefCell::new(Ast::new( 180 | AstNode::Break("break".to_string()), 181 | stat.byte_range(), 182 | None, 183 | )))), 184 | "return_statement" => { 185 | let str = stat.utf8_text(content)?; 186 | Ok(Rc::new(RefCell::new(Ast::new( 187 | AstNode::Return(String::from(str)), 188 | stat.byte_range(), 189 | None, 190 | )))) 191 | } 192 | "if_statement" => parse_if_stat(stat, content), 193 | "while_statement" => parse_while_stat(stat, content), 194 | "do_statement" => parse_do_while_stat(stat, content), 195 | "for_statement" => parse_for_stat(stat, content), 196 | "for_range_loop" => parse_range_for_stat(stat, content), 197 | "switch_statement" => parse_switch_stat(stat, content), 198 | "goto_statement" => parse_goto_stat(stat, content), 199 | "expression_statement" | "declaration" => { 200 | let str = stat.utf8_text(content)?; 201 | Ok(Rc::new(RefCell::new(Ast::new( 202 | AstNode::Stat(String::from(str)), 203 | stat.byte_range(), 204 | None, 205 | )))) 206 | } 207 | // ignore all unrecognized token 208 | c => Err(Error::GarbageToken(c)), 209 | } 210 | } 211 | 212 | fn parse_if_stat(if_stat: Node, content: &[u8]) -> Result>> { 213 | let condition = if_stat 214 | .child_by_field_name("condition") 215 | .ok_or(Error::ChildNotFound)?; 216 | let blk1 = if_stat.child_by_field_name("consequence"); 217 | let blk2 = if_stat.child_by_field_name("alternative"); 218 | let cond_str = condition.utf8_text(content)?; 219 | let body = parse_stat(blk1.ok_or(Error::ChildNotFound)?, content)?; 220 | 221 | let otherwise = if let Some(blk2) = blk2 { 222 | let cnt = blk2.child_count(); 223 | Some(parse_stat( 224 | blk2.child(cnt - 1).ok_or(Error::ChildNotFound)?, 225 | content, 226 | )?) 227 | } else { 228 | None 229 | }; 230 | 231 | let res = Rc::new(RefCell::new(Ast::new( 232 | AstNode::If { 233 | cond: String::from(cond_str), 234 | body, 235 | otherwise, 236 | }, 237 | if_stat.byte_range(), 238 | None, 239 | ))); 240 | Ok(res) 241 | } 242 | 243 | fn parse_while_stat(while_stat: Node, content: &[u8]) -> Result>> { 244 | let condition = while_stat 245 | .child_by_field_name("condition") 246 | .ok_or(Error::ChildNotFound)?; 247 | let body = while_stat.child_by_field_name("body"); 248 | let cond_str = condition.utf8_text(content)?; 249 | let body = parse_stat(body.ok_or(Error::ChildNotFound)?, content)?; 250 | 251 | let res = Rc::new(RefCell::new(Ast::new( 252 | AstNode::While { 253 | cond: String::from(cond_str), 254 | body, 255 | }, 256 | while_stat.byte_range(), 257 | None, 258 | ))); 259 | Ok(res) 260 | } 261 | 262 | /// return first child, or return the case label 263 | fn get_case_child_and_label<'a>( 264 | mut case_stat: tree_sitter::TreeCursor<'a>, 265 | content: &[u8], 266 | ) -> Result<(Option>, String)> { 267 | // dump_node(&case_stat.node(), None); 268 | let label = { 269 | let tmp = if case_stat 270 | .node() 271 | .child(0) 272 | .ok_or(Error::ChildNotFound)? 273 | .kind() 274 | == "case" 275 | { 276 | case_stat 277 | .node() 278 | .child(1) 279 | .ok_or(Error::ChildNotFound)? 280 | .utf8_text(content)? 281 | } else { 282 | case_stat 283 | .node() 284 | .child(0) 285 | .ok_or(Error::ChildNotFound)? 286 | .utf8_text(content)? 287 | }; 288 | tmp.into() 289 | }; 290 | case_stat.goto_first_child(); 291 | if case_stat.node().kind() == "case" { 292 | // case lit : 293 | case_stat.goto_next_sibling(); 294 | case_stat.goto_next_sibling(); 295 | } else if case_stat.node().kind() == "default" { 296 | // default : 297 | case_stat.goto_next_sibling(); 298 | } 299 | while [":", "comment"].contains(&case_stat.node().kind()) { 300 | if !case_stat.goto_next_sibling() { 301 | return Ok((None, label)); 302 | } 303 | } 304 | // dump_node(&case_stat.node(), None); 305 | Ok((Some(case_stat), label)) 306 | } 307 | 308 | fn parse_switch_stat(switch_stat: Node, content: &[u8]) -> Result>> { 309 | let condition = switch_stat 310 | .child_by_field_name("condition") 311 | .ok_or(Error::ChildNotFound)?; 312 | let body = switch_stat 313 | .child_by_field_name("body") 314 | .ok_or(Error::ChildNotFound)?; 315 | let cond_str = condition.utf8_text(content)?; 316 | let mut stats = Vec::new(); 317 | let mut labels = Vec::new(); 318 | let mut cases = Vec::new(); 319 | let mut cursor = body.walk(); 320 | cursor.goto_first_child(); // brace 321 | cursor.goto_next_sibling(); // case statement 322 | // dbg!(cursor.node()); 323 | loop { 324 | let (child, label) = get_case_child_and_label(cursor.clone(), content)?; 325 | labels.push(label.clone()); 326 | cases.push(label); 327 | if let Some(child) = child { 328 | let mut cursor = child; 329 | let first_idx = stats.len(); 330 | loop { 331 | let stat = parse_stat(cursor.node(), content)?; 332 | stats.push(stat); 333 | if !cursor.goto_next_sibling() { 334 | break; 335 | } 336 | } 337 | stats[first_idx].borrow_mut().label = Some(labels.clone()); 338 | labels.clear(); 339 | } 340 | if !cursor.goto_next_sibling() { 341 | break; 342 | } 343 | if cursor.node().kind() != "case_statement" { 344 | break; 345 | } 346 | } 347 | let inner = Rc::new(RefCell::new(Ast::new( 348 | AstNode::Compound(stats), 349 | switch_stat.byte_range(), 350 | None, 351 | ))); 352 | let res = Rc::new(RefCell::new(Ast::new( 353 | AstNode::Switch { 354 | cond: String::from(cond_str), 355 | cases, 356 | body: inner, 357 | }, 358 | switch_stat.byte_range(), 359 | None, 360 | ))); 361 | Ok(res) 362 | } 363 | 364 | fn parse_goto_stat(goto_stat: Node, content: &[u8]) -> Result>> { 365 | let label_str = goto_stat 366 | .child_by_field_name("label") 367 | .ok_or(Error::ChildNotFound)? 368 | .utf8_text(content)?; 369 | Ok(Rc::new(RefCell::new(Ast::new( 370 | AstNode::Goto(label_str.to_owned()), 371 | goto_stat.byte_range(), 372 | None, 373 | )))) 374 | } 375 | 376 | fn parse_do_while_stat(do_while_stat: Node, content: &[u8]) -> Result>> { 377 | let condition = do_while_stat 378 | .child_by_field_name("condition") 379 | .ok_or(Error::ChildNotFound)?; 380 | let body = do_while_stat.child_by_field_name("body"); 381 | let cond_str = condition.utf8_text(content)?; 382 | let body = parse_stat(body.ok_or(Error::ChildNotFound)?, content)?; 383 | let res = Rc::new(RefCell::new(Ast::new( 384 | AstNode::DoWhile { 385 | cond: String::from(cond_str), 386 | body, 387 | }, 388 | do_while_stat.byte_range(), 389 | None, 390 | ))); 391 | 392 | Ok(res) 393 | } 394 | 395 | fn parse_for_stat(for_stat: Node, content: &[u8]) -> Result>> { 396 | let mut cursor = for_stat.walk(); 397 | let init = for_stat.child_by_field_name("initializer"); 398 | let cond = for_stat.child_by_field_name("condition"); 399 | let update = for_stat.child_by_field_name("update"); 400 | let mut init_str: String = String::new(); 401 | let mut cond_str: String = String::from("true"); 402 | let mut update_str: String = String::new(); 403 | if let Some(init) = init { 404 | let init = init.utf8_text(content)?; 405 | init_str = String::from(init); 406 | } 407 | if let Some(cond) = cond { 408 | let cond = cond.utf8_text(content)?; 409 | cond_str = String::from(cond); 410 | } 411 | if let Some(update) = update { 412 | let update = update.utf8_text(content)?; 413 | update_str = String::from(update); 414 | } 415 | cursor.goto_first_child(); 416 | while cursor.goto_next_sibling() {} 417 | let body = parse_stat(cursor.node(), content)?; 418 | let res = Rc::new(RefCell::new(Ast::new( 419 | AstNode::For { 420 | init: init_str, 421 | cond: cond_str, 422 | upd: update_str, 423 | body, 424 | }, 425 | for_stat.byte_range(), 426 | None, 427 | ))); 428 | Ok(res) 429 | } 430 | 431 | fn parse_range_for_stat(range_for_stat: Node, content: &[u8]) -> Result>> { 432 | let ty = range_for_stat 433 | .child_by_field_name("type") 434 | .ok_or(Error::ChildNotFound)?; 435 | let declarator = range_for_stat 436 | .child_by_field_name("declarator") 437 | .ok_or(Error::ChildNotFound)?; 438 | let range = range_for_stat 439 | .child_by_field_name("right") 440 | .ok_or(Error::ChildNotFound)?; 441 | let body = range_for_stat 442 | .child_by_field_name("body") 443 | .ok_or(Error::ChildNotFound)?; 444 | let body = parse_stat(body, content)?; 445 | let type_text = ty.utf8_text(content)?; 446 | let init_text = declarator.utf8_text(content)?; 447 | let range_text = range.utf8_text(content)?; 448 | let real_init_text = format!("{init_text}_iter = {range_text}.begin()"); 449 | let real_cond_text = format!("{init_text}_iter != {range_text}.end()"); 450 | let real_update_text = format!("++{init_text}_iter"); 451 | let res = Rc::new(RefCell::new(Ast::new( 452 | AstNode::For { 453 | init: real_init_text, 454 | cond: real_cond_text, 455 | upd: real_update_text, 456 | body: Rc::new(RefCell::new(Ast::new( 457 | AstNode::Compound(vec![ 458 | Rc::new(RefCell::new(Ast::new( 459 | AstNode::Stat(format!("{type_text} {init_text} = *{init_text}_iter")), 460 | range_for_stat.byte_range(), 461 | None, 462 | ))), 463 | body, 464 | ]), 465 | range_for_stat.byte_range(), 466 | None, 467 | ))), 468 | }, 469 | range_for_stat.byte_range(), 470 | None, 471 | ))); 472 | Ok(res) 473 | } 474 | --------------------------------------------------------------------------------