{"id":48816093,"url":"https://github.com/chazu/trashtalk","last_synced_at":"2026-04-14T11:03:26.273Z","repository":{"id":331942287,"uuid":"1115994656","full_name":"chazu/trashtalk","owner":"chazu","description":"Like Smalltalk-80 but in bash. If you squint.","archived":false,"fork":false,"pushed_at":"2026-04-13T14:25:06.000Z","size":157236,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T16:24:06.808Z","etag":null,"topics":["abomination","bash","framework","ohgodwhy","smalltalk"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chazu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-14T01:14:01.000Z","updated_at":"2026-04-13T14:25:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/chazu/trashtalk","commit_stats":null,"previous_names":["chazu/trashtalk"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/chazu/trashtalk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazu%2Ftrashtalk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazu%2Ftrashtalk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazu%2Ftrashtalk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazu%2Ftrashtalk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chazu","download_url":"https://codeload.github.com/chazu/trashtalk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chazu%2Ftrashtalk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31793227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["abomination","bash","framework","ohgodwhy","smalltalk"],"created_at":"2026-04-14T11:03:21.726Z","updated_at":"2026-04-14T11:03:26.267Z","avatar_url":"https://github.com/chazu.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/chazu/trashtalk/blob/main/img/logo.png\"\u003e\n\u003c/p\u003e\n\n\n# Trashtalk\n\nA Smalltalk-inspired message-passing system for Bash.\n\nTrashtalk implements message passing, inheritance, traits, aspect-oriented programming and persistent instances - with bash.\n\n## Why would you do this?\n\nI'm not a big fan of bash. I think POSIX is the computing environment we deserve, not the one we need. Bash's ubiquity is its strongest selling point, so strong in fact that bash scripting remains the more-or-less correct choice for a lot of situations, especially in my line of work. This really gets my goat.\n\nI've seen others twist bash/sh into strange loops to give themselves superpowers - both in-person and from afar: a few small tricks, conventions or utilities can become a force-multiplier for software authorship. _Personal_ software authorship. Trashtalk started as a minimal message-passing implementation in bash, intended as an experiment in the direction of enabling expressive personal tool-making in the ugly substrate of shell-scripting.\n\nIt lingered in my dotfiles repo for years.\n\nThen LLMs came. I said \"Hey Claude, what do you think about this gewgaw over here?\" Claude said \"You're absolutely right!\" and we were off - it morphed into a DSL transpiled into bash, then I added a compiler written in golang to provide native compilation for a subset of the DSL, then I started trying to add a TUI-based Smalltalk-style IDE on top of it. Things continued to get weirder and weirder, each day I travelled half the distance between here and v1.0, and eventually it dawned on me that I'd gone too far, so I dialed back Trashtalk and jettisoned the non-bash bits. More precisely, I spun them off into their own projects. Anyhow, here we are.\n\n## What's it good for?\n\nSo far I've only really used Trashtalk to work on Trashtalk. I'll let you know when that changes. Until then, some things I'm thinking about doing include:\n\n- Exploring the idea of an acme-like editor as a substitute for the whiz-bang TUI I tried so desperately to make work\n- Improving the SQLite instance persistence layer, maybe adding some kind of superadjacent analytical layer using duckdb\n\nIf you have any ideas that aren't terribly rude, I'd love to hear them!\n\n## Architecture\n\nTrashtalk uses a **DSL compiler** that transforms Smalltalk-inspired source files (`.trash`) into namespaced Bash functions. This way, we implement message passing without polluting the global namespace. Or, well, we pollute it _in a principled fashion_.\n\n```\n┌─────────────────┐     ┌──────────────┐     ┌─────────────────┐\n│  Source (.trash)│────▶│   Compiler   │────▶│ Compiled (bash) │\n│                 │     │              │     │                 │\n│ Counter subclass│     │  jq-compiler │     │ __Counter__     │\n│   method: inc   │     │              │     │   increment()   │\n└─────────────────┘     └──────────────┘     └─────────────────┘\n                                                      │\n                                                      ▼\n                                             ┌─────────────────┐\n                                             │   Dispatcher    │\n                                             │                 │\n                                             │ @ Counter inc   │\n                                             │       ▼         │\n                                             │ __Counter__     │\n                                             │   increment()   │\n                                             └─────────────────┘\n```\n\n### Key Components\n\n- **DSL Compiler** (`lib/jq-compiler/`) - jq-based two-pass compiler that transforms `.trash` source files into executable Bash\n- **Dispatcher** (`lib/trash.bash`) - Routes `@` message sends to the appropriate namespaced function\n- **Source Files** (`trash/*.trash`) - Human-readable class definitions\n- **Compiled Files** (`trash/.compiled/`) - Generated Bash code (also copied to `trash/` for runtime)\n\n## Installation\n\nClone or copy this repository to `~/.trashtalk`:\n\n```bash\ngit clone \u003crepo-url\u003e ~/.trashtalk\n```\n\nAdd the following to your `.bashrc` or `.zshrc`:\n\n```bash\nsource ~/.trashtalk/lib/trash.bash\n```\n\n## Quick Start\n\n```bash\n# Send a message to an object\n@ Trash info\n\n# Create a counter instance\ncounter=$(@ Counter new)\n@ $counter setValue 5\n@ $counter increment 3\n@ $counter show\n\n# Create an array\narr=$(@ Array new)\n@ $arr push hello\n@ $arr push world\n@ $arr show\n\n# System introspection\n@ Trash listObjects\n@ Trash methodsFor Counter\n@ Trash help\n```\n\n## DSL Syntax\n\nClasses are defined in `.trash` files using a Smalltalk-inspired syntax:\n\n### Basic Class Definition\n\n```smalltalk\n# Counter - A simple counter class\nCounter subclass: Object\n  include: Debuggable\n  instanceVars: value:0 step:1\n\n  method: increment [\n    | newValue |\n    newValue := $(( $(_ivar value) + $(_ivar step) ))\n    _ivar_set value \"$newValue\"\n    echo \"$newValue\"\n  ]\n\n  method: setValue: val [\n    _ivar_set value \"$val\"\n  ]\n\n  method: show [\n    echo \"Counter value: $(_ivar value)\"\n  ]\n```\n\n### DSL Elements\n\n| Element | Syntax | Description |\n|---------|--------|-------------|\n| Class declaration | `ClassName subclass: SuperClass` | Declare a class with inheritance |\n| Trait declaration | `TraitName trait` | Declare a trait (mixin) |\n| Include trait | `include: TraitName` | Mix in a trait |\n| Instance variables | `instanceVars: name:default` | Declare instance vars with defaults |\n| Dependencies | `requires: 'path/to/file.bash'` | Source external dependencies |\n| Method | `method: name [body]` | Define an instance method |\n| Method with args | `method: foo: x bar: y [body]` | Keyword-style arguments |\n| Class method | `classMethod: name [body]` | Define a class method |\n| Raw method | `rawMethod: name [body]` | Pass-through (no transformation) |\n| Test method | `testMethod: name [body]` | Define an inline test (see Testing) |\n| Local variables | `\\| var1 var2 \\|` | Declare local variables |\n| Assignment | `var := value` | Assign to variable |\n| Self reference | `@ self methodName` | Message to self |\n\n### Method Body Transformations\n\nThe compiler transforms DSL constructs to Bash:\n\n```smalltalk\n# DSL syntax:\nmethod: example: arg [\n  | result |\n  result := $(some_command)\n  @ self debug: \"Got result: $result\"\n  @ OtherClass doSomething: \"$result\" with: \"$arg\"\n]\n\n# Compiles to:\n__MyClass__example() {\n  local arg=\"$1\"\n  local result\n  result=$(some_command)\n  @ \"$_RECEIVER\" debug \"Got result: $result\"\n  @ OtherClass doSomething_with \"$result\" \"$arg\"\n}\n```\n\n### Raw Methods\n\nUse `rawMethod:` for code that shouldn't be transformed (heredocs, traps, complex bash):\n\n```smalltalk\nrawMethod: createConfig: name [\n  cat \u003e \"$CONFIG_DIR/$name\" \u003c\u003c 'EOF'\n# Configuration file\nsetting=value\nEOF\n  echo \"Created config: $name\"\n]\n```\n\n### Traits\n\nTraits provide reusable behavior without inheritance:\n\n```smalltalk\nDebuggable trait\n\n  method: debug: message [\n    [[ \"${TRASH_DEBUG:-1}\" == \"0\" ]] \u0026\u0026 return 0\n    local timestamp\n    timestamp=$(date '+%Y-%m-%d %H:%M:%S')\n    echo \"[$timestamp] DEBUG ($_RECEIVER): $message\" \u003e\u00262\n  ]\n\n  method: inspect [\n    echo \"Object: $_RECEIVER\"\n    echo \"Class: $_SUPERCLASS\"\n  ]\n```\n\n### Aspect-Oriented Programming (AOP)\n\nTrashtalk supports before/after advice for cross-cutting concerns like logging, validation, or notifications:\n\n```smalltalk\nAccount subclass: Object\n  instanceVars: balance:0\n\n  method: withdraw: amount [\n    balance := balance - amount\n  ]\n\n  method: deposit: amount [\n    balance := balance + amount\n  ]\n\n  # Run before withdraw: executes\n  before: withdraw: do: [\n    @ self log: \"Attempting withdrawal\"\n  ]\n\n  # Run after deposit: completes\n  after: deposit: do: [\n    @ self notifyBalanceChanged\n  ]\n```\n\nAdvice hooks execute automatically - `before:do:` runs prior to the method, `after:do:` runs after it returns.\n\n### Inline Testing\n\nTrashtalk supports defining tests directly in class files using `testMethod:`. Tests run automatically when using `@ Trash edit: ClassName` - if tests fail, you're returned to the editor.\n\n```smalltalk\nCounter subclass: Object\n  instanceVars: value:0 step:1\n\n  method: increment [\n    value := value + step.\n    ^ value\n  ]\n\n  method: setStep: s [\n    step := s\n  ]\n\n  testMethod: testIncrement [\n    pragma: primitive\n    local c result\n    c=$(@ Counter new)\n    result=$(@ \"$c\" increment)\n    _assert_eq \"$result\" \"1\" \"increment returns 1\"\n    @ \"$c\" destroy\n  ]\n\n  testMethod: testCustomStep [\n    pragma: primitive\n    local c\n    c=$(@ Counter new)\n    @ \"$c\" setStep: 5\n    _assert_eq \"$(@ \"$c\" increment)\" \"5\" \"custom step works\"\n    @ \"$c\" destroy\n  ]\n```\n\n#### Assertion Functions\n\nTests use TAP (Test Anything Protocol) assertions:\n\n| Function | Description |\n|----------|-------------|\n| `_assert_eq \"$actual\" \"$expected\" \"desc\"` | Assert values are equal |\n| `_assert_neq \"$actual\" \"$unexpected\" \"desc\"` | Assert values are not equal |\n| `_assert_true \"$value\" \"desc\"` | Assert value is non-empty |\n| `_assert_false \"$value\" \"desc\"` | Assert value is empty |\n| `_assert_contains \"$haystack\" \"$needle\" \"desc\"` | Assert string contains substring |\n| `_assert_ok \"command\" \"desc\"` | Assert command succeeds (exit 0) |\n\n#### Running Tests\n\n```bash\n# Run tests for a class\n@ Trash runTestsFor: Counter\n\n# Check if a class has tests\n@ Trash hasTestsFor: Counter\n\n# Tests run automatically during edit flow\n@ Trash edit: Counter\n```\n\nOutput follows TAP format:\n\n```\n# Running tests for Counter\nok 1 - increment returns 1\nok 2 - custom step works\n1..2\n# All 2 tests passed\n```\n\n## Compiling Classes\n\nCompile a single class:\n\n```bash\nmake single CLASS=MyClass\n```\n\nCompile all classes:\n\n```bash\nmake compile\n```\n\nOr use the compiler directly:\n\n```bash\nlib/jq-compiler/driver.bash compile trash/MyClass.trash \u003e trash/.compiled/MyClass\n```\n\n## Profiling\n\nTrashtalk includes a built-in profiling system to help identify performance bottlenecks and optimize method dispatch.\n\n### Enabling Profiling\n\nSet `TRASH_PROFILE=1` to enable profiling output:\n\n```bash\n# Profile to stderr\nTRASH_PROFILE=1 @ Counter new\n\n# Profile to a file\nTRASH_PROFILE=1 TRASH_PROFILE_FILE=profile.log @ MyApp run\n```\n\n### Profile Output Format\n\nProfiling logs entry and exit points with timing:\n\n```\n[1767909948.119] → Counter.new [native]\n[daemon] Counter.new 44ms route=fallback reason=no_plugin\n[1767909948.248] → Counter.new [native→bash]\n[1767909948.295] ← Counter.new [native→bash] 153ms\n```\n\n- `→` marks method entry\n- `←` marks method exit with elapsed time\n- Route types: `native`, `bash`, `native→bash`, `bash:direct`\n\n### Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `TRASH_PROFILE=1` | Enable profiling output |\n| `TRASH_PROFILE_FILE=path` | Write to file instead of stderr |\n| `TRASH_PROFILE_DEPTH=N` | Only log calls up to depth N |\n| `TRASH_PROFILE_MIN_MS=N` | Only log calls taking \u003e= N milliseconds |\n\n### Profile Analyzer\n\nUse `bin/trash-profile-analyze` to generate reports from profile logs:\n\n```bash\n# Generate profile data\nTRASH_PROFILE=1 @ MyApp run 2\u003eprofile.log\n\n# Analyze the profile\nbin/trash-profile-analyze profile.log\n```\n\nThe analyzer generates a report showing:\n\n- **Dispatch Routing**: Breakdown of native vs bash execution\n- **Slowest Methods**: Top 10 methods by execution time\n- **Most Called Methods**: Top 10 methods by call count\n- **Classes by Call Count**: Which classes are used most\n- **Recommendations**: Suggestions for optimization (e.g., classes that would benefit from native plugins)\n\nExample output:\n\n```\n================================================================================\n                        TRASHTALK PROFILE REPORT\n================================================================================\n\nRun duration: 2.5 seconds\nTotal method calls: 150\nTotal method time: 2340ms\n\nDISPATCH ROUTING\n----------------\n  [native→bash]          120 calls ( 80%)   avg    15ms   total   1800ms\n  [bash]                  30 calls ( 20%)   avg    18ms   total    540ms\n\nSLOWEST METHODS (top 10)\n------------------------\n     153ms  Dictionary.new                           [native→bash]\n      89ms  Array.map                                [bash]\n      ...\n\nRECOMMENDATIONS\n---------------\n  1. Dictionary has 45 calls but no native support - prioritize for dylib\n```\n\n## Core Classes\n\n| Class | Description |\n|-------|-------------|\n| `Object` | Root class with new, findAll, count methods |\n| `Trash` | System introspection and management |\n| `Store` | SQLite-backed instance persistence |\n| `Array` | Dynamic array with push, pop, map, filter |\n| `Counter` | Simple counter with increment/decrement |\n| `File` | File system operations (read, write, temp files) |\n| `Future` | Async computation with result retrieval |\n| `Process` | External OS process management (subprocess-like) |\n| `ReplServer` | Socket-based REPL server for Emacs integration |\n\n### Traits\n\n| Trait | Description |\n|-------|-------------|\n| `Debuggable` | Debug logging, inspection, ancestry tracing |\n\n## Message Sending\n\n```bash\n# Basic syntax\n@ \u003cReceiver\u003e \u003cselector\u003e [args...]\n\n# Examples\n@ Trash info                      # No arguments\n@ Counter new                     # Returns instance ID\n@ $counter increment 5            # Instance method with arg\n@ Store getField_field \"$id\" name # Keyword method (compiled form)\n```\n\n## Instance Persistence\n\nInstances are stored in SQLite via the Store class:\n\n```bash\n# Create and persist\ncounter=$(@ Counter new)\n@ $counter setValue 42\n\n# Find later\n@ Counter findAll                 # List all Counter instances\n@ Counter find \"value \u003e 10\"       # Query with predicate\n@ Counter count                   # Count instances\n```\n\n## Dependencies\n\nVendored in `lib/vendor/`:\n- `sqlite-json.bash` - SQLite-based JSON document store and key-value persistence\n- `tuplespace/` - Event coordination\n- `bsfl.sh` - Bash utility functions\n- `fun.sh` - Functional programming utilities\n\nExternal tools (install separately):\n- `jo` - JSON output from shell\n- `jq` - JSON processor\n- `sqlite3` - Database engine\n- `uuidgen` - UUID generation (usually pre-installed)\n\n## Emacs Integration\n\nTrashtalk includes a major mode for Emacs with syntax highlighting, indentation, and REPL integration for interactive development.\n\n### Installation\n\nAdd to your `init.el`:\n\n```elisp\n(add-to-list 'load-path \"~/.trashtalk/emacs\")\n(require 'trashtalk-mode)\n```\n\nOr with `use-package`:\n\n```elisp\n(use-package trashtalk-mode\n  :load-path \"~/.trashtalk/emacs\"\n  :mode \"\\\\.trash\\\\'\")\n```\n\n### REPL Server\nTODO Think we nuked it\nThe REPL server provides interactive evaluation, hot reloading, and introspection from Emacs.\n\n**Start the server** in a terminal:\n\n```bash\n@ ReplServer start\n```\n\n**Connect from Emacs** with `C-c C-z` in any `.trash` buffer.\n\n### Key Bindings\nTODO Lord have mercy this is too much\n| Key | Command | Description |\n|-----|---------|-------------|\n| `C-c C-c` | `trashtalk-eval-defun` | Evaluate method at point |\n| `C-c C-r` | `trashtalk-eval-region` | Evaluate selected region |\n| `C-c C-l` | `trashtalk-eval-line` | Evaluate current line |\n| `C-c C-b` | `trashtalk-eval-buffer` | Evaluate entire buffer |\n| `C-c C-k` | `trashtalk-reload-current-file` | Recompile and reload class |\n| `C-c C-z` | `trashtalk-repl-connect` | Connect to REPL server |\n| `C-c C-i` | `trashtalk-info-at-point` | Show info for symbol at point |\n| `C-c C-m` | `trashtalk-methods-for-class` | List methods for a class |\n\n### REPL Protocol\nTODO Didn't we kill this??? Shit should we bring it back?\nThe server uses a simple line-based protocol over a Unix socket (`/tmp/trashtalk-repl.sock`):\n\n```\nRequest:  COMMAND:payload\nResponse: STATUS:result\n```\n\nCommands: `EVAL`, `COMPLETE`, `INFO`, `METHODS`, `RELOAD`, `PING`, `QUIT`\n\nYou can also interact with the server from the command line:\n\n```bash\necho \"PING\" | nc -U /tmp/trashtalk-repl.sock\necho \"EVAL:@ Counter new\" | nc -U /tmp/trashtalk-repl.sock\n```\n\n## File Structure\n\n```\n~/.trashtalk/\n├── emacs/\n│   └── trashtalk-mode.el    # Emacs major mode with REPL support\n├── lib/\n│   ├── trash.bash           # Main runtime \u0026 dispatcher\n│   ├── jq-compiler/         # jq-based DSL compiler\n│   │   ├── driver.bash      # CLI entry point\n│   │   ├── tokenizer.bash   # Source → JSON tokens\n│   │   ├── parser.jq        # Tokens → AST\n│   │   └── codegen.jq       # AST → Bash code\n│   └── vendor/              # Vendored dependencies\n├── trash/\n│   ├── *.trash              # DSL source files\n│   ├── .compiled/           # Compiled output\n│   │   └── traits/          # Compiled traits\n│   └── traits/              # Trait source files\n└── tests/                   # Test scripts\n```\n\n## Version\n\nSupposedly v1.0.0\n\n## Author\n\nChaz Straney\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchazu%2Ftrashtalk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchazu%2Ftrashtalk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchazu%2Ftrashtalk/lists"}