Repository: benjamincorcoran/sasdocs

Issue:
/ options for datasteps and procedures
# Issue
Parser currently fails when presented with a datastep or procedure with explicitly defined options. 

```sas
data test / view=testvew;
    set test;
run;

Comments:

Patch:
diff --git a/sasdocs/objects.py b/sasdocs/objects.py
index 0ec2506..4536ab3 100644
--- a/sasdocs/objects.py
+++ b/sasdocs/objects.py
@@ -406,6 +406,7 @@ class dataStep(baseSASObject):
     inputs = attr.ib()
     header = attr.ib(repr=False, default=None)
     body = attr.ib(repr=False, default=None)
+    options = attr.ib(default=None)
 
 @attr.s
 class procedure(baseSASObject):
@@ -738,7 +739,7 @@ cmnt = (inlinecmnt|multicmnt).map(comment)
 
 # Complex SAS Objects
 # sasName: Any named object in SAS, can contain macrovariable as part of name
-sasName = (wrd|mcv).many()
+sasName = (wrd|mcv).at_least(1)
 
 # Marcovariable definition:
 mcvDef = ps.seq(
@@ -758,7 +759,7 @@ datalineArg = ps.seq(
 # e.g. keep=A B C 
 datalineArgNB = ps.seq(
     option = sasName << (opspc + eq + opspc), 
-    setting = ps.regex(r'.*?(?=\s+\w+\s*=)|.*?(?=\))')
+    setting = ps.regex(r'.*?(?=\s+\w+\s*=)|.*?(?=\))|.*?(?=;)')
 ).combine_dict(dataArg)
 
 datalineArgPt = ps.seq(
@@ -777,7 +778,7 @@ datalineOptions = lb >> (datalineArg|datalineArgPt|datalineArgNB|sasName).sep_by
 
 dataObj = ps.seq(
     library = (sasName << dot).optional(),
-    dataset = dot >> sasName | sasName,
+    dataset = (dot >> sasName) | sasName,
     options = datalineOptions.optional()
 ).combine_dict(dataObject)
 
@@ -792,7 +793,9 @@ dataLine = dataObj.sep_by(spc)
 # terminating run is thrown away
 
 datastep = ps.seq(
-    outputs = (ps.regex(r'data', flags=re.IGNORECASE) + spc) >> dataLine << col,
+    outputs = (ps.regex(r'data', flags=re.IGNORECASE) + spc) >> dataLine,
+    options = (opspc + fs + opspc >> (datalineArg|datalineArgPt|datalineArgNB|sasName).sep_by(spc)).optional(), 
+    _col = opspc + col,
     header = (ps.regex(r'(?:(?!run).)*(?=set|merge)', flags=reFlags)).optional(),
     inputs = ((opspc + ps.regex(r'set|merge',flags=re.IGNORECASE) + opspc) >> dataLine << col).optional(),
     body = ps.regex(r'.*?(?=run)', flags=reFlags),


Test patch:
diff --git a/tests/test_objects.py b/tests/test_objects.py
index 03e1f5f..2eeba96 100644
--- a/tests/test_objects.py
+++ b/tests/test_objects.py
@@ -58,9 +58,11 @@ def test_dataLine_parse(case, expected):
 
 testcases = [
     ("data test.test lib2.test(where=(ax=b) rename=(a=b)); format a; set test; a = 1; b = 2; output; run;", dataStep(outputs=[dataObject(library=['test'], dataset=['test'], options=None), dataObject(library=['lib2'], dataset=['test'], options=[dataArg(option=['where'], setting='(ax=b)'), dataArg(option=['rename'], setting='(a=b)')])], header=' format a; ', inputs=[dataObject(library=None, dataset=['test'], options=None)], body=' a = 1; b = 2; output; ')),
-    ("data out; input a; datalines; 1; run;", dataStep(outputs=[dataObject(library=None,dataset=['out'],options=None)],inputs=None,header=None,body=' input a; datalines; 1; '))
+    ("data out; input a; datalines; 1; run;", dataStep(outputs=[dataObject(library=None,dataset=['out'],options=None)],inputs=None,header=None,body=' input a; datalines; 1; ')),
+    ("data out /view=out; set in; run;", dataStep(options=[dataArg(option=['view'], setting='out')], outputs=[dataObject(library=None, dataset=['out'], options=None)], inputs=[dataObject(library=None, dataset=['in'], options=None)], header=" ", body=" "))
 ]
 
+
 @pytest.mark.parametrize("case,expected", testcases)
 def test_dataStep_parse(case, expected):
     assert datastep.parse(case) == expected