# Copyright (C) 2021 Jasper den Ouden.
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the Affero GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
from type_stack_calc.ib.simple_ib_fun import SimpleInbuildFunction
from type_stack_calc.base.n_args import BaseNArgs
from type_stack_calc.extractor import Extractor
from type_stack_calc.pe.drop import PostExtractDrop
from type_stack_calc.ib.plain_component import PlainComponent
from type_stack_calc.util.stack_check import assert_acceptable_vals
class BaseComponentSet(BaseNArgs):
"""Sets a variable."""
with_vars, n = True, 2
c_name, infix, fun_tp = '=', True, 'method'
def __init__(self, component):
self.component = component
self.initial_set, self.definitional = False, False
def deal_relevant_comp(self, rl, args):
args[0].relevant_comp = rl
for c in rl.values():
if getattr(c, 'affects_comp', None) is None:
c.affects_comp = dict()
c.affects_comp[id(args[0])] = args[0]
def extract(self, extractor, gen, tp):
"""Only needs extraction if specifying type now. Though can be specified to always do it."""
# TODO return for instance should _never_ need extraction?
if self.initial_set or extractor.always_extract_set:
# Extract a bit.
ilist = []
# Extractor for internals that includes the component that may replace the temporary-variable.
ext = Extractor((ilist.append, extractor[1]))
extractor[1](self) # This should get the component.
ext.extract_1(gen)
varlist = ilist.copy() # Variables into non-extracted also.
ext.extract_1_w_var(gen, self.component) # And this is the value.
extractor[1](ilist + [PostExtractDrop])
extractor[0](varlist) # ... which goes into the arguments.
else: # Plainly set in side.
self.extract_top(extractor, gen, tp)
def extract_top(self, extractor, gen, tp):
extractor[0](self)
extractor.extract_1(gen)
extractor.extract_1_w_var(gen, self.component)
def to_c(self, tc, gen):
wfile, prep = tc[:2]
var = next(gen)
assert isinstance(var, BaseComponent), (self, self.component, var, list(gen))
if var.key == '~r': # ~ indicates inbuilt "weirdo variable".
wfile("return ")
else:
if self.initial_set:
#assert var.val is not None, var # TODO
wfile((var.val and var.val.c_name or f"Bug({var})") + " ")
wfile(f"{var.key} = ")
tc.new_context(prep, '(').c_1(gen)
def tp_calc(self, sc, vs, args):
to, component = args
if isinstance(to, BaseComponent): # If component, get the value.
to = to.val
assert not isinstance(to, BaseComponent), f"More than one layer of components? ({to})"
assert_acceptable_vals((to,), component, vs,
desc="Setting variable")
self.initial_set = component.sc_set(sc, to)
self.definitional = getattr(to, 'definitional', None)
assert id(component.access_obj.get_comp(component.key)) == id(component)
#self.initial_set = c.access_obj.sc_set_param(sc, c, to)
sc.does.add('something') # Variables/components were modified.
if not getattr(self, 'destructive', None):
self.destructive = getattr(to, 'destructive', None) and 'setcomp'
return [to], self
def __repr__(self): return ".set"
class BaseComponent:
"""Handles (global)variables, members of access_objects.
NOTE: it doesn't access `.set`, `.glob` from the value because the methods for those are `with_vars`, so `deal_with_var` won't unpack the variables then."""
def __init__(self, access_obj, key):
self.val = None
assert_acceptable_vals((access_obj,), key,
desc="Making component object for {access_obj}, {key}")
assert access_obj is not None, key
self.access_obj, self.key = access_obj, key
if key[0] == '~': self.destructive = 'returnlike'
@property
def param_set(self):
"""The 'method' that sets stuff."""
return self._param_set_cls(self)
param_ = param_set # Alias to empty string.(Just a dot accesses it)
@property
def setter(self):
return [self, self.param_set]
@property # TODO remove?
def tp_stack(self): return [self.val]
def _param_glob(self):
"""Ask for global variable."""
assert False, f"Only local variables can ask to become globals. {self} ({type(self)})"
param_glob = SimpleInbuildFunction(1, _param_glob, with_vars=True,
name='glob')
def get_comp(self, key):
"""Gets a component. Looks in the variable before the value!"""
if got := getattr(self, f"param_{key}", None):
return PlainComponent(key, got)
assert_acceptable_vals((self.val,), desc='Getting component value invalid')
return self.val.get_comp(key)
def __repr__(self):
assert not isinstance(self.val, BaseComponent), (type(self), self.key)
return f"{self._repr_kind}:{self.key}:{self.val}"
@property
def c_name(self): return self.key
def to_c(self, tc, _gen):
"""Produce C code, if constant, it's used and the variable name commented out."""
val = self.val
assert val is not None, self
const_c = val.is_const and getattr(val, 'c_name', None)
tc[0](const_c + f"/*{self.key}*/" if const_c else self.key)
def sc_set(self, _sc, _to): # Placeholder.
"""Sets variable to a value. Will notify stuff of the name, and set/smoosh the type.
Not actually defined in BaseComponent, defined downstream."""
assert False, f"BUG: probably sc_set not defined for {self}"