Skip to content

Commit

Permalink
improve minification due to rename logic accidentally not being aware…
Browse files Browse the repository at this point in the history
… it can do things like 'local l = l', 'function f(f)', etc - oops. (also remove needlessly complex scopespec stuff)
  • Loading branch information
thisismypassport committed Jun 26, 2023
1 parent 46bed62 commit e90ddd1
Show file tree
Hide file tree
Showing 19 changed files with 540 additions and 527 deletions.
21 changes: 6 additions & 15 deletions pico_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

def lint_code(ctxt, root, lint_rules):
errors = []
vars = defaultdict(list)
builtin_globals = ctxt.builtins
custom_globals = set()

Expand Down Expand Up @@ -50,7 +49,6 @@ def preprocess_vars(node):

if assign and (func == None or func.kind == "_init" or (func.kind is None and func.name == "_init" and func.find_parent(NodeType.function) == None)):
custom_globals.add(node.name)
vars[node.name].append(node.var)

if node.kind == VarKind.local and not node.new:
if is_assign_target(node) or is_op_assign_target(node) or is_function_target(node):
Expand All @@ -62,7 +60,6 @@ def preprocess_vars(node):
for glob in node.lang.get_defined_globals():
if glob not in custom_globals and is_identifier(glob):
custom_globals.add(glob)
vars[glob].append(root.globals[glob])

root.traverse_nodes(preprocess_vars, tokens=preprocess_tokens, extra=True)

Expand All @@ -71,10 +68,11 @@ def preprocess_vars(node):
def lint_pre(node):
if node.type == NodeType.var:
if node.kind == VarKind.local and node.new:
if lint_duplicate and vars[node.name] and node.name not in ('_', '_ENV'):
prev_var = vars[node.name][-1]
if isinstance(prev_var, Global):
add_error("Local '%s' has the same name as a global" % node.name, node)
if lint_duplicate and node.name not in ('_', '_ENV'):
prev_var = node.scope.parent.find(node.name)
if prev_var is None:
if node.name in custom_globals:
add_error("Local '%s' has the same name as a global" % node.name, node)
elif prev_var.scope.funcdepth < node.var.scope.funcdepth:
if prev_var.scope.funcdepth == 0:
add_error("Local '%s' has the same name as a local declared at the top level" % node.name, node)
Expand All @@ -85,8 +83,6 @@ def lint_pre(node):
else:
add_error("Local '%s' has the same name as a local declared in the same scope" % node.name, node)

vars[node.name].append(node.var)

if lint_unused and node.var not in used_locals and not node.name.startswith("_"):
if node.var in assigned_locals:
add_error("Local '%s' is only ever assigned to, never used" % node.name, node)
Expand All @@ -113,12 +109,7 @@ def lint_pre(node):
add_lang_error = lambda msg: add_error("%s: %s" % (node.name, msg), node)
node.lang.lint(on_error=add_lang_error, builtins=builtin_globals, globals=custom_globals)

def lint_post(node):
for scope in node.end_scopes:
for item in scope.items:
vars[item].pop()

root.traverse_nodes(lint_pre, lint_post, extra=True)
root.traverse_nodes(lint_pre, extra=True)
return errors

from pico_process import Error
16 changes: 9 additions & 7 deletions pico_minify.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from pico_parse import NodeType
from pico_parse import k_unary_ops_prec, k_binary_op_precs, k_right_binary_ops

class Focus(Enum):
values = ("none", "chars", "compressed")

# essentially only returns decvalue right now, given mostly non-fract. inputs
# TODO: test with fract-ish inputs to see what's best to do.
def format_fixnum(value, allow_minus=False):
Expand Down Expand Up @@ -92,11 +95,11 @@ def format_string_literal(value, use_ctrl_chars=True, long=None, quote=None):

return strlong if len(strlong) < len(strlit) else strlit

def minify_string_literal(token, focus_chars, value=None):
def minify_string_literal(token, focus, value=None):
if value is None:
value = parse_string_literal(token.value)

