gitoriaLog in with ident

notes

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Branchmain2149e902notes mission 002 (4/4): code order — README file map + same-output test, STATUS, LOG, report; tests/letcount.py, tests/realdata-baseline.mjs, tests/realdata-compare.pymremain/tests/letcount.py

3.4 KB

  1. #!/usr/bin/env python3
  2. # tests/letcount.py (copied from tickets.worldapi.org mission 010 via gitoria mission 002; notes mission 002): the code-order counts of an app tree (antcolony docs/code-order.md) — root .hl files, project.hl lines, and every `let`:
  3. # reassigned the name is assigned again later in its block → `let` is right
  4. # loop-bound declared inside a for/while body (re-bound each pass —
  5. # Hybriel refuses a plain declaration there on pass 2) → `let` is right
  6. # never neither → should be a plain declaration
  7. # python3 tests/letcount.py . [-v] (-v lists every `let` that should be a plain declaration) (scans *.hl, components/, lib/, tools/, tests/; not plugins/)
  8. import os, re, sys
  9. root = sys.argv[1]; verbose = '-v' in sys.argv
  10. files = []
  11. for d in ['', 'components', 'lib', 'tools', 'tests']:
  12. p = os.path.join(root, d)
  13. if os.path.isdir(p): files += [os.path.join(p, f) for f in sorted(os.listdir(p)) if f.endswith('.hl')]
  14. def strip(src):
  15. # blank out strings and comments (same length) so braces/names inside them do not count
  16. out = list(src); i = 0; n = len(src)
  17. while i < n:
  18. c = src[i]
  19. if src.startswith('//', i):
  20. j = src.find('\n', i); j = n if j < 0 else j
  21. for k in range(i, j): out[k] = ' '
  22. i = j; continue
  23. if c in '\'"`':
  24. j = i + 1
  25. while j < n and src[j] != c:
  26. if c == "'" and src[j] == '\\' and j + 1 < n and src[j + 1] == "'": j += 2; continue
  27. j += 1
  28. for k in range(i + 1, min(j, n)):
  29. if out[k] != '\n': out[k] = ' '
  30. i = j + 1; continue
  31. i += 1
  32. return ''.join(out)
  33. tot = {'reassigned': 0, 'loop-bound': 0, 'never': 0}
  34. per = {}
  35. for f in files:
  36. s = strip(open(f).read())
  37. opens = []; match = {}; stack = []
  38. for i, c in enumerate(s):
  39. if c == '{': stack.append(i)
  40. elif c == '}' and stack: match[stack.pop()] = i
  41. for m in re.finditer(r'\blet\s+(\w+)\s*=', s):
  42. name, p = m.group(1), m.start()
  43. encl = [o for o, e in match.items() if o < p < e]
  44. encl.sort(reverse=True) # innermost first
  45. inner_end = match[encl[0]] if encl else len(s)
  46. kind = 'never'
  47. if re.search(r'(?<![\w.#])' + re.escape(name) + r'\s*(=(?!=)|\+\+|--|\+=|-=)', s[m.end():inner_end]): kind = 'reassigned'
  48. else:
  49. for o in encl:
  50. head = s[max(0, o - 300):o]
  51. line = head[head.rfind('\n') + 1:] if '\n' in head else head
  52. if re.search(r'=>\s*$', head) or re.match(r'\s*(on\s|static\s|\w+\s*\([^)]*\)\s*$)', line) and not re.match(r'\s*(if|for|while|else)\b', line): break
  53. if re.search(r'\b(for|while)\s*\([^{}]*\)\s*$', head): kind = 'loop-bound'; break
  54. tot[kind] += 1
  55. per.setdefault(os.path.relpath(f, root), {'reassigned': 0, 'loop-bound': 0, 'never': 0})[kind] += 1
  56. if verbose and kind == 'never': print(f' never: {os.path.relpath(f, root)}:{s.count(chr(10), 0, p) + 1} {name}')
  57. rootHl = [f for f in os.listdir(root) if f.endswith('.hl')]
  58. pl = sum(1 for _ in open(os.path.join(root, 'project.hl')))
  59. print(f'root .hl files: {len(rootHl)} ({" ".join(sorted(rootHl))})')
  60. print(f'project.hl lines: {pl}')
  61. print(f'let total {sum(tot.values())}: reassigned {tot["reassigned"]}, loop-bound {tot["loop-bound"]}, never reassigned {tot["never"]}')
  62. for f, k in sorted(per.items()): print(f' {f}: {k}')

Branches

  • mainmain branch

Latest commits

  • 2149e902notes mission 002 (4/4): code order — README file map + same-output test, STATUS, LOG, report; tests/letcount.py, tests/realdata-baseline.mjs, tests/realdata-compare.pymre
  • 47db4fc1notes mission 002 (3/4): code order — let only where reassigned (58 dropped; 34 left: 19 reassigned, 15 loop-bound); gates 50/0 + 18/0, live-data run = step 2mre
  • 3f8cb383notes mission 002 (2/4): code order — topics, map, thin wrappers: lib/util.hl, lib/notes(-helpers).hl, lib/users.hl (+userOfLoginCode, tagOf), lib/api(-helpers).hl; project.hl = map; login routes take &req/&sessions (failed-login reason now kept); dead notes#1 functions removed; gates 50/0 + 18/0mre
  • 8cda5412notes mission 002 (1/4): code order — files moved: lib/notes.hl, lib/users.hl, lib/jsoncheck.hl, components/styles.hl (imports only); gates 50/0 + 18/0, live-data run identicalmre
  • 47dad68bnotes: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gates 50/0 + 18/0mre
  • a4a2b2aeantcolony#40: tracker missions moved too — references to them in missions/reports/LOG.md updatedmre
  • 3dea0ef2notes: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gate 50/0mre
  • e27c7d71notes: Hybriel master 8efba065 (#126 GC by bytes, #48 lambda params copy; audit: nothing to fix; gate 50/0)mre
  • 124613b6antcolony#40: mission references point to the moved missionsmre
  • 1ca2f34dantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • 2bebebdanotes: Hybriel master ff51cf46 (re-vendor round)mre
  • 3eff126dnotes#3: installable app (manifest + own icon/favicon; notes' own sw.js kept)mre
  • 9883c540deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • eee693b8deploy.sh: never send .git or .gitignore to Byrodinmre
  • c8904061State of 2026-09-27, before the move to gitoriamre