Compare commits

...

56 Commits

Author SHA1 Message Date
Jake Bailey 4ee23cbc55
Stop recommending comma-separated CLI args (#1305) 2025-12-16 19:24:18 +00:00
Rick 370d494723
feat: add support for C++20 modules (#1278)
* add support for C++20 modules

* add support for C++20 modules

* add support for C++20 modules

* add C++20 module support

* add C++20 module support

* fixed? param comment

* hope to have fixed the lines error

* hopefully actually fixed the lines error

* this should fix everything

* if this doesn't fix it i don't know what will
2025-12-07 12:18:49 +01:00
Zhizhen He fc5cea5a12
chore: fix typos (#1303) 2025-12-07 12:17:50 +01:00
Kem Chen 17e8418d9f
feat: Add language support for Ark TypeScript (#1300)
reference: https://en.wikipedia.org/wiki/ArkTS
2025-12-03 10:38:28 +01:00
Ben Beasley 5b597c070c
Update clap-cargo from 0.13 to 0.18 (#1298) 2025-11-26 14:40:30 +01:00
Filip Czaplicki 6f3556bd66
Add colors to --help (#1296) 2025-11-25 14:53:21 +01:00
gtker 179155fe73
Add support for M1 assembly, the hex0/1/2 family, and kaem files (#1295) 2025-11-18 21:28:04 +01:00
Hoang Nguyen 21f3da2c38
Add macro file extension `fnlm` for Fennel (#1293) 2025-11-16 20:04:01 +01:00
Mihai-Laurențiu Țucă fe513a7bee
Fix several typos (#1294) 2025-11-16 20:03:00 +01:00
Oscar Ahlén 2208e0073f
Add Avalonia Xaml support (#1280) 2025-09-22 09:33:57 +02:00
Martin Leduc d80c99d7ae
Change name from VB6 to VB6/VBA and add missing VB6 extensions (#1281) 2025-09-22 09:33:28 +02:00
Ilaï Deutel 7535d00140
Add `cli` default feature to remove unnecessary dependencies from library (#1203) 2025-09-15 18:40:05 +02:00
XAMPPRocky f78be23e9c
chore: remove alpha prefix 2025-08-15 22:53:55 +02:00
github-actions[bot] 45e5df6af5
chore: release v13.0.0-alpha.9 (#1229)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-28 10:46:35 +02:00
XAMPPRocky e0ab4375d8
chore: use download-artifact v4 2025-07-21 12:20:13 +02:00
XAMPPRocky 3bcde18dcf
chore: update upload artifact to v4 2025-07-21 12:14:46 +02:00
XAMPPRocky 62a564da6e
Update README.md 2025-02-24 18:25:37 +01:00
Ben Beasley 7f258f473c
Fix CRLF or mixed CRLF/LF line terminations in Markdown files (#1219) 2025-01-23 18:42:23 +01:00
Ben Beasley 97e6892d67
Fix a minor typo in CLI help text (#1217)
The typo was introduced during the clap v3 migration in
177d32e024.
2025-01-23 18:41:48 +01:00
Ben Beasley 6f2b9b2216
Fix a missing space in CLI help text (#1218) 2025-01-23 18:41:28 +01:00
SandaruKasa 9b0f179f8f
Relax lifetime constraints in language::embedding (#1225) 2025-01-14 18:03:46 +01:00
github-actions[bot] edbd5d5cbb
chore: release v13.0.0-alpha.8 (#1195)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-14 14:41:15 +01:00
James McCrystal f71c75dd71
add Mojo support (#1107) (#1185)
* fix #1107

Adds support for Mojo language

* add Mojo support with test (#1107)

* add Mojo support with test (#1107)

* add Mojo support with test (#1107)
2025-01-14 14:07:47 +01:00
Erik Schierboom 029045a5a3
Add support for 8th language (#1192)
* Add support for 8th language

* Update languages.json

---------

Co-authored-by: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com>
2024-11-20 11:12:50 +01:00
Erik Schierboom bf83c0a01a
Add support for `Roc` language (#1197) 2024-11-18 11:18:19 +01:00
Erik Schierboom 06499611c8
Add support for Ballerina language (#1196) 2024-11-18 11:16:59 +01:00
Simon Fowler d2b30026a6
Remove 'conf' extension from the Bitbake config. (#1001)
This really can't be claimed by a single language, unless it's an
attempt at a "generic" config file format.
2024-11-16 11:42:15 +01:00
Erik Schierboom 480125cf5c
Add support for Cairo language (#1193) 2024-11-16 11:41:46 +01:00
Erik Schierboom 563f15395d
Add support for Uiua language (#1191) 2024-11-16 11:40:13 +01:00
github-actions[bot] 837fe44f28
chore: release v13.0.0-alpha.7 (#1186) 2024-11-10 10:35:33 +01:00
Tony Zorman 91f2043478
Fix alternative output formats (#1188) 2024-11-10 10:16:49 +01:00
Lucas Franceschino 3f07a0c7c6
Add missing extension `fsti` for F* (#1184)
This PR just adds `fsti` to list of extensions for F*.
(while `fst` are for implementations, `fsti` are for interfaces)
2024-10-31 09:46:29 +01:00
github-actions[bot] 16a97f3e91
chore: release (#1182)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-11 13:56:45 +02:00
embeddedc 349b01c4b6
Add language definition for Lauterbach PRACTICE Script (#1162)
Co-authored-by: embeddedc <>
2024-10-11 12:37:24 +02:00
Marc Addeo 60928375d3
Add support for justfiles (#1175)
* Add support for justfiles

* Fix test file for justfile

* Use Just as language name instead of Justfile

* Add Just to README
2024-10-11 12:36:47 +02:00
Steven Shaw 8f62624dd9
Add Virgil (#1178) 2024-10-11 12:36:14 +02:00
Baraa Al-Masri c2699d06c3
Add templ support (#1122)
* Add support for templ

* Remove file extension `tpl` (conflict with Pan)

* Add quotes and important_syntax

* Update readme

* Add test for templ
2024-09-30 11:24:00 +02:00
petrisch 167af762d1
Update README.md with HiCAD from d4a1814 (#1143) 2024-09-30 11:23:25 +02:00
Tony Zorman 3688ee5cdb
Add BQN support (#1151) 2024-09-30 10:46:49 +02:00
CrazyboyQCD 320d1d2c58
chore: add more extensions for `Hlsl` (#1164) 2024-09-30 10:46:12 +02:00
Pete Lomax 4faf752140
Add Phix (#1167) 2024-09-30 10:45:47 +02:00
Tony Zorman 2b1752ee09
Add APL support (#1152) 2024-09-30 10:45:18 +02:00
Per Nordlöw 7ea37aaed7
Add support for SIL (#1153) 2024-09-30 10:44:55 +02:00
Kevin Ji 758d89f126
Use `OR` operator in Cargo.toml `license` field (#1165)
The use of `/` is deprecated, per the Cargo reference:
https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields
2024-09-29 19:03:52 +02:00
yjh 1923f2137c
feat: add `mbti` extension for MoonBit (#1168) 2024-09-19 10:40:37 +02:00
Kornel c61fcbe2c3
Disable legacy Cargo features (#1158) 2024-09-01 14:45:31 +02:00
Zhiqiang Zhang d2f54a8fe0
add slint language support (#1054) 2024-08-30 09:57:27 +02:00
András B Nagy 143a99aaa3
Add Pyret support (#1032) 2024-08-30 09:56:54 +02:00
Shynur 8a19d10e62
Recognize GNUmakefile (#1021)
* language.json (Makefile): Add filename - gnumakefile.
2024-08-30 09:56:12 +02:00
github-actions[bot] 6e75f90751
chore: release (#1150) 2024-08-23 17:04:29 +02:00
qtfkwk bf009ef2be
fix issue https://github.com/XAMPPRocky/tokei/issues/1147 (#1149) 2024-08-23 15:45:39 +02:00
qtfkwk 3e09c2352e
Fix issue #1145 (part 2) (#1148) 2024-08-23 15:45:13 +02:00
github-actions[bot] ff11207539
chore: release (#1144) 2024-08-22 19:46:40 +02:00
qtfkwk 03b0dc56a2
fix: fix issue https://github.com/XAMPPRocky/tokei/issues/1145 (#1146)
Co-authored-by: qtfkwk <qtfkwk+tokei@gmail.com>
2024-08-22 19:40:21 +02:00
Ignace Maes 612981e4a8
Add support for Glimmer JS/TS (#1052)
* feat: add glimmer lang support

* tests: add glimmer lang test files

* fix: use js comment matches

* tests: add style block example

* fix: use template tag

* feat: add template multiline comments to gjs/gts

* fix(tests): comment line include heads

* tests: add template comment examples

* fix(test): rename test file as it doesn't like dashes

* fix: no hbs comments
2024-08-22 11:35:06 +02:00
qtfkwk 2645a116d1
Fix issue #1141 (#1142) 2024-08-21 10:07:22 +02:00
38 changed files with 3496 additions and 1410 deletions

View File

@ -21,7 +21,7 @@ jobs:
repo: cross
matches: ${{ matrix.platform }}
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: cross-${{ matrix.platform }}
path: ${{ steps.cross.outputs.install_path }}
@ -64,7 +64,7 @@ jobs:
needs: install-cross
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: cross-apple-darwin
path: /usr/local/bin/
@ -94,7 +94,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Download Cross
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: cross-linux-musl
path: /tmp/

File diff suppressed because it is too large Load Diff

50
Cargo.lock generated
View File

@ -43,9 +43,9 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.8"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
@ -201,18 +201,29 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.16"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap-cargo"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936551935c8258754bb8216aec040957d261f977303754b9bf1a213518388006"
dependencies = [
"anstyle",
"clap",
]
[[package]]
name = "clap_builder"
version = "4.5.15"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [
"anstream",
"anstyle",
@ -222,10 +233,22 @@ dependencies = [
]
[[package]]
name = "clap_lex"
version = "0.7.2"
name = "clap_derive"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "colorchoice"
@ -1309,12 +1332,12 @@ dependencies = [
[[package]]
name = "terminal_size"
version = "0.3.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
dependencies = [
"rustix",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@ -1354,11 +1377,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokei"
version = "13.0.0-alpha.3"
version = "13.0.0"
dependencies = [
"aho-corasick",
"arbitrary",
"clap",
"clap-cargo",
"colored",
"crossbeam-channel",
"dashmap",

View File

@ -3,7 +3,6 @@ authors = ["Erin Power <xampprocky@gmail.com>"]
build = "build.rs"
categories = ["command-line-utilities", "development-tools", "visualization"]
description = "Count your code, quickly."
edition = "2018"
homepage = "https://tokei.rs"
include = [
"Cargo.lock",
@ -15,22 +14,29 @@ include = [
"src/**/*",
]
keywords = ["utility", "cli", "cloc", "lines", "statistics"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
name = "tokei"
readme = "README.md"
repository = "https://github.com/XAMPPRocky/tokei.git"
version = "13.0.0-alpha.3"
version = "13.0.0"
rust-version = "1.71"
edition = "2021"
[features]
all = ["cbor", "yaml"]
cbor = ["hex", "serde_cbor"]
default = []
yaml = ["serde_yaml"]
cbor = ["dep:hex", "dep:serde_cbor"]
cli = ["dep:clap", "dep:colored", "dep:env_logger", "dep:num-format"]
default = ["cli"]
yaml = ["dep:serde_yaml"]
[profile.release]
lto = "thin"
panic = "abort"
[[bin]]
name = "tokei"
required-features = ["cli"]
[build-dependencies]
tera = "1.20.0"
ignore = "0.4.22"
@ -40,8 +46,8 @@ json5 = "0.4.1"
[dependencies]
aho-corasick = "1.1.3"
arbitrary = { version = "1.3.2", features = ["derive"] }
clap = { version = "4", features = ["cargo", "string", "wrap_help"] }
colored = "2.1.0"
clap = { version = "4", optional = true, features = ["cargo", "string", "wrap_help"] }
colored = { version = "2.1.0", optional = true }
crossbeam-channel = "0.5.13"
encoding_rs_io = "0.1.7"
grep-searcher = "0.1.13"
@ -53,14 +59,16 @@ term_size = "0.3.2"
toml = "0.8.19"
parking_lot = "0.12.3"
dashmap = { version = "6.0.1", features = ["serde"] }
num-format = "0.4.4"
num-format = { version = "0.4.4", optional = true }
once_cell = "1.19.0"
regex = "1.10.6"
serde_json = "1.0.125"
etcetera = "0.8.0"
table_formatter = "0.6.1"
clap-cargo = "0.18.1"
[dependencies.env_logger]
optional = true
features = []
version = "0.11.5"

1217
README.md

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ To launch a fuzzing job: `cargo +nightly fuzz run <target>` - it will run until
To use multiple cores: `cargo +nightly fuzz run <target> --jobs=6`
To speed things up (at the expensive of missing bugs that only manifest in larger files):
To speed things up (at the expense of missing bugs that only manifest in larger files):
`cargo +nightly fuzz run <target> -- -max_len=200`
Available fuzz targets:

View File

@ -33,6 +33,12 @@
"multi_line_comments": [["/*", "*/"]],
"extensions": ["als"]
},
"Apl": {
"name": "APL",
"line_comment": ["⍝"],
"extensions": ["apl", "aplf", "apls"],
"quotes": [["'", "'"]]
},
"Arduino": {
"name": "Arduino C++",
"line_comment": ["//"],
@ -40,6 +46,13 @@
"quotes": [["\\\"", "\\\""]],
"extensions": ["ino"]
},
"ArkTS": {
"name": "Ark TypeScript",
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
"quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]],
"extensions": ["ets"]
},
"Arturo": {
"line_comment": [";"],
"quotes": [["\\\"", "\\\""]],
@ -122,11 +135,25 @@
"line_comment": ["#"],
"extensions": ["am"]
},
"AvaloniaXaml": {
"name": "AXAML",
"multi_line_comments": [["<!--", "-->"]],
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["axaml"]
},
"AWK": {
"line_comment": ["#"],
"shebangs": ["#!/bin/awk -f"],
"extensions": ["awk"]
},
"Ballerina": {
"line_comment": ["//", "#"],
"quotes": [
["\\\"", "\\\""],
["`", "`"]
],
"extensions": ["bal"]
},
"Bash": {
"name": "BASH",
"shebangs": ["#!/bin/bash"],
@ -161,7 +188,13 @@
"name": "Bitbake",
"line_comment": ["#"],
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["bb", "bbclass", "bbappend", "inc", "conf"]
"extensions": ["bb", "bbclass", "bbappend", "inc"]
},
"Bqn": {
"name": "BQN",
"line_comment": ["#"],
"extensions": ["bqn"],
"quotes": [["\\\"", "\\\""], ["'", "'"]]
},
"BrightScript": {
"quotes": [["\\\"", "\\\""]],
@ -180,6 +213,14 @@
"multi_line_comments": [["{-", "-}"]],
"extensions": ["cabal"]
},
"Cairo": {
"line_comment": ["//"],
"extensions": ["cairo"],
"quotes": [
["\\\"", "\\\""],
["'", "'"]
]
},
"Cangjie": {
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
@ -299,6 +340,14 @@
"quotes": [["\\\"", "\\\""]],
"extensions": ["hh", "hpp", "hxx", "inl", "ipp"]
},
"CppModule": {
"name": "C++ Module",
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
"quotes": [["\\\"", "\\\""]],
"verbatim_quotes": [["R\\\"(", ")\\\""]],
"extensions": ["cppm", "ixx", "ccm", "mpp", "mxx", "cxxm", "hppm", "hxxm"]
},
"Crystal": {
"line_comment": ["#"],
"shebangs": ["#!/usr/bin/crystal"],
@ -444,6 +493,14 @@
"line_comment": [";"],
"extensions": ["edn"]
},
"Eighth": {
"name": "8th",
"line_comment": ["\\\\ ", "-- "],
"multi_line_comments": [["(*", "*)"]],
"nested": true,
"quotes": [["\\\"", "\\\""]],
"extensions": ["8th"]
},
"Elisp": {
"name": "Emacs Lisp",
"line_comment": [";"],
@ -499,7 +556,7 @@
"Fennel" : {
"line_comment": [";", ";;"],
"quotes": [["\\\"", "\\\""]],
"extensions": ["fnl"]
"extensions": ["fnl", "fnlm"]
},
"Fish": {
"shebangs": ["#!/bin/fish"],
@ -566,7 +623,7 @@
"quotes": [["\\\"", "\\\""]],
"line_comment": ["//"],
"multi_line_comments": [["(*", "*)"]],
"extensions": ["fst"]
"extensions": ["fst", "fsti"]
},
"Futhark": {
"line_comment": ["--"],
@ -598,6 +655,22 @@
"quotes": [["\\\"", "\\\""]],
"extensions": ["gleam"]
},
"GlimmerJs": {
"name": "Glimmer JS",
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"], ["<!--", "-->"]],
"quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]],
"important_syntax": ["<template", "<style"],
"extensions": ["gjs"]
},
"GlimmerTs": {
"name": "Glimmer TS",
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"], ["<!--", "-->"]],
"quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]],
"important_syntax": ["<template", "<style"],
"extensions": ["gts"]
},
"Glsl": {
"name": "GLSL",
"line_comment": ["//"],
@ -690,6 +763,18 @@
"blank": true,
"extensions": ["hex"]
},
"Hex0": {
"extensions": ["hex0"],
"line_comment": ["#", ";"]
},
"Hex1": {
"extensions": ["hex1"],
"line_comment": ["#", ";"]
},
"Hex2": {
"extensions": ["hex2"],
"line_comment": ["#", ";"]
},
"HiCad": {
"name": "HICAD",
"line_comment": ["REM", "rem"],
@ -700,7 +785,7 @@
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
"quotes": [["\\\"", "\\\""]],
"extensions": ["hlsl"]
"extensions": ["hlsl", "fx", "fxsub"]
},
"HolyC": {
"line_comment": ["//"],
@ -846,6 +931,13 @@
"name": "Jupyter Notebooks",
"extensions": ["ipynb"]
},
"Just": {
"shebangs": ["#!/usr/bin/env just --justfile"],
"env": ["just"],
"line_comment": ["#"],
"extensions": ["just"],
"filenames": ["justfile"]
},
"K": {
"name": "K",
"nested": true,
@ -859,6 +951,11 @@
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["kak"]
},
"Kaem": {
"name": "Kaem",
"line_comment": ["#"],
"extensions": ["kaem"]
},
"Kotlin": {
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
@ -977,6 +1074,12 @@
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["lucius"]
},
"M1Assembly": {
"name": "M1 Assembly",
"extensions": ["m1"],
"line_comment": ["#", ";"],
"quotes": [["\\\"", "\\\""]]
},
"M4": {
"extensions": ["m4"],
"line_comment": ["#", "dnl"],
@ -990,7 +1093,7 @@
"Makefile": {
"line_comment": ["#"],
"extensions": ["makefile", "mak", "mk"],
"filenames": ["makefile"]
"filenames": ["gnumakefile", "makefile"]
},
"Markdown": {
"literate": true,
@ -1048,6 +1151,12 @@
"extensions": ["def"],
"line_comment": [";"]
},
"Mojo": {
"line_comment": ["#"],
"doc_quotes": [["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["'''", "'''"]],
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["mojo", "🔥"]
},
"MonkeyC": {
"name": "Monkey C",
"extensions": ["mc"],
@ -1058,7 +1167,7 @@
"MoonBit": {
"line_comment": ["//"],
"quotes": [["\\\"", "\\\""]],
"extensions": ["mbt"]
"extensions": ["mbt", "mbti"]
},
"MoonScript": {
"line_comment": ["--"],
@ -1208,6 +1317,14 @@
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["pest"]
},
"Phix": {
"line_comment": ["--", "//", "#!"],
"multi_line_comments": [["/*", "*/"], ["--/*", "--*/"]],
"nested": true,
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"verbatim_quotes": [["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["`", "`"]],
"extensions": ["e","exw"]
},
"Php": {
"name": "PHP",
"line_comment": ["#", "//"],
@ -1261,6 +1378,12 @@
],
"extensions": ["ps1", "psm1", "psd1", "ps1xml", "cdxml", "pssc", "psc1"]
},
"PRACTICE": {
"name": "Lauterbach PRACTICE Script",
"line_comment": [";", "//"],
"quotes": [["\\\"", "\\\""]],
"extensions": ["cmm"]
},
"Processing": {
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
@ -1305,6 +1428,13 @@
"multi_line_comments": [["{-", "-}"]],
"extensions": ["purs"]
},
"Pyret": {
"line_comment": ["#"],
"multi_line_comments": [["#|", "|#"]],
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["arr"],
"nested": true
},
"Python": {
"line_comment": ["#"],
"doc_quotes": [["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["'''", "'''"]],
@ -1426,6 +1556,15 @@
"blank": true,
"extensions": ["rst"]
},
"Roc": {
"line_comment": ["#"],
"quotes": [
["\\\"", "\\\""],
["'", "'"]
],
"doc_quotes": [["\\\"\\\"\\\"", "\\\"\\\"\\\""]],
"extensions": ["roc"]
},
"RON": {
"name": "Rusty Object Notation",
"line_comment": ["//"],
@ -1505,6 +1644,13 @@
"quotes": [["\\\"", "\\\""]],
"extensions": ["shader", "cginc"]
},
"SIL": {
"name": "SIL",
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"], ["/+", "+/"]],
"quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]],
"extensions": ["sil"]
},
"Slang": {
"name": "Slang",
"line_comment": ["//"],
@ -1626,6 +1772,12 @@
"quotes": [["\\\"", "\\\""]],
"extensions": ["sv", "svh"]
},
"Slint": {
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
"quotes": [["\\\"", "\\\""]],
"extensions": ["slint"]
},
"Tact": {
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
@ -1643,6 +1795,14 @@
"quotes": [["\\\"", "\\\""], ["'", "'"]],
"extensions": ["tera"]
},
"Templ": {
"name": "Templ",
"line_comment": ["//"],
"multi_line_comments": [["<!--", "-->"], ["/*", "*/"]],
"quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]],
"important_syntax": ["templ", "script", "css"],
"extensions": ["templ", "tmpl"]
},
"Tex": {
"name": "TeX",
"line_comment": ["%"],
@ -1704,6 +1864,11 @@
"quotes": [["\\\"", "\\\""]],
"extensions": ["typ"]
},
"Uiua": {
"line_comment": ["#"],
"quotes": [["\\\"", "\\\""]],
"extensions": ["ua"]
},
"UMPL": {
"line_comment": ["!"],
"quotes": [["`", "`"]],
@ -1770,9 +1935,9 @@
"extensions": ["vala"]
},
"VB6": {
"name": "VB6",
"name": "VB6/VBA",
"line_comment": ["'"],
"extensions": ["frm", "bas", "cls"]
"extensions": ["frm", "bas", "cls", "ctl", "dsr"]
},
"VBScript": {
"name": "VBScript",
@ -1802,6 +1967,12 @@
"multi_line_comments": [["/*", "*/"]],
"extensions": ["vhd", "vhdl"]
},
"Virgil": {
"line_comment": ["//"],
"multi_line_comments": [["/*", "*/"]],
"quotes": [["\\\"", "\\\""]],
"extensions": ["v3"]
},
"VisualBasic": {
"name": "Visual Basic",
"quotes": [["\\\"", "\\\""]],

View File

@ -1,4 +1,4 @@
use std::process;
use std::{process, str::FromStr};
use clap::{crate_description, value_parser, Arg, ArgAction, ArgMatches};
use colored::Colorize;
@ -52,7 +52,6 @@ pub struct Cli {
pub types: Option<Vec<LanguageType>>,
pub compact: bool,
pub number_format: num_format::CustomFormat,
pub verbose: u64,
}
impl Cli {
@ -60,6 +59,7 @@ impl Cli {
let matches = clap::Command::new("tokei")
.version(crate_version())
.author("Erin P. <xampprocky@gmail.com> + Contributors")
.styles(clap_cargo::style::CLAP_STYLING)
.about(concat!(
crate_description!(),
"\n",
@ -80,7 +80,7 @@ impl Cli {
Arg::new("exclude")
.long("exclude")
.short('e')
.num_args(0..)
.action(ArgAction::Append)
.help("Ignore all files & directories matching the pattern."),
)
.arg(
@ -109,7 +109,7 @@ impl Cli {
Arg::new("input")
.num_args(1..)
.conflicts_with("languages")
.help("The path(s) to the file or directory to be counted.(default current directory)"),
.help("The path(s) to the file or directory to be counted. (default current directory)"),
)
.arg(
Arg::new("languages")
@ -142,7 +142,7 @@ impl Cli {
.action(ArgAction::SetTrue)
.help(
"\
Don't respect .ignore and .tokeignore files, including this in \
Don't respect .ignore and .tokeignore files, including those in \
parent directories.\
",
))
@ -159,13 +159,7 @@ impl Cli {
Arg::new("output")
.long("output")
.short('o')
.value_parser(|x: &str| {
if Format::all().contains(&x) {
Ok(x.to_string())
} else {
Err(format!("Invalid output format: {x:?}"))
}
})
.value_parser(Format::from_str)
.help(
"Outputs Tokei in a specific format. Compile with additional features for \
more format support.",
@ -277,12 +271,19 @@ impl Cli {
// Sorting category should be restricted by clap but parse before we do
// work just in case.
let (sort, sort_reverse) = if let Some(sort) = matches.get_one::<Sort>("sort") {
(Some(*sort), false)
let (sort, sort_reverse) = if let Some(sort) = matches.get_one::<String>("sort") {
(Some(sort.clone()), false)
} else {
let sort = matches.get_one::<Sort>("rsort");
let sort = matches.get_one::<String>("rsort");
(sort.cloned(), sort.is_some())
};
let sort = sort.map(|x| match Sort::from_str(&x) {
Ok(sort) => sort,
Err(e) => {
eprintln!("Error:\n{}", e);
process::exit(1);
}
});
// Format category is overly accepting by clap (so the user knows what
// is supported) but this will fail if support is not compiled in and
@ -312,7 +313,6 @@ impl Cli {
types,
compact,
number_format,
verbose,
};
debug!("CLI Config: {:#?}", cli);
@ -326,15 +326,15 @@ impl Cli {
pub fn ignored_directories(&self) -> Vec<&str> {
let mut ignored_directories: Vec<&str> = Vec::new();
if let Some(user_ignored) = self.matches.get_many::<&str>("exclude") {
ignored_directories.extend(user_ignored);
if let Some(user_ignored) = self.matches.get_many::<String>("exclude") {
ignored_directories.extend(user_ignored.map(|x| x.as_str()));
}
ignored_directories
}
pub fn input(&self) -> Vec<&str> {
match self.matches.get_many::<&str>("input") {
Some(vs) => vs.cloned().collect(),
match self.matches.get_many::<String>("input") {
Some(vs) => vs.map(|x| x.as_str()).collect(),
None => vec!["."],
}
}

View File

@ -111,10 +111,6 @@ impl NumberFormatStyle {
}
}
pub fn all() -> &'static [&'static str] {
&["commas", "dots", "plain", "underscores"]
}
pub fn get_format(self) -> Result<num_format::CustomFormat, num_format::Error> {
num_format::CustomFormat::builder()
.grouping(num_format::Grouping::Standard)

View File

@ -52,7 +52,7 @@ pub struct Config {
/// Whether to output only the paths for downstream batch processing
/// *Default:* false
#[serde(skip)]
/// adds a closure for each function, e.g., print the result
/// Adds a closure for each function, e.g., print the result
pub for_each_fn: Option<fn(LanguageType, Report)>,
}
@ -140,7 +140,7 @@ impl Config {
}
/*
/// Configuration for a individual [`LanguageType`].
/// Configuration for an individual [`LanguageType`].
///
/// ```
/// use std::collections::HashMap;

View File

@ -1,12 +1,27 @@
// Set of common pub consts.
/// Fallback row length
pub const FALLBACK_ROW_LEN: usize = 81;
// Column widths used for console printing.
/// Language column width
pub const LANGUAGE_COLUMN_WIDTH: usize = 10;
/// Path column width
pub const PATH_COLUMN_WIDTH: usize = 80;
/// Files column width
pub const FILES_COLUMN_WIDTH: usize = 8;
/// Lines column width
pub const LINES_COLUMN_WIDTH: usize = 12;
/// Code column width
pub const CODE_COLUMN_WIDTH: usize = 12;
/// Comments column width
pub const COMMENTS_COLUMN_WIDTH: usize = 12;
/// Blanks column width
pub const BLANKS_COLUMN_WIDTH: usize = 12;

View File

@ -80,33 +80,37 @@ pub(crate) struct SimpleCapture<'a> {
}
impl<'a> HtmlLike<'a> {
pub fn start_script_in_range(
&'a self,
pub fn start_script_in_range<'this>(
&'this self,
start: usize,
end: usize,
) -> Option<impl Iterator<Item = &'a Capture<'a>>> {
) -> Option<impl Iterator<Item = &'this Capture<'a>>> {
filter_range(self.start_script.as_ref()?, start, end)
}
pub fn start_style_in_range(
&'a self,
pub fn start_style_in_range<'this>(
&'this self,
start: usize,
end: usize,
) -> Option<impl Iterator<Item = &'a Capture<'a>>> {
) -> Option<impl Iterator<Item = &'this Capture<'a>>> {
filter_range(self.start_style.as_ref()?, start, end)
}
pub fn start_template_in_range(
&'a self,
pub fn start_template_in_range<'this>(
&'this self,
start: usize,
end: usize,
) -> Option<impl Iterator<Item = &'a Capture<'a>>> {
) -> Option<impl Iterator<Item = &'this Capture<'a>>> {
filter_range(self.start_template.as_ref()?, start, end)
}
}
impl<'a> SimpleCapture<'a> {
pub fn starts_in_range(&'a self, start: usize, end: usize) -> Option<&Capture<'a>> {
pub fn starts_in_range<'this>(
&'this self,
start: usize,
end: usize,
) -> Option<&'this Capture<'a>> {
filter_range(self.starts.as_ref()?, start, end).and_then(|mut it| it.next())
}
@ -128,11 +132,11 @@ impl<'a> SimpleCapture<'a> {
}
}
fn filter_range<'a>(
dataset: &'a [Capture<'a>],
fn filter_range<'dataset, 'cap>(
dataset: &'dataset [Capture<'cap>],
start: usize,
end: usize,
) -> Option<impl Iterator<Item = &'a Capture<'a>>> {
) -> Option<impl Iterator<Item = &'dataset Capture<'cap>>> {
let pos = dataset
.binary_search_by_key(&start, |cap| cap.start())
.ok()?;
@ -171,7 +175,9 @@ impl<'a> RegexCache<'a> {
LanguageType::Html
| LanguageType::RubyHtml
| LanguageType::Svelte
| LanguageType::Vue => {
| LanguageType::Vue
| LanguageType::GlimmerJs
| LanguageType::GlimmerTs => {
let html = HtmlLike {
start_script: save_captures(&START_SCRIPT, lines, start, end),
start_style: save_captures(&START_STYLE, lines, start, end),

View File

@ -64,7 +64,7 @@ impl Languages {
/// provided by [`Language`].
///
/// Takes a `&[&str]` of paths to recursively traverse, paths can be
/// relative, absolute or glob paths. a second `&[&str]` of paths to ignore,
/// relative, absolute or glob paths. A second `&[&str]` of paths to ignore,
/// these strings use the `.gitignore` syntax, such as `target`
/// or `**/*.bk`.
///

View File

@ -57,6 +57,7 @@ mod stats;
pub use self::{
config::Config,
consts::*,
language::{Language, LanguageType, Languages},
sort::Sort,
stats::{find_char_boundary, CodeStats, Report},

View File

@ -2,7 +2,6 @@
pub(crate) trait AsciiExt {
fn is_whitespace(&self) -> bool;
fn is_not_line_ending_whitespace(&self) -> bool;
fn is_line_ending_whitespace(&self) -> bool;
}
@ -11,10 +10,6 @@ impl AsciiExt for u8 {
*self == b' ' || (b'\x09'..=b'\x0d').contains(self)
}
fn is_not_line_ending_whitespace(&self) -> bool {
self.is_whitespace() && !self.is_line_ending_whitespace()
}
fn is_line_ending_whitespace(&self) -> bool {
*self == b'\n'
}

10
tests/data/apl.apl Normal file
View File

@ -0,0 +1,10 @@
⍝ 10 lines 3 code 3 comments 4 blanks
256=2*8
⍝ Comment
¨ ' '() 'A Programming Language'
⍝ A magic square of length ⊢
MS (-÷2)(),×

40
tests/data/ballerina.bal Normal file
View File

@ -0,0 +1,40 @@
// 40 lines 29 code 6 comments 5 blanks
# Returns Bob's response to someone talking to him.
#
# + input - whatever is said to Bob
# + return - Bob's response
public function hey(string input) returns string {
string trimmed = input.trim();
boolean silent = isSilence(trimmed);
boolean asking = isQuestion(trimmed);
boolean yelling = isYelling(trimmed);
match [silent, yelling, asking] {
[true, _, _] => {
return "Fine. Be that way!";
}
[_, true, true] => {
return "Calm down, I know what I'm doing!";
}
[_, true, false] => {
return "Whoa, chill out!";
}
[_, false, true] => {
return "Sure.";
}
_ => {
return "Whatever.";
}
}
}
isolated function isSilence(string input) returns boolean => input.length() == 0;
isolated function isQuestion(string input) returns boolean => input.endsWith("?");
function isYelling(string input) returns boolean {
// contains an uppercase letter and does not contain a lowercase letter
return input.includesMatch(re `\p{Lu}`)
&& !input.includesMatch(re `\p{Ll}`);
}

10
tests/data/bqn.bqn Normal file
View File

@ -0,0 +1,10 @@
# 10 lines 3 code 3 comments 4 blanks
256=28
# Comment
<'a'/"Big Questions Notation"
# A magic square of length ⊢
MS (-÷2)(˘˘)˜×˜

51
tests/data/cairo.cairo Normal file
View File

@ -0,0 +1,51 @@
//! 51 lines 32 code 13 comments 6 blanks
//! ```rust
//! fn main () {
//! // Comment
//!
//! println!("Hello World!");
//! }
//! ```
/// The main function
fn main() {
let x: ByteArray = "\"/*##\"\"##\'\'";
// comment
loop {
if x.len() >= 2 && x[0] == '*' && x[1] == '/' { // found the */
break;
}
}
}
fn foo<T, +Drop<T>>(name: T) {
let this_ends = 'a "\'test/"*.';
call1();
call2();
let this_does_not = // a // nested // comment " //
///"*/another /*test
call3();
//*/";
}
fn call1() {}
fn call2() {}
fn call3() {}
fn foobar() {
let does_not_start: ByteArray = // "
"until here,
test/*
test"; // a quote: "
let also_doesnt_start =
/// " */
'until here,
test,'; // another quote: "
}
fn foo2() {
let a = 4; // ///
let b = '5';
let c = 6; // ///
}

View File

@ -11,7 +11,7 @@
/* Cheeky // block comments */
// Caculate a factorial
// Calculate a factorial
proc factorial(n: int): int {
var x = 1; // this will eventually be returned
for i in 1..n {

46
tests/data/cppm.cppm Normal file
View File

@ -0,0 +1,46 @@
/* 46 lines 37 code 3 comments 6 blanks */
#include <stdio.h>
// bubble_sort_function
void bubble_sort(int a[10], int n) {
int t;
int j = n;
int s = 1;
while (s > 0) {
s = 0;
int i = 1;
while (i < j) {
if (a[i] < a[i - 1]) {
t = a[i];
a[i] = a[i - 1];
a[i - 1] = t;
s = 1;
}
i++;
}
j--;
}
}
int main() {
int a[] = {4, 65, 2, -31, 0, 99, 2, 83, 782, 1};
int n = 10;
int i = 0;
printf(R"(Before sorting:\n\n" )");
// Single line comment
while (i < n) {
printf("%d ", a[i]);
i++;
}
bubble_sort(a, n);
printf("\n\nAfter sorting:\n\n");
i = 0;
while (i < n) {
printf("%d ", a[i]);
i++;
}
}

22
tests/data/eight.8th Normal file
View File

@ -0,0 +1,22 @@
\ 22 lines 9 code 8 comments 5 blanks
(* multiline comments
(* a nested
comment *)
*
*)
-- here's a single line comment
"Hello, " var, foo -- line ending comment
\ here's another single line comment
"!" var, bar \ a different line ending comment
: hello \ s --
foo @ s:<+
bar @ s:+
. cr
;
"World" hello
bye

27
tests/data/glimmer_js.gjs Normal file
View File

@ -0,0 +1,27 @@
// 27 lines, 18 code, 6 comments, 3 blanks
import { helper } from '@ember/component/helper';
import { modifier } from 'ember-modifier';
// A single-line comment
const plusOne = helper(([num]) => num + 1);
/**
* A multi-line comment
*/
const setScrollPosition = modifier((element, [position]) => {
element.scrollTop = position
});
<template>
<!-- A HTML-like comment -->
<div class="scroll-container" {{setScrollPosition @scrollPos}}>
{{#each @items as |item index|}}
Item #{{plusOne index}}: {{item}}
{{/each}}
</div>
<style>
div {
background-color: #E04E39;
}
</style>
</template>

18
tests/data/glimmer_ts.gts Normal file
View File

@ -0,0 +1,18 @@
// 18 lines, 10 code, 6 comments, 2 blanks
import type { TemplateOnlyComponent } from '@glimmer/component';
// A single-line comment
const localVariable = 'foo';
/**
* A multi-line comment
*/
const Greet: TemplateOnlyComponent<{ name: string }> = <template>
<!-- A HTML-like comment -->
<p>Hello, {{@name}}! {{localVariable}}</p>
<style>
p {
background-color: #E04E39;
}
</style>
</template>

154
tests/data/hex0.hex0 Normal file
View File

@ -0,0 +1,154 @@
# 154 lines 89 code 34 comments 31 blanks
# SPDX-FileCopyrightText: 2017 Jeremiah Orians <jeremiah@pdp10.guru>
# SPDX-FileCopyrightText: 2023 Andrius Štikonas <andrius@stikonas.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later
## ELF Header
#:ELF_base
7F 45 4C 46 ## e_ident[EI_MAG0-3] ELF's magic number
02 ## e_ident[EI_CLASS] Indicating 64 bit
01 ## e_ident[EI_DATA] Indicating little endianness
01 ## e_ident[EI_VERSION] Indicating original elf
03 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 ## e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 ## e_ident[EI_PAD]
02 00 ## e_type Indicating Executable
3E 00 ## e_machine Indicating AMD64
01 00 00 00 ## e_version Indicating original elf
78 00 60 00 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)
40 00 00 00 00 00 00 00 ## e_phoff Address of program header table
00 00 00 00 00 00 00 00 ## e_shoff Address of section header table
00 00 00 00 ## e_flags
40 00 ## e_ehsize Indicating our 64 Byte header
38 00 ## e_phentsize size of a program header table
01 00 ## e_phnum number of entries in program table
00 00 ## e_shentsize size of a section header table
00 00 ## e_shnum number of entries in section table
00 00 ## e_shstrndx index of the section names
## Program Header
#:ELF_program_headers
01 00 00 00 ## p_type
01 00 00 00 ## p_flags: PF-X = 1
00 00 00 00 00 00 00 00 ## p_offset
00 00 60 00 00 00 00 00 ## p_vaddr
00 00 60 00 00 00 00 00 ## p_physaddr
E5 00 00 00 00 00 00 00 ## p_filesz
E5 00 00 00 00 00 00 00 ## p_memsz
01 00 00 00 00 00 00 00 ## Required alignment
#:ELF_text
# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
#:_start (0x600078)
58 ; pop_rax # Get the number of arguments
5F ; pop_rdi # Get the program name
5F ; pop_rdi # Get the actual input name
31F6 ; xor_esi,esi # prepare read_only, rsi = 0
6A 02 ; push !2 # prepare syscall number
58 ; pop_rax # the syscall number for open()
99 ; cdq # Extra sure, rdx = 0
0F05 ; syscall # Now open that damn file
5F ; pop_rdi # Get the actual output name
50 ; push_rax # Preserve the file pointer we were given
66BE 4102 ; mov_si, @577 # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
66BA C001 ; mov_dx, @448 # Prepare file as RWX for owner only (700 in octal)
6A 02 ; push !2 # prepare syscall number
58 ; pop_rax # the syscall number for open()
0F05 ; syscall # Now open that damn file
93 ; xchg_ebx,eax # Preserve the file pointer we were given
99 ; cdq # rdx = 0 since file descriptor is nonnegative
FFC2 ; inc_edx # rdx = 1 (count for read/write)
#:loop_reset_all (0x600096)
31ED ; xor_ebp,ebp # ebp = 0 (no prior hex val)
# Comment tracking is done with ecx.
# ecx is decremented if we hit a
# comment (';' or '#') and reset
# if we hit a new-line.
#:loop_reset_comment (0x600098)
52 ; push_rdx
59 ; pop_rcx # Set no current comment
#:loop_add_comment (0x60009A)
FFC9 ; dec_ecx
#:loop (0x60009C)
# Read a byte
5F ; pop_rdi # Get infile
54 ; push_rsp
5E ; pop_rsi # Set buffer
# rdx is already set to 1.
31C0 ; xor_eax,eax # Set read syscall in rax
51 ; push_rcx # Save comment tracking
0F05 ; syscall # Do the actual read
59 ; pop_rcx # Restore comment tracking
57 ; push_rdi # Re-save infile
85C0 ; test_eax,eax # Check what we got
75 06 ; jne !cont # No EOF
# Exit successfully
B0 3C ; mov_al, !60 # Set exit syscall in rax
31FF ; xor_edi,edi # Set return success (rdi = 0)
0F05 ; syscall # Exit
#:cont (0x6000B0)
8A06 ; mov_al,[rsi] # Move prog byte in eax
# New line check
3C 0A ; cmp_al, !10 # Check new-line
74 E2 ; je !loop_reset_comment # If new-line, end comment handling
# In comment check
85C9 ; test_ecx,ecx # Skip byte if we are in a comment
75 E2 ; jne !loop
# Start comment check
3C 23 ; cmp_al, !35 # Start of '#' comment
74 DC ; je !loop_add_comment
3C 3B ; cmp_al, !59 # Start of ';' comment
74 D8 ; je !loop_add_comment
# Start of hex str to int
2C 30 ; sub_al, !48 # Subtract ascii '0' from al
2C 0A ; sub_al, !10 # Check for value in '0'-'9'
72 08 ; jb !write # We have hex value, write it
2C 07 ; sub_al, !7 # Subtract ('A'-'0') from al
24 DF ; and_al, !0xDF # Remove lower case bit
3C 07 ; cmp_al, !7 # Check for value 'A'-'F'
73 CC ; jae !loop # We have hex value, write it
#:write (0x6000D0)
C1E5 04 ; shl_ebp, !4 # Shift up existing hex digit
04 0A ; add_al, !10 # Finish converting ascii to raw value
01C5 ; add_ebp,eax # Combine the hex digits
# Check if this is first digit in hex val
F7DB ; neg_ebx # Flip sign of r10 to indicate we got a digit
7C C1 ; jl !loop # Negative -> first digit, get another one
# We have both digits in low byte of ebp, good to write
892E ; mov_[rsi],ebp # Move edge to buffer
89DF ; mov_edi,ebx # Move outfile to rdi
B0 01 ; mov_al, !1 # Set write syscall in rax
0F05 ; syscall # Do the write
EB B1 ; jmp !loop_reset_all # Start a fresh byte
#:ELF_end (0x6000E5)

589
tests/data/hex1.hex1 Normal file
View File

@ -0,0 +1,589 @@
# 589 lines 387 code 91 comments 111 blanks
# SPDX-FileCopyrightText: 2016 Jeremiah Orians <jeremiah@pdp10.guru>
# SPDX-FileCopyrightText: 2017 Jan Nieuwenhuizen <janneke@gnu.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later
## ELF Header
# :ELF_base
7F 45 4C 46 ## e_ident[EI_MAG0-3] ELF's magic number
02 ## e_ident[EI_CLASS] Indicating 64 bit
01 ## e_ident[EI_DATA] Indicating little endianness
01 ## e_ident[EI_VERSION] Indicating original elf
03 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 ## e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 ## e_ident[EI_PAD]
02 00 ## e_type Indicating Executable
3E 00 ## e_machine Indicating AMD64
01 00 00 00 ## e_version Indicating original elf
78 00 60 00 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)
40 00 00 00 00 00 00 00 ## e_phoff Address of program header table
00 00 00 00 00 00 00 00 ## e_shoff Address of section header table
00 00 00 00 ## e_flags
40 00 ## e_ehsize Indicating our 64 Byte header
38 00 ## e_phentsize size of a program header table
01 00 ## e_phnum number of entries in program table
00 00 ## e_shentsize size of a section header table
00 00 ## e_shnum number of entries in section table
00 00 ## e_shstrndx index of the section names
## Program Header
# :ELF_program_headers
01 00 00 00 ## p_type
07 00 00 00 ## ph_flags: PF-X|PF-W|PF-R = 7
00 00 00 00 00 00 00 00 ## p_offset
00 00 60 00 00 00 00 00 ## p_vaddr
00 00 60 00 00 00 00 00 ## p_physaddr
EF 05 00 00 00 00 00 00 ## p_filesz
EF 05 00 00 00 00 00 00 ## p_memsz
01 00 00 00 00 00 00 00 ## Required alignment
# :ELF_text
# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
# Register usage:
# RAX, RDX, RSI, RDI => Temps
# R15 => Flag
# R14 => High bits
# R13 => IP
# R12 => MALLOC
# R11 => HEAD
# Struct format: (size 24)
# NEXT => 0
# TARGET => 8
# NAME => 16
# :_start
48C7C7 00000000 ; mov_rdi, %0 # Get current pointer
E8 %w ; call %malloc # Get current HEAP
4889C7 ; mov_rdi,rax # Using current
4989C4 ; mov_r12,rax # Setup MALLOC
4881C7 00008000 ; add_rdi, %8388608 # Create space for temp [8MB]
E8 %w ; call %malloc # Give ourselves 8192000 bytes to work with
4C8925 %T ; mov_[rip+DWORD],r12 %scratch # Allocate space for scratch area
4981C4 00080000 ; add_r12, %0x800 # 2 KiB of scratch
58 ; pop_rax # Get the number of arguments
5F ; pop_rdi # Get the program name
5F ; pop_rdi # Get the actual input name
48C7C6 00000000 ; mov_rsi, %0 # prepare read_only
48C7C0 02000000 ; mov_rax, %2 # the syscall number for open()
0F05 ; syscall # Now open that damn file
4989C1 ; mov_r9,rax # Preserve the file pointer we were given
5F ; pop_rdi # Get the actual output name
48C7C6 41020000 ; mov_rsi, %577 # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
48C7C2 C0010000 ; mov_rdx, %448 # Prepare file as RWX for owner only (700 in octal)
48C7C0 02000000 ; mov_rax, %2 # the syscall number for open()
0F05 ; syscall # Now open that damn file
4883F8 00 ; cmp_rax, !0 # Check for missing output
0F8F %R ; jg %_start_out # Have real input
48C7C0 01000000 ; mov_rax, %1 # Use stdout
:R # :_start_out
4989C2 ; mov_r10,rax # Preserve the file pointer we were given
E8 %H ; call %ClearScratch # Zero scratch
49C7C7 FFFFFFFF ; mov_r15, %-1 # Our flag for byte processing
49C7C6 00000000 ; mov_r14, %0 # temp storage for the sum
49C7C5 00006000 ; mov_r13, %0x00600000 # Our starting IP
49C7C3 00000000 ; mov_r11, %0 # HEAD = NULL
E8 %a ; call %First_pass # Process it
# rewind input file
4C89CF ; mov_rdi,r9 # Using our input file
48C7C6 00000000 ; mov_rsi, %0 # Offset Zero
48C7C2 00000000 ; mov_rdx, %0 # Whence Zero
48C7C0 08000000 ; mov_rax, %8 # lseek
4153 ; push_r11 # Protect HEAD
0F05 ; syscall
415B ; pop_r11 # Restore HEAD
49C7C7 FFFFFFFF ; mov_r15, %-1 # Our flag for byte processing
49C7C6 00000000 ; mov_r14, %0 # temp storage for the sum
49C7C5 00006000 ; mov_r13, %0x00600000 # Our starting IP
E8 %k ; call %Second_pass # Process it
E9 %v ; jmp %Done
:a # :First_pass
E8 %x ; call %Read_byte
# Deal with EOF
4883F8 FC ; cmp_rax, !-4
0F84 %i ; je %First_pass_done
# Check for :
4883F8 3A ; cmp_rax, !0x3A
0F85 %b ; jne %First_pass_0
# Deal with label
E9 %C ; jmp %StoreLabel
:b # :First_pass_0
# Check for !
4883F8 21 ; cmp_rax, !0x21
0F84 %h ; je %First_pass_pointer
# Check for @
4883F8 40 ; cmp_rax, !0x40
0F84 %h ; je %First_pass_pointer
# Check for $
4883F8 24 ; cmp_rax, !0x24
0F84 %h ; je %First_pass_pointer
# Check for %
4883F8 25 ; cmp_rax, !0x25
0F84 %h ; je %First_pass_pointer
# Check for &
4883F8 26 ; cmp_rax, !0x26
0F84 %h ; je %First_pass_pointer
# Deal with everything else
E8 %j ; call %hex # Process our char
# Deal with EOF
4883F8 FC ; cmp_rax, !-4
0F84 %i ; je %First_pass_done
# deal with -1 values
4883F8 00 ; cmp_rax, !0
0F8C %a ; jl %First_pass
# deal with toggle
4983FF 00 ; cmp_r15, !0
0F84 %c ; je %First_pass_1
4983C5 01 ; add_r13, !1 # Increment IP
:c # :First_pass_1
49F7D7 ; not_r15
E9 %a ; jmp %First_pass
:d # :Update_Pointer
# Check for !
4883F8 21 ; cmp_rax, !0x21
0F84 %g ; je %Update_Pointer_1
# Check for @
4883F8 40 ; cmp_rax, !0x40
0F84 %f ; je %Update_Pointer_2
# Check for $
4883F8 24 ; cmp_rax, !0x24
0F84 %f ; je %Update_Pointer_2
# Check for %
4883F8 25 ; cmp_rax, !0x25
0F84 %e ; je %Update_Pointer_4
# Check for &
4883F8 26 ; cmp_rax, !0x26
0F84 %e ; je %Update_Pointer_4
# deal with bad input
E8 %Q # call %fail
:e # :Update_Pointer_4
4983C5 02 ; add_r13, !2 # Increment IP
:f # :Update_Pointer_2
4983C5 01 ; add_r13, !1 # Increment IP
:g # :Update_Pointer_1
4983C5 01 ; add_r13, !1 # Increment IP
C3 ; ret
:h # :First_pass_pointer
# Deal with Pointer to label
E8 %d ; call %Update_Pointer # Increment IP
488B1D %T ; mov_rbx,[rip+DWORD] %scratch # Using scratch
E8 %A ; call %consume_token # Read token
E8 %H ; call %ClearScratch # Throw away token
4883F8 3E ; cmp_rax, !0x3E # check for '>'
0F85 %a ; jne %First_pass # Loop again
# Deal with %label>label case
488B1D %T ; mov_rbx,[rip+DWORD] %scratch # Write to scratch
E8 %A ; call %consume_token # get token
E8 %H ; call %ClearScratch # Clean up after ourselves
E9 %a ; jmp %First_pass # Loop again
:i # :First_pass_done
C3 ; ret
:j # :hex
# deal with EOF
4883F8 FC ; cmp_rax, !-4
0F84 %n ; je %EOF
# deal with line comments starting with #
4883F8 23 ; cmp_rax, !0x23
0F84 %s ; je %ascii_comment
# deal with line comments starting with ;
4883F8 3B ; cmp_rax, !0x3B
0F84 %s ; je %ascii_comment
# deal all ascii less than 0
4883F8 30 ; cmp_rax, !0x30
0F8C %r ; jl %ascii_other
# deal with 0-9
4883F8 3A ; cmp_rax, !0x3A
0F8C %o ; jl %ascii_num
# deal with all ascii less than A
4883F8 41 ; cmp_rax, !0x41
0F8C %r ; jl %ascii_other
# deal with A-F
4883F8 47 ; cmp_rax, !0x47
0F8C %q ; jl %ascii_high
# deal with all ascii less than a
4883F8 61 ; cmp_rax, !0x61
0F8C %r ; jl %ascii_other
# deal with a-f
4883F8 67 ; cmp_rax, !0x67
0F8C %p ; jl %ascii_low
# The rest that remains needs to be ignored
E9 %r ; jmp %ascii_other
:k # :Second_pass
E8 %x ; call %Read_byte
# Deal with EOF
4883F8 FC ; cmp_rax, !-4
0F84 %m ; je %Second_pass_done
# Simply drop the label
4883F8 3A ; cmp_rax, !0x3A
0F85 %l ; jne %Second_pass_0
488B1D %T ; mov_rbx,[rip+DWORD] %scratch # Using scratch
E8 %A ; call %consume_token # Read token
E8 %H ; call %ClearScratch # Throw away token
E9 %k ; jmp %Second_pass
:l # :Second_pass_0
# Deal with % pointer
4883F8 25 ; cmp_rax, !0x25
0F84 %L ; je %StorePointer_rel4
# Deal with @ pointer
4883F8 40 ; cmp_rax, !0x40
0F84 %M ; je %StorePointer_rel2
# Deal with ! pointer
4883F8 21 ; cmp_rax, !0x21
0F84 %N ; je %StorePointer_rel1
# Deal with & pointer
4883F8 26 ; cmp_rax, !0x26
0F84 %O ; je %StorePointer_abs4
# Deal with $ pointer
4883F8 24 ; cmp_rax, !0x24
0F84 %P ; je %StorePointer_abs2
# :Second_pass_1
# Deal with everything else
E8 %j ; call %hex # Process our char
# Deal with EOF
4883F8 FC ; cmp_rax, !-4
0F84 %m ; je %Second_pass_done
# deal with -1 values
4883F8 00 ; cmp_rax, !0
0F8C %k ; jl %Second_pass
# deal with toggle
4983FF 00 ; cmp_r15, !0
0F84 %u ; je %print
# process first byte of pair
4989C6 ; mov_r14,rax
49C7C7 00000000 ; mov_r15, %0
E9 %k ; jmp %Second_pass
:m # :Second_pass_done
:n # :EOF
C3 ; ret
:o # :ascii_num
83E8 30 ; sub_rax, !0x30
C3 ; ret
:p # :ascii_low
83E8 57 ; sub_rax, !0x57
C3 ; ret
:q # :ascii_high
83E8 37 ; sub_rax, !0x37
C3 ; ret
:r # :ascii_other
48C7C0 FFFFFFFF ; mov_rax, %-1
C3 ; ret
:s # :ascii_comment
E8 %x ; call %Read_byte
4883F8 0D ; cmp_rax, !0x0D
0F84 %t ; je %ascii_comment_cr
4883F8 0A ; cmp_rax, !0x0A
0F85 %s ; jne %ascii_comment
:t # :ascii_comment_cr
48C7C0 FFFFFFFF ; mov_rax, %-1
C3 ; ret
# process second byte of pair
:u # :print
# update the sum and store in output
49C1E6 04 ; shl_r14, !4
4C01F0 ; add_rax,r14
# flip the toggle
49F7D7 ; not_r15
# Print our first Hex
48C7C2 01000000 ; mov_rdx, %1 # set the size of chars we want
E8 %z ; call %print_chars
4983C5 01 ; add_r13, !1 # Increment IP
E9 %k ; jmp %Second_pass
:v # :Done
# program completed Successfully
48C7C7 00000000 ; mov_rdi, %0 # All is well
48C7C0 3C000000 ; mov_rax, %0x3C # put the exit syscall number in eax
0F05 ; syscall # Call it a good day
# Malloc isn't actually required if the program being built fits in the initial memory
# However, it doesn't take much to add it.
# Requires a value in RDI
:w # :malloc
48C7C0 0C000000 ; mov_rax, %12 # the Syscall # for SYS_BRK
4153 ; push_r11 # Protect r11
0F05 ; syscall # call the Kernel
415B ; pop_r11 # Restore r11
C3 ; ret
:x # :Read_byte
# Attempt to read 1 byte from STDIN
48C7C2 01000000 ; mov_rdx, %1 # set the size of chars we want
488D35 %S ; lea_rsi,[rip+DWORD] %write # Where to put it
4C89CF ; mov_rdi,r9 # Where are we reading from
48C7C0 00000000 ; mov_rax, %0 # the syscall number for read
4153 ; push_r11 # Protect r11
0F05 ; syscall # call the Kernel
415B ; pop_r11 # Restore r11
4885C0 ; test_rax,rax # check what we got
0F84 %y ; je %Read_byte_1 # Got EOF call it done
# load byte
8A05 %S ; mov_al,[rip+DWORD] %write # load char
480FB6C0 ; movzx_rax,al # We have to zero extend it to use it
C3 ; ret
# Deal with EOF
:y # :Read_byte_1
48C7C0 FCFFFFFF ; mov_rax, %-4 # Put EOF in rax
C3 ; ret
:z # :print_chars
50 ; push_rax # Push address of chars onto stack
4889E6 ; mov_rsi,rsp # What we are writing
4C89D7 ; mov_rdi,r10 # Write to target file
48C7C0 01000000 ; mov_rax, %1 # the syscall number for write
4153 ; push_r11 # Protect HEAD
0F05 ; syscall # call the Kernel
415B ; pop_r11 # Restore HEAD
58 ; pop_rax # deallocate stack
C3 ; ret
# Receives pointer in RBX
# Writes out char and updates RBX
:A # :consume_token
E8 %x ; call %Read_byte # Consume_token
# Check for \t
4883F8 09 ; cmp_rax, !0x09
0F84 %B ; je %consume_token_done
# Check for \n
4883F8 0A ; cmp_rax, !0x0A
0F84 %B ; je %consume_token_done
# Check for ' '
4883F8 20 ; cmp_rax, !0x20
0F84 %B ; je %consume_token_done
# Check for '>'
4883F8 3E ; cmp_rax, !0x3E
0F84 %B ; je %consume_token_done
# Looks like we are still reading token
8803 ; mov_[rbx],al # Store char
4883C3 01 ; add_rbx, !1 # Point to next spot
E9 %A ; jmp %consume_token # loop until done
:B # :consume_token_done
48C7C1 00000000 ; mov_rcx, %0 # Pad with nulls
48890B ; mov_[rbx],rcx
4883C3 08 ; add_rbx, !8
C3 ; ret
:C # :StoreLabel
4C89E0 ; mov_rax,r12 # ENTRY
4981C4 18000000 ; add_r12, %24 # CALLOC
4C8968 08 ; mov_[rax+BYTE],r13 !8 # ENTRY->TARGET = IP
4C8918 ; mov_[rax],r11 # ENTRY->NEXT = JUMP_TABLE
4989C3 ; mov_r11,rax # JUMP_TABLE = ENTRY
4D8963 10 ; mov_[r11+BYTE],r12 !16 # ENTRY->NAME = TOKEN
4C89E3 ; mov_rbx,r12 # Write Starting after struct
E8 %A ; call %consume_token # Collect whole string
4989DC ; mov_r12,rbx # Update HEAP
E9 %a ; jmp %First_pass
:D # :GetTarget
488B3D %T ; mov_rdi,[rip+DWORD] %scratch # Reset scratch
4C89D9 ; mov_rcx,r11 # Grab JUMP_TABLE
488B71 10 ; mov_rsi,[rcx+BYTE] !16 # I->NAME
:E # :GetTarget_loop
8A06 ; mov_al,[rsi] # I->NAME[0]
8A1F ; mov_bl,[rdi] # scratch[0]
480FB6DB ; movzx_rbx,bl # Zero extend
480FB6C0 ; movzx_rax,al # Zero extend
38D8 ; cmp_al,bl # IF TOKEN == I->NAME
0F85 %F ; jne %GetTarget_miss # Oops
4883C6 01 ; add_rsi, !1
4881C7 01000000 ; add_rdi, %1
3C 00 ; cmp_al, !0
0F85 %E ; jne %GetTarget_loop # Loop until
E9 %G ; jmp %GetTarget_done # Match
# Miss
:F # :GetTarget_miss
488B09 ; mov_rcx,[rcx] # I = I->NEXT
4883F9 00 ; cmp_rcx, !0 # IF NULL == I
0F84 %Q ; je %fail # Abort hard
488B71 10 ; mov_rsi,[rcx+BYTE] !16 # I->NAME
488B3D %T ; mov_rdi,[rip+DWORD] %scratch # Reset scratch
E9 %E ; jmp %GetTarget_loop
:G # :GetTarget_done
488B41 08 ; mov_rax,[rcx+BYTE] !8 # Get address
C3 ; ret
:H # :ClearScratch
50 ; push_rax # Protect against changes
53 ; push_rbx # And overwrites
51 ; push_rcx # While we work
488B1D %T ; mov_rbx,[rip+DWORD] %scratch # Where our scratch is
48C7C0 00000000 ; mov_rax, %0 # Using null
:I # :ClearScratch_loop
488B0B ; mov_rcx,[rbx] # Get current value
8803 ; mov_[rbx],al # Because we want null
4883C3 01 ; add_rbx, !1 # Increment
4883F9 00 ; cmp_rcx, !0 # Check if we hit null
0F85 %I ; jne %ClearScratch_loop # Keep looping
59 ; pop_rcx # Don't Forget to
5B ; pop_rbx # Restore Damage
58 ; pop_rax # Entirely
C3 ; ret
:J # :StorePointer
E8 %d ; call %Update_Pointer # Increment IP
488B1D %T ; mov_rbx,[rip+DWORD] %scratch # Write to scratch
E8 %A ; call %consume_token # get token
50 ; push_rax # Protect base_sep_p
488B05 %T ; mov_rax,[rip+DWORD] %scratch # Pointer to scratch
E8 %D ; call %GetTarget # Get address of pointer
E8 %H ; call %ClearScratch # Clean up after ourselves
4C89EA ; mov_rdx,r13 # base = IP
5B ; pop_rbx # Restore base_sep_p
4883FB 3E ; cmp_rbx, !0x3E # If base_sep_p == '>'
0F85 %K ; jne %StorePointer_done # If not
# Deal with %label>label case
50 ; push_rax # We need to preserve main target
488B1D %T ; mov_rbx,[rip+DWORD] %scratch # Write to scratch
E8 %A ; call %consume_token # get token
488B05 %T ; mov_rax,[rip+DWORD] %scratch # Pointer to scratch
E8 %D ; call %GetTarget # Get address of pointer
E8 %H ; call %ClearScratch # Clean up after ourselves
4889C2 ; mov_rdx,rax # Use our new base
58 ; pop_rax # Restore main target
:K # :StorePointer_done
C3 ; ret
:L # :StorePointer_rel4
E8 %J ; call %StorePointer # Do Common
4829D0 ; sub_rax,rdx # target - ip
48C7C2 04000000 ; mov_rdx, %4 # set the size of chars we want
E8 %z ; call %print_chars
E8 %H ; call %ClearScratch # Clean up after ourselves
E9 %k ; jmp %Second_pass
:M # :StorePointer_rel2
E8 %J ; call %StorePointer # Do Common
4829D0 ; sub_rax,rdx # target - ip
48C7C2 02000000 ; mov_rdx, %2 # set the size of chars we want
E8 %z ; call %print_chars
E8 %H ; call %ClearScratch # Clean up after ourselves
E9 %k ; jmp %Second_pass
:N # :StorePointer_rel1
E8 %J ; call %StorePointer # Do Common
4829D0 ; sub_rax,rdx # target - ip
48C7C2 01000000 ; mov_rdx, %1 # set the size of chars we want
E8 %z ; call %print_chars
E8 %H ; call %ClearScratch # Clean up after ourselves
E9 %k ; jmp %Second_pass
:O # :StorePointer_abs4
E8 %J ; call %StorePointer # Do Common
48C7C2 04000000 ; mov_rdx, %4 # set the size of chars we want
E8 %z ; call %print_chars
E8 %H ; call %ClearScratch # Clean up after ourselves
E9 %k ; jmp %Second_pass
:P # :StorePointer_abs2
E8 %J ; call %StorePointer # Do Common
48C7C2 02000000 ; mov_rdx, %2 # set the size of chars we want
E8 %z ; call %print_chars
E8 %H ; call %ClearScratch # Clean up after ourselves
E9 %k ; jmp %Second_pass
:Q # :fail
# Some shit went wrong
48C7C7 01000000 ; mov_rdi, %1 # All is wrong
48C7C0 3C000000 ; mov_rax, %0x3C # put the exit syscall number in eax
0F05 ; syscall # Call it a good day
:S # :write
00000000 ; NULL
00000000 ; NULL
:T # :scratch
00000000 ; NULL
00000000 ; NULL
# :ELF_end

110
tests/data/hex2.hex2 Normal file
View File

@ -0,0 +1,110 @@
# 110 lines 78 code 7 comments 25 blanks
# SPDX-FileCopyrightText: 2019 Jeremiah Orians <jeremiah@pdp10.guru>
#
# SPDX-License-Identifier: GPL-3.0-or-later
## ELF Header
:ELF_base
7F 45 4C 46 ## e_ident[EI_MAG0-3] ELF's magic number
02 ## e_ident[EI_CLASS] Indicating 64 bit
01 ## e_ident[EI_DATA] Indicating little endianness
01 ## e_ident[EI_VERSION] Indicating original elf
03 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00 ## e_ident[EI_ABIVERSION] Set at 0 because none cares
00 00 00 00 00 00 00 ## e_ident[EI_PAD]
02 00 ## e_type Indicating Executable
3E 00 ## e_machine Indicating AMD64
01 00 00 00 ## e_version Indicating original elf
&_start 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)
%ELF_program_headers>ELF_base 00 00 00 00 ## e_phoff Address of program header table
00 00 00 00 00 00 00 00 ## e_shoff Address of section header table
00 00 00 00 ## e_flags
40 00 ## e_ehsize Indicating our 64 Byte header
38 00 ## e_phentsize size of a program header table
01 00 ## e_phnum number of entries in program table
00 00 ## e_shentsize size of a section header table
00 00 ## e_shnum number of entries in section table
00 00 ## e_shstrndx index of the section names
## Program Header
:ELF_program_headers
01 00 00 00 ## p_type
07 00 00 00 ## ph_flags: PF-X|PF-W|PF-R = 7
00 00 00 00 00 00 00 00 ## p_offset
&ELF_base 00 00 00 00 ## p_vaddr
&ELF_base 00 00 00 00 ## p_physaddr
%ELF_end>ELF_base 00 00 00 00 ## p_filesz
%ELF_end>ELF_base 00 00 00 00 ## p_memsz
01 00 00 00 00 00 00 00 ## Required alignment
:ELF_text
:_start
58 ; pop_rax # Get the number of arguments
5F ; pop_rdi # Get the program name
5F ; pop_rdi # Get the actual output name
48C7C6 41020000 ; mov_rsi, %577 # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
48C7C2 80010000 ; mov_rdx, %384 # Prepare file as RW for owner only (600 in octal)
48C7C0 02000000 ; mov_rax, %2 # the syscall number for open()
0F05 ; syscall # Now open that file
4989C7 ; mov_r15,rax # Preserve the file pointer we were given
48C7C0 0C000000 ; mov_rax, %12 # the Syscall # for SYS_BRK
48C7C7 00000000 ; mov_rdi, %0 # Get current brk
0F05 ; syscall # Let the kernel do the work
4989C6 ; mov_r14,rax # Set our malloc pointer
48C7C0 0C000000 ; mov_rax, %12 # the Syscall # for SYS_BRK
4C89F7 ; mov_r14,rax # Using current pointer
4881C7 00001000 ; add_rdi, %0x100000 # Allocate 1MB
0F05 ; syscall # Let the kernel do the work
:core
5F ; pop_rdi # Get the actual input name
4883FF 00 ; cmp_rdi, !0 # Check for null string
0F84 %done ; je %done # Hit null be done
48C7C6 00000000 ; mov_rsi, %0 # prepare read_only
48C7C2 00000000 ; mov_rdx, %0 # prevent any interactions
48C7C0 02000000 ; mov_rax, %2 # the syscall number for open()
0F05 ; syscall # Now open that damn file
4989C5 ; mov_r13,rax # Protect INPUT
:keep
48C7C2 00001000 ; mov_rdx, %0x100000 # set the size of chars we want
4C89F6 ; mov_rsi,r14 # Where to put it
4C89EF ; mov_rdi,r13 # Where are we reading from
48C7C0 00000000 ; mov_rax, %0 # the syscall number for read
0F05 ; syscall # call the Kernel
50 ; push_rax # Protect the number of bytes read
4889C2 ; mov_rdx,rax # Number of bytes to write
4C89F6 ; mov_rsi,r14 # What we are writing
4C89FF ; mov_rdi,r15 # Write to target file
48C7C0 01000000 ; mov_rax, %1 # the syscall number for write
0F05 ; syscall # call the Kernel
58 ; pop_rax # Get bytes read
483D 00001000 ; cmp_rax, %0x100000 # Check if buffer was fully used
0F84 %keep ; je %keep # Keep looping if was full
E9 %core ; jmp %core # Otherwise move to next file
:done
# program completed Successfully
48C7C7 00000000 ; mov_rdi, %0 # All is well
48C7C0 3C000000 ; mov_rax, %0x3C # put the exit syscall number in eax
0F05 ; syscall # Call it a good day
:ELF_end

View File

@ -2,7 +2,7 @@
# A function to perform arithmetic
def add_mul(adder; multiplier):
# comment chararacter in quotes
# comment character in quotes
"# Result: " + ((. + adder) * multiplier | tostring);
# and demonstrate it

215
tests/data/justfile Normal file
View File

@ -0,0 +1,215 @@
# 215 lines 154 code 11 comments 50 blanks
set shell := ["sh", "-c"]
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
set allow-duplicate-recipes
set positional-arguments
set dotenv-load
set export
alias s := serve
bt := '0'
export RUST_BACKTRACE_1 := bt
log := "warn"
export JUST_LOG := (log + "ing" + `grep loop /etc/networks | cut -f2`)
tmpdir := `mktemp`
version := "0.2.7"
tardir := tmpdir / "awesomesauce-" + version
foo1 := / "tmp"
foo2_3 := "a/"
tarball := tardir + ".tar.gz"
export RUST_BACKTRACE_2 := "1"
string-with-tab := "\t"
string-with-newline := "\n"
string-with-carriage-return := "\r"
string-with-double-quote := "\""
string-with-slash := "\\"
string-with-no-newline := "\
"
# Newlines in variables
single := '
hello
'
double := "
goodbye
"
escapes := '\t\n\r\"\\'
# this string will evaluate to `foo\nbar\n`
x := '''
foo
bar
'''
# this string will evaluate to `abc\n wuv\nbar\n`
y := """
abc
wuv
xyz
"""
for:
for file in `ls .`; do \
echo $file; \
done
serve:
touch {{tmpdir}}/file
# This backtick evaluates the command `echo foo\necho bar\n`, which produces the value `foo\nbar\n`.
stuff := ```
echo foo
echo bar
```
an_arch := trim(lowercase(justfile())) + arch()
trim_end := trim_end("99.99954% ")
home_dir := replace(env_var('HOME') / "yep", 'yep', '')
quoted := quote("some things beyond\"$()^%#@!|-+=_*&'`")
smartphone := trim_end_match('blah.txt', 'txt')
museum := trim_start_match(trim_start(trim_end_matches(' yep_blah.txt.txt', '.txt')), 'yep_')
water := trim_start_matches('ssssssoup.txt', 's')
congress := uppercase(os())
fam := os_family()
path_1 := absolute_path('test')
path_2 := '/tmp/subcommittee.txt'
ext_z := extension(path_2)
exe_name := file_name(just_executable())
a_stem := file_stem(path_2)
a_parent := parent_directory(path_2)
sans_ext := without_extension(path_2)
camera := join('tmp', 'dir1', 'dir2', path_2)
cleaned := clean('/tmp/blah/..///thing.txt')
id__path := '/tmp' / sha256('blah') / sha256_file(justfile())
_another_var := env_var_or_default("HOME", justfile_directory())
python := `which python`
exists := if path_exists(just_executable()) =~ '^/User' { uuid() } else { 'yeah' }
foo := if env_var("_") == "/usr/bin/env" { `touch /tmp/a_file` } else { "dummy-value" }
foo_b := if "hello" == "goodbye" { "xyz" } else { if "no" == "no" { "yep"} else { error("123") } }
foo_c := if "hello" == "goodbye" {
"xyz"
} else if "a" == "a" {
"abc"
} else {
"123"
}
bar:
@echo {{foo}}
bar2 foo_stuff:
echo {{ if foo_stuff == "bar" { "hello" } else { "goodbye" } }}
executable:
@echo The executable is at: {{just_executable()}}
rustfmt:
find {{invocation_directory()}} -name \*.rs -exec rustfmt {} \;
test:
echo "{{home_dir}}"
linewise:
Write-Host "Hello, world!"
serve2:
@echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT"
shebang := if os() == 'windows' {
'powershell.exe'
} else {
'/usr/bin/env pwsh'
}
shebang:
#!{{shebang}}
$PSV = $PSVersionTable.PSVersion | % {"$_" -split "\." }
$psver = $PSV[0] + "." + $PSV[1]
if ($PSV[2].Length -lt 4) {
$psver += "." + $PSV[2] + " Core"
} else {
$psver += " Desktop"
}
echo "PowerShell $psver"
@foo:
echo bar
@test5 *args='':
bash -c 'while (( "$#" )); do echo - $1; shift; done' -- "$@"
test2 $RUST_BACKTRACE="1":
# will print a stack trace if it crashes
cargo test
notify m="":
keybase chat send --topic-type "chat" --channel <channel> <team> "upd(<repo>): {{m}}"
# Sample project script 2
script2 *ARGS:
{{ python }} script2.py {{ ARGS }}
braces:
echo 'I {{{{LOVE}} curly braces!'
_braces2:
echo '{{'I {{LOVE}} curly braces!'}}'
_braces3:
echo 'I {{ "{{" }}LOVE}} curly braces!'
foo2:
-@cat foo
echo 'Done!'
test3 target tests=path_1:
@echo 'Testing {{target}}:{{tests}}…'
./test --tests {{tests}} {{target}}
test4 triple=(an_arch + "-unknown-unknown") input=(an_arch / "input.dat"):
./test {{triple}}
variadic $VAR1_1 VAR2 VAR3 VAR4=("a") +$FLAGS='-q': foo2 braces
cargo test {{FLAGS}}
time:
@-date +"%H:%S"
-cat /tmp/nonexistent_file.txt
@echo "finished"
justwords:
grep just \
--text /usr/share/dict/words \
> /tmp/justwords
# Subsequent dependencies
# https://just.systems/man/en/chapter_37.html
# To test, run `$ just -f test-suite.just b`
a:
echo 'A!'
b: a && d
echo 'B start!'
just -f {{justfile()}} c
echo 'B end!'
c:
echo 'C!'
d:
echo 'D!'

43
tests/data/kaem.kaem Normal file
View File

@ -0,0 +1,43 @@
# 43 lines 2 code 33 comments 8 blanks
#! /usr/bin/env bash
# Mes --- Maxwell Equations of Software
# Copyright © 2017 Jan Nieuwenhuizen <janneke@gnu.org>
# Copyright © 2017 Jeremiah Orians
#
# This file is part of Mes.
#
# Mes is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# Mes is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mes. If not, see <http://www.gnu.org/licenses/>.
# Can also be run by kaem or any other shell of your personal choice
# To run in kaem simply: kaem --verbose --strict
# Warning all binaries prior to the use of blood-elf will not be readable by
# Objdump, you may need to use ndism or gdb to view the assembly in the binary.
###############################################
# Phase-0 Build hex0 from bootstrapped binary #
###############################################
./bootstrap-seeds/POSIX/AMD64/hex0-seed ./AMD64/hex0_AMD64.hex0 ./AMD64/artifact/hex0
# hex0 should have the exact same checksum as hex0-seed as they are both supposed
# to be built from hex0_amd64.hex0 and by definition must be identical
#########################################
# Phase-0b Build minimal kaem from hex0 #
#########################################
./AMD64/artifact/hex0 ./AMD64/kaem-minimal.hex0 ./AMD64/artifact/kaem-0
# for checksum validation reasons

280
tests/data/m1.m1 Normal file
View File

@ -0,0 +1,280 @@
# 280 lines 197 code 47 comments 36 blanks
## Copyright (C) 2016 Jeremiah Orians
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0. If not, see <http://www.gnu.org/licenses/>.
# M2-Planet standards
DEFINE NULL 00000000
# Registers
DEFINE R0 0
DEFINE R1 1
DEFINE R2 2
DEFINE R3 3
DEFINE R4 4
DEFINE R5 5
DEFINE R6 6
DEFINE R7 7
DEFINE R8 8
DEFINE R9 9
DEFINE R10 A
DEFINE R11 B
DEFINE R12 C
DEFINE BP C
DEFINE R13 D
DEFINE SP D
DEFINE R14 E
DEFINE LR E
DEFINE R15 F
DEFINE PC F
# Register masks for push/pop16
DEFINE {R0} 0100
DEFINE {R1} 0200
DEFINE {R2} 0400
DEFINE {R3} 0800
DEFINE {R4} 1000
DEFINE {R8} 0001
DEFINE {R9} 0002
DEFINE {R10} 0004
DEFINE {R11} 0008
DEFINE {BP} 0010
DEFINE {LR} 0040
# Bitshift constants
DEFINE NO_SHIFT 0
DEFINE LEFT 1
DEFINE RIGHT 3
DEFINE ARITH_RIGHT 5
# LOAD/STORE
DEFINE HALF_MEMORY E1
DEFINE MEMORY E5
DEFINE NO_OFFSET B0
DEFINE STORE32 08
DEFINE STORE16 0C
DEFINE STORE8 0C
DEFINE LOAD32 09
DEFINE LOADU8 0
DEFINE LOADS8 D0
DEFINE LOADS16 F0
DEFINE LOAD 0D
DEFINE LOADI8_ALWAYS 0A0E3
DEFINE LOADI8_G 0A0C3
DEFINE LOADI8_GE 0A0A3
DEFINE LOADI8_EQUAL 0A003
DEFINE LOADI8_NE 0A013
DEFINE LOADI8_LE 0A0D3
DEFINE LOADI8_L 0A0B3
DEFINE LOADI8_HI 0A083
DEFINE LOADI8_HS 0A023
DEFINE LOADI8_LS 0A093
DEFINE LOADI8_LO 0A033
# JUMP/BRANCH
DEFINE JUMP_ALWAYS EA
DEFINE JUMP_EQUAL 0A
DEFINE JUMP_NE 1A
DEFINE CALL_ALWAYS EB
DEFINE CALL_REG_ALWAYS FF2FE1
DEFINE RETURN FF2FE1
# Data movement
DEFINE MOVE_ALWAYS A0E1
DEFINE MVN_ALWAYS 0E0E1
DEFINE MVN_LT 0E0B1
DEFINE MVNI8_EQUAL 0E003
DEFINE PUSH_ALWAYS 2DE9
DEFINE POP_ALWAYS BDE8
# Arithmetic/logic
DEFINE AUX_ALWAYS E1
DEFINE IMM_ALWAYS E3
DEFINE ARITH_ALWAYS E2
DEFINE ARITH_GE A2
DEFINE ARITH_LT B2
DEFINE ARITH_NE 12
DEFINE ARITH2_ALWAYS E0
DEFINE ARITH2_GE A0
DEFINE ADC 0A
DEFINE ADCS 0B
DEFINE ADD 08
DEFINE ADDS 09
DEFINE AND 00
DEFINE CMP 005
DEFINE CMPI8 005
DEFINE MUL 0
DEFINE MULS 1
DEFINE OR 08
DEFINE SHIFT A0
DEFINE SUB 04
DEFINE RSUB 06
DEFINE XOR 02
# SYSCALL
DEFINE SYSCALL_ALWAYS 000000EF
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet. If not, see <http://www.gnu.org/licenses/>.
:_start
'0' SP BP NO_SHIFT MOVE_ALWAYS ; Setup Base Pointer
;; Prepare argv
!4 R0 ADD BP ARITH_ALWAYS ; ARGV_address = BP + 4
{R0} PUSH_ALWAYS ; Put argv on the stack
;; Prepare envp
'0' BP R0 NO_SHIFT MOVE_ALWAYS ; Address we need to load from
!0 R0 LOAD32 R0 MEMORY ; Get ARGC
!2 R0 ADD R0 ARITH_ALWAYS ; OFFSET = ARGC + 2
'0' R0 R0 '1' MOVE_ALWAYS ; OFFSET = OFFSET * WORDSIZE
'0' R0 R0 ADD BP ARITH2_ALWAYS ; ENVP_address = BP + OFFSET
{R0} PUSH_ALWAYS ; Put envp on the stack
;; Stack offset
!4 BP ADD BP ARITH_ALWAYS ; Fix BP
^~FUNCTION___init_malloc CALL_ALWAYS ; Setup for malloc
^~FUNCTION___init_io CALL_ALWAYS ; Setup for FILE*
^~FUNCTION_main CALL_ALWAYS ; Jump right into main
{R1} POP_ALWAYS ; Fix stack
{R1} POP_ALWAYS ; Fix stack
{R1} POP_ALWAYS ; Fix stack
{R0} PUSH_ALWAYS ; put return on the stack
{R0} PUSH_ALWAYS ; So that _exit will have it
{R0} PUSH_ALWAYS ; So that _exit will have it
:FUNCTION_exit
^~FUNCTION___kill_io CALL_ALWAYS
:FUNCTION__exit
!4 R0 SUB R12 ARITH_ALWAYS
!0 R0 LOAD32 R0 MEMORY
!1 R7 LOADI8_ALWAYS
SYSCALL_ALWAYS ; exit
# Unsigned Divide
:divide
{R4} PUSH_ALWAYS ; Protect R4
{R3} PUSH_ALWAYS ; Protect R3
{R2} PUSH_ALWAYS ; Protect R2
'0' R0 R3 NO_SHIFT MOVE_ALWAYS ; MOV R3,R0
'0' R1 R2 NO_SHIFT MOVE_ALWAYS ; MOV R2,R1
!0 R0 LOADI8_ALWAYS ; MOV R0,#0
!0 CMPI8 R2 IMM_ALWAYS ; CMP R2,#0
!1 R0 SUB R0 ARITH_LT ; SUBLT R0,R0,#1
!0 CMPI8 R3 IMM_ALWAYS ; CMP R3,#0
!0 R3 RSUB R3 ARITH_LT ; RSBLT R3,R3,#0
'0' R0 R0 MVN_LT ; MVNLT R0,R0
'0' R0 R4 NO_SHIFT MOVE_ALWAYS ; MOV R4,R0
!32 R0 LOADI8_ALWAYS ; MOV R0,#32.
!0 R1 LOADI8_ALWAYS ; MOV R1,#0
:divide_loop
'0' R2 R2 ADDS R2 ARITH2_ALWAYS ; ADDS R2,R2,R2
'0' R1 R1 ADCS R1 ARITH2_ALWAYS ; ADCS R1,R1,R1
'0' R3 CMP R1 AUX_ALWAYS ; CMP R1,R3
'0' R3 R1 SUB R1 ARITH2_GE ; SUBGE R1,R1,R3
!1 R2 ADD R2 ARITH_GE ; ADDGE R2,R2,#1
!1 R0 SUB R0 ARITH_ALWAYS ; SUB R0,R0,#1
!0 CMPI8 R0 IMM_ALWAYS ; CMP R0,#0
^~divide_loop JUMP_NE ; BNE loop
'0' R2 R0 NO_SHIFT MOVE_ALWAYS ; MOV R0,R2
{R2} POP_ALWAYS ; Restore R2
{R3} POP_ALWAYS ; Restore R3
{R4} POP_ALWAYS ; Restore R4
'1' LR RETURN
# Signed Divide
:divides
{R4} PUSH_ALWAYS ; Protect R4
{R3} PUSH_ALWAYS ; Protect R3
{R2} PUSH_ALWAYS ; Protect R2
'0' R0 R3 NO_SHIFT MOVE_ALWAYS ; MOV R3,R0
'0' R1 R2 NO_SHIFT MOVE_ALWAYS ; MOV R2,R1
!0 R0 LOADI8_ALWAYS ; MOV R0,#0
!0 CMPI8 R2 IMM_ALWAYS ; CMP R2,#0
!0 R2 RSUB R2 ARITH_LT ; RSBLT R2,R2,#0
!1 R0 SUB R0 ARITH_LT ; SUBLT R0,R0,#1
!0 CMPI8 R3 IMM_ALWAYS ; CMP R3,#0
!0 R3 RSUB R3 ARITH_LT ; RSBLT R3,R3,#0
'0' R0 R0 MVN_LT ; MVNLT R0,R0
'0' R0 R4 NO_SHIFT MOVE_ALWAYS ; MOV R4,R0
!32 R0 LOADI8_ALWAYS ; MOV R0,#32.
!0 R1 LOADI8_ALWAYS ; MOV R1,#0
:divides_loop
'0' R2 R2 ADDS R2 ARITH2_ALWAYS ; ADDS R2,R2,R2
'0' R1 R1 ADCS R1 ARITH2_ALWAYS ; ADCS R1,R1,R1
'0' R3 CMP R1 AUX_ALWAYS ; CMP R1,R3
'0' R3 R1 SUB R1 ARITH2_GE ; SUBGE R1,R1,R3
!1 R2 ADD R2 ARITH_GE ; ADDGE R2,R2,#1
!1 R0 SUB R0 ARITH_ALWAYS ; SUB R0,R0,#1
!0 CMPI8 R0 IMM_ALWAYS ; CMP R0,#0
^~divides_loop JUMP_NE ; BNE loop
!0 CMPI8 R4 IMM_ALWAYS ; CMP R4,#0
!0 R2 RSUB R2 ARITH_NE ; RSBNE R2,R2,#0
'0' R2 R0 NO_SHIFT MOVE_ALWAYS ; MOV R0,R2
{R2} POP_ALWAYS ; Restore R2
{R3} POP_ALWAYS ; Restore R3
{R4} POP_ALWAYS ; Restore R4
'1' LR RETURN
# Unsigned Modulus
:modulus
{LR} PUSH_ALWAYS ; Prepare to leverage divide
^~divide CALL_ALWAYS ; Use divide
'0' R1 R0 NO_SHIFT MOVE_ALWAYS ; MOV R0,R1
{LR} POP_ALWAYS ; Prepare for return
'1' LR RETURN
# Signed Modulus
:moduluss
{LR} PUSH_ALWAYS ; Prepare to leverage divide
^~divides CALL_ALWAYS ; Use divides
'0' R1 R0 NO_SHIFT MOVE_ALWAYS ; MOV R0,R1
{LR} POP_ALWAYS ; Prepare for return
'1' LR RETURN
:GLOBAL__envp
NULL
:mystring
"this is my string\n"

21
tests/data/mojo.mojo Normal file
View File

@ -0,0 +1,21 @@
# 21 lines 15 code 3 comments 3 blanks
'''
This is a docstring.
# It has multiple lines.
This is the end of the docstring.
'''
def main():
# Hello Mojo!
string = "Hello Mojo!"
# The following line prints the string "Hello Mojo!"
print(string)
"""
This piece of code prints
the numbers "9", "6", and "3".
'Here is a quote.'
"""
for x in range(9, 0, -3):
print(x)

40
tests/data/phix.e Normal file
View File

@ -0,0 +1,40 @@
/* 40 lines 25 code 8 comments 7 blanks */
-- copied from cpp, not necessarily idiomatic Euphoria code
include std/sequence.e
-- bubble_sort_function
public function bubble_sort(sequence a)
integer t = 0
integer j = length(a)
integer s = 1
while s > 0 do
s = 0
integer i = 2
while i <= j do
if a[i] < a[i - 1] then
t = a[i]
a[i] = a[i - 1]
a[i - 1] = t
s = 1
end if
i += 1
end while
j -= 1
end while
return a
end function
sequence a = {4, 65, 2, -31, 0, 99, 2, 83, 782, 1}
-- Single line comment
? {"Before:", a}
a = bubble_sort(a)
/* multi
* line
* comment
*/
? {"After:", a, equal(a, {-31,0,1,2,2,4,65,83,99,782})}

22
tests/data/pyret.arr Normal file
View File

@ -0,0 +1,22 @@
# 22 lines 9 code 8 comments 5 blanks
fun single-quote():
doc: "this is a documentation string"
'foo'
end
#|
Hello, this is a multiline message
|#
# This is a line message
fun double-quotes():
"bar"
end
nested = #|
doesn't start yet
or yet
|#
"nested"

36
tests/data/roc.roc Normal file
View File

@ -0,0 +1,36 @@
# 36 lines 18 code 10 comments 8 blanks
module [square]
# this is a comment
# this is another comment
a1 = 1
a2 = 3.14159 # pi
expect
# simple check
a1 == 1
expect
a2 |> Num.toStr == "3.14159"
## Compute the square
square = \x ->
s = x * x
# the line above is blank
s
expect square 3 == 9
## """
## this is not a multiline string,
## it's a doc comment
## """
multilineString =
"""
# this line is not a comment, it's actually code
The line above is not blank, it's actually code
"""
expect multilineString |> Str.toUtf8 |> List.first == Ok '#'

26
tests/data/slint.slint Normal file
View File

@ -0,0 +1,26 @@
// 26 lines 21 code 2 comments 3 blanks
component MyButton inherits Text {
color: black;
// ...
}
export component MyApp inherits Window {
preferred-width: 200px;
preferred-height: 100px;
Rectangle {
width: 200px;
height: 100px;
background: green;
}/* */
MyButton {
x:0;y:0; // hello
text: "hello";
}
MyButton { // world
y:0;
x: 50px;
text: "world";
}
}

24
tests/data/templ.templ Normal file
View File

@ -0,0 +1,24 @@
// 24 lines, 13 code, 8 comments, 3 blanks
package test
templ Foo() {
<div id="bar">
<!--
HTML comments are also allowed.
-->
<button class={ button() } onClick={ doSomething() }>Baz</button>
</div>
}
/*
some css class.
*/
css button() {
padding: 7px;
border-radius: 5px;
}
// doSomething does something
script doSomething() {
alert("something")
}

9
tests/data/uiua.ua Normal file
View File

@ -0,0 +1,9 @@
# 9 lines 5 code 3 comments 1 blanks
# Calculate factorial
# Result ? Number
Factorial ← |1 (
×. # Line comment
)
FactorialThree ← Factorial 3 # Another line comment
FactorialFour ← Factorial 4