Skip to content

Commit

Permalink
New operators, regex operations, bugfixes.
Browse files Browse the repository at this point in the history
- Added `(r|l)?strip` operators
- Added w variable
- ^ operator now works with regex
- Fixed bug with q and @ operator (@<, @>)
- Fixed bug with Pattern arguments to IN, NI
- Updated documentation
- Simplified .gitignore patterns
  • Loading branch information
dloscutoff committed Jun 8, 2015
1 parent 063bf12 commit cb19dfa
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 38 deletions.
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
__pycache__
github.com
*NOT for repos*
*DATED*
*OLD*
(*
5 changes: 3 additions & 2 deletions Documentation/Example programs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
; GCD of two numbers:
Wb%:aSaba

; Quine--duplicate the string and replace each space with double quote followed by space:
" X2RsC34.s" X2RsC34.s
; Two quine strategies:
" X2RsC34.s" X2RsC34.s Duplicate the string and replace each space with double quote followed by space
x:"x: xRsRPx"xRsRPx Repr substitution, as in the standard Python quine

; First (a) Fibonacci numbers, starting w/ 1:
La{Po+:xSox}
Expand Down
14 changes: 13 additions & 1 deletion Documentation/Operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ U/B/T = Unary/Binary/Ternary

<pre><> B</pre> Group iterable into sections of given length

<pre><| B</pre> Strip from right

<pre><| U</pre> Strip whitespace from right

<pre>= B</pre> Numeric equal

<pre>== B</pre> Exactly equal
Expand Down Expand Up @@ -84,6 +88,14 @@ U/B/T = Unary/Binary/Ternary

<pre>| B</pre> Logical or (short-circuiting)

<pre>|> B</pre> Strip from left

<pre>|> U</pre> Strip whitespace from left

<pre>|| B</pre> Strip

<pre>|| U</pre> Strip whitespace

### Alphabetic operators

<pre>A U</pre> Convert (first) char to ASCII value (or Unicode point)
Expand Down Expand Up @@ -170,7 +182,7 @@ U/B/T = Unary/Binary/Ternary

<pre>SN U</pre> Sort iterable using numeric comparison

<pre>SN U</pre> Sort iterable using string comparison
<pre>SS U</pre> Sort iterable using string comparison

<pre>ST B</pre> Convert to string (for lists, the format of the result depends on command-line flags)

Expand Down
2 changes: 2 additions & 0 deletions Documentation/Pre-defined variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ For the most up-to-date catalogue, see list near the top of `execution.py`. The

`v` -1

`w` Pattern matching 1 or more whitespace characters (i.e. `\s+`)

`x` Empty string

`y` Empty string (could change in future)
Expand Down
199 changes: 171 additions & 28 deletions execution.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import math, random, itertools
import itertools, math, random, re
import tokens
import operators as ops
from parsing import isExpr
Expand Down Expand Up @@ -37,6 +37,7 @@ def __init__(self, listFormat=None, showWarnings=False):
"t": Scalar("10"),
"u": nil,
"v": Scalar("-1"),
"w": Pattern("\\s+"),
"x": Scalar(""),
"y": Scalar(""),
"z": Scalar(""),
Expand Down Expand Up @@ -219,22 +220,28 @@ def varTable(self, varName):
return self.vars

def getRval(self, expr):
#!print("In getRval", repr(expr))
#!print("In getRval", expr)
if type(expr) in (list, tokens.Name):
expr = self.evaluate(expr)
if type(expr) in (Scalar, Pattern, List, Range, Block, Nil):
# Already an rval
result = expr
return expr
elif type(expr) is ops.Operator:
# This may happen if we're rval-ing everything in a chained
# comparison expression
result = expr
return expr
elif type(expr) is Lval:
name = expr.name
if name in self.specialVars:
# This is a special variable--execute its "get" method
if "get" in self.specialVars[name]:
return self.specialVars[name]["get"]()
# This is a special variable
if expr.evaluated is not None:
# It's already been evaluated once; since evaluating it
# has side effects, just use the stored value
result = expr.evaluated
elif "get" in self.specialVars[name]:
# Execute the variable's get method, and store the result
# in the Lval in case it gets evaluated again
result = expr.evaluated = self.specialVars[name]["get"]()
else:
self.err.warn("Special var %s does not implement 'get'"
% name)
Expand All @@ -246,30 +253,29 @@ def getRval(self, expr):
result = self.varTable(name)[name]
except KeyError:
self.err.warn("Referencing uninitialized variable", name)
result = nil
try:
for index in expr.sliceList:
if type(result) in (List, Scalar):
result = result[index]
else:
self.err.warn("Cannot index into", type(result))
return nil
except IndexError:
self.err.warn("Invalid index into %r:" % result, index)
return nil
result = result.copy()
try:
for index in expr.sliceList:
if type(result) in (List, Scalar):
result = result[index]
else:
self.err.warn("Cannot index into", type(result))
return nil
except IndexError:
self.err.warn("Invalid index into %r:" % result, index)
return nil
#!print("Return %r from getRval()" % result)
return result.copy()
else:
self.err.die("Implementation error: unexpected type",
type(expr), "in getRval()")
#!print(result)
return result

def assign(self, lval, rval):
"""Sets the value of lval to rval."""
#!print("In assign,", lval, rval)
name = lval.name
if name in self.specialVars:
# This is a special variable--execute its "get" method
# This is a special variable--execute its "set" method
if lval.sliceList:
self.err.warn("Cannot assign to slice of special var", name)
return
Expand Down Expand Up @@ -675,7 +681,7 @@ def BLOCK(self, statements):
returnExpr = None
return Block(statements, returnExpr)

def CARTESIANPRODUCT(self, list1, list2):
def CARTESIANPRODUCT(self, list1, list2=None):
if list2 is None:
if type(list1) in (Scalar, List, Range):
lists = list1
Expand All @@ -685,7 +691,9 @@ def CARTESIANPRODUCT(self, list1, list2):
return nil
else:
lists = [list1, list2]
noniterables = [item for item in lists if type(item) in (Nil, Block)]
noniterables = [item for item in lists if type(item) in (Nil,
Block,
Pattern)]
if noniterables:
# There are some of the "lists" that are not iterable
# TBD: maybe this can find a non-error meaning?
Expand Down Expand Up @@ -878,7 +886,7 @@ def IFTE(self, test, trueBranch, falseBranch):

def IN(self, lhs, rhs):
if type(lhs) is Pattern and type(rhs) is Scalar:
matches = lhs.findall(str(rhs))
matches = lhs.asRegex().findall(str(rhs))
return Scalar(len(matches))
elif type(rhs) in (Scalar, List, Range):
return Scalar(rhs.count(lhs))
Expand Down Expand Up @@ -972,6 +980,45 @@ def LOWERCASE(self, rhs):
self.err.warn("Unimplemented argtype for LOWERCASE:", type(rhs))
return nil

def LSTRIP(self, string, extra=None):
if extra is nil:
return string
elif type(extra) is Scalar:
extra = str(extra)
elif type(extra) in (Pattern, List, Range) or extra is None:
pass
else:
self.err.warn("Unimplemented argtype for rhs of LSTRIP:",
type(extra))
return nil

if type(string) in (List, Range):
return List(self.LSTRIP(item, extra) for item in string)
elif type(string) is Scalar:
if type(extra) in (List, Range):
for item in extra:
string = self.LSTRIP(string, item)
return string
elif type(extra) is str:
return Scalar(str(string).lstrip(extra))
elif type(extra) is Pattern:
# Python doesn't have a regex strip operation--we have to
# roll our own
patternStr = str(extra)
if patternStr == "":
return string
string = str(string)
beginning = re.compile("^" + patternStr)
while beginning.search(string):
string = beginning.sub("", string)
return Scalar(string)
elif extra is None:
return Scalar(str(string).lstrip())
else:
self.err.warn("Unimplemented argtype for lhs of LSTRIP:",
type(string))
return nil

def MAP(self, function, iterable):
if type(function) is Block and type(iterable) in (Scalar, List, Range):
result = (self.functionCall(function, [item])
Expand Down Expand Up @@ -1062,7 +1109,7 @@ def NOT(self, rhs):

def NOTIN(self, lhs, rhs):
if type(lhs) is Pattern and type(rhs) is Scalar:
matchExists = lhs.search(str(rhs))
matchExists = lhs.asRegex().search(str(rhs))
return Scalar(not matchExists)
else:
return Scalar(lhs not in rhs)
Expand Down Expand Up @@ -1490,6 +1537,45 @@ def ROOT(self, lhs, rhs):
type(lhs), "and", type(rhs))
return nil

def RSTRIP(self, string, extra=None):
if extra is nil:
return string
elif type(extra) is Scalar:
extra = str(extra)
elif type(extra) in (Pattern, List, Range) or extra is None:
pass
else:
self.err.warn("Unimplemented argtype for rhs of RSTRIP:",
type(extra))
return nil

if type(string) in (List, Range):
return List(self.RSTRIP(item, extra) for item in string)
elif type(string) is Scalar:
if type(extra) in (List, Range):
for item in extra:
string = self.RSTRIP(string, item)
return string
elif type(extra) is str:
return Scalar(str(string).rstrip(extra))
elif type(extra) is Pattern:
# Python doesn't have a regex strip operation--we have to
# roll our own
patternStr = str(extra)
if patternStr == "":
return string
string = str(string)
end = re.compile(patternStr + "$")
while end.search(string):
string = end.sub("", string)
return Scalar(string)
elif extra is None:
return Scalar(str(string).rstrip())
else:
self.err.warn("Unimplemented argtype for lhs of RSTRIP:",
type(string))
return nil

def SEND(self, head, *tail):
# A send-expression's semantics depend on the type of the head:
# - Block: function call
Expand Down Expand Up @@ -1549,13 +1635,21 @@ def SORTSTRING(self, iterable):
def SPLIT(self, string, sep=None):
if type(sep) is Scalar:
sep = str(sep)
elif sep is not None:
# TODO: warning message
elif type(sep) is not Pattern and sep is not None:
# Some other type, not a valid separator
self.err.warn("Unimplemented separator type for SPLIT:",
type(sep))
return nil
if type(string) is Scalar:
if sep is None or sep == "":
result = (Scalar(char) for char in str(string))
elif type(sep) is Pattern:
if str(sep) == "":
result = (Scalar(char) for char in str(string))
else:
sep = sep.asSeparator()
result = (Scalar(substr)
for substr in sep.split(str(string)))
else:
result = (Scalar(substr) for substr in str(string).split(sep))
return List(result)
Expand Down Expand Up @@ -1620,6 +1714,48 @@ def STREQUAL(self, lhs, rhs):
result = False
return Scalar(result)

def STRIP(self, string, extra=None):
if extra is nil:
return string
elif type(extra) is Scalar:
extra = str(extra)
elif type(extra) in (Pattern, List, Range) or extra is None:
pass
else:
self.err.warn("Unimplemented argtype for rhs of STRIP:",
type(extra))
return nil

if type(string) in (List, Range):
return List(self.STRIP(item, extra) for item in string)
elif type(string) is Scalar:
if type(extra) in (List, Range):
for item in extra:
string = self.STRIP(string, item)
return string
elif type(extra) is str:
return Scalar(str(string).strip(extra))
elif type(extra) is Pattern:
# Python doesn't have a regex strip operation--we have to
# roll our own
patternStr = str(extra)
if patternStr == "":
return string
string = str(string)
beginning = re.compile("^" + patternStr)
while beginning.search(string):
string = beginning.sub("", string)
end = re.compile(patternStr + "$")
while end.search(string):
string = end.sub("", string)
return Scalar(string)
elif extra is None:
return Scalar(str(string).strip())
else:
self.err.warn("Unimplemented argtype for lhs of STRIP:",
type(string))
return nil

def STRGREATER(self, lhs, rhs):
if type(lhs) is type(rhs) is Scalar:
result = str(lhs) > str(rhs)
Expand Down Expand Up @@ -1858,15 +1994,22 @@ def __init__(self, base, sliceValue=None):
self.sliceList = base.sliceList[:]
if sliceValue is not None:
self.sliceList.append(sliceValue)
self.evaluated = base.evaluated
else:
self.name = str(base)
self.sliceList = []
self.evaluated = None

def __str__(self):
slices = ",".join(map(str, self.sliceList))
if slices:
slices = "|" + slices
return "Lval({})".format(self.name + slices)
if self.evaluated is not None:
evaluated = "=" + str(self.evaluated)
else:
evaluated = ""
string = "Lval({})".format(self.name + evaluated + slices)
return string

def __eq__(self, rhs):
if type(rhs) is Lval:
Expand Down
Loading

0 comments on commit cb19dfa

Please sign in to comment.