380890030bbb2540950a6dfd3fcca34d18620f5c — Jasper den Ouden 1 year, 9 months ago d236482
Added loader. Note circular module loading required me to add it to ConstStr afterwards, not great..
M bin/tsc.py => bin/tsc.py +1 -1
@@ 28,7 28,7 @@ else:

assert __name__ == '__main__', \
    f"Only intended as program, used as {__main___}"
main = Main()
main = Main(paths=[sys.path[0] + "/examples/"])

i = 0
while i < len(sys.argv):

A examples/load.tsc => examples/load.tsc +6 -0
@@ 0,0 1,6 @@

9 0 10 Tp.Range.Int.c n.

n "factorial".load .factorial q.set  # Does work

33  4 + x .

M type_stack_calc/Main.py => type_stack_calc/Main.py +10 -3
@@ 13,14 13,17 @@ from type_stack_calc.sc_parser import parser

from type_stack_calc.ib.scope import scopify

import type_stack_calc.intro.load as loader  # TODO stupid-side-effects-based.

import sys
from subprocess import Popen, PIPE
import subprocess
import pathlib

class Finished(BaseException): pass

class HighlightCmd(tuple):
    """Generates a list for argv doing highlighting commands."""

    def params(self, **params):
        return [s.format_map(params) for s in self[1]]

@@ 28,14 31,15 @@ class HighlightCmd(tuple):
        return subprocess.Popen(['which', self[1][0]]).wait() == 0

