Files
dc/.agent/skills/sas/SKILL.md
T
4gl 28104f83e1
Build / Build-and-ng-test (pull_request) Successful in 5m9s
Lighthouse Checks / lighthouse (pull_request) Successful in 21m27s
Build / Build-and-test-development (pull_request) Successful in 22m4s
chore(test): test for mpe_targetloader update
2026-07-31 15:01:00 +01:00

3.5 KiB

name, description
name description
sas Use this skill whenever writing, modifying, or debugging SAS code in this repository — SAS macros, sasjs services, hooks, or test files (*.sas). Triggers include: SAS macro compilation errors, parameter parsing issues, writing sasjs tests with mp_assert / mp_assertdsobs / mp_assertscope, macro-quoting questions (%str, %nrstr, %superq), %mp_abort usage, or unexpected behaviour when passing free-text values (descriptions, messages) to macros. Also covers repo conventions such as running `sasjs lint` after touching .sas files and the maximum line length rule.

SAS Development

Conventions and pitfalls for SAS code in this repository (macros in sas/sasjs/macros/, services in sas/sasjs/services/, tests alongside them as *.test.sas).

Always %str() free-text macro parameters

A comma inside a macro parameter value is parsed as a parameter delimiter. Free-text parameters such as desc= (in %mp_assert, %mp_assertdsobs, %mp_assertscope) and msg= (in %mp_abort) must be wrapped in %str() — even when they currently contain no comma, so later edits cannot silently break the call.

Wrong — the text after the comma becomes an unexpected positional parameter and the compilation fails (or worse, is misparsed):

%mp_assert(iftrue=(&oldrows=0),
  desc=Test 2 - all staged records loaded, delete flags ignored,
  outds=work.test_results
)

Right:

%mp_assert(iftrue=(&oldrows=0),
  desc=%str(Test 2 - all staged records loaded, delete flags ignored),
  outds=work.test_results
)

The same applies to any macro parameter that carries user-facing text: %mp_abort(msg=%str(...)), etlsource= values containing punctuation (use %superq() when passing macro variables that may contain special characters), etc.

Related quoting rules of thumb:

  • %str() masks commas, parentheses, semicolons and quotes at compile time — sufficient for static text like desc=.
  • Use %nrstr() if the text must also mask % and & (rare in descriptions).
  • Use %superq(var) when forwarding a macro variable whose value may contain special characters (e.g. etlsource= in mpe_targetloader.sas).

Tests must be idempotent

A test file must pass when run repeatedly (including after a run that failed partway). Conventions:

  • Start the file with %let syscc=0; — many macros (e.g. %mp_lockanytable(LOCK)) abort on entry if &syscc>0, and any WARNING in a previous test bumps syscc to 4.
  • Clean up all persistent state, not just the obvious one: MPE_TABLES registrations, MPE_LOCKANYTABLE lock records (a run dying between LOCK and UNLOCK leaves a stale LOCKED row), physical tables in dctest (proc datasets ... delete), and global macro variables created via select ... into: (%symdel).
  • Make prep defensive: delete-then-insert config records (handles leftovers from an aborted run), and recreate physical tables rather than assuming they are absent.

Repo conventions for .sas files

  • Run npx sasjs lint from the sas/ directory after creating or modifying any .sas file; the files you touched must have zero warnings (pre-existing warnings in other files can be ignored).
  • Maximum line length is 80 characters (lint-enforced) — this is why long msg=%str(...) values sometimes need shortening.
  • Test files are named <thing>.test.sas (or <thing>.test.N.sas) and run via npm run 4gl && sasjs test -t 4gl from sas/ — see .agent/docs/testing.md.
  • sas/sasjsbuild/ is generated build output — never hand-edit it; edit sources under sas/sasjs/ only.