{"id":13587156,"url":"https://github.com/adam-mcdaniel/atom","last_synced_at":"2025-05-12T18:31:29.728Z","repository":{"id":55120862,"uuid":"327740710","full_name":"adam-mcdaniel/atom","owner":"adam-mcdaniel","description":"Shell scripting that will knock your socks off","archived":false,"fork":false,"pushed_at":"2021-01-08T14:22:33.000Z","size":674,"stargazers_count":271,"open_issues_count":1,"forks_count":6,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-14T01:05:57.011Z","etag":null,"topics":["scripting","scripting-language","shell","terminal"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adam-mcdaniel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-07T22:38:32.000Z","updated_at":"2025-01-31T19:51:32.000Z","dependencies_parsed_at":"2022-08-14T12:40:18.050Z","dependency_job_id":null,"html_url":"https://github.com/adam-mcdaniel/atom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-mcdaniel%2Fatom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-mcdaniel%2Fatom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-mcdaniel%2Fatom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-mcdaniel%2Fatom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adam-mcdaniel","download_url":"https://codeload.github.com/adam-mcdaniel/atom/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253798085,"owners_count":21966002,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["scripting","scripting-language","shell","terminal"],"created_at":"2024-08-01T15:06:03.236Z","updated_at":"2025-05-12T18:31:29.261Z","avatar_url":"https://github.com/adam-mcdaniel.png","language":"Rust","readme":"# atom\n\nShell scripting that will knock your socks off.\n\n\u003cp float=\"left\"\u003e\n  \u003ca href=\"https://asciinema.org/a/383254\"\u003e\u003cimg src=\"./assets/atom-splash.png\" width=\"48%\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://asciinema.org/a/383254\"\u003e\u003cimg src=\"./assets/atom-about.png\" width=\"48%\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n##### _NOTE: Click the image above for a video demonstration._\n\n## About the Author\n\nI'm a freshman in college bored in quarantine, and looking for any work. If you enjoy my projects, consider supporting me by buying me a coffee! \n\n\u003ca href=\"https://www.buymeacoffee.com/adammcdaniel\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/default-violet.png\" height=41px width=174px style=\"!important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;\" \u003e\u003c/a\u003e\n\n## Why write a shell?\n\nAs I see it, there are two sides of a shell. The interactive side, and the scripting side. Creating a _**good**_ shell means balancing these two modes well. If you make a language too well suited to _scripting_, then file navigation, and other interactive features will suffer. On the other hand, if you make a language too well suited for _interactive_ commands **(like bash)**, then scripting becomes very difficult.\n\nAtom tries to strike a better balance between the two modes than bash, and, in my opinion, does so rather successfully. It does seem to be more well suited to scripting instead of interactive programming, but I don't really mind the sacrifice all that much.\n\nAtom's design goals are:\n1. Shell scripting _must_ be powerful enough to function as a traditional high level language.\n2. At the same time, there must not be a lot of syntactic sugar for writing _interactive_ commands.\n3. **Incorrect code should be rejected**. There should be no attempt to understand the user's incorrect code **(see [JavaScript](https://javascript.com))**. _Bad code is bad code_.\n4. Declarative and functional programming first, **imperative last**.\n\nI would say these goals serve atom very well.\n\nTo show Atom's scripting capabilities, I wrote an entire card game using it!\n\n\u003cp float=\"left\"\u003e\n  \u003ca href=\"https://asciinema.org/a/383254\"\u003e\u003cimg src=\"./assets/rummy-splash.png\" width=\"48%\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://asciinema.org/a/383254\"\u003e\u003cimg src=\"./assets/rummy-game.png\" width=\"48%\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nThe CPU is actually much better than I am, and has beaten me with twice or thrice my score multiple times. It doesn't cheat at all, it only ranks its cards by synergy, encourages picking up cards that synergize well with its best cards, and discarding its worst cards.\n\nIf you want to try all of my custom macros, and to have my splash screen, use my `.atom-prelude` file in your home directory and experiment away! To play my card game specifically, run `rummy@play'`.\n\n## Usage\n\nAtom is drastically different than any other shell, both with its outward syntax and internal functionality.\n\nFor example, atom supports traditional tables, lists, strings, ints, floats, bools, etc. like [this drastically better and more professional shell that I should have just started using to begin with (but there's no fun in that. \"Not invented here syndrome\" really does have a hold on me doesn't it?)](https://www.nushell.sh/).\n\nBut atom also takes direct inspiration from languages like [lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)) by including symbols as first class types, and by implementing iterative constructs like `for` and `while` loops as _values_, not operations on values.\n\nAdditionally, it adds lambdas (which can capture their environment), and macros (which can change their parent environment).\n\n### Interactive Syntax\n\nWhile this syntax will work in all of your scripts as valid statements, my intent is for this to be mainly used for interactive programming.\n\nGenerally, executing programs (and functions too) in atom and in bash is only different by one character.\n\nBash:\n```bash\n$ g++ main.cpp -o main\n```\n\nAtom:\n```\n$ g++' main.cpp -o main\n```\n\nYou might be thinking that writing a quote after every program name is not so ergonomic, but it actually isn't half bad. I find now that when I use bash, I can't seem to type any commands _without_ it accidentally.\n\nThe reason I chose the `'` character is because it is the most readily available character that isn't used in any symbols (besides the semicolon) that is also _not_ accessed with the shift key. The command `g++' main.cpp -o main` really is just one extra keystroke.\n\nNot that much of a sacrifice for scripting power bestowed by the gods.\n\n#### Aliases\n\nYou might find that you have defined a symbol `g++` with a non-callable value (not a macro or a function), like `5`.\n\nIf this is the case, simply wrap the symbol `g++` in quotes,\nand then run it like so: `\"g++\"' main.cpp -o main`.\n\nThis also means that you can make aliases rather simply by defining a symbol with a string or a path.\n\nThis snippet defines `ls` as an alias for the [`lsd`](https://github.com/Peltoche/lsd) program:\n```python\nls := \"lsd\";\nls'\n```\n\n### Scripting Syntax\n\nAgain, scripting syntax is really just syntax encouraged for scripting. All syntax you see on this README will work anywhere.\n\nTo start off simple, let's define a variable.\n\n```python\nx := 5;\n```\n\nWow! We've changed our environment! This means that whenever the symbol `x` is evaluated, it becomes `5` instead!\n\nNow let's define something else.\n\n```python\nWEEKDAYS := [\n\t\"Sunday\",\n\t\"Monday\",\n\t\"Tuesday\",\n\t\"Wednesday\",\n\t\"Thursday\",\n\t\"Friday\",\n\t\"Saturday\"\n];\n\ngrades := {\n    \"adam\": 50,\n    \"literally everyone else\": 100\n};\n```\n\nBravo! Now the symbol `WEEKDAYS` is bound to a list containing all the weekdays' names, and the symbol `grades` is bound to a table containing grades for students!\n\nNow let's try to do some fun stuff with lambdas.\n\n```haskell\nmin := \\x,y -\u003e x \u003c y? x : y;\nmax := fn(x, y) -\u003e x \u003e y? x : y;\n\n# You can put brackets around the lambda body for multiple statements\nreturn-five :=   () -\u003e { print(\"returning 5\"); 5 };\nreturn-six  := fn() -\u003e 6;\n\nincrement := x -\u003e x + 1;\ndecrement := fn(x) -\u003e x - 1;\n```\n\nAs you can see, we can use the `\\... -\u003e ` or `fn(...) -\u003e ` syntactic sugar to create lambda functions with multiple arguments, `() -\u003e ` to create a lambda that takes no arguments, or just a symbol and an arrow to create a lambda that takes a single argument. How handy!\n\nYou can also use the `fn` keyword to define functions without assigning them.\n\n```rust\nfn is-leapyear(year) {\n\tif year % 4 = 0 and year % 100 != 0 {\n\t\ttrue\n\t} else if year % 100 = 0 and year % 400 = 0 {\n\t\ttrue\n\t} else {\n\t\tfalse\n\t}\n};\n\nfn days-in-month(month, year) {\n\tmonth = 2? 28 + to-int(is-leapyear(year)) : 31 - (((month - 1) % 7) % 2)\n};\n\nfn day-of-week(m, d, y) {\n\tt := [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];\n\n    # Notice that semicolons go on the end of if statements too\n\tif m \u003c 3 { y := y - 1 };\n    \n    # The last expression in a function is the value that is returned\n\t(((y + to-int(y / 4)) - to-int(y / 100)) + to-int(y / 400) + t[m - 1] + d) % 7\n};\n```\n\nPlease note the semicolon at the end of _every expression_. It is required for everything except for the last value of a block!\n\nYou can also define macros in a similar manner with the `macro` keyword:\n\n```rust\n# We write `; nil` at the end of the macro so that\n# the macro returns nil, instead of the new CWD value.\nroot := macro() -\u003e { CWD := ROOT; nil };\n\nmacro quit() {\n\tprint(\"Goodbye!👋\");\n\tsleep(0.4);\n\texit();\n};\n```\n\nIsn't this nice???\n\n### Functions vs. Macros\n\nFunctions and macros look very similar, and perform almost identical tasks, but they have drastically different implications.\n\nTo explain it simply, a function creates its own local scope, with local variables that are dropped when the function returns. Macros, however, use the _current scope that the macro is called in_ as its scope.\n\nVariables that would have been overwritten by the macro's parameters, are saved. Take the following code for example:\n\n```rust\nx := 5;\ny := nil;\nmacro test(x) { y := x };\n\nprint(x, y);\ntest(\"x is still 5, but y is not nil\");\nprint(x, y);\n```\n\nMacros let you read and write global state, while (user defined) functions have no side effects on the global state of the program.\n\nBuiltin functions are a different beast entirely, though...\n\n### Builtin Functions vs. Functions\n\nBuiltin functions are a bit of a misnomer. They're half macro, half function, and a little bit of something else not quite quantifiable. How do they work? The world may never know.\n\nThe reason they're so mystical is because _they can change the scope they were called in (like macros), they have the option to not evaluate their arguments (unlike macros), and they can take varying numbers of arguments._. For example, take the `cd` builtin function. Not only does it _not_ evaluate its arguments (calling `cd` with `Desktop` doesn't evaluate the symbol `Desktop`), it modifies the `CWD` variable in the current scope. How weird is that?\n\n```python\n$ home'\n$ cd' Desktop\n```\n\nAdditionally, builtin functions like `echo`, `print`, and `to-str` can take varying numbers of arguments.\n\n```python\n$ print(\"x is equal to\", x := 5, \"and y is equal to\", y := 6)\n```\n\nAlthough these features of builtin functions are cool and bizarre, they really aren't used in many instances. _**Functions like `print` and `cd` are exceptions to the rule**_, and you can expect most functions to behave _exactly_ as regular user defined functions without any bizarre catches.\n\n## Modules\n\nAtom has an extensive list of builtin libraries (for a shell written in a week or so, that is). Here's a list of builtin modules.\n\nModules (which is just a fancy name for tables intended to function as libraries), can be accessed with the `@member` operator or the `[\"member\"]` operators.\n\nExample usage:\n```python\nshuffled-deck := rand@shuffle(cards@deck@all);\necho(shuffled-deck[0]);\n```\n\n| Module | Description | Members |\n|-|-|-|\n| `rand` | A module embodied with chaos. Use your power of entropy wisely, young scripters. | `{ int: fn(int, int) -\u003e int, shuffle: fn([any]) -\u003e [any], choose: fn([any]) -\u003e any }` |\n| `fmt` | A module for formatting strings. As of right now, there are only functions that manipulate color, boldness, underlining, etc. | `{ red: fn(str) -\u003e str, green: fn(str) -\u003e str, blue: fn(str) -\u003e str, yellow: fn(str) -\u003e str, magenta: fn(str) -\u003e str, cyan: fn(str) -\u003e str, black: fn(str) -\u003e str, gray: fn(str) -\u003e str,grey: fn(str) -\u003e str,white: fn(str) -\u003e str, dark: { red: fn(str) -\u003e str, green: fn(str) -\u003e str, blue: fn(str) -\u003e str, cyan: fn(str) -\u003e str, yellow: fn(str) -\u003e str, magenta: fn(str) -\u003e str, }, bold: fn(str) -\u003e str, invert: fn(str) -\u003e str, underline: fn(str) -\u003e str }` |\n| `widget` | A small module for creating widgets for displaying text in the terminal. Widgets have a title, a content string, a width, and a height. | `{ create: fn(str, str, int, int) -\u003e str,add-horizontal: fn(str...) -\u003e str,add-vertical: fn(str...) -\u003e str }` |\n| `math` | A module for various math functions. Trigonometry, multiple logarithms, etc. | `{ E: float, PI: float, TAU: float, pow: fn(float, float) -\u003e float, log: fn(float, float) -\u003e float, log10: fn(float) -\u003e float, log2: fn(float) -\u003e float, sqrt: fn(float) -\u003e float, cbrt: fn(float) -\u003e float,sin: fn(float) -\u003e float, cos: fn(float) -\u003e float, tan: fn(float) -\u003e float,asin: fn(float) -\u003e float, acos: fn(float) -\u003e float, atan: fn(float) -\u003e float }` |\n| `os` | A small module for getting info about the operating system. Useful for creating cross-platform scripts. | `{ name: str,  family: str, version: str }` |\n| `sh` | A small module for getting info about the shell, such as the version, the path to the executable, the executable's parent directory, and the path the to prelude script (the script run at the shell's startup, like `.bashrc`).  The `version` member contains the major, minor, and patch integers. | `{ exe: path, dir: path, version: [int],  prelude: path }` |\n| `file` | A small module for file manipulation. It's not much yet. Keep it simple. | `{ read: fn(path or str or sym) -\u003e str, write: fn(path or str or sym, str) -\u003e nil, append: fn(path or str or sym, str) -\u003e nil }` |\n| `date` | The date module is a bit weird. It's not so much a module, more of a hidden function. Every time `date` is accessed, it uses a constantly updating table with the current date info, and the date as a string. | `{ day: int, weekday: int, month: int, year: int, str: str }` |\n| `time` | The time module functions just like the date module, but for time. My favorite thing so far about this module is writing macros that print fake and bizarre `g++` errors if the user tries to compile on the first second of the minute. Purely evil stuff waiting to happen, this module is. | `{ hour: int, minute: int, hour: int, str: str }` |\n| `cards` | A module for card games. Cards are just strings with their respective Unicode representation. So, for example, the value `cards@deck@aces[0]` is `\"🂡\"`. In every list containing multiple suites in the module, they alternate between Spades, Hearts, Diamonds, Clubs. So, `cards@deck@all` is `[\"🂡\", \"🂱\", \"🃁\", \"🃑\", \"🂢\", ..., \"🃞\"]`.       | `{ deck: { all: [str], aces: [str],  kings: [str], queens: [str],  jacks: [str], faces: [str],  numbers: [str] }, suites: {  spades: str, clubs: str,  hearts: str, diamonds: str },  suite: fn(str) -\u003e str,  value: fn(str) -\u003e int,  name: fn(str) -\u003e str,  from-name: fn(str) -\u003e str,  back: str }` |\n| `chess` | A module for chess. Chess boards are stored as lists of rows, which are lists of pieces. Pieces, similar to cards, are just strings with their respective Unicode representation. So, `cards@white@king` is `\"♔\"`, and `cards@black@king` is `\"♚\"`. | `{ white: { king: str, queen: str,  rook: str, bishop: str, knight: str,  pawn: str }, black: { king: str, queen: str,  rook: str, bishop: str, knight: str,  pawn: str }, space: str, is-piece: fn(str) -\u003e bool, is-space: fn(str) -\u003e bool, is-white: fn(str) -\u003e bool, is-black: fn(str) -\u003e bool, create: fn() -\u003e [[str]], flip: fn([[str]]) -\u003e [[str]], get: fn([[str]], str) -\u003e str, fmt: fn([[str]]) -\u003e str, print: fn([[str]]) -\u003e nil, mv: fn([[str]], str, str) -\u003e [[str]], add: fn([[str]], str, str) -\u003e [[str]], rm: fn([[str]], str) -\u003e [[str]] }` |\n\nThese are all intended to make scripting extremely ergonomic. With builtin libraries for a wide variety of tasks, making scripts will be incredibly easy.\n\nWe bring the blocks, you bring the glue.\n\n## Constants and Builtin Functions\n\nThese constants and builtin functions are intended to be used extremely often in scripting and in the interactive prompt, so they are all included in the global scope.\n\nThey can all be overwritten, if you wish. I would be careful about using macros to assign to these values! Be _absolutely sure_ that your code won't break something before you run it!\n\n| Name | Description | Type | Value |\n|:-:|:-:|:-:|:-:|\n| `nil` or `()` | The atom equivalent of python's `None` | `nil` | `nil` |\n| `true` and `truth` | The boolean value for true. | `bool` | `true` |\n| `false` | The boolean value for false. | `bool` | `false` |\n| `CWD` | The path of the current working directory. | `path` | See description. |\n| `HOME` | The path of the home directory. | ^ | ^ |\n| `VIDS` | The path of the videos directory. | ^ | ^ |\n| `DESK` | The path of the desktop directory. | ^ | ^ |\n| `PICS` | The path of the pictures directory. | ^ | ^ |\n| `DOCS` | The path of the documents directory. | ^ | ^ |\n| `DOWN` | The path of the downloads directory. | ^ | ^ |\n| `report` | The function that's called to print the result of a command. This can be written to format results in a custom way, or to not print nil values | `fn(any) -\u003e nil` | By default, `fn(val) -\u003e print(\" =\u003e\", val)` |\n| `prompt` | The function used to generate the prompt for the user to enter commands on. It takes the current working directory as a parameter. | `fn(path) -\u003e str` | By default, `fn(cwd) -\u003e to-str(cwd) + \"\u003e \"` |\n| `incomplete-prompt` | The function used to generate the prompt for the user to enter commands on after they've entered an incomplete line of code. It takes the current working directory as a parameter. | ^ | By default,  `fn(cwd) -\u003e \" \" * len(cwd) + \"\u003e \"` |\n| `absolute` | This function takes a path, removes any extraneous portions of the path (such as `foo/../bar`), and also makes the path an absolute path. So `./testing` in the home directory would become `/home/adam/testing`, for example. | `fn(path) -\u003e path`  or `fn(sym) -\u003e path` or `fn(str) -\u003e path` | Native code. |\n| `exists` | This function returns whether or not any path exists. | `fn(path) -\u003e bool` or `fn(sym) -\u003e bool` or `fn(str) -\u003e bool` | ^ |\n| `is-err` | This function returns whether or not the evaluation of the inner expression returns an error. | `fn(any) -\u003e bool` | ^ |\n| `is-syntax-err` | This function returns whether or not an error is a syntax error. This is mainly intended for use with the `report` function. | `fn(any) -\u003e bool` | ^ |\n| `sleep` | Make the shell pause for a given number of seconds. | `fn(float) -\u003e nil` | ^ |\n| `to-path` | Convert a string or symbol to a path. | `fn(path or str or sym) -\u003e path` | ^ |\n| `to-float` | Convert a string, integer, float, or boolean to a floating point value. | `fn(str or int or float or bool) -\u003e float` | ^ |\n| `to-int` | Convert a string, integer, float, or boolean to an integer. | `fn(str or int or float or bool) -\u003e int` | ^ |\n| `input` | Get user input with a prompt. | `fn(any...) -\u003e str` | ^ |\n| `rev` | Reverse a string or a list. | `fn(str) -\u003e str or fn([any]) -\u003e [any]` | ^ |\n| `split` | Split a string with a given delimiter. | `fn(str, str) -\u003e str` | ^ |\n| `sort` | Sort a list of integers. | `fn([int]) -\u003e [int]` | ^ |\n| `join` | Join a list with a separator. | `fn([any], any) -\u003e str` | ^ |\n| `env` | A table containing all bindings in scope. | `macro() -\u003e table` | ^ |\n| `HOME`, `VIDS`, `DESK`, `PICS`, `DOCS`, `DOWN` | The path to the respective directory. | `path` | ^ |\n| `home`, `vids`, `desk`, `pics`, `docs`, `down` | Macros that set the current working directory to the respective directory. | `macro() -\u003e nil` | `macro() -\u003e CWD := ...` |\n| `exit` or `quit` | Exit the current shell session. | `fn() -\u003e nil` | Native code. |\n| `unbind` | Unbind a symbol with a given name. | `macro(str) -\u003e nil` | ^ |\n| `print` | Print one or more values, and return the last one. | `fn(any...) -\u003e any` | ^ |\n| `echo` | Print one or more values, and return `nil`. | `fn(any...) -\u003e nil` | ^ |\n| `pwd` or `cwd` | Print the current working directory. | `macro(path or string or sym) -\u003e nil` | ^ |\n| `cd` | Change the directory. This macro is a special form, _it does not evaluate its argument_. For example, if `x` is defined as `5`, `cd' x` will **NOT** perform `cd' 5`, it will perform `cd' \"x\"`. | `macro(path or string or sym) -\u003e nil` | ^ |\n| `cd-eval` | Change the directory to an evaluated value. This is used when you want to `cd` into a folder with an unknown name while writing the script. | `macro(any) -\u003e nil` | ^ |\n| `clear` or `cls` | Clear the console. | `fn() -\u003e nil` | `fn() -\u003e { if os@family = \"linux\" or os@family = \"unix\" { clear' } else if os@family = \"windows\" { cls' } else { print(\"\\n\" * 255) } }` |\n| `keys` | Get the list of keys in a table. | `fn(table) -\u003e [str]` | Native code. |\n| `vals` | Get the list of values in a table. | `fn(table) -\u003e [any]` | ^ |\n| `insert` | Return a table with a value inserted with a given key. | `fn(table, str, any) -\u003e table` | ^ |\n| `remove` | Return a table with a value removed with a given key. | `fn(table, str) -\u003e table` | ^ |\n| `len` | Get the length of a list or string, the number of pairs in a table, or the number of components to a path. | `fn([any] or table or str or path) -\u003e int` | ^ |\n| `push` | Add a given element to a list. | `fn([any], any) -\u003e [any]` | ^ |\n| `pop` | Return the last element of a list. | `fn([any]) -\u003e any` | ^ |\n| `zip` | Zip two lists together. This creates a list of pairs (lists of length two), with each pair containing an element of the first list and an element of the second list. | `fn([any], [any]) -\u003e [[any, any]]` | ^ |\n| `head` | Get the first element of a list. | `fn([any]) -\u003e any` | `fn(list) -\u003e list[0]` |\n| `tail` | Get the list without the first element. | `fn([any]) -\u003e [any]` | Native code. |\n| `map` | Map a function over a list. | `fn(fn(any) -\u003e any, [any]) -\u003e [any]` | `fn(f, list) -\u003e { result := []; for x in list { result := push(result, f(x)); }; result }` |\n| `filter` | Filter a list with a given function. | `fn(fn(any) -\u003e bool, [any]) -\u003e [any]` | `fn(f, list) -\u003e { result := []; for x in list { if f(x) { result := push(result, x); }; }; result }` |\n| `reduce` | Reduce a list to an atomic value with a function that takes an accumulator and an element of the list, and returns the new accumulator.  Reduce takes three arguments, the function, the initial value of the accumulator, and the list to reduce. | `fn(fn(any, any) -\u003e any, any, [any]) -\u003e any` | `fn(f, acc, list) -\u003e {  for x in list { acc := f(acc, x); }; acc }` |\n| `back` | A macro that sets the current working directory to the parent of the current working directory. | `macro() -\u003e nil` | `macro() -\u003e { cd' .. }` |\n| `add` | A function that adds two values. | `fn(any, any) -\u003e any` | `fn(x, y) -\u003e x + y` |\n| `mul` | A function that multiplies two values. | ^ | `fn(x, y) -\u003e x * y` |\n| `sub` | A function that subtracts two values. | `fn(int or float, int or float) -\u003e int or float` | `fn(x, y) -\u003e x - y` |\n| `div` | A function that divides two values | ^ | `fn(x, y) -\u003e x / y` |\n| `rem` | A function that gets the remainder of two values. | ^ | `fn(x, y) -\u003e x % y` |\n| `sum` | Sum a list of values. | `fn([any]) -\u003e any` | `fn(list) -\u003e reduce(add, 0, list)` |\n| `prod` | Get the product of a list of values. | ^ | `fn(list) -\u003e reduce(mul, 1, list)` |\n| `inc` | Increment a number. | `fn(int) -\u003e int or fn(float) -\u003e float` | `fn(x) -\u003e x + 1` |\n| `dec` | Decrement a number. | ^ | `fn(x) -\u003e x - 1` |\n| `double` | Double a number. | ^ | `fn(x) -\u003e x * 2` |\n| `triple` | Triple a number. | ^ | `fn(x) -\u003e x * 3` |\n| `quadruple` | Quadruple a number. | ^ | `fn(x) -\u003e x * 4` |\n| `quintuple` | Quintuple a number. | ^ | `fn(x) -\u003e x * 5` |\n\n## Installation\n\nTo install, you must download Rust from [here](https://www.rust-lang.org/).\n\n#### Development Build\n\n```bash\n# Download the repo and install from source\ngit clone https://github.com/adam-mcdaniel/atom\ncd atom\ncargo install -f --path .\n```\n\n#### Releases\nTo get the current release build, install from [crates.io](https://crates.io/crates/atomsh).\n\n```bash\n# Also works for updating atomsh\ncargo install -f atomsh\n```\n\n#### After Install\n\n```bash\n# Just run the atom executable!\natom\n```\n","funding_links":["https://www.buymeacoffee.com/adammcdaniel"],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadam-mcdaniel%2Fatom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadam-mcdaniel%2Fatom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadam-mcdaniel%2Fatom/lists"}