Commit 99d979a04610ecd023e0f8ce84ae0c97e085e438

cedporter 2020-01-07T09:10:22

SAS: Added CASL support (#2112) This adds CASL support, a scripting language used within PROC CAS in SAS. https://go.documentation.sas.com/?cdcId=pgmsascdc&cdcVersion=9.4_3.4&docsetId=pgmdiff&docsetTarget=p06ibhzb2bklaon1a86ili3wpil9.htm&locale=en

diff --git a/components/prism-sas.js b/components/prism-sas.js
index a3a4844..80ae9ed 100644
--- a/components/prism-sas.js
+++ b/components/prism-sas.js
@@ -29,12 +29,22 @@
 
 	var punctuation = /[$%@.(){}\[\];,\\]/;
 
+	var func = {
+		pattern: /%?\w+(?=\()/,
+		alias: 'keyword'
+	};
+
 	var args = {
+		'function' : func,
 		'arg-value': {
-			pattern: /(=)[A-Z]+/i,
+			pattern: /(\s*=\s*)[A-Z\.]+/i,
 			lookbehind: true
 		},
 		'operator': /=/,
+		'macro-variable': {
+			pattern: /&[^\.]*\./i,
+			alias: 'string'
+		},
 		'arg': {
 			pattern: /[A-Z]+/i,
 			alias: 'keyword'
@@ -45,6 +55,29 @@
 		'string': string
 	};
 
+	var format = {
+		pattern: /\b(?:format|put)\b=?[\w'$.]+/im,
+		inside: {
+			'keyword': /^(?:format|put)(?=\=)/i,
+			'equals': /=/,
+			'format': {
+				pattern: /(?:\w|\$\d)+\.\d?/i,
+				alias: 'number'
+			}
+		}
+	};
+
+	var altformat = {
+		pattern: /\b(?:format|put)\s+[\w']+(?:\s+[$.\w]+)+(?=;)/i,
+		inside: {
+			'keyword': /^(?:format|put)/i,
+			'format': {
+				pattern: /[\w$]+\.\d?/,
+				alias: 'number'
+			}
+		}
+	};
+
 	var globalStatements = {
 		pattern: /((?:^|[\s])=?)(?:catname|checkpoint execute_always|dm|endsas|filename|footnote|%include|libname|%list|lock|missing|options|page|resetline|%run|sasfile|skip|sysecho|title\d?)\b/i,
 		lookbehind: true,
@@ -57,6 +90,34 @@
 		alias: 'keyword'
 	};
 
+	var actionSets = 'accessControl|cdm|aggregation|aStore|ruleMining|audio|autotune|bayesianNetClassifier|bioMedImage|boolRule|builtins|cardinality|sccasl|clustering|copula|countreg|dataDiscovery|dataPreprocess|dataSciencePilot|dataStep|decisionTree|deepLearn|deepNeural|varReduce|simSystem|ds2|deduplication|ecm|entityRes|espCluster|explainModel|factmac|fastKnn|fcmpact|fedSql|freqTab|gam|gleam|graphSemiSupLearn|gVarCluster|hiddenMarkovModel|hyperGroup|image|iml|ica|kernalPca|langModel|ldaTopic|sparseML|mlTools|mixed|modelPublishing|mbc|network|optNetwork|neuralNet|nonlinear|nmf|nonParametricBayes|optimization|panel|pls|percentile|pca|phreg|qkb|qlim|quantreg|recommend|tsReconcile|deepRnn|regression|reinforcementLearn|robustPca|sampling|sparkEmbeddedProcess|search(?:Analytics)?|sentimentAnalysis|sequence|configuration|session(?:Prop)?|severity|simple|smartData|sandwich|spatialreg|stabilityMonitoring|spc|loadStreams|svDataDescription|svm|table|conditionalRandomFields|text(?:Rule(?:Develop|Score)|Mining|Parse|Topic|Util|Filters|Frequency)|tsInfo|timeData|transpose|uniTimeSeries';
+
+	var casActions = {
+		pattern: RegExp('(^|\\s)(?:action\\s+)?(?:<act>)\\.[a-z]+\\b[^;]+'.replace(/<act>/g, actionSets), 'i'),
+		lookbehind: true,
+		inside: {
+			'keyword': RegExp('(?:<act>)\\.[a-z]+\\b'.replace(/<act>/g, actionSets), 'i'),
+			'action': {
+				pattern: /(?:action)/i,
+				alias: 'keyword'
+			},
+			'function': func,
+			'arg-value': args['arg-value'],
+			'operator': args.operator,
+			'comment': comment,
+			'argument': args.arg,
+			'number': number,
+			'numeric-constant': numericConstant,
+			'punctuation': punctuation,
+			'string': string
+		}
+	};
+
+	var keywords = {
+		pattern: /((?:^|\s)=?)(?:after|analysis|and|array|barchart|barwidth|begingraph|by|call|cas|cbarline|cfill|class(?:lev)?|close|column|computed?|contains|continue|data(?=\=)|define|delete|describe|document|do\s+over|do|dol|drop|dul|end(?:source|comp)?|entryTitle|else|eval(?:uate)?|exec(?:ute)?|exit|fill(?:attrs)?|file(?:name)?|flist|fnc|function(?:list)?|goto|global|group(?:by)?|headline|headskip|histogram|if|infile|keep|keylabel|keyword|label|layout|leave|legendlabel|length|libname|loadactionset|merge|midpoints|name|noobs|nowd|_?null_|ods|options|or|otherwise|out(?:put)?|over(?:lay)?|plot|put|print|raise|ranexp|rannor|rbreak|retain|return|select|set|session|sessref|source|statgraph|sum|summarize|table|temp|terminate|then\s+do|then|title\d?|to|var|when|where|xaxisopts|yaxisopts|y2axisopts)\b/i,
+		lookbehind: true,
+	};
+
 	Prism.languages.sas = {
 		'datalines': {
 			pattern: /^(\s*)(?:(?:data)?lines|cards);[\s\S]+?^\s*;/im,
@@ -130,6 +191,43 @@
 			}
 		},
 
+		'proc-cas': {
+			pattern: /(^proc\s+cas(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|data);|(?![\s\S]))/im,
+			lookbehind: true,
+			inside: {
+				'statement-var': {
+					pattern: /((?:^|\s)=?)saveresult\s+[^;]+/im,
+					lookbehind: true,
+					inside: {
+						'statement': {
+							pattern: /^saveresult\s+\S+/i,
+							inside: {
+								keyword: /^(?:saveresult)/i
+							}
+						},
+						rest: args
+					}
+				},
+				'cas-actions': casActions,
+				'statement': {
+					pattern: /((?:^|\s)=?)(?:default|(?:un)?set|on|output|upload)[^;]+/im,
+					lookbehind: true,
+					inside: args
+				},
+				'step': step,
+				'keyword': keywords,
+				'function': func,
+				'comment': comment,
+				'format': format,
+				'altformat': altformat,
+				'global-statements': globalStatements,
+				'number': number,
+				'numeric-constant': numericConstant,
+				'punctuation': punctuation,
+				'string': string
+			}
+		},
+
 		'proc-args': {
 			pattern: RegExp(/(^proc\s+\w+\s+)(?!\s)(?:[^;"']|<str>)+;/.source.replace(/<str>/g, stringPattern), 'im'),
 			lookbehind: true,
@@ -176,33 +274,12 @@
 			lookbehind: true,
 			inside: args
 		},
-		'function': {
-			pattern: /%?\w+(?=\()/,
-			alias: 'keyword'
-		},
-		'format': {
-			pattern: /\b(?:format|put)\b=?[\w'$.]+/im,
-			inside: {
-				'keyword': /^(?:format|put)(?=\=)/i,
-				'equals': /=/,
-				'format': {
-					pattern: /(?:\w|\$\d)+\.\d?/i,
-					alias: 'number'
-				}
-			}
-		},
-		'altformat': {
-			pattern: /\b(?:format|put)\s+[\w']+(?:\s+[$.\w]+)+(?=;)/i,
-			inside: {
-				'keyword': /^(?:format|put)/i,
-				'format': {
-					pattern: /[\w$]+\.\d?/,
-					alias: 'number'
-				}
-			}
-		},
-		'numeric-constant': numericConstant,
+		'cas-actions': casActions,
 		'comment': comment,
+		'function': func,
+		'format': format,
+		'altformat': altformat,
+		'numeric-constant': numericConstant,
 		'datetime': {
 			// '1jan2013'd, '9:25:19pm't, '18jan2003:9:27:05am'dt
 			pattern: RegExp(stringPattern + '(?:dt?|t)'),
@@ -210,10 +287,7 @@
 		},
 		'string': string,
 		'step': step,
-		'keyword': {
-			pattern: /((?:^|\s)=?)(?:action|after|analysis|and|array|barchart|barwidth|begingraph|by|cas|cbarline|cfill|class(?:lev)?|close|column|computed?|contains|data(?=\=)|define|document|do\s+over|do|dol|drop|dul|end|entryTitle|else|endcomp|eval(?:uate)?|exec(?:ute)?|fill(?:attrs)?|filename|group(?:by)?|headline|headskip|histogram|if|infile|keep|keylabel|keyword|label|layout|legendlabel|length|libname|merge|midpoints|name|noobs|nowd|ods|options|or|out(?:put)?|overlay|plot|ranexp|rannor|rbreak|retain|set|session|sessref|statgraph|sum|summarize|table|temp|then\s+do|then|title\d?|to|var|where|xaxisopts|yaxisopts|y2axisopts)\b/i,
-			lookbehind: true,
-		},
+		'keyword': keywords,
 		// In SAS Studio syntax highlighting, these operators are styled like keywords
 		'operator-keyword': {
 			pattern: /\b(?:eq|ne|gt|lt|ge|le|in|not)\b/i,
diff --git a/components/prism-sas.min.js b/components/prism-sas.min.js
index 91ee6c0..bab73d6 100644
--- a/components/prism-sas.min.js
+++ b/components/prism-sas.min.js
@@ -1 +1 @@
-!function(e){var t="(?:\"(?:\"\"|[^\"])*\"(?!\")|'(?:''|[^'])*'(?!'))",a=/\b(?:\d[\da-f]*x|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b/i,n={pattern:RegExp(t+"[bx]"),alias:"number"},s=[/\/\*[\s\S]*?\*\//,{pattern:/(^\s*|;\s*)\*[^;]*;/m,lookbehind:!0}],i={pattern:RegExp(t),greedy:!0},r=/[$%@.(){}\[\];,\\]/,o={"arg-value":{pattern:/(=)[A-Z]+/i,lookbehind:!0},operator:/=/,arg:{pattern:/[A-Z]+/i,alias:"keyword"},number:a,"numeric-constant":n,punctuation:r,string:i},l={pattern:/((?:^|[\s])=?)(?:catname|checkpoint execute_always|dm|endsas|filename|footnote|%include|libname|%list|lock|missing|options|page|resetline|%run|sasfile|skip|sysecho|title\d?)\b/i,lookbehind:!0,alias:"keyword"},d={pattern:/(^|\s)(?:submit(?:\s+(?:load|parseonly|norun))?|endsubmit)\b/i,lookbehind:!0,alias:"keyword"};e.languages.sas={datalines:{pattern:/^(\s*)(?:(?:data)?lines|cards);[\s\S]+?^\s*;/im,lookbehind:!0,alias:"string",inside:{keyword:{pattern:/^(?:(?:data)?lines|cards)/i},punctuation:/;/}},"proc-sql":{pattern:/(^proc\s+(?:fed)?sql(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{sql:{pattern:RegExp("^[ \t]*(?:select|alter\\s+table|(?:create|describe|drop)\\s+(?:index|table(?:\\s+constraints)?|view)|create\\s+unique\\s+index|insert\\s+into|update)(?:<str>|[^;\"'])+;".replace(/<str>/g,t),"im"),alias:"language-sql",inside:e.languages.sql},"global-statements":l,"sql-statements":{pattern:/(^|\s)(?:disconnect\s+from|exec(?:ute)?|begin|commit|rollback|reset|validate)\b/i,lookbehind:!0,alias:"keyword"},number:a,"numeric-constant":n,punctuation:r,string:i}},"proc-groovy":{pattern:/(^proc\s+groovy(?:\s+[\w|=]+)?;)(?:\s*submit)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{groovy:{pattern:RegExp("(^[ \t]*submit(?:\\s+(?:load|parseonly|norun))?)(?:<str>|[^\"'])+?(?=endsubmit;)".replace(/<str>/g,t),"im"),lookbehind:!0,alias:"language-groovy",inside:e.languages.groovy},"submit-statement":d,"global-statements":l,number:a,"numeric-constant":n,punctuation:r,string:i}},"proc-lua":{pattern:/(^proc\s+lua(?:\s+[\w|=]+)?;)(?:\s*submit)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{lua:{pattern:RegExp("(^[ \t]*submit(?:\\s+(?:load|parseonly|norun))?)(?:<str>|[^\"'])+?(?=endsubmit;)".replace(/<str>/g,t),"im"),lookbehind:!0,alias:"language-lua",inside:e.languages.lua},"submit-statement":d,"global-statements":l,number:a,"numeric-constant":n,punctuation:r,string:i}},"proc-args":{pattern:RegExp("(^proc\\s+\\w+\\s+)(?!\\s)(?:[^;\"']|<str>)+;".replace(/<str>/g,t),"im"),lookbehind:!0,inside:o},"macro-keyword":{pattern:/((?:^|\s)=?)%(?:ABORT|BQUOTE|BY|CMS|COPY|DISPLAY|DO|ELSE|END|EVAL|GLOBAL|GO|GOTO|IF|INC|INCLUDE|INDEX|INPUT|KTRIM|LENGTH|LET|LIST|LOCAL|NRBQUOTE|NRQUOTE|NRSTR|PUT|QKTRIM|QSCAN|QSUBSTR|QSYSFUNC|QUOTE|QUPCASE|RETURN|RUN|SCAN|STR|SUBSTR|SUPERQ|SYMDEL|SYMGLOBL|SYMLOCAL|SYMEXIST|SYSCALL|SYSEVALF|SYSEXEC|SYSFUNC|SYSGET|SYSRPUT|THEN|TO|TSO|UNQUOTE|UNTIL|UPCASE|WHILE|WINDOW)\b/i,lookbehind:!0,alias:"keyword"},"macro-declaration":{pattern:/^%macro[^;]+(?=;)/im,inside:{keyword:/%macro/i}},"macro-end":{pattern:/^%mend[^;]+(?=;)/im,inside:{keyword:/%mend/i}},macro:{pattern:/%_\w+(?=\()/,alias:"keyword"},input:{pattern:/\binput\s+[-\w\s/*.$&]+;/i,inside:{input:{alias:"keyword",pattern:/^input/i},comment:s,number:a,"numeric-constant":n}},"options-args":{pattern:/(^options)[-'"|/\\<>*+=:()\w\s]*(?=;)/im,lookbehind:!0,inside:o},function:{pattern:/%?\w+(?=\()/,alias:"keyword"},format:{pattern:/\b(?:format|put)\b=?[\w'$.]+/im,inside:{keyword:/^(?:format|put)(?=\=)/i,equals:/=/,format:{pattern:/(?:\w|\$\d)+\.\d?/i,alias:"number"}}},altformat:{pattern:/\b(?:format|put)\s+[\w']+(?:\s+[$.\w]+)+(?=;)/i,inside:{keyword:/^(?:format|put)/i,format:{pattern:/[\w$]+\.\d?/,alias:"number"}}},"numeric-constant":n,comment:s,datetime:{pattern:RegExp(t+"(?:dt?|t)"),alias:"number"},string:i,step:{pattern:/(^|\s+)(?:proc\s+\w+|quit|run|data(?!\=))\b/i,alias:"keyword",lookbehind:!0},keyword:{pattern:/((?:^|\s)=?)(?:action|after|analysis|and|array|barchart|barwidth|begingraph|by|cas|cbarline|cfill|class(?:lev)?|close|column|computed?|contains|data(?=\=)|define|document|do\s+over|do|dol|drop|dul|end|entryTitle|else|endcomp|eval(?:uate)?|exec(?:ute)?|fill(?:attrs)?|filename|group(?:by)?|headline|headskip|histogram|if|infile|keep|keylabel|keyword|label|layout|legendlabel|length|libname|merge|midpoints|name|noobs|nowd|ods|options|or|out(?:put)?|overlay|plot|ranexp|rannor|rbreak|retain|set|session|sessref|statgraph|sum|summarize|table|temp|then\s+do|then|title\d?|to|var|where|xaxisopts|yaxisopts|y2axisopts)\b/i,lookbehind:!0},"operator-keyword":{pattern:/\b(?:eq|ne|gt|lt|ge|le|in|not)\b/i,alias:"operator"},number:a,operator:/\*\*?|\|\|?|!!?|¦¦?|<[>=]?|>[<=]?|[-+\/=&]|[~¬^]=?/i,punctuation:r}}(Prism);
\ No newline at end of file
+!function(e){var t="(?:\"(?:\"\"|[^\"])*\"(?!\")|'(?:''|[^'])*'(?!'))",a=/\b(?:\d[\da-f]*x|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b/i,n={pattern:RegExp(t+"[bx]"),alias:"number"},i={pattern:/(^|\s+)(?:proc\s+\w+|quit|run|data(?!\=))\b/i,alias:"keyword",lookbehind:!0},s=[/\/\*[\s\S]*?\*\//,{pattern:/(^\s*|;\s*)\*[^;]*;/m,lookbehind:!0}],r={pattern:RegExp(t),greedy:!0},o=/[$%@.(){}\[\];,\\]/,l={pattern:/%?\w+(?=\()/,alias:"keyword"},d={function:l,"arg-value":{pattern:/(\s*=\s*)[A-Z\.]+/i,lookbehind:!0},operator:/=/,"macro-variable":{pattern:/&[^\.]*\./i,alias:"string"},arg:{pattern:/[A-Z]+/i,alias:"keyword"},number:a,"numeric-constant":n,punctuation:o,string:r},c={pattern:/\b(?:format|put)\b=?[\w'$.]+/im,inside:{keyword:/^(?:format|put)(?=\=)/i,equals:/=/,format:{pattern:/(?:\w|\$\d)+\.\d?/i,alias:"number"}}},p={pattern:/\b(?:format|put)\s+[\w']+(?:\s+[$.\w]+)+(?=;)/i,inside:{keyword:/^(?:format|put)/i,format:{pattern:/[\w$]+\.\d?/,alias:"number"}}},u={pattern:/((?:^|[\s])=?)(?:catname|checkpoint execute_always|dm|endsas|filename|footnote|%include|libname|%list|lock|missing|options|page|resetline|%run|sasfile|skip|sysecho|title\d?)\b/i,lookbehind:!0,alias:"keyword"},m={pattern:/(^|\s)(?:submit(?:\s+(?:load|parseonly|norun))?|endsubmit)\b/i,lookbehind:!0,alias:"keyword"},b="accessControl|cdm|aggregation|aStore|ruleMining|audio|autotune|bayesianNetClassifier|bioMedImage|boolRule|builtins|cardinality|sccasl|clustering|copula|countreg|dataDiscovery|dataPreprocess|dataSciencePilot|dataStep|decisionTree|deepLearn|deepNeural|varReduce|simSystem|ds2|deduplication|ecm|entityRes|espCluster|explainModel|factmac|fastKnn|fcmpact|fedSql|freqTab|gam|gleam|graphSemiSupLearn|gVarCluster|hiddenMarkovModel|hyperGroup|image|iml|ica|kernalPca|langModel|ldaTopic|sparseML|mlTools|mixed|modelPublishing|mbc|network|optNetwork|neuralNet|nonlinear|nmf|nonParametricBayes|optimization|panel|pls|percentile|pca|phreg|qkb|qlim|quantreg|recommend|tsReconcile|deepRnn|regression|reinforcementLearn|robustPca|sampling|sparkEmbeddedProcess|search(?:Analytics)?|sentimentAnalysis|sequence|configuration|session(?:Prop)?|severity|simple|smartData|sandwich|spatialreg|stabilityMonitoring|spc|loadStreams|svDataDescription|svm|table|conditionalRandomFields|text(?:Rule(?:Develop|Score)|Mining|Parse|Topic|Util|Filters|Frequency)|tsInfo|timeData|transpose|uniTimeSeries",g={pattern:RegExp("(^|\\s)(?:action\\s+)?(?:<act>)\\.[a-z]+\\b[^;]+".replace(/<act>/g,b),"i"),lookbehind:!0,inside:{keyword:RegExp("(?:<act>)\\.[a-z]+\\b".replace(/<act>/g,b),"i"),action:{pattern:/(?:action)/i,alias:"keyword"},function:l,"arg-value":d["arg-value"],operator:d.operator,comment:s,argument:d.arg,number:a,"numeric-constant":n,punctuation:o,string:r}},k={pattern:/((?:^|\s)=?)(?:after|analysis|and|array|barchart|barwidth|begingraph|by|call|cas|cbarline|cfill|class(?:lev)?|close|column|computed?|contains|continue|data(?=\=)|define|delete|describe|document|do\s+over|do|dol|drop|dul|end(?:source|comp)?|entryTitle|else|eval(?:uate)?|exec(?:ute)?|exit|fill(?:attrs)?|file(?:name)?|flist|fnc|function(?:list)?|goto|global|group(?:by)?|headline|headskip|histogram|if|infile|keep|keylabel|keyword|label|layout|leave|legendlabel|length|libname|loadactionset|merge|midpoints|name|noobs|nowd|_?null_|ods|options|or|otherwise|out(?:put)?|over(?:lay)?|plot|put|print|raise|ranexp|rannor|rbreak|retain|return|select|set|session|sessref|source|statgraph|sum|summarize|table|temp|terminate|then\s+do|then|title\d?|to|var|when|where|xaxisopts|yaxisopts|y2axisopts)\b/i,lookbehind:!0};e.languages.sas={datalines:{pattern:/^(\s*)(?:(?:data)?lines|cards);[\s\S]+?^\s*;/im,lookbehind:!0,alias:"string",inside:{keyword:{pattern:/^(?:(?:data)?lines|cards)/i},punctuation:/;/}},"proc-sql":{pattern:/(^proc\s+(?:fed)?sql(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{sql:{pattern:RegExp("^[ \t]*(?:select|alter\\s+table|(?:create|describe|drop)\\s+(?:index|table(?:\\s+constraints)?|view)|create\\s+unique\\s+index|insert\\s+into|update)(?:<str>|[^;\"'])+;".replace(/<str>/g,t),"im"),alias:"language-sql",inside:e.languages.sql},"global-statements":u,"sql-statements":{pattern:/(^|\s)(?:disconnect\s+from|exec(?:ute)?|begin|commit|rollback|reset|validate)\b/i,lookbehind:!0,alias:"keyword"},number:a,"numeric-constant":n,punctuation:o,string:r}},"proc-groovy":{pattern:/(^proc\s+groovy(?:\s+[\w|=]+)?;)(?:\s*submit)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{groovy:{pattern:RegExp("(^[ \t]*submit(?:\\s+(?:load|parseonly|norun))?)(?:<str>|[^\"'])+?(?=endsubmit;)".replace(/<str>/g,t),"im"),lookbehind:!0,alias:"language-groovy",inside:e.languages.groovy},"submit-statement":m,"global-statements":u,number:a,"numeric-constant":n,punctuation:o,string:r}},"proc-lua":{pattern:/(^proc\s+lua(?:\s+[\w|=]+)?;)(?:\s*submit)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{lua:{pattern:RegExp("(^[ \t]*submit(?:\\s+(?:load|parseonly|norun))?)(?:<str>|[^\"'])+?(?=endsubmit;)".replace(/<str>/g,t),"im"),lookbehind:!0,alias:"language-lua",inside:e.languages.lua},"submit-statement":m,"global-statements":u,number:a,"numeric-constant":n,punctuation:o,string:r}},"proc-cas":{pattern:/(^proc\s+cas(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|data);|(?![\s\S]))/im,lookbehind:!0,inside:{"statement-var":{pattern:/((?:^|\s)=?)saveresult\s+[^;]+/im,lookbehind:!0,inside:{statement:{pattern:/^saveresult\s+\S+/i,inside:{keyword:/^(?:saveresult)/i}},rest:d}},"cas-actions":g,statement:{pattern:/((?:^|\s)=?)(?:default|(?:un)?set|on|output|upload)[^;]+/im,lookbehind:!0,inside:d},step:i,keyword:k,function:l,comment:s,format:c,altformat:p,"global-statements":u,number:a,"numeric-constant":n,punctuation:o,string:r}},"proc-args":{pattern:RegExp("(^proc\\s+\\w+\\s+)(?!\\s)(?:[^;\"']|<str>)+;".replace(/<str>/g,t),"im"),lookbehind:!0,inside:d},"macro-keyword":{pattern:/((?:^|\s)=?)%(?:ABORT|BQUOTE|BY|CMS|COPY|DISPLAY|DO|ELSE|END|EVAL|GLOBAL|GO|GOTO|IF|INC|INCLUDE|INDEX|INPUT|KTRIM|LENGTH|LET|LIST|LOCAL|NRBQUOTE|NRQUOTE|NRSTR|PUT|QKTRIM|QSCAN|QSUBSTR|QSYSFUNC|QUOTE|QUPCASE|RETURN|RUN|SCAN|STR|SUBSTR|SUPERQ|SYMDEL|SYMGLOBL|SYMLOCAL|SYMEXIST|SYSCALL|SYSEVALF|SYSEXEC|SYSFUNC|SYSGET|SYSRPUT|THEN|TO|TSO|UNQUOTE|UNTIL|UPCASE|WHILE|WINDOW)\b/i,lookbehind:!0,alias:"keyword"},"macro-declaration":{pattern:/^%macro[^;]+(?=;)/im,inside:{keyword:/%macro/i}},"macro-end":{pattern:/^%mend[^;]+(?=;)/im,inside:{keyword:/%mend/i}},macro:{pattern:/%_\w+(?=\()/,alias:"keyword"},input:{pattern:/\binput\s+[-\w\s/*.$&]+;/i,inside:{input:{alias:"keyword",pattern:/^input/i},comment:s,number:a,"numeric-constant":n}},"options-args":{pattern:/(^options)[-'"|/\\<>*+=:()\w\s]*(?=;)/im,lookbehind:!0,inside:d},"cas-actions":g,comment:s,function:l,format:c,altformat:p,"numeric-constant":n,datetime:{pattern:RegExp(t+"(?:dt?|t)"),alias:"number"},string:r,step:i,keyword:k,"operator-keyword":{pattern:/\b(?:eq|ne|gt|lt|ge|le|in|not)\b/i,alias:"operator"},number:a,operator:/\*\*?|\|\|?|!!?|¦¦?|<[>=]?|>[<=]?|[-+\/=&]|[~¬^]=?/i,punctuation:o}}(Prism);
\ No newline at end of file
diff --git a/tests/languages/sas/keyword_feature.test b/tests/languages/sas/keyword_feature.test
index 2b19e9e..b0fca4c 100644
--- a/tests/languages/sas/keyword_feature.test
+++ b/tests/languages/sas/keyword_feature.test
@@ -1,4 +1,4 @@
-action after analysis and array barchart barwidth begingraph by
+after analysis and array barchart barwidth begingraph by
 cas cbarline cfill class classlev close column compute computed contains data=
 define document do do over dol drop dul end entryTitle else endcomp eval
 evaluate exec execute fill fillattrs filename group groupby headline headskip
@@ -10,7 +10,7 @@ then do title to var where xaxisopts yaxisopts y2axisopts
 ----------------------------------------------------
 
 [
-	["keyword", "action"], ["keyword", "after"], ["keyword", "analysis"],
+	["keyword", "after"], ["keyword", "analysis"],
 	["keyword", "and"], ["keyword", "array"], ["keyword", "barchart"],
 	["keyword", "barwidth"], ["keyword", "begingraph"], ["keyword", "by"],
 	["keyword", "cas"], ["keyword", "cbarline"], ["keyword", "cfill"],
diff --git a/tests/languages/sas/proccas_feature.test b/tests/languages/sas/proccas_feature.test
new file mode 100644
index 0000000..5bc03d3
--- /dev/null
+++ b/tests/languages/sas/proccas_feature.test
@@ -0,0 +1,141 @@
+proc cas;
+   session casauto;
+   builtins.actionSetInfo result=results;
+   print results.setinfo[,{'actionset', 'label'}];
+run;
+quit;
+
+proc cas;
+  session casauto;
+  output log;
+  table.loadTable / path="iris.sashdat";
+  simple.summary result=iris / table={name="iris"};
+  tableIris=findtable(iris);
+  saveresult tableIris csv="sum.csv";
+run;
+quit;
+
+proc cas;
+  action table.fileinfo / path="%.csv";
+run;
+quit;
+
+----------------------------------------------------
+
+[
+	["step", "proc cas"],
+	["punctuation", ";"],
+	["proc-cas",
+		[
+			["keyword", "session"],
+			" casauto",
+			["punctuation", ";"],
+			["cas-actions", [
+					["keyword", "builtins.actionSetInfo"],
+					["argument", "result"],
+					["operator", "="],
+				["arg-value", "results"]
+				]
+			],
+			["punctuation", ";"],
+			["keyword", "print"],
+			" results",
+			["punctuation", "."],
+			"setinfo",
+			["punctuation", "["],
+			["punctuation", ","],
+			["punctuation", "{"],
+			["string", "'actionset'"],
+			["punctuation", ","],
+			["string", "'label'"],
+			["punctuation", "}"],
+			["punctuation", "]"],
+			["punctuation", ";"],
+			["step", "run"],
+			["punctuation", ";"]
+		]
+	],
+	["step", "quit"],
+	["punctuation", ";"],
+	["step", "proc cas"],
+	["punctuation", ";"],
+	["proc-cas",
+		[
+			["keyword", "session"],
+			" casauto",
+			["punctuation", ";"],
+			["statement", [
+				["arg", "output"],
+				["arg", "log"]
+			]],
+			["punctuation", ";"],
+			["cas-actions", [
+					["keyword", "table.loadTable"],
+					" / ",
+					["argument", "path"],
+					["operator", "="],
+					["string", "\"iris.sashdat\""]
+					]
+		],
+		["punctuation", ";"],
+		["cas-actions", [
+				["keyword", "simple.summary"],
+				["argument", "result"],
+				["operator", "="],
+				["arg-value", "iris"],
+				" / ",
+				["argument", "table"],
+				["operator", "="],
+				["punctuation", "{"],
+				["argument", "name"],
+				["operator", "="],
+				["string", "\"iris\""],
+				["punctuation", "}"]
+			]
+		],
+		["punctuation", ";"],
+		"\r\n  tableIris=",
+		["function", "findtable"],
+		["punctuation", "("],
+		"iris",
+		["punctuation", ")"],
+		["punctuation", ";"],
+		["statement-var", [
+			[ "statement", [
+					["keyword", "saveresult"],
+					" tableIris"
+				]
+			],
+			["arg", "csv"],
+			["operator", "="],
+			["string", "\"sum.csv\""]
+		]],
+		["punctuation", ";"],
+		["step", "run"],
+		["punctuation", ";"]
+		]
+	],
+	["step", "quit"],
+	["punctuation", ";"],
+	["step", "proc cas"],
+	["punctuation", ";"],
+	["proc-cas",
+		[
+			["cas-actions",
+					[
+					["action", "action"],
+					["keyword", "table.fileinfo"],
+					" / ",
+					["argument", "path"],
+					["operator", "="],
+					["string", "\"%.csv\""]
+				]
+			],
+			["punctuation", ";"],
+			["step", "run"],
+			["punctuation", ";"]
+		]
+	],
+	["step", "quit"],
+	["punctuation", ";"]
+]