/* ===================================================================== test_sas_syntax.sas — minimal highlighting coverage + edge cases ===================================================================== */ /* ---- 1. COMMENTS --------------------------------------------------- */ /* block comment */ * statement comment at start of line; * multi-line statement comment; %* macro comment; data _null_; x = 1; * inline comment after semicolon; y = 2; * chained; * second; a = 4; * comment; b = 5; /* b=5 resumes normal highlighting */ z = 4 * 2; * arithmetic * then comment on next statement; x2 = %* macro comment mid-expression; 4; y2 = /* block comment mid-expression */ 5; run; /* ---- 2. STRING LITERALS — all suffix types ------------------------- */ options validvarname=any; data _null_; /* double-quoted */ plain = "hello world"; escaped = "it's ""quoted"""; date = "01jan2024"d; dtime = "01jan2024:12:00:00"dt; time = "12:00:00"t; bits = "1010"b; naml = "my variable"n; hex = "FFAB3C"x; /* single-quoted */ sq_plain = 'hello world'; sq_escape = 'it''s quoted'; sq_date = '01jan2024'd; sq_dtime = '01jan2024:12:00:00'dt; sq_time = '12:00:00't; sq_bits = '1010'b; sq_naml = 'my variable'n; sq_hex = 'FFAB3C'x; /* uppercase suffixes */ uc_date = "01jan2024"D; uc_dtime = "01jan2024:00:00:00"DT; uc_time = "06:00:00"T; uc_hex = "FF"X; run; /* ---- 3. NUMERIC LITERALS ------------------------------------------- */ data _null_; int_val = 42; neg_int = -7; float = 3.14159; sci_pos = 1.5e10; sci_neg = 2.0E-3; leading = .9; sas_hex = 0c1x; run; /* ---- 4. FORMATS AND INFORMATS -------------------------------------- */ data _null_; format date_var date9.; format num_var comma12.2; format chr_var $char20.; format best_var best.; informat raw_in $50.; informat dt_in anydtdtm20.; run; /* ---- 5. OPERATORS -------------------------------------------------- */ data _null_; /* arithmetic — * and / dont trigger comment context */ a = 2 ** 3; b = 15 * 3; c = 10 / 4; d = a + b - c; z = 15 * 3 / 4 + 100; /* comparison — symbolic */ eq_ = (z = 45); ne_ = (z ^= 45); ne2 = (z ~= 45); gt_ = (z > 10); lt_ = (z < 200); ge_ = (z >= 10); le_ = (z <= 200); mn_ = z <> 100; /* min operator */ mx_ = z >< 100; /* max operator */ cat_ = "a" || "b"; /* concatenation */ /* trailing-modifier operators */ name = "Jones"; eq_t = (name =: "Jo"); gt_t = (name >: "Jo"); ne_t = (name ^=: "Jo"); /* mnemonic operators */ x = 5; y = 10; if x gt 0 and y lt 20 then put "gt lt"; if x ge 5 and y le 10 then put "ge le"; if x eq 5 or y ne 0 then put "eq ne"; if not (x = 0) then put "not"; if x in (1 2 3 4 5) then put "in"; if name like "Jo%" then put "like"; if name contains "ohn" then put "contains"; if x between 1 and 10 then put "between"; if x is missing then put "missing"; run; /* ---- 6. SPECIAL / AUTOMATIC VARIABLES ------------------------------ */ data _null_; put _n_; put _error_; if _n_ = 1 then put "first"; keep _all_; drop _character_; retain _numeric_; run; /* ---- 7. CONTROL FLOW ----------------------------------------------- */ data _null_; do i = 1 to 10 by 2; if i = 5 then continue; if i = 9 then leave; put i; end; i = 0; do while (i < 10); i + 1; end; i = 0; do until (i >= 10); i + 1; end; select (i); when (1) put "one"; when (2) put "two"; otherwise put "other"; end; if i > 5 then put "big"; else put "small"; goto done; link sub; return; done: sub: return; run; /* ---- 8. DATA STEP STATEMENTS + DATASET OPTIONS --------------------- */ data work.out (keep=name age compress=yes); /* dataset options on SET — (opt=val) must NOT colour ds name as function */ set sashelp.class (obs=50 where=(age > 12) keep=name age sex); merge work.a (in=ina) work.b (in=inb); by id; array nums{3} a b c; attrib name length=$20 label="Full name"; length score 8; retain cumcount 0; /* known functions highlighted; non-function before ( is Normal Text */ z = max(a, b); w = substr(name, 1, 3); if notafunction(z) = 1 then output; /* notafunction -> Normal Text */ stop; run; /* ---- 9. CARDS / DATALINES ------------------------------------------ */ data work.inline; input name $ age; cards; Alice 30 Bob 25 ; run; data work.cards4test; input line $ 1-20; cards4; line with ; inside another ; line here ;;;; run; /* ---- 10. PROC (GENERIC) + CONTEXT SWITCHING ------------------------ */ proc means data=sashelp.class n mean std min max; var age height weight; class sex; output out=work.stats mean= std= / autoname; run; proc sort data=sashelp.class out=work.sorted (keep=name age) nodupkey; by sex descending age; run; /* No RUN — next proc/data boundary switches context */ proc datasets library=work nolist; delete sorted; data work.after_no_run; x = 1; run; /* ---- 11. PROC SQL -------------------------------------------------- */ proc sql; create table work.sql_out as select name, age, height / weight as ratio from sashelp.class where age > 12 and sex = 'M' order by age desc; select count(*) as n_obs, sum(age * 1.0) / count(*) as mean_age from sashelp.class; drop table work.sql_out; quit; /* SQL with no QUIT — next step boundary exits */ proc sql; select name from sashelp.class; data work.after_sql; set sashelp.class; run; /* ---- 12. PROC PYTHON (embedded block) ------------------------------ */ proc python; submit; import sys, math x = 2 ** 10 # Python comment s = "hello" s2 = 'world' multi = """ triple-quoted string """ print(sys.version, math.pi, x) endsubmit; quit; /* ---- 13. PROC LUA (embedded block) --------------------------------- */ proc lua; submit; -- Lua single-line comment local x = 10 * 3 -- * is arithmetic local s = "hello" local s2 = 'world' local ml = [[multi-line string]] --[[ multi-line Lua comment ]] print(x, s, s2) endsubmit; quit; /* ---- 14. MACRO LANGUAGE -------------------------------------------- */ %macro greet(name=World, n=1); %local i; %do i = 1 %to &n; %put Hello, &name! (iteration &i of &n); %end; %mend greet; %greet(name=SAS, n=3); %let myvar = 42; %let mystr = Hello World; %if &myvar > 10 %then %do; %put myvar (&myvar) is large; %end; %else %do; %put myvar (&myvar) is small; %end; /* indirect macro reference */ %let prefix = abc; %let abcval = found; %put &prefix - simple; %put &&prefix.val - indirect = &abcval; /* macro quoting / utility functions */ %let q1 = %str(a semicolon: ;); %let q2 = %nrstr(¬resolved); %let len = %length(&mystr); %let up = %upcase(&mystr); %let ev = %eval(2 + 3); %let sev = %sysevalf(1.5 * 2.0); %global gvar1 gvar2; %local lvar1; %* macro comment; /* ---- 15. LIBNAME / FILENAME / OPTIONS ------------------------------ */ libname mylib "/path/to/data" access=readonly; libname mylib clear; filename myfile "/path/to/file.txt" lrecl=256; filename myfile clear; /* option=value pattern: option name -> SecKeyword, value -> Normal Text */ options obs=max nodate nonumber mprint mlogic symbolgen; options validvarname=any compress=yes bufno=20; /* ---- 16. MISC PROCS ------------------------------------------------ */ proc mixed data=sashelp.class; class sex; model height = age sex / solution ddfm=kr; random intercept / subject=name type=un; lsmeans sex / pdiff adjust=tukey; run; proc report data=sashelp.class nowd; column name sex age height; define name / display "Student"; define height / analysis mean format=6.2 "Avg Height"; break after sex / summarize; compute after sex; line "--- end of group ---"; endcomp; run; proc fcmp outlib=work.funcs.mathlib; function cube(x); return (x ** 3); endfunc; subroutine swap(a, b); outargs a, b; temp = a; a = b; b = temp; endsub; run; endsas; %abort abend;