Skip to content

Commit

Permalink
Wed Oct 23 10:13:10 PDT 2024
Browse files Browse the repository at this point in the history
  • Loading branch information
papajohn committed Oct 23, 2024
1 parent c7d3be3 commit 6df9166
Show file tree
Hide file tree
Showing 18 changed files with 1,712 additions and 12 deletions.
2 changes: 1 addition & 1 deletion _internal/targets.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"due": "10/23/24", "link": "hw/hw07/", "name": "HW 07: Object-Oriented Programming", "piazza_name": "HW 07", "release": "10/16/24"}, {"due": "10/25/24", "link": "lab/lab08/", "name": "Lab 08: Inheritance, Linked Lists", "piazza_name": "Lab 08", "release": "10/21/24"}, {"due": "11/20/24", "link": "proj/ants/", "name": "Ants", "piazza_name": "Ants", "release": "10/16/24"}]
[{"due": "10/23/24", "link": "hw/hw07/", "name": "HW 07: Object-Oriented Programming", "piazza_name": "HW 07", "release": "10/16/24"}, {"due": "10/25/24", "link": "lab/lab08/", "name": "Lab 08: Inheritance, Linked Lists", "piazza_name": "Lab 08", "release": "10/21/24"}, {"due": "11/4/24", "link": "hw/hw08/", "name": "HW 08: Linked Lists", "piazza_name": "HW 08", "release": "10/23/24"}, {"due": "11/20/24", "link": "proj/ants/", "name": "Ants", "piazza_name": "Ants", "release": "10/16/24"}]
3 changes: 0 additions & 3 deletions disc/disc08/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,6 @@ <h3 id="q2-overlap">Q2: Overlap</h3>
<p>Take a vote to decide whether to use recursion or iteration.
Either way works (and the solutions are about the same complexity/difficulty).</p>

<!-- Want some guidance? Post <code>@discuss over here!</code> and your group number to the
<code>#discuss&#x2d;queue</code> channel on Discord. -->


<div class="monaco-storable" id="overlap-input" style="height:846px;"></div>
<a href="javascript:void" id="modal-link-overlap-input">Run in 61A Code</a>
Expand Down
180 changes: 180 additions & 0 deletions hw/hw08/construct_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
from ast import parse, NodeVisitor, Name

# For error messages (student-facing) only
_NAMES = {
'Add': '+',
'And': 'and',
'Assert': 'assert',
'Assign': '=',
'AnnAssign': '=',
'AugAssign': 'op=',
'BitAnd': '&',
'BitOr': '|',
'BitXor': '^',
'Break': 'break',
'Recursion': 'recursive call',
'ClassDef': 'class',
'Continue': 'continue',
'Del': 'del',
'Delete': 'delete',
'Dict': '{...}',
'DictComp': '{...}',
'Div': '/',
'Ellipsis': '...',
'Eq': '==',
'ExceptHandler': 'except',
'ExtSlice': '[::]',
'FloorDiv': '//',
'For': 'for',
'FunctionDef': 'def',
'Filter': 'filter',
'GeneratorExp': '(... for ...)',
'Global': 'global',
'Gt': '>',
'GtE': '>=',
'If': 'if',
'IfExp': '...if...else...',
'Import': 'import',
'ImportFrom': 'from ... import ...',
'In': 'in',
'Index': '...[...]',
'Invert': '~',
'Is': 'is',
'IsNot': 'is not ',
'LShift': '<<',
'Lambda': 'lambda',
'List': '[...]',
'ListComp': '[...for...]',
'Lt': '<',
'LtE': '<=',
'Mod': '%',
'Mult': '*',
'NamedExpr': ':=',
'Nonlocal': 'nonlocal',
'Not': 'not',
'NotEq': '!=',
'NotIn': 'not in',
'Or': 'or',
'Pass': 'pass',
'Pow': '**',
'RShift': '>>',
'Raise': 'raise',
'Return': 'return',
'Set': '{ ... } (set)',
'SetComp': '{ ... for ... } (set)',
'Slice': '[ : ]',
'Starred': '',
'Str': 'str',
'Sub': '-',
'Subscript': '[]',
'Try': 'try',
'Tuple': '(... , ... )',
'UAdd': '+',
'USub': '-',
'While': 'while',
'With': 'with',
'Yield': 'yield',
'YieldFrom': 'yield from',
}

def check(source_file, checked_funcs, disallow, source=None):
"""Checks that AST nodes whose type names are present in DISALLOW
(an object supporting 'in') are not present in the function(s) named
CHECKED_FUNCS in SOURCE. By default, SOURCE is the contents of the
file SOURCE_FILE. CHECKED_FUNCS is either a string (indicating a single
name) or an object of some other type that supports 'in'. CHECKED_FUNCS
may contain __main__ to indicate an entire module. Prints reports of
each prohibited node and returns True iff none are found.
See ast.__dir__() for AST type names. The special node name 'Recursion'
checks for overtly recursive calls (i.e., calls of the form NAME(...) where
NAME is an enclosing def."""
return ExclusionChecker(disallow).check(source_file, checked_funcs, source)

class ExclusionChecker(NodeVisitor):
"""An AST visitor that checks that certain constructs are excluded from
parts of a program. ExclusionChecker(EXC) checks that AST node types
whose names are in the sequence or set EXC are not present. Its check
method visits nodes in a given function of a source file checking that the
indicated node types are not used."""

def __init__(self, disallow=()):
"""DISALLOW is the initial default list of disallowed
node-type names."""
self._disallow = set(disallow)
self._checking = False
self._errs = 0

