#!/usr/bin/env python3
# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-import sys ,os ,filecmp
+import sys ,os ,filecmp ,stat
from pathlib import Path
def get_project_name(repo_path: Path) -> str:
return repo_path.resolve().name
+def format_perms(mode: int) -> str:
+ # Extract the 9-character permission string (e.g., rwxr-xr-x), dropping the leading file type char
+ return stat.filemode(mode)[1:]
+
def CLI(argv=None) -> int:
if argv is None:
argv = sys.argv[1:]
return 1
project_name = get_project_name(project_dir)
+
+ # Data structures for the output sections
+ opus_warnings = []
+ missing_files = []
+
+ # Time-based modification buckets
+ mod_harmony_newer = []
+ mod_same_time = []
+ mod_project_newer = []
+
+ perms_only_changes = []
+ added_files = []
+
+ # --- Section 1: Opus File Structural Audit ---
+ project_opus = f"0pus_{project_name}"
+ harmony_opus = "0pus_Harmony"
+ found_opus = False
+
+ for TM_item in os.listdir(project_dir):
+ if TM_item.startswith("0pus_") and os.path.isfile(project_dir / TM_item):
+ if TM_item == project_opus:
+ found_opus = True
+ else:
+ opus_warnings.append(f"Incorrectly named Opus file found: '{TM_item}'. Expected '{project_opus}'.")
+
+ if not found_opus:
+ opus_warnings.append(f"Expected Opus file '{project_opus}' is MISSING from project root.")
# Semantic boundaries: isolate the skeleton by ignoring user zones
IGNORED_DIRS = {".git" ,"scratchpad" ,"consumer" ,"authored" ,"__pycache__"}
rel_path = f_path.relative_to(project_dir)
project_files.add(str(rel_path))
- # Map the dynamically generated Opus file
- harmony_opus = "0pus_Harmony"
- project_opus = f"0pus_{project_name}"
-
+ # Align the Opus file names for comparison
if project_opus in project_files:
project_files.remove(project_opus)
project_files.add(harmony_opus)
+ else:
+ for TM_item in list(project_files):
+ if TM_item.startswith("0pus_") and "/" not in TM_item:
+ project_files.remove(TM_item)
all_files = sorted(list(harmony_files | project_files))
- drift_count = 0
for TM_rel in all_files:
if TM_rel not in project_files:
- print(f"Missing in project: {TM_rel}")
- drift_count += 1
+ if TM_rel == harmony_opus and not found_opus:
+ continue
+ missing_files.append(TM_rel)
elif TM_rel not in harmony_files:
- print(f"Orphaned in project: {TM_rel}")
- drift_count += 1
+ added_files.append(TM_rel)
else:
h_abs = harmony_dir / TM_rel
p_abs = project_dir / TM_rel
if TM_rel == harmony_opus:
p_abs = project_dir / project_opus
- if not filecmp.cmp(h_abs ,p_abs ,shallow=False):
- print(f"Modified: {TM_rel}")
- drift_count += 1
+ if p_abs.exists():
+ h_stat = h_abs.stat()
+ p_stat = p_abs.stat()
+
+ h_perms = format_perms(h_stat.st_mode)
+ p_perms = format_perms(p_stat.st_mode)
+ perms_differ = (h_perms != p_perms)
+
+ display_str = f"({h_perms}, {p_perms}) {TM_rel}" if perms_differ else TM_rel
+ content_differs = not filecmp.cmp(h_abs ,p_abs ,shallow=False)
+
+ if content_differs:
+ h_time = h_stat.st_mtime
+ p_time = p_stat.st_mtime
+
+ if h_time > p_time:
+ mod_harmony_newer.append(display_str)
+ elif h_time < p_time:
+ mod_project_newer.append(display_str)
+ else:
+ mod_same_time.append(display_str)
+ elif perms_differ:
+ perms_only_changes.append(display_str)
+
+ # --- Output Generation ---
+ print(f"\nSkeleton Audit: Harmony vs {project_name}")
+ print("=" * 60)
+
+ print("\n1. 0pus File Audit")
+ print("-" * 20)
+ if not opus_warnings:
+ print(" [OK] Opus file is correctly named and present.")
+ else:
+ for TM_w in opus_warnings:
+ print(f" [WARNING] {TM_w}")
+ print("\n2. Missing from Project (In Harmony, missing locally)")
+ print("-" * 20)
+ if not missing_files:
+ print(" [OK] No Harmony skeleton files are missing.")
+ else:
+ for TM_f in missing_files:
+ print(f" - {TM_f}")
+
+ print("\n3. Modified Skeleton Files (Diverged from Harmony)")
+ print("-" * 20)
+ if not (mod_harmony_newer or mod_same_time or mod_project_newer):
+ print(" [OK] No shared skeleton files have been modified.")
+ else:
+ if mod_harmony_newer:
+ print(" [Harmony newer]")
+ for TM_f in mod_harmony_newer:
+ print(f" ~ {TM_f}")
+ if mod_same_time:
+ print(" [Same modification time]")
+ for TM_f in mod_same_time:
+ print(f" ~ {TM_f}")
+ if mod_project_newer:
+ print(" [Project newer]")
+ for TM_f in mod_project_newer:
+ print(f" ~ {TM_f}")
+
+ print("\n4. Permissions Only Changes")
+ print("-" * 20)
+ if not perms_only_changes:
+ print(" [OK] No permission-only changes detected.")
+ else:
+ for TM_f in perms_only_changes:
+ print(f" * {TM_f}")
+
+ print("\n5. Project-Specific Files (Not in Harmony)")
+ print("-" * 20)
+ if not added_files:
+ print(" [INFO] No additional files outside the standard skeleton.")
+ else:
+ for TM_f in added_files:
+ print(f" + {TM_f}")
+
+ print("\n" + "=" * 60)
+ drift_count = len(opus_warnings) + len(missing_files) + len(mod_harmony_newer) + len(mod_same_time) + len(mod_project_newer) + len(perms_only_changes)
if drift_count == 0:
- print("Skeleton is perfectly synchronized with Harmony.")
+ print("Result: Skeleton is perfectly synchronized with Harmony.")
else:
- print(f"\nTotal drift: {drift_count} files.")
+ print(f"Total Structural Drift (Sections 1-4): {drift_count} issues.")
return 0
<RT-article>
<RT-title
author="Thomas Walker Lynch"
- date="2026-03-09"
+ date="2026-06-23"
title="Introduction to Harmony">
</RT-title>
<ul>
<li>Sources the project-wide setup (<RT-code>shared/tool/setup</RT-code>) to establish the core environment variables (e.g., <RT-code>REPO_HOME</RT-code> and <RT-code>PROJECT</RT-code>).</li>
<li>Conditionally sources <RT-code>shared/authored/setup</RT-code> (if present) to apply administrator-injected, project-specific tool configurations.</li>
+ <li>Dynamically sources all <RT-code>.init</RT-code> files found in the <RT-code>shared/linked-project/</RT-code> directory.</li>
<li>Configures the <RT-code>PATH</RT-code> to include shared tools, library environments, and the specific <RT-code><role>/tool</RT-code> directory.</li>
<li>Changes the working directory into the specified role's workspace.</li>
<li>Sources the <RT-code><role>/tool/setup</RT-code> script. While the earlier steps apply the standard Harmony skeleton setup, this final step applies the role setup that is customized for this specific project.</li>
<h1>After git clone</h1>
<p>
- Because git does not track certain directories (such as <RT-code>shared/linked-project/</RT-code> and <RT-code>consumer/made/</RT-code>), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.
+ Because git does not track certain artifact directories (such as <RT-code>consumer/</RT-code> outputs), a freshly cloned repository lacks external dependencies and consumable products. Team members must perform a few steps to populate these areas.
</p>
<h2>Third-party tools</h2>
<p>
- Harmony is language agnostic. When a project makes use of project-specific C, Python, NodeJS, Java, or other tools, the project administrator configures the project to expect these tools in the <RT-code>shared/linked-project</RT-code> directory.
+ Harmony is language agnostic. When a project makes use of project-specific C, Python, NodeJS, Java, or other tools, the project administrator configures the project to expect these tools in the <RT-code>shared/linked-project</RT-code> directory via the `.init` plugin system.
</p>
<p>
- Because multiple team members will have to repeat the third-party install process after cloning a project, the administrator should carefully document the third party tools installation steps and place the resulting documents in the <RT-code>shared/document</RT-code> directory. (The most common installation method is to clone the third party tool next to the project, then to symbolic link it under <RT-code>shared/linked-project/</RT-code>.)
- </p>
- <p>
- If a person clones the project and does not set up third-party tools, commands will fall through the search path and perhaps find system-installed tools. Using system default tools is generally undesirable because each person who clones the project might be running different versions. This variation makes it harder for team members to coordinate and for consumers of the project work product to build functioning tools.
+ Because multiple team members will have to repeat the third-party install process after cloning a project, the administrator should carefully document the installation steps and place the resulting documents in the <RT-code>administrator/document</RT-code> directory. The standard installation method is to clone the external tool into the parent directory alongside the project, create a symlink to it under <RT-code>shared/linked-project/</RT-code> using the <RT-code>project/</RT-code> parent link, and supply an <RT-code>.init</RT-code> script to manage the local environment variables.
</p>
<h2>Consumer build</h2>
In this section we use the term 'consumer' to mean any team member that wants to make use of the project work product. The tester will want to test it, and the consumer role will want to deploy it, etc.
</p>
<p>
- An earlier version of Harmony used platform-specific <RT-code>made</RT-code> directories, but this left binaries in the repository and added excessive complexity.
- So as to avoid that mess, the current version of Harmony requires a work product consumer to run a local build after cloning the project. The results of the build will appear in the <RT-code>consumer/made</RT-code> directory.
+ Because compiled binaries and final layouts are not tracked in the repository, the current version of Harmony requires a work product consumer to run a local build after cloning the project. The results of the build will appear directly in the <RT-code>consumer/</RT-code> directory.
</p>
<p>
- To facilitate this, the developer must explicitly document the project's build and promote procedure, saving this guide as <RT-code>developer/document/build.html</RT-code>.
- </p>
- <p>
- The consumer must then read this document and execute the described steps to compile the source and locally populate their <RT-code>consumer/made</RT-code> directory.
+ To facilitate this, the developer must explicitly document the project's build and promote procedure, saving this guide as <RT-code>developer/document/build.html</RT-code>. The consumer must then read this document and execute the described steps to locally populate their <RT-code>consumer/</RT-code> directory.
</p>
<p>
<li><RT-code>> bash</RT-code></li>
<li><RT-code>> cd <project></RT-code></li>
<li><RT-code>> . setup developer</RT-code></li>
- <li><RT-code>> make CLI</RT-code></li>
+ <li><RT-code>> build <namespace></RT-code></li>
<li><RT-code>> promote write</RT-code></li>
<li><RT-code>> exit</RT-code></li>
<li><RT-code>> bash</RT-code></li>
</ol>
<p>
- This sequence opens a bash shell, assumes the developer role to orchestrate the build, makes the work product, then promotes it to the consumer's workspace. The <RT-code>exit</RT-code> command drops the developer role. The last two lines put the person into the <role> workspace, typically for testing or deploying. Commonly, deployment is a matter of adding the <RT-code>consumer/made</RT-code> directory into the executable search path.
+ This sequence opens a bash shell, assumes the developer role to orchestrate the build, makes the work product, then promotes it to the consumer's workspace. The <RT-code>exit</RT-code> command drops the developer role. The last two lines put the person into the <role> workspace, typically for testing or deploying.
</p>
Files found in a directory named <RT-code>authored</RT-code> were written by project team members. They did not come with the Harmony skeleton, nor with the installation of other software. Project build tools treat <RT-code>authored</RT-code> directories as strictly read-only. Typically these files constitute the intellectual property of a project.
</p>
<p>
- All source code that gets built into a promotion or project release must be placed in the developers' <RT-code>authored</RT-code> directory. The story is not as clean for build tools and other files. New documents go into document directories, and new tools go into the tool directories, etc. As a specific example, the developer will almost certainly edit the <RT-code>developer/tool/make</RT-code> file.
+ All source code that gets built into a promotion or project release must be placed in the developers' <RT-code>authored</RT-code> directory. The story is not as clean for build tools and other files. New documents go into document directories, and new tools go into the tool directories, etc. As a specific example, the developer will almost certainly edit the <RT-code>developer/tool/build</RT-code> file.
</p>
<p>
When the Harmony version line in the <RT-code>shared/tool/version</RT-code> file is left in place, it is straightforward for a project administrator to determine which Harmony skeleton files have been edited in a project, and which new files have been added.
<li><RT-code>administrator/</RT-code> : Project-local tools and skeleton maintenance.</li>
<li><RT-code>developer/</RT-code> : Primary workspace for developers.</li>
<li><RT-code>tester/</RT-code> : Regression and validation workspace for testers.</li>
- <li><RT-code>consumer/</RT-code> : Consumption workspace holding the <RT-code>consumer/made</RT-code> directory.</li>
+ <li><RT-code>consumer/</RT-code> : Consumption workspace acting as the final artifact sink.</li>
<li><RT-code>shared/</RT-code> : Shared ecosystem tools and global environments.</li>
</ul>
<li><RT-code>authored/</RT-code> : Human-written source. Tracked by Git.</li>
<li><RT-code>made/</RT-code> : Tracked artifacts generated by tools (e.g., links to CLI entry points).</li>
<li><RT-code>experiment/</RT-code> : Try-it-here code. Short-lived spot testing.</li>
- <li><RT-code>scratchpad/</RT-code> : Git-ignored directory. Holds all intermediate build outputs, including the <RT-code>made</RT-code> directory for promotions.</li>
- <li><RT-code>tool/</RT-code> : Developer-specific tools (like the promote and make scripts).</li>
+ <li><RT-code>scratchpad/</RT-code> : Git-ignored directory. Holds all intermediate build outputs, including the staged namespace directories for promotions.</li>
+ <li><RT-code>tool/</RT-code> : Developer-specific tools (like the promote and build scripts).</li>
</ul>
<h2>The tester work area</h2>
<h2>The shared tree</h2>
<p>
- This directory contains ecosystem tools and global environments available to all roles. This includes the shared tool directory, as well as third-party installations (like Python virtual environments or compilers) required by the project. To assist in project specific modifications to the Harmony skeleton, Harmony comes with an empty <RT-code>shared/authored</RT-code> directory that is listed earlier in the executable search path than <RT-code>shared/tool</RT-code>.
+ This directory contains ecosystem tools and global environments available to all roles. This includes the shared tool directory, as well as third-party symlinks and <RT-code>.init</RT-code> scripts required by the project. To assist in project specific modifications to the Harmony skeleton, Harmony comes with an empty <RT-code>shared/authored</RT-code> directory that is listed earlier in the executable search path than <RT-code>shared/tool</RT-code>.
</p>
<h2>The consumer tree</h2>
<p>
- The <RT-code>consumer/made/</RT-code> tree is where developers put work product that is ready to be consumed. The entire <RT-code>consumer/made</RT-code> directory is git-ignored and treated as a transient deployment target.
- </p>
- <p>
- Artifacts arrive in the <RT-code>consumer/made/</RT-code> tree <em>only</em> when the promote script is invoked, which performs a flat-copy from the developer's <RT-code>scratchpad/made</RT-code> directory.
+ The <RT-code>consumer/</RT-code> tree is where developers put work product that is ready to be consumed. The entire directory is git-ignored and treated as a transient deployment target. Artifacts arrive in the <RT-code>consumer/</RT-code> tree <em>only</em> when the promote script is invoked, which performs a flat-copy from the developer's <RT-code>scratchpad/made</RT-code> directory.
</p>
<h2>Document directories</h2>
</p>
<ul>
<li><RT-code>document/</RT-code> : Top-level onboarding, project-wide structure, such as this document.</li>
- <li><RT-code>consumer/made/document/</RT-code> : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).</li>
+ <li><RT-code>consumer/<namespace>/document/</RT-code> : Documentation for end-users of made code (e.g., man pages, application manuals, library API references).</li>
<li><RT-code>administrator/document/</RT-code> : Documentation for maintaining the project skeleton and global tools.</li>
<li><RT-code>developer/document/</RT-code> : Documentation for developers, including coding standards and internal API guides.</li>
<li><RT-code>tester/document/</RT-code> : Documentation for testers detailing test plans and tools.</li>
</ul>
<p>
- Note that the <RT-code>consumer/made</RT-code> directory is untracked by git and maintained by a tool. (Said tool is <RT-code>developer/tool/promote</RT-code>. It is owned and used by the developer as part of the build process.) Documents that are being promoted for eventual release, and appear in the <RT-code>made</RT-code> directory originate from somewhere from the <RT-code>developer/authored</RT-code> directory depending on how the developer has organized his workspace, but probably in <RT-code>developer/authored/document</RT-code>. Perhaps a future version of Harmony will have a tech-writer role, but that is not the case now.
- </p>
-
- <p>
- Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style-JS_public</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
+ Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-Style</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document.
</p>
<h2>Untracked directories</h2>
<ol>
- <li><RT-code>consumer/made/</RT-code></li>
- <li><RT-code>shared/linked-project/</RT-code></li>
+ <li><RT-code>consumer/</RT-code> (Excluding the base <RT-code>.gitignore</RT-code>)</li>
+ <li><RT-code>shared/linked-project/</RT-code> (Excluding <RT-code>.init</RT-code> and <RT-code>.gitignore</RT-code> files)</li>
<li><RT-code>**/scratchpad/</RT-code></li>
</ol>
<h1>Developer promotion and project releases</h1>
<p>
- As a first step, a developer creates a <RT-term>promotion candidate</RT-term> inside of the <RT-code>consumer/made/</RT-code> directory. This is typically done by running <RT-code>make</RT-code> to stage the artifacts into <RT-code>scratchpad/made</RT-code>, where they can be experimented on, followed by running <RT-code>promote write</RT-code>. The developer will often modify the versions of one or both of those tools that come with the Harmony skeleton. The promotion candidate remains stable until the next <RT-term>promotion</RT-term>.
+ As a first step, a developer creates a <RT-term>promotion candidate</RT-term> inside of the <RT-code>consumer/</RT-code> directory. This is typically done by running <RT-code>build</RT-code> to stage the artifacts into <RT-code>scratchpad/made</RT-code>, followed by running <RT-code>promote write</RT-code>. The developer will often modify the versions of one or both of those tools that come with the Harmony skeleton. The promotion candidate remains stable until the next <RT-term>promotion</RT-term>.
</p>
<p>
- Then the tester runs tests on the promotion candidate. Tests must only read from the <RT-code>consumer/made/</RT-code> directory, though local copies can be made and edited as experiments. Currently bugs are filed using an external issues tool.
+ Then the tester runs tests on the promotion candidate. Tests must only read from the <RT-code>consumer/</RT-code> directory, though local copies can be made and edited as experiments.
</p>
<p>
It is common for a developer to open a second window on his desktop, and then enter the project as a tester in that second window. The developer can then make a promotion candidate, run the tests, edit source code, and perhaps tests, and then quickly spin through the test-debug-fix-promote cycle repeatedly.
When the product manager determines the work product to be sufficiently reliable and feature rich, the administrator will make a <RT-term>project release</RT-term>. He will do this by creating a branch called <RT-code>release_v<major></RT-code> and tagging it. The major release numbers go up incrementally.
</p>
- <h1>The version 2.2 Harmony directory tree</h1>
+ <h1>The Harmony directory tree</h1>
<RT-code>
- 2026-03-09 01:42:16 Z [Harmony:administrator] Thomas_developer@StanleyPark
+ 2026-06-23 13:41:45 Z [Harmony:administrator] Thomas_developer@StanleyPark
§/home/Thomas/subu_data/developer/project§
> tree Harmony
Harmony
│ ├── archive
│ └── setup
├── consumer
- │ ├── scratchpad
- │ └── tool
- │ └── env
+ │ └── .gitignore
├── developer
│ ├── authored
│ │ └── hello.CLI.c
- </RT-code>
- <RT-code>
│ ├── document
│ │ ├── 02_RT_Code_Format.html
│ │ ├── 03_Naming_and_Directory_Conventions.html
│ ├── scratchpad
│ └── tool
│ ├── do_all
- │ ├── make
+ │ ├── build
│ ├── makefile
│ ├── promote
│ └── setup
│ ├── role-and-workflow_product-development.html
│ └── setup.js
├── LICENSE
- </RT-code>
- <RT-code>
├── README.md
├── scratchpad
- │ ├── Harmony__79f9d52__2026-03-07_085628Z.tar
- │ └── Harmony__e665bb7__2026-03-09_013712Z.tar
├── setup
├── shared
│ ├── authored
│ ├── document
- │ │ ├── installation_generic.org
- │ │ ├── installation_Python.org
+ │ │ ├── install_generic.org
+ │ │ ├── install_Python.org
│ │ └── setup.js
│ ├── made
│ ├── dictionary_style-directory.js
- │ ├── third_party
- │ │ ├── RT-style-JS_public -> ../../../RT-style-JS_public/
- │ │ └── upstream
+ │ ├── linked-project
+ │ │ ├── project -> ../../../
+ │ │ ├── Python-3.12.3 -> project/Python-3.12.3
+ │ │ ├── Python.init
+ │ │ ├── RT-Style -> project/RT-Style/consumer
+ │ │ └── RT-Style.init
│ └── tool
│ ├── scratchpad
│ ├── setup
- </RT-code>
- <RT-code>
│ ├── style
│ └── version
└── tester
│ └── data_test-1.py
└── tool
└── setup
-
- 30 directories, 36 files
-
- 2026-03-09 01:42:19 Z [Harmony:administrator] Thomas_developer@StanleyPark
- §/home/Thomas/subu_data/developer/project§
- >
</RT-code>
</RT-article>
+++ /dev/null
-2026-03-26 03:41:44
-
-when making a skeleton from Harmony, set skeleton docs and other files not to be edited to read only
-
+++ /dev/null
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-RT_Format — Reasoning Technology code formatter (Shallow Tokenizer)
-
-Commands:
- RT_Format write [--lisp] <file ...> Format files in place (rewrite originals)
- RT_Format copy [--lisp] <file ...> Save backups as <file>~ then format originals
- RT_Format pipe [--lisp] Read from stdin, write to stdout
- RT_Format self_test Run built-in tests
- RT_Format version Show tool version
- RT_Format help | --help Show usage
-"""
-
-import sys ,re ,shutil ,os
-from typing import List ,Tuple ,Optional ,TextIO
-
-RTF_VERSION = "0.4.0-tokenized"
-
-USAGE = """\
-Usage:
- RT_Format write [--lisp] <file ...>
- RT_Format copy [--lisp] <file ...>
- RT_Format pipe [--lisp]
- RT_Format self_test
- RT_Format version
- RT_Format help | --help
-"""
-
-BR_OPEN = "([{<"
-BR_CLOSE = ")]}>"
-PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
-REV = dict( zip(BR_CLOSE ,BR_OPEN) )
-
-# --------------- Lexer ----------------
-
-class RT_Token:
- def __init__(self ,kind: str ,text: str):
- self.kind = kind
- self.text = text
-
- def __repr__(self):
- return f"<{self.kind}:{repr(self.text)}>"
-
-# The regex prioritizes exact matches.
-# Comments include //, #, and /* ... */ blocks.
-# Strings include Python '''/""" blocks, plus standard single/double quotes.
-TOKEN_REGEX = re.compile(
- r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
- r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
- r'|(?P<SPACE>[ \t]+)'
- r'|(?P<NEWLINE>\n)'
- r'|(?P<COMMA>,)'
- r'|(?P<BR_OPEN>[\[\(\{<])'
- r'|(?P<BR_CLOSE>[\]\)\}>])'
- r'|(?P<CODE>[^ \t\n,\[\(\{<\]\)\}>"\'#/]+|/)'
-)
-
-def tokenize(text: str) -> List[RT_Token]:
- tokens = []
- for TM_match in TOKEN_REGEX.finditer(text):
- kind = TM_match.lastgroup
- text_val = TM_match.group(kind)
- tokens.append( RT_Token(kind ,text_val) )
- return tokens
-
-def group_lines( tokens: List[RT_Token] ) -> List[ List[RT_Token] ]:
- lines = []
- current = []
- for TM_tok in tokens:
- current.append(TM_tok)
- if TM_tok.kind == "NEWLINE":
- lines.append(current)
- current = []
- if current:
- lines.append(current)
- return lines
-
-# --------------- Formatting Passes ----------------
-
-def pass_vertical_commas( lines: List[List[RT_Token]] ) -> None:
- for TM_idx in range( len(lines) - 1 ):
- current_line = lines[TM_idx]
-
- # Find the last significant token
- last_sig_idx = -1
- for TM_i in range( len(current_line) - 1 ,-1 ,-1 ):
- if current_line[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
- last_sig_idx = TM_i
- break
-
- if last_sig_idx>= 0 and current_line[last_sig_idx].kind == "COMMA":
- # Remove the trailing comma
- comma_tok = current_line.pop(last_sig_idx)
-
- # Migrate to the next line with code
- for TM_j in range( TM_idx + 1 ,len(lines) ):
- next_line = lines[TM_j]
- first_sig_idx = -1
- for TM_k ,TM_tok in enumerate(next_line):
- if TM_tok.kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
- first_sig_idx = TM_k
- break
-
- if first_sig_idx>= 0:
- next_line.insert(first_sig_idx ,comma_tok)
- break
-
-def pass_horizontal_commas( line: List[RT_Token] ) -> None:
- new_line = []
- for TM_tok in line:
- if TM_tok.kind == "COMMA":
- is_vertical = all(t.kind == "SPACE" for t in new_line)
- if not is_vertical:
- while new_line and new_line[-1].kind == "SPACE":
- new_line.pop()
- if new_line:
- new_line.append( RT_Token("SPACE" ," ") )
- new_line.append(TM_tok)
- elif TM_tok.kind == "SPACE":
- if new_line and new_line[-1].kind == "COMMA":
- continue # Drop space after comma
- new_line.append(TM_tok)
- else:
- new_line.append(TM_tok)
- line[:] = new_line
-
-def pass_tighten_brackets( line: List[RT_Token] ) -> None:
- new_line = []
- for TM_tok in line:
- if TM_tok.kind == "SPACE":
- if new_line and new_line[-1].kind == "BR_OPEN":
- continue
- new_line.append(TM_tok)
- elif TM_tok.kind == "BR_CLOSE":
- while new_line and new_line[-1].kind == "SPACE":
- new_line.pop()
- new_line.append(TM_tok)
- else:
- new_line.append(TM_tok)
- line[:] = new_line
-
-def get_bracket_spans( line: List[RT_Token] ) -> List[ Tuple[int ,int] ]:
- stack = []
- spans = []
- for TM_i ,TM_tok in enumerate(line):
- if TM_tok.kind == "BR_OPEN":
- stack.append( (TM_tok.text ,TM_i) )
- elif TM_tok.kind == "BR_CLOSE":
- if stack and REV[TM_tok.text] == stack[-1][0]:
- _ ,pos = stack.pop()
- if not stack:
- spans.append( (pos ,TM_i) )
- return spans
-
-def contains_inner_brackets( line: List[RT_Token] ,start: int ,end: int ) -> bool:
- for TM_i in range(start + 1 ,end):
- if line[TM_i].kind in ("BR_OPEN" ,"BR_CLOSE"):
- return True
- return False
-
-def pass_pad_outermost( line: List[RT_Token] ,is_lisp: bool ) -> None:
- if is_lisp:
- return
-
- while True:
- spans = get_bracket_spans(line)
- changed = False
-
- # Process from right to left to avoid shifting indices
- for TM_start ,TM_end in reversed(spans):
- if contains_inner_brackets(line ,TM_start ,TM_end):
- left_has = (TM_start + 1 <len(line)) and (line[TM_start + 1].kind == "SPACE")
- right_has = ( TM_end - 1>= 0 ) and ( line[TM_end - 1].kind == "SPACE" )
-
- if not left_has or not right_has:
- if not right_has:
- line.insert( TM_end ,RT_Token("SPACE" ," ") )
- if not left_has:
- line.insert( TM_start + 1 ,RT_Token("SPACE" ," ") )
- changed = True
- break # Re-evaluate spans after mutation
- if not changed:
- break
-
-# --------------- Public API ----------------
-
-def format_tokens( tokens: List[RT_Token] ,is_lisp: bool ) -> str:
- lines = group_lines(tokens)
- pass_vertical_commas(lines)
-
- for TM_line in lines:
- pass_horizontal_commas(TM_line)
- pass_tighten_brackets(TM_line)
- pass_pad_outermost(TM_line ,is_lisp)
-
- return "".join(t.text for TM_line in lines for t in TM_line)
-
-def rt_format_text(text: str ,is_lisp: bool) -> str:
- tokens = tokenize(text)
- return format_tokens(tokens ,is_lisp)
-
-def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
- text = inp.read()
- out.write( rt_format_text(text ,is_lisp) )
-
-# --------------- Self-test ----------------
-
-def run_self_test() -> bool:
- ok = True
- def chk(src ,exp):
- nonlocal ok
- got = rt_format_text(src ,False)
- if got != exp:
- print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
- ok = False
-
- chk("a,b,c" ,"a ,b ,c")
- chk("a , b , c" ,"a ,b ,c")
- chk(" ,vertical_arg" ," ,vertical_arg")
-
- chk("int a=0,\n b=1,\n c=2;" ,"int a=0\n ,b=1\n ,c=2;")
-
- chk("f ( x )" ,"f(x)")
- chk("f(x) + g(y)" ,"f(x) + g(y)")
- chk(" {" ," {")
-
- src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
- exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
- chk(src ,exp)
-
- chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
- chk("compute(x, f(y" ,"compute( x ,f(y") # Tolerant fragment fallback omitted for brevity, but structurally sound.
-
- print("SELFTEST OK" if ok else "SELFTEST FAILED")
- return ok
-
-# --------------- CLI ----------------
-
-def write_files( paths: List[str] ,is_lisp: bool ) -> int:
- for TM_path in paths:
- with open(TM_path ,"r" ,encoding="utf-8") as f:
- data = f.read()
- formatted = rt_format_text(data ,is_lisp)
- with open(TM_path ,"w" ,encoding="utf-8") as f:
- f.write(formatted)
- return 0
-
-def copy_files( paths: List[str] ,is_lisp: bool ) -> int:
- for TM_path in paths:
- shutil.copy2(TM_path ,TM_path + "~")
- return write_files(paths ,is_lisp)
-
-def get_usage() -> str:
- prog_name = os.path.basename( sys.argv[0] )
- return f"""\
-Usage:
- {prog_name} write [--lisp] <file ...>
- {prog_name} copy [--lisp] <file ...>
- {prog_name} pipe [--lisp]
- {prog_name} self_test
- {prog_name} version
- {prog_name} help | --help
-"""
-
-def CLI(argv=None) -> int:
- args = list( sys.argv[1:] if argv is None else argv )
- usage_text = get_usage()
-
- if not args or args[0] in {"help" ,"--help" ,"-h"}:
- print(usage_text)
- return 0
-
- is_lisp = "--lisp" in args
- args = [TM_a for TM_a in args if TM_a != "--lisp"]
-
- if not args:
- return 0
-
- cmd = args[0]
- rest = args[1:]
-
- if cmd == "version":
- print(RT_FORMAT_VERSION)
- return 0
- if cmd == "self_test":
- ok = run_self_test()
- return 0 if ok else 1
- if cmd == "pipe":
- rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
- return 0
- if cmd == "write":
- if not rest:
- print("write: missing <file ...>\n" + usage_text)
- return 2
- return write_files(rest ,is_lisp)
- if cmd == "copy":
- if not rest:
- print("copy: missing <file ...>\n" + usage_text)
- return 2
- return copy_files(rest ,is_lisp)
-
- print(f"Unknown command: {cmd}\n" + usage_text)
- return 2
-
-if __name__ == "__main__":
- sys.exit( CLI() )
\ No newline at end of file
+++ /dev/null
-( defun RT-format-buffer()
- (interactive)
- (save-excursion
- ( shell-command-on-region(point-min)(point-max)
- "RT-formatter pipe" t t)) )
+++ /dev/null
-
-(defun RT-formatter-buffer ()
- "Format the current buffer using RTfmt."
- (interactive)
- (if (not (executable-find "RT-formatter"))
- (message "Error: RTfmt executable not found in PATH.")
- (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
- (args (list "pipe")))
- (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
- (setq args (append args (list "--lisp"))))
- (unwind-protect
- (let ((exit-code (apply #'call-process-region
- (point-min) (point-max)
- "RT-formatter"
- nil temp-buffer nil
- args)))
- (if (zerop exit-code)
- (let ((formatted-text (with-current-buffer temp-buffer (buffer-string))))
- (save-excursion
- (delete-region (point-min) (point-max))
- (insert formatted-text))
- (message "RT-formatter formatting successful."))
- (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
- (kill-buffer temp-buffer)))))
-
-;; ( defun RT-format-buffer()
-;; (interactive)
-;; (save-excursion
-;; ( shell-command-on-region(point-min)(point-max)
-;; "RT-formatter pipe" t t)) )
+++ /dev/null
-(defun RT-formatter-buffer ()
- "Format the current buffer using RTfmt."
- (interactive)
- (if (not (executable-find "RT-formatter"))
- (message "Error: RTfmt executable not found in PATH.")
- (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
- (args (list "pipe")))
- (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
- (setq args (append args (list "--lisp"))))
- (unwind-protect
- (let ((exit-code (apply #'call-process-region
- (point-min) (point-max)
- "RT-formatter"
- nil temp-buffer nil
- args)))
- (if (zerop exit-code)
- (progn
- ;; Applies a non-destructive diff, preserving point and markers natively
- (replace-buffer-contents temp-buffer)
- (message "RT-formatter formatting successful."))
- (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
- (kill-buffer temp-buffer)))))
+++ /dev/null
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-RTfmt — Reasoning Technology code formatter (Predicate Tokenizer)
-
-Commands:
- RTfmt write [--lisp] <file ...> Format files in place (rewrite originals)
- RTfmt copy [--lisp] <file ...> Save backups as <file>~ then format originals
- RT-formatter pipe [--lisp] Read from stdin, write to stdout
- RTfmt self_test Run built-in tests
- RTfmt version Show tool version
- RTfmt help | --help Show usage
-"""
-
-import sys ,re ,shutil ,os
-from typing import List ,Tuple ,Optional ,TextIO
-
-RTF_VERSION = "0.5.0-predicate"
-
-def get_usage() -> str:
- prog_name = os.path.basename(sys.argv[0])
- return f"""\
-Usage:
- {prog_name} write [--lisp] <file ...>
- {prog_name} copy [--lisp] <file ...>
- {prog_name} pipe [--lisp]
- {prog_name} self_test
- {prog_name} version
- {prog_name} help | --help
-"""
-
-# Removed < and > so they are treated as standard CODE operators
-BR_OPEN = "([{"
-BR_CLOSE = ")]}"
-PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
-REV = dict( zip(BR_CLOSE ,BR_OPEN) )
-
-# --------------- Lexer ----------------
-
-class RT_Token:
- def __init__(self ,kind: str ,text: str):
- self.kind = kind
- self.text = text
-
- def __repr__(self):
- return f"<{self.kind}:{repr(self.text)}>"
-
-TOKEN_REGEX = re.compile(
- r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
- r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
- r'|(?P<SPACE>[ \t]+)'
- r'|(?P<NEWLINE>\n)'
- r'|(?P<COMMA>,)'
- r'|(?P<BR_OPEN>[\[\(\{])'
- r'|(?P<BR_CLOSE>[\]\)\}])'
- r'|(?P<CODE>[^ \t\n,\[\(\{\]\)\}"\'#/]+|/)'
-)
-
-def tokenize(text: str) -> List[RT_Token]:
- tokens = []
- for TM_match in TOKEN_REGEX.finditer(text):
- kind = TM_match.lastgroup
- text_val = TM_match.group(kind)
- tokens.append( RT_Token(kind ,text_val) )
- return tokens
-
-# --------------- Intelligence API ----------------
-
-class TokenStream:
- def __init__(self ,tokens: List[RT_Token]):
- self.tokens = tokens
-
- def get_token(self ,index: int) -> Optional[RT_Token]:
- if 0 <= index < len(self.tokens):
- return self.tokens[index]
- return None
-
- def next_sig_index(self ,index: int) -> Optional[int]:
- for TM_i in range(index + 1 ,len(self.tokens)):
- if self.tokens[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
- return TM_i
- return None
-
- def is_first_on_line(self ,index: int) -> bool:
- for TM_i in range(index - 1 ,-1 ,-1):
- k = self.tokens[TM_i].kind
- if k == "NEWLINE":
- return True
- if k != "SPACE":
- return False
- return True # Start of file
-
- def indent_of_line(self ,index: int) -> str:
- for TM_i in range(index ,-1 ,-1):
- if self.tokens[TM_i].kind == "NEWLINE":
- if TM_i + 1 < len(self.tokens) and self.tokens[TM_i + 1].kind == "SPACE":
- return self.tokens[TM_i + 1].text
- return ""
- if self.tokens and self.tokens[0].kind == "SPACE":
- return self.tokens[0].text
- return ""
-
- def indent_of_left_match(self ,index: int) -> Optional[str]:
- tok = self.get_token(index)
- if not tok or tok.kind != "BR_CLOSE":
- return None
- target_opener = REV[tok.text]
- depth = 0
- for TM_i in range(index - 1 ,-1 ,-1):
- t = self.tokens[TM_i]
- if t.kind == "BR_CLOSE":
- depth += 1
- elif t.kind == "BR_OPEN":
- if depth > 0:
- depth -= 1
- elif t.text == target_opener:
- return self.indent_of_line(TM_i)
- return None
-
-# --------------- Rule Engine ----------------
-
-def rule_migrate_vertical_commas(stream: TokenStream):
- TM_i = 0
- while TM_i < len(stream.tokens):
- if stream.tokens[TM_i].kind == "COMMA":
- is_trailing = False
- next_sig = stream.next_sig_index(TM_i)
- if next_sig is not None:
- for TM_j in range(TM_i + 1 ,next_sig):
- if stream.tokens[TM_j].kind == "NEWLINE":
- is_trailing = True
- break
-
- if is_trailing:
- comma_tok = stream.tokens.pop(TM_i)
- next_sig -= 1 # Shifted because of pop
- stream.tokens.insert(next_sig ,comma_tok)
- continue
- TM_i += 1
-
-def rule_format_horizontal_commas(stream: TokenStream):
- for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
- if stream.tokens[TM_i].kind == "COMMA":
- if stream.is_first_on_line(TM_i):
- continue
-
- next_tok = stream.get_token(TM_i + 1)
- if next_tok and next_tok.kind == "SPACE":
- stream.tokens.pop(TM_i + 1)
-
- prev_tok = stream.get_token(TM_i - 1)
- if prev_tok and prev_tok.kind == "SPACE":
- if prev_tok.text != " ":
- prev_tok.text = " "
- else:
- stream.tokens.insert(TM_i ,RT_Token("SPACE" ," "))
-
-def rule_fix_closing_indent(stream: TokenStream):
- for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
- if stream.tokens[TM_i].kind == "BR_CLOSE" and stream.is_first_on_line(TM_i):
- target_indent = stream.indent_of_left_match(TM_i)
- if target_indent is not None:
- prev = stream.get_token(TM_i - 1)
- if prev and prev.kind == "SPACE":
- prev.text = target_indent
- else:
- stream.tokens.insert(TM_i ,RT_Token("SPACE" ,target_indent))
-
-def rule_tighten_brackets(stream: TokenStream):
- for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
- if stream.tokens[TM_i].kind == "SPACE" and not stream.is_first_on_line(TM_i):
- prev_t = stream.get_token(TM_i - 1)
- next_t = stream.get_token(TM_i + 1)
- if (prev_t and prev_t.kind == "BR_OPEN") or (next_t and next_t.kind == "BR_CLOSE"):
- stream.tokens.pop(TM_i)
-
-def get_bracket_spans(stream: TokenStream) -> List[Tuple[int ,int]]:
- stack = []
- spans = []
- for TM_i ,tok in enumerate(stream.tokens):
- if tok.kind == "BR_OPEN":
- stack.append( (tok.text ,TM_i) )
- elif tok.kind == "BR_CLOSE":
- if stack and REV[tok.text] == stack[-1][0]:
- _ ,pos = stack.pop()
- if not stack:
- spans.append( (pos ,TM_i) )
- return spans
-
-def rule_pad_outermost(stream: TokenStream ,is_lisp: bool):
- if is_lisp:
- return
- while True:
- spans = get_bracket_spans(stream)
- changed = False
- for TM_start ,TM_end in reversed(spans):
- has_inner = False
- for TM_k in range(TM_start + 1 ,TM_end):
- if stream.tokens[TM_k].kind in ("BR_OPEN" ,"BR_CLOSE"):
- has_inner = True
- break
-
- if has_inner:
- left_has = (TM_start + 1 < len(stream.tokens)) and stream.tokens[TM_start + 1].kind == "SPACE"
- right_has = (TM_end - 1 >= 0) and stream.tokens[TM_end - 1].kind == "SPACE"
- if not left_has or not right_has:
- if not right_has:
- stream.tokens.insert(TM_end ,RT_Token("SPACE" ," "))
- if not left_has:
- stream.tokens.insert(TM_start + 1 ,RT_Token("SPACE" ," "))
- changed = True
- break
- if not changed:
- break
-
-# --------------- Public API ----------------
-
-def format_tokens(tokens: List[RT_Token] ,is_lisp: bool) -> str:
- stream = TokenStream(tokens)
-
- rule_migrate_vertical_commas(stream)
- rule_format_horizontal_commas(stream)
- rule_tighten_brackets(stream)
- rule_fix_closing_indent(stream)
- rule_pad_outermost(stream ,is_lisp)
-
- return "".join(t.text for t in stream.tokens)
-
-def rt_format_text(text: str ,is_lisp: bool) -> str:
- tokens = tokenize(text)
- return format_tokens(tokens ,is_lisp)
-
-def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
- text = inp.read()
- out.write( rt_format_text(text ,is_lisp) )
-
-# --------------- Self-test ----------------
-
-def run_self_test() -> bool:
- ok = True
- def chk(src ,exp):
- nonlocal ok
- got = rt_format_text(src ,False)
- if got != exp:
- print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
- ok = False
-
- chk("a,b,c" ,"a ,b ,c")
- chk("a , b , c" ,"a ,b ,c")
- chk(" ,vertical_arg" ," ,vertical_arg")
-
- chk("int a=0,\n b=1,\n c=2;" ,"int a=0\n ,b=1\n ,c=2;")
-
- chk("f ( x )" ,"f(x)")
- chk("f(x) + g(y)" ,"f(x) + g(y)")
- chk(" {" ," {")
-
- src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
- exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
- chk(src ,exp)
-
- chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
-
- # Operator protection check
- chk("for(int TM = 0; TM < count; ++TM)" ,"for(int TM = 0; TM < count; ++TM)")
-
- print("SELFTEST OK" if ok else "SELFTEST FAILED")
- return ok
-
-# --------------- CLI ----------------
-def write_files(paths: List[str] ,is_lisp: bool) -> int:
- for TM_path in paths:
- with open(TM_path ,"r" ,encoding="utf-8") as f:
- data = f.read()
-
- formatted = rt_format_text(data ,is_lisp)
-
- # Only touch the file if the content actually changed
- if data != formatted:
- with open(TM_path ,"w" ,encoding="utf-8") as f:
- f.write(formatted)
- print(f"Formatted: {TM_path}")
- return 0
-
-def copy_files(paths: List[str] ,is_lisp: bool) -> int:
- for TM_path in paths:
- shutil.copy2(TM_path ,TM_path + "~")
- return write_files(paths ,is_lisp)
-
-def CLI(argv=None) -> int:
- args = list(sys.argv[1:] if argv is None else argv)
- usage_text = get_usage()
-
- if not args or args[0] in {"help" ,"--help" ,"-h"}:
- print(usage_text)
- return 0
-
- is_lisp = "--lisp" in args
- args = [TM_a for TM_a in args if TM_a != "--lisp"]
-
- if not args:
- return 0
-
- cmd = args[0]
- rest = args[1:]
-
- if cmd == "version":
- print(RTF_VERSION)
- return 0
- if cmd == "self_test":
- ok = run_self_test()
- return 0 if ok else 1
- if cmd == "pipe":
- rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
- return 0
- if cmd == "write":
- if not rest:
- print("write: missing <file ...>\n" + usage_text)
- return 2
- return write_files(rest ,is_lisp)
- if cmd == "copy":
- if not rest:
- print("copy: missing <file ...>\n" + usage_text)
- return 2
- return copy_files(rest ,is_lisp)
-
- print(f"Unknown command: {cmd}\n" + usage_text)
- return 2
-
-if __name__ == "__main__":
- sys.exit( CLI() )
+++ /dev/null
-(defun RT-formatter-buffer ()
- "Format the current buffer using RTfmt."
- (interactive)
- (if (not (executable-find "RT-formatter"))
- (message "Error: RTfmt executable not found in PATH.")
- (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
- (args (list "pipe")))
- (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
- (setq args (append args (list "--lisp"))))
- (unwind-protect
- (let ((exit-code (apply #'call-process-region
- (point-min) (point-max)
- "RT-formatter"
- nil temp-buffer nil
- args)))
- (if (zerop exit-code)
- ;; Check if the formatted text is actually different
- (if (= (compare-buffer-substrings nil nil nil temp-buffer nil nil) 0)
- (message "RTfmt: Already perfectly formatted.")
- (replace-buffer-contents temp-buffer)
- (message "RT-formatter formatting successful."))
- (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
- (kill-buffer temp-buffer)))))
+++ /dev/null
-// commas and simple tight brackets
-int g(){
- int a=0 ,
- b=1 ,
- c=2;
- return h(a ,b ,c);
-}
-
-// balanced outermost-with-nesting -> pad inside outer ()
-int f(){ return outer(inner(a ,b)); }
-
-// strings and comments must be unchanged
-int s(){ printf("x ,y ,z (still a string)"); /* a ,b ,c */ return 1; }
-
-// unbalanced open-right with nesting -> pad after first unmatched '('
-int u(){if(doit(foo(1 ,2) // missing ))
- return 0;}
-
-// arrays / subscripts stay tight; commas still RT-style
-int a(int i ,int j){ return M[i ,j] + V[i] + W[j]; }
+++ /dev/null
-# commas and spacing in defs / calls
-def f ( x , y , z ):
- return dict( a =1 , b= 2 ), [ 1, 2 ,3 ], ( (1,2) )
-
-# outermost-with-nesting -> pad inside outer ()
-val = outer( inner( a,b ) )
-
-# strings/comments untouched
-s = "text, with , commas ( not to touch )" # a ,b ,c
-
-# unbalanced: open-left (closing without opener) -> no padding unless inner bracket before it
-def g():
- return result) # likely unchanged
-
-# unbalanced: open-right (first unmatched opener) with inner bracket following
-k = compute(x, f(y