class Main(StackCalc):
    """Class for main type stack calculation program."""

    highlight_try = list(map(HighlightCmd,
        [('bat', ['bat', '-f', '-pp', '-l', "{lang}"]),
         ('vimcat', ['vimcat', '-c', 'set', "filetype={lang}"]),
         ('cat', ['cat'])]))

    def __init__(self, want=None):
    def __init__(self, want=None, paths=None):
        StackCalc.__init__(self, paths=paths)
        self.want = set(['highlight']) if want is None else want
        self.highlight_with = next(filter(HighlightCmd.available,

@@ 168,6 172,9 @@ class Main(StackCalc):
                                     text=True, stdin=PIPE,
                        self._c_variants(proc.stdin, vs.items(), 0)
                        # TODO package names in C?
                        for load_vs in loader.loader.values():
                            self._c_variants(proc.stdin, load_vs.items(), 0)

A type_stack_calc/intro/load.py => type_stack_calc/intro/load.py +42 -0
@@ 0,0 1,42 @@
"""Introduces the import function."""

from pathlib import Path
from type_stack_calc.sc_parser import parser

from type_stack_calc.intro import intro
from type_stack_calc.ib.scope import scopify
def _ignore(_c1): pass

class LoadCall(dict):
    def __repr__(self): return f"LoadCall"

    fun_tp, n = 'method', 1
    c_name = 'LoadCall'

    def load(self, sc, vs, path):
        """Gets loaded from dictionary if available, otherwise looks in `sc.paths`, and runs again."""
        got = self.get(path)
        if got is None:  # Don't have it yet.
            for origin_path in sc.paths:  # Try use the paths available.
                p = Path(origin_path + path + ".tsc")
                if p.exists():
                    new_vs = scopify(intro)
                    sc.tp_calc(parser(p.read_text()), new_vs, [], _ignore)
                    got = new_vs
            assert got is not None, \
                f"Could not find {path}, searched {sc.paths}"
            self[path] = got
        return got

    def tp_calc(self, sc, vs, inp):
        load_path = inp[0]
        assert isinstance(load_path, str), \
            f"Must be constant string right now, got {load_path}"
        got = self.load(sc, vs, load_path)
        return [got], got

from type_stack_calc.tp.str import ConstStr

loader = LoadCall()
ConstStr.param_load = loader

M type_stack_calc/tp_calc.py => type_stack_calc/tp_calc.py +23 -19
@@ 37,32 37,37 @@ def interpret_word(word):
class StackCalc:
    """Calculates types, heart of the implementation, basically."""

    def __init__(self, origin=None):
        self.origin = origin 
    def __init__(self, origin=None, paths=None):
        self.origin = origin  # Note: component_collection does use it.
        self.paths = [] if paths is None else paths
        self.does = set()

    def deal_with_var(self, var, code, vs, stack, into):
        """Gets variable, applies as function if relevant."""
        val = var.val  # For instance a function immediately called.

        assert_acceptable_vals(stack, val)
        # If it has a type function, run it.
        if tp_fun := val and getattr(val, 'tp_calc', None):
        if tp_fun := getattr(val, 'tp_calc', None):
            if val.fun_tp == 'plain':
                stack = stack[:-1]  # Ditch the origin.
#            elif val.fun_tp == 'class_method':
#                assert isinstance(stack[-1], Scope)
                assert val.fun_tp == 'method'
                assert val.fun_tp == 'method', \
                    "Only `plain` and `method` function types permitted. Got: {val.fun_tp}"

            # Strip to number of arguments if `n` indicates that.
            n = getattr(val, 'n', None)
            args, rest = (stack, []) if (n is None) else \
            inp, rest = (stack, []) if (n is None) else \
                (([], stack) if n==0 else (stack[-n:], stack[:-n]))

            if n is not None:
                assert len(args) == n, (stack, n, stack[-n:], args)
                assert len(inp) == n, \
                    f"Not enough arguments available {len(inp)} vs needed {n}\n  {stack} {inp}"
            # Figure out the local variables that might be relevant.
            relevant_comp = dict()
            for a in args:
            for a in inp:
                if isinstance(a, ScopeVariable):
                    relevant_comp[id(a)] = a
                if rcd := getattr(a, 'relevant_comp', None):

@@ 71,28 76,26 @@ class StackCalc:

            # If it won't deal with variables, extract the values for it.
            if not getattr(val, 'with_vars', None):
                args = [(a.val if isinstance(a, BaseComponent) else a)
                        for a in args]
                assert not any(isinstance(a, BaseComponent) for a in args)
                inp = [(a.val if isinstance(a, BaseComponent) else a)
                        for a in inp]
                assert not any(isinstance(a, BaseComponent) for a in inp)
            # Actually calculate the type, return new stack and result.
            stack, *tc_code = (
                tp_fun(self, code, vs, args, into)
                tp_fun(self, code, vs, inp, into)
                if getattr(val, 'mac', None) else
                tp_fun(self, vs, args))
                tp_fun(self, vs, inp))

            assert_acceptable_vals(stack, args, rest, code, var)
            assert_acceptable_vals(stack, inp, rest, code, var)

            if fun := getattr(val, 'deal_relevant_comp', None):
                fun(relevant_comp, args)
                fun(relevant_comp, inp)
            for el in stack:  # Remember all the relevant local variables.
                if fun := getattr(el, 'deal_relevant_comp', None):
                    fun(relevant_comp, args)
                    fun(relevant_comp, inp)
                el.relevant_comp = relevant_comp

            return (rest + stack, *tc_code)

        assert_acceptable_vals(stack, val)

        return stack[:-1] + [var], var  # No calculation needed.

    special_fun= {'+', '-', '*', '/', '%', '>', '<'}

@@ 115,7 118,7 @@ class StackCalc:

            if word[:1] == '.' or word in self.special_fun:  # It's a component.
                assert len(stack) >= 1
                assert len(stack) >= 1, "Trying to access, but empty stack."
                assert_acceptable_vals((stack[-1],), stack)

                got = stack[-1].get_comp(word[1:] if word[:1]=='.' else word)

@@ 130,7 133,7 @@ class StackCalc:

            # It is a variable, or accesses something.
            if insert := getattr(got.val, 'insert_code', None):
                assert getattr(got.val, 'tp_calc', None) is None
                assert getattr(got.val, 'tp_calc', None) is None, "BUG: cannot have both `insert_code` and `tp_calc`."
                plus, stack = insert(self, code, vs, stack)
                inserted = plus + inserted

@@ 138,4 141,5 @@ class StackCalc:
                assert_acceptable_vals(otp, "Producing type-calced-code.")
                for el in otp:  # Insert the tc code.

            assert_acceptable_vals(stack, got, code)