if focus_chars:
if focus == Focus.chars:
return format_string_literal(value)
else:
# haven't found a good balanced heuristic for 'long' yet
Expand Down Expand Up @@ -124,14 +127,13 @@ def minify_needs_comments(minify):
def minify_code(source, ctxt, root, minify):

minify_lines = minify_wspace = minify_tokens = minify_comments = True
focus_chars = focus_compressed = False
focus = Focus.none
if isinstance(minify, dict):
minify_lines = minify.get("lines", True)
minify_wspace = minify.get("wspace", True)
minify_tokens = minify.get("tokens", True)
minify_comments = minify.get("comments", True)
focus_chars = minify.get("focus") == "chars"
focus_compressed = minify.get("focus") == "compressed"
focus = Focus(minify.get("focus", "none"))

shorthand_vlines = set()

Expand All @@ -151,7 +153,7 @@ def fixup_tokens(token):

sublang = getattr(token, "sublang", None)
if sublang and sublang.minify:
token.modify(minify_string_literal(token, focus_chars, value=sublang.minify()))
token.modify(minify_string_literal(token, focus, value=sublang.minify()))

if minify_tokens:

Expand Down Expand Up @@ -203,7 +205,7 @@ def fixup_tokens(token):
token.modify("~")

if token.type == TokenType.string:
token.modify(minify_string_literal(token, focus_chars))
token.modify(minify_string_literal(token, focus))

if token.type == TokenType.number:
outer_prec = get_precedence(token.parent.parent) if token.parent.type == NodeType.const else None
Expand Down
91 changes: 52 additions & 39 deletions pico_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pico_tokenize import is_identifier, parse_string_literal, k_identifier_split_re

class VarKind(Enum):
values = ("local", "global_", "member")
values = ("local", "global_", "member", "label")

class VarBase():
def __init__(m, name, implicit=False):
Expand All @@ -29,11 +29,18 @@ def __init__(m, parent=None, depth=0, funcdepth=0):
def add(m, var):
m.items[var.name] = var

def find(m, item):
if item in m.items:
return m.items[item]
def find(m, name):
var = m.items.get(name)
if var:
return var
elif m.parent:
return m.parent.find(item)
return m.parent.find(name)

def chain(m):
curr = m
while curr:
yield curr
curr = curr.parent

@lazy_property
def used_locals(m):
Expand All @@ -53,6 +60,37 @@ def used_members(m): # note - only for members used as if they were globals,
def has_used_members(m):
return lazy_property.is_set(m, "used_members")

"""class LabelScope:
def __init__(m, parent=None, funcdepth=0):
m.parent = parent
m.funcdepth = funcdepth
m.labels = None
m.gotos = None
def add(m, var):
if m.labels is None:
m.labels = {}
m.labels[var.name] = var
def find(m, name):
var = m.labels.get(name) if m.labels else None
if var:
return var
elif m.parent and m.parent.funcdepth == m.funcdepth:
return m.parent.find(name)
def add_goto(m, node):
if m.gotos is None:
m.gotos = []
m.gotos.append(node)
@lazy_property
def used_labels(m):
return set()
@property
def has_used_labels(m):
return lazy_property.is_set(m, "used_labels")"""

