Browse Source

change the sed() parser to split out the command (aisdxX) from the args
change the parseQuotedArgs() to return a tuple (pattern, filename)

Pat Beirne 3 years ago
parent
commit
eb68b76c8a
2 changed files with 22 additions and 20 deletions
  1. 4 2
      README.md
  2. 18 18
      tf.py

+ 4 - 2
README.md

@@ -169,7 +169,7 @@ If the single-letter command is preceded by a number or number-range, then the e
 
 The `i/a/d` commands should be preceeded by a line number, or range; `sed()` will *insert*, *append* or *delete* once for each line in the range. 
  
-The ``x/X` patterns are wrapped in a pair of delimiter characters, typically /, although any other character is allowed (except space or any of `\^$()[]`). Valid X commands are:
+The ``x/X` patterns are wrapped in a pair of delimiter characters, typically `/`, although almost any other character is allowed other than space or one of `\^$()[]`. Valid X commands are:
 
 ```
 x/abcd/
@@ -177,7 +177,7 @@ x/abcd/
 x!ratio x/y!
 ```
 
-Similarly, the s patterns are wrapped in a triplet of delimiter characters, typcially / also. If the search pattern has `()` groups, the replace pattern can refer to them with ``\1 \2`,etc. Valid 's' commands are
+Similarly, the s patterns are wrapped in a triplet of delimiter characters, typcially / also. If the search pattern has `()` groups, the replace pattern can refer to them with ``\1 \2`,etc. This `sed()` assumed the `g` suffix, it replaces multiple occurrences on a line. Valid 's' commands are
 
 ```
 s/toronto/Toronto/
@@ -267,6 +267,8 @@ In its present form, the module has these limitations:
 * with sed, lines are parsed and saved one-line-at-a-time, so pattern matching to \n and \r does not work; sed cannot work over line boundaries
 * this simple shell is different than [mpfshell](https://github.com/wendlers/mpfshell) in that this shell runs entirely on the target device. There is no allowance in this shell for transferring files in/out of the target.
 * after a restart of your *MicroPython* board, you can invoke the shell with `import tf`; if you `^C` out of the shell, the second invocation of `tf` will have to be `import tf` followed by `tf.main()`, since the python interpreter caches the module and only loads it once per restart; you can intentionally restart the REPL prompt by hitting `^D` 
+* for the `sed()` function and command line, the 
+[search](https://docs.micropython.org/en/latest/library/ure.html) pattern can have wildcards like ``\s`, `\w` and `\d`. The replace pattern cannot have *any* of these, and can only have `\0`, `\1`, etc
  
 ## Examples
 

+ 18 - 18
tf.py

@@ -43,10 +43,10 @@ def grep(filename, pattern, numbers=False):
 def sed(filename, sed_cmd, bak_ext=".bak"):
   # parse the sed_cmd
   # group 1,3 are the n-start, n-end    group 4 is command: aidsxX
-  a=re.search("^(\d*)([,-](\d+|\$))?\s*([sdaixX].*)",sed_cmd)
+  a=re.search("^(\d*)([,-](\d+|\$))?\s*([sdaixX])(.*)",sed_cmd)
   if not a:
     raise ValueError("sed() failed; pattern must be a number-range followed by one of sdaixX; no changes applied")
-  cmd=a.group(4)
+  op,args=a.group(4),a.group(5)
 
   s,e=1,1000000
   if a.group(1):
@@ -54,26 +54,25 @@ def sed(filename, sed_cmd, bak_ext=".bak"):
   if a.group(3):
     e=1000000 if a.group(3)=='$' else int(a.group(3))
 
-  op=cmd[0]
   if op in "aid" and e-s==1000000:
     raise ValueError("sed(a/i/d) should have a line number")
   #print("sed command parser of <{}> returned {} {} {}".format(op,cmd,a.group(1),a.group(3)))
   if op in "sxX":
-    if len(cmd)<2 or cmd[1] in "\^$()[]": 
-      raise ValueError("invalid sed argument: {}".format(cmd))
-    dl=cmd[1]
+    if len(args)<2 or args[0] in "\^$()[]": 
+      raise ValueError("invalid sed argument: "+op+args)
+    dl=args[0]
     if op=='s':
-      gs=re.search("s"+dl+"([^"+dl+"]*)"+dl+"([^"+dl+"]*)"+dl,cmd)
+      gs=re.search(dl+"([^"+dl+"]*)"+dl+"([^"+dl+"]*)"+dl,args)
     else:
-      gs=re.search("[xX]"+dl+"([^"+dl+"]*)"+dl,cmd)
+      gs=re.search(dl+"([^"+dl+"]*)"+dl,args)
     if not gs:
-      raise ValueError("invalid sed search pattern: {}".format(cmd))
+      raise ValueError("invalid sed search pattern: "+op+args)
     ss=gs.group(1)
     if op=='s':
       r=gs.group(2)
     sp=re.compile(ss) 
 
-  extra=a.group(4)[1:] + '\n' 
+  args=args + '\n' 
 
   try:
     os.rename(filename,filename+bak_ext)
@@ -95,7 +94,7 @@ def sed(filename, sed_cmd, bak_ext=".bak"):
           continue   # delete line
         if op=='i' and m:
           #print("insert a line before {} <{}>".format(i,extra))
-          g.write(extra)
+          g.write(args)
           h+=1
         if op in "aids":
           g.write(lin)
@@ -104,7 +103,7 @@ def sed(filename, sed_cmd, bak_ext=".bak"):
           h+=1
         if op=='a' and m:
           #print("append a line after {} <{}>".format(i,extra))       
-          g.write(extra)
+          g.write(args)
           h+=1
       #f.write("--file modifed by sed()--\n")
   return (i, h)
@@ -139,15 +138,16 @@ def _help():
   ext_cmd('help')
 
 def parseQuotedArgs(st):
+  #returns (pattern,file)
   st=st.strip()  
   if st[0]=="'":
-    p=re.search("'(.*?[^\\\\])'",st)
+    p=re.search("'(.*?[^\\\\])'\s*(\S*)",st)
     if not p:
       print("error in quoted pattern:",st)
       return 
-    return p.group(1)
+    return p.group(1),p.group(2)
   else:
-    return st.split()[0]
+    return st.split(None,1)
 
 def main():
   print("Simple shell: cp/copy mv/move rm/del cat/list cd dir/ls mkdir rmdir grep sed help")
@@ -183,9 +183,9 @@ def main():
         continue
       try:
         if op=='grep':
-          grep(rp[-1],p,numbers=True)
+          grep(p[1],p[0],numbers=True)
         else:
-          r=sed(rp[-1],p)
+          r=sed(p[1],p[0])
           if r:
             print("Lines processed: {}  Lines modifed: {}".format(*r))
       except (ValueError, OSError) as e:
@@ -216,7 +216,7 @@ def main():
       except IndexError:
         print("not enough argments; check syntax with 'help'")
       except OSError:
-        print("file/folder not found or cannot be writtencd")
+        print("file/folder not found or cannot be written")
     gc.collect()
   
 if __name__=="tf":