def generic_visit(self, node):
if self._checking and type(node).__name__ in self._disallow:
self._report(node)
super().generic_visit(node)

def visit_Module(self, node):
if "__main__" in self._checked_funcs:
self._checking = True
self._checked_name = self._source_file
super().generic_visit(node)

def visit_Call(self, node):
if 'Recursion' in self._disallow and \
type(node.func) is Name and \
node.func.id in self._func_nest:
self._report(node, "should not be recursive")
self.generic_visit(node)

def visit_FunctionDef(self, node):
self._func_nest.append(node.name)
if self._checking:
self.generic_visit(node)
elif node.name in self._checked_funcs:
self._checked_name = "Function " + node.name
checking0 = self._checking
self._checking = True
super().generic_visit(node)
self._checking = checking0
self._func_nest.pop()

def _report(self, node, msg=None):
node_name = _NAMES.get(type(node).__name__, type(node).__name__)
if msg is None:
msg = "should not contain '{}'".format(node_name)
print("{} {}".format(self._checked_name, msg))
self._errs += 1

def errors(self):
"""Returns the number of number of prohibited constructs found in
the last call to check."""
return self._errs

def check(self, source_file, checked_funcs, disallow=None, source=None):
"""Checks that AST nodes whose type names are present in DISALLOW
(an object supporting the contains test) are not present in
the function(s) named CHECKED_FUNCS in SOURCE. By default, SOURCE
is the contents of the file SOURCE_FILE. DISALLOW defaults to the
argument given to the constructor (and resets that value if it is
present). CHECKED_FUNCS is either a string (indicating a single
name) or an object of some other type that supports 'in'.
CHECKED_FUNCS may contain __main__ to indicate an entire module.
Prints reports of each prohibited node and returns True iff none
are found.
See ast.__dir__() for AST type names. The special node name
'Recursion' checks for overtly recursive calls (i.e., calls of the
form NAME(...) where NAME is an enclosing def."""

self._checking = False
self._source_file = source_file
self._func_nest = []
if type(checked_funcs) is str:
self._checked_funcs = { checked_funcs }
else:
self._checked_funcs = set(checked_funcs)
if disallow is not None:
self._disallow = set(disallow)
if source is None:
with open(source_file, 'r', errors='ignore') as inp:
source = inp.read()
p = parse(source, source_file)
self._errs = 0

self.visit(p)
return self._errs == 0
24 changes: 24 additions & 0 deletions hw/hw08/hw08.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "Homework 8",
"endpoint": "cal/c88c/fa24/hw08",
"src": [
"hw08.py"
],
"tests": {
"hw*.py": "doctest",
"tests/*.py": "ok_test"
},
"default_tests": [
"store_digits",
"deep_map_mut"
],
"protocols": [
"restore",
"file_contents",
"help",
"grading",
"analytics",
"unlock",
"backup"
]
}
85 changes: 85 additions & 0 deletions hw/hw08/hw08.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
def store_digits(n):
"""Stores the digits of a positive number n in a linked list.
>>> s = store_digits(1)
>>> s
Link(1)
>>> store_digits(2345)
Link(2, Link(3, Link(4, Link(5))))
>>> store_digits(876)
Link(8, Link(7, Link(6)))
>>> store_digits(2450)
Link(2, Link(4, Link(5, Link(0))))
>>> store_digits(20105)
Link(2, Link(0, Link(1, Link(0, Link(5)))))
>>> # a check for restricted functions
>>> import inspect, re
>>> cleaned = re.sub(r"#.*\\n", '', re.sub(r'"{3}[\s\S]*?"{3}', '', inspect.getsource(store_digits)))
>>> print("Do not use str or reversed!") if any([r in cleaned for r in ["str", "reversed"]]) else None
"""
"*** YOUR CODE HERE ***"


def deep_map_mut(func, s):
"""Mutates a deep link s by replacing each item found with the
result of calling func on the item. Does NOT create new Links (so
no use of Link's constructor).
Does not return the modified Link object.
>>> link1 = Link(3, Link(Link(4), Link(5, Link(6))))
>>> print(link1)
<3 <4> 5 6>
>>> # Disallow the use of making new Links before calling deep_map_mut
>>> Link.__init__, hold = lambda *args: print("Do not create any new Links."), Link.__init__
>>> try:
... deep_map_mut(lambda x: x * x, link1)
... finally:
... Link.__init__ = hold
>>> print(link1)
<9 <16> 25 36>
"""
"*** YOUR CODE HERE ***"


class Link:
"""A linked list.
>>> s = Link(1)
>>> s.first
1
>>> s.rest is Link.empty
True
>>> s = Link(2, Link(3, Link(4)))
>>> s.first = 5
>>> s.rest.first = 6
>>> s.rest.rest = Link.empty
>>> s # Displays the contents of repr(s)
Link(5, Link(6))
>>> s.rest = Link(7, Link(Link(8, Link(9))))
>>> s
Link(5, Link(7, Link(Link(8, Link(9)))))
>>> print(s) # Prints str(s)
<5 7 <8 9>>
"""
empty = ()

def __init__(self, first, rest=empty):
assert rest is Link.empty or isinstance(rest, Link)
self.first = first
self.rest = rest

def __repr__(self):
if self.rest is not Link.empty:
rest_repr = ', ' + repr(self.rest)
else:
rest_repr = ''
return 'Link(' + repr(self.first) + rest_repr + ')'

def __str__(self):
string = '<'
while self.rest is not Link.empty:
string += str(self.first) + ' '
self = self.rest
return string + str(self.first) + '>'

Binary file added hw/hw08/hw08.zip
Binary file not shown.
Loading

0 comments on commit 6df9166

Please sign in to comment.