class NodeType(Enum):
values = ("var", "index", "member", "const", "group", "unary_op", "binary_op", "call",
"table", "table_index", "table_member", "varargs", "assign", "op_assign",
Expand All @@ -74,33 +112,11 @@ def __init__(m, type, children, **kwargs):
else:
m.source, m.idx, m.endidx = None, None, None

m.type, m.children, m.value, m.scopespec = type, children, None, None
m.type, m.children, m.value = type, children, None
m.__dict__.update(kwargs)

for child in children:
child.parent = m

# a scopespec is either: None if there's no scoping change,
# a Scope that acts upon the node,
# (True, node) for a scope that the node starts (but does not end)
# (False, nodes) for scopes that the node ends (but does not start)

@property
def start_scope(m):
if m.scopespec:
if isinstance(m.scopespec, Scope):
return m.scopespec
elif m.scopespec[0]:
return m.scopespec[1]

@property
def end_scopes(m):
if m.scopespec:
if isinstance(m.scopespec, Scope):
return (m.scopespec,)
elif not m.scopespec[0]:
return m.scopespec[1]
return ()

def get_tokens(m):
tokens = []
Expand Down Expand Up @@ -207,7 +223,7 @@ def parse_var(token=None, newscope=None, member=False, implicit=False):
if var and hasattr(token, "keys_kind"):
var.keys_kind = token.keys_kind

return Node(NodeType.var, [token], name=name, kind=kind, var_kind=var_kind, var=var, new=bool(newscope), parent_scope=scope)
return Node(NodeType.var, [token], name=name, kind=kind, var_kind=var_kind, var=var, new=bool(newscope), scope=newscope or scope)

def parse_function(stmt=False, local=False):
nonlocal scope, funcdepth
Expand Down Expand Up @@ -271,7 +287,7 @@ def parse_function(stmt=False, local=False):
scope = scope.parent
funcdepth -= 1

funcnode = Node(NodeType.function, tokens, target=target, params=params, body=body, name=name, scopespec=funcscope, kind=func_kind)
funcnode = Node(NodeType.function, tokens, target=target, params=params, body=body, name=name, kind=func_kind)
if self_param:
funcnode.add_extra_child(self_param)
return funcnode
Expand Down Expand Up @@ -486,8 +502,7 @@ def parse_repeat():
body, until = parse_block(with_until=True)
tokens.append(body)
tokens.append(until)
repeat = Node(NodeType.repeat, tokens, body=body, until=until, scopespec=body.scopespec)
body.scopespec = None
repeat = Node(NodeType.repeat, tokens, body=body, until=until)
return repeat

def parse_until():
Expand Down Expand Up @@ -525,7 +540,7 @@ def parse_for():
require("end", tokens)
scope = scope.parent

return Node(NodeType.for_, tokens, target=target, min=min, max=max, step=step, body=body, scopespec=newscope)
return Node(NodeType.for_, tokens, target=target, min=min, max=max, step=step, body=body)

else:
newscope = Scope(scope, depth + 1, funcdepth)
Expand All @@ -543,7 +558,7 @@ def parse_for():
require("end", tokens)
scope = scope.parent

return Node(NodeType.for_in, tokens, targets=targets, sources=sources, body=body, scopespec=newscope)
return Node(NodeType.for_in, tokens, targets=targets, sources=sources, body=body)

def parse_return(vline):
tokens = [peek(-1)]
Expand Down Expand Up @@ -572,7 +587,7 @@ def parse_local():
func = parse_function(stmt=True, local=True)
tokens.append(func)

return Node(NodeType.local, tokens, targets=[func.name], sources=[func], scopespec=(True, newscope), func_local=True)
return Node(NodeType.local, tokens, targets=[func.name], sources=[func], func_local=True)

else:
targets = parse_list(tokens, lambda: parse_var(newscope=newscope))
Expand All @@ -584,7 +599,7 @@ def parse_local():
newscope.add(target.var)
scope = newscope

return Node(NodeType.local, tokens, targets=targets, sources=sources, scopespec=(True, newscope), func_local=False)
return Node(NodeType.local, tokens, targets=targets, sources=sources, func_local=False)

def parse_assign(first):
tokens = [first]
Expand Down Expand Up @@ -678,12 +693,10 @@ def parse_block(vline=None, with_until=False):
until = parse_until()

depth -= 1
scopes = []
while scope != oldscope:
scopes.append(scope)
scope = scope.parent

node = Node(NodeType.block, tokens, stmts=stmts, scopespec=(False, scopes))
node = Node(NodeType.block, tokens, stmts=stmts)
if with_until:
return node, until
else:
Expand Down
Loading

0 comments on commit e90ddd1

Please sign in to comment.