From 1f1700e9a8091e761816008007bedf69d46c8ee7 Mon Sep 17 00:00:00 2001 From: timotheyca Date: Mon, 6 Sep 2021 14:20:24 +0300 Subject: [PATCH] tracing + smarter memoization + better __str__ --- bu4/evaluation/av/aftervalue.py | 6 ++++ bu4/evaluation/av/envtype.py | 6 ++-- bu4/evaluation/av/lambdaenv.py | 18 ++++++---- bu4/evaluation/constructs/elambda.py | 4 ++- bu4/evaluation/constructs/proxy.py | 3 ++ bu4/evaluation/targets/avcall.py | 3 ++ bu4/evaluation/targets/avcontainer.py | 6 +++- bu4/linking/constructs/lcall.py | 5 +++ bu4/linking/constructs/lexception.py | 1 + bu4/linking/constructs/linked.py | 1 + bu4/linking/constructs/llambda.py | 6 ++-- bu4/linking/constructs/lname.py | 1 + bu4/linking/constructs/lnull.py | 1 + bu4/tracing/probe.py | 49 +++++++++++++++++++++++++++ docs/extensions.md | 27 +++++++++++++++ {manual => docs}/main.md | 0 main.py | 28 ++++++++++++++- manual/extensions.md | 26 -------------- src/sys5.bu4 | 5 +++ src/sys6.bu4 | 17 ++++++++++ 20 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 bu4/tracing/probe.py create mode 100644 docs/extensions.md rename {manual => docs}/main.md (100%) delete mode 100644 manual/extensions.md create mode 100644 src/sys5.bu4 create mode 100644 src/sys6.bu4 diff --git a/bu4/evaluation/av/aftervalue.py b/bu4/evaluation/av/aftervalue.py index f5f6a7e..564c093 100644 --- a/bu4/evaluation/av/aftervalue.py +++ b/bu4/evaluation/av/aftervalue.py @@ -23,6 +23,9 @@ class AfterValue(Evaluable): self.evaluable = self.evaluable.next() return self + def __str__(self): + return f'{self.evaluable}->{self.target}' + class AVChain(AVTarget): def __init__(self, target: AVTarget, aftertarget: AVTarget): @@ -31,3 +34,6 @@ class AVChain(AVTarget): def given(self, value: EValue) -> Evaluable: return AfterValue(self.target.given(value), self.aftertarget) + + def __str__(self): + return f'{self.target}->{self.aftertarget}' diff --git a/bu4/evaluation/av/envtype.py b/bu4/evaluation/av/envtype.py index 46f87bb..24e8aaa 100644 --- a/bu4/evaluation/av/envtype.py +++ b/bu4/evaluation/av/envtype.py @@ -2,13 +2,13 @@ from typing import Mapping -from bu4.evaluation.av.aftervalue import AfterValue - __all__ = ('envtype',) +from bu4.evaluation.constructs.evaluable import Evaluable + try: from typing import TypeAlias except ImportError: TypeAlias = type -envtype: TypeAlias = Mapping[bytes, AfterValue] +envtype: TypeAlias = Mapping[bytes, Evaluable] diff --git a/bu4/evaluation/av/lambdaenv.py b/bu4/evaluation/av/lambdaenv.py index 45e5ef8..3414c58 100644 --- a/bu4/evaluation/av/lambdaenv.py +++ b/bu4/evaluation/av/lambdaenv.py @@ -2,7 +2,6 @@ from typing import Mapping, Iterator -from bu4.evaluation.av.aftervalue import AfterValue from bu4.evaluation.av.envtype import envtype from bu4.evaluation.constructs.evaluable import Evaluable from bu4.evaluation.targets.avcontainer import AVContainer @@ -10,8 +9,8 @@ from bu4.evaluation.targets.avcontainer import AVContainer __all__ = ('LambdaEnv',) -class LambdaEnv(Mapping): - def __getitem__(self, k: bytes) -> AfterValue: +class LambdaEnv(Mapping[bytes, Evaluable]): + def __getitem__(self, k: bytes) -> Evaluable: if k == self.name: return self.container else: @@ -20,11 +19,18 @@ class LambdaEnv(Mapping): def __len__(self) -> int: return len(self.env) + 1 - def __iter__(self) -> Iterator[AfterValue]: + def __iter__(self) -> Iterator[Evaluable]: yield self.name yield from self.env - def __init__(self, env: envtype, name: bytes, evaluable: Evaluable): + def __init__(self, env: envtype, name: bytes, evaluable: Evaluable, *, memoize): self.env = env self.name = name - self.container = AVContainer(evaluable).after_value + self.container = AVContainer(evaluable, name).after_value if memoize else evaluable + + @staticmethod + def strfy(self): + return ''.join(f'\x7b{evaluable}|{name.decode()}\x7d' for name, evaluable in self.items()) + + def __str__(self): + return self.strfy(self) diff --git a/bu4/evaluation/constructs/elambda.py b/bu4/evaluation/constructs/elambda.py index 344ebeb..d18e3bd 100644 --- a/bu4/evaluation/constructs/elambda.py +++ b/bu4/evaluation/constructs/elambda.py @@ -15,10 +15,12 @@ class ELambda(EValue): self.env = env self.name = name self.value = value + self.used = name in value.future + self.memoize = name in value.multifuture def call(self, argument: Evaluable) -> Evaluable: return ELinked( - LambdaEnv(self.env, self.name, argument), + LambdaEnv(self.env, self.name, argument, memoize=self.memoize) if self.used else self.env, self.value ) diff --git a/bu4/evaluation/constructs/proxy.py b/bu4/evaluation/constructs/proxy.py index 29713e4..eabdb63 100644 --- a/bu4/evaluation/constructs/proxy.py +++ b/bu4/evaluation/constructs/proxy.py @@ -41,6 +41,9 @@ class AntiProxy: def __call__(self, value): return antiproxy(sync(self.evaluable.call(eproxy(value)))) + def __str__(self): + return str(self.evaluable) + def eproxy(value: Any) -> Evaluable: if isinstance(value, Evaluable): diff --git a/bu4/evaluation/targets/avcall.py b/bu4/evaluation/targets/avcall.py index 4e532cf..a8cf13c 100644 --- a/bu4/evaluation/targets/avcall.py +++ b/bu4/evaluation/targets/avcall.py @@ -17,3 +17,6 @@ class AVCall(AVTarget): def given(self, value: EValue) -> Evaluable: return value.call(ELinked(self.env, self.argument)) + + def __str__(self): + return f'({self.argument})' diff --git a/bu4/evaluation/targets/avcontainer.py b/bu4/evaluation/targets/avcontainer.py index 54a77d1..f51743a 100644 --- a/bu4/evaluation/targets/avcontainer.py +++ b/bu4/evaluation/targets/avcontainer.py @@ -9,9 +9,13 @@ __all__ = ('AVContainer',) class AVContainer(AVTarget): - def __init__(self, evaluable: Evaluable): + def __init__(self, evaluable: Evaluable, name: bytes): self.after_value = AfterValue(evaluable, self) + self.name = name def given(self, value: EValue) -> Evaluable: self.after_value.evaluable = value return value + + def __str__(self): + return f'[{self.name.decode()}]' diff --git a/bu4/linking/constructs/lcall.py b/bu4/linking/constructs/lcall.py index b4c7bab..e4288e2 100644 --- a/bu4/linking/constructs/lcall.py +++ b/bu4/linking/constructs/lcall.py @@ -18,6 +18,11 @@ class LCall(Linked): self.argument = argument self.lambda_ = lambda_ self.future = self.argument.future | self.lambda_.future + self.multifuture = ( + self.argument.multifuture | + self.lambda_.multifuture | + (self.argument.future & self.lambda_.future) + ) def evaluable(self, env: envtype) -> Evaluable: return AfterValue(ELinked(env, self.lambda_), AVCall(env, self.argument)) diff --git a/bu4/linking/constructs/lexception.py b/bu4/linking/constructs/lexception.py index e21f30a..29a04a4 100644 --- a/bu4/linking/constructs/lexception.py +++ b/bu4/linking/constructs/lexception.py @@ -12,6 +12,7 @@ class LException(Linked): def __init__(self, name: bytes): self.name = name self.future = set() + self.multifuture = set() def evaluable(self, env: envtype) -> Evaluable: return EException(self.name) diff --git a/bu4/linking/constructs/linked.py b/bu4/linking/constructs/linked.py index f73146e..a9be66b 100644 --- a/bu4/linking/constructs/linked.py +++ b/bu4/linking/constructs/linked.py @@ -8,6 +8,7 @@ __all__ = ('Linked',) class Linked: future: set[bytes] + multifuture: set[bytes] def evaluable(self, env: envtype) -> Evaluable: raise NotImplementedError diff --git a/bu4/linking/constructs/llambda.py b/bu4/linking/constructs/llambda.py index 67bec92..9662757 100644 --- a/bu4/linking/constructs/llambda.py +++ b/bu4/linking/constructs/llambda.py @@ -14,11 +14,13 @@ class LLambda(Linked): def __init__(self, name: bytes, value: Linked): self.name = name self.value = value - self.future = self.value.future - {self.name} + self.future = self.value.future - {name} + self.multifuture = self.future + self.used = name in value.future def evaluable(self, env: envtype) -> Evaluable: return ELambda( - {name: env[name] for name in self.future}, + {name: env[name] for name in self.future} if self.used else env, self.name, self.value ) diff --git a/bu4/linking/constructs/lname.py b/bu4/linking/constructs/lname.py index 314d828..ffd9bf7 100644 --- a/bu4/linking/constructs/lname.py +++ b/bu4/linking/constructs/lname.py @@ -11,6 +11,7 @@ class LName(Linked): def __init__(self, name: bytes): self.name = name self.future = {name} + self.multifuture = set() def evaluable(self, env: envtype) -> Evaluable: return env[self.name] diff --git a/bu4/linking/constructs/lnull.py b/bu4/linking/constructs/lnull.py index 54d1451..619f007 100644 --- a/bu4/linking/constructs/lnull.py +++ b/bu4/linking/constructs/lnull.py @@ -11,6 +11,7 @@ __all__ = ('LNull',) class LNull(Linked): def __init__(self): self.future = set() + self.multifuture = set() def evaluable(self, env: envtype) -> Evaluable: return ENull() diff --git a/bu4/tracing/probe.py b/bu4/tracing/probe.py new file mode 100644 index 0000000..000f503 --- /dev/null +++ b/bu4/tracing/probe.py @@ -0,0 +1,49 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.evaluation.constructs.elambda import ELambda +from bu4.evaluation.constructs.evaluable import Evaluable +from bu4.evaluation.constructs.evalue import EValue +from bu4.evaluation.sync import sync + + +class Probe(EValue): + def call(self, argument: Evaluable) -> Evaluable: + return ProbeB(self, argument) + + def start_and_list(self): + probe = self + list_ = [] + while isinstance(probe, ProbeB): + list_.append(probe.argument) + probe = probe.prev + assert isinstance(probe, ProbeA) + return probe.start, reversed(list_) + + def __str__(self): + start, list_ = self.start_and_list() + return f'probe {start} [ {" ; ".join(map(str, list_))} ]' + + +class ProbeA(Probe): + def __init__(self, start): + self.start = start + + +class ProbeB(Probe): + def __init__(self, prev: Probe, argument): + self.prev = prev + self.argument = argument + + +_probe_index = 0 + + +def trace(lambda_: EValue): + global _probe_index + size = 0 + while True: + if not isinstance(lambda_, ELambda): + return size, lambda_ + lambda_ = sync(lambda_.call(ProbeA(_probe_index))) + _probe_index += 1 + size += 1 diff --git a/docs/extensions.md b/docs/extensions.md new file mode 100644 index 0000000..a7da4d9 --- /dev/null +++ b/docs/extensions.md @@ -0,0 +1,27 @@ +Application (essential): +* Call + +Abstraction: +* Lambda +* Name + +Common: +* Null (=exception/undefined) +* Quot +* Qopn-Qcls +* Skip + +Special: +* Namespaces (evaluable imports) +* String literals (exceptions) +* Static literals (constants and platform values) +* Trace/probes +* Classes (named lambda constructs) +* Methods (static types, optionally explicit) +* Factories (static types, optionally explicit) +* Await/after constructs with callbacks (and mutability/io monads) +* Speculative execution (asynchronousity and multithreading) +* Index-based linking (more static) +* Hashes (v13 extension) +* Typing decorators (v13 extension) +* Evaluable to parsed conversion (compilation inversion) diff --git a/manual/main.md b/docs/main.md similarity index 100% rename from manual/main.md rename to docs/main.md diff --git a/main.py b/main.py index 096ca43..5b2867d 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,13 @@ # Copyright (c) PARRRATE T&V 2021. All rights reserved. - import random +from collections import deque from bu4.common_interface import with_common_interface from bu4.evaluation.constructs.proxy import antiproxy, eproxy from bu4.evaluation.sync import sync from bu4.parsing.toolchain.readfile import readfile from bu4.synced import synced +from bu4.tracing.probe import trace, Probe from timesample import TimeSample with TimeSample('all'): @@ -64,3 +65,28 @@ with TimeSample('all'): TimeSample.print(sys1) with TimeSample('sys4'): TimeSample.print(with_common_interface(synced(readfile('src/sys4')))) + with TimeSample('sys5'): + sys5 = synced(readfile('src/sys5')) + queue = deque([(0, 0, sys5)]) + for trace_index in range(1, 100 + 1): + if not queue: + break + tab, parent, evaluable = queue.popleft() + for _ in range(10000): + evaluable = evaluable.next() + _probe_index, probe = trace(evaluable) + prefix = ('.', ' ' * tab, parent, trace_index) + if isinstance(probe, Probe): + start, list_ = probe.start_and_list() + TimeSample.print(*prefix, _probe_index, probe) + queue.extend((tab + 1, trace_index, deeper_evaluable) for deeper_evaluable in list_) + else: + TimeSample.print(*prefix, '...') + with TimeSample('sys6'): + sys6 = synced(readfile('src/sys6')).call(synced('?')) + + for _ in range(100): + sys6 = sys6.next() + print(sys6) + for _ in range(10000000): + sys6 = sys6.next() diff --git a/manual/extensions.md b/manual/extensions.md deleted file mode 100644 index 2ab5143..0000000 --- a/manual/extensions.md +++ /dev/null @@ -1,26 +0,0 @@ -Application (essential): -* Call - -Abstraction: -* Lambda -* Name - -Common: -* Null (=exception/undefined) -* Quot -* Qopn-Qcls -* Skip - -Special: -* Namespaces (E- inputs) -* String literals (=exceptions) -* Trace/probes -* Classes (named lambda constructs) -* Methods (statically typed, optionally explicitly) -* Factories (statically typed, optionally explicitly) -* Await/after constructs with callbacks (and mutability/io quasimonads) -* Speculative execution (anychronousity and multithreading) -* Index-based linking (more static) -* Hashes (v13 extension) -* Bitstring call protocol (v13 extension) -* Evaluable to parsed diff --git a/src/sys5.bu4 b/src/sys5.bu4 new file mode 100644 index 0000000..65022d1 --- /dev/null +++ b/src/sys5.bu4 @@ -0,0 +1,5 @@ +@@ + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +[b3] +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/sys6.bu4 b/src/sys6.bu4 new file mode 100644 index 0000000..fac364d --- /dev/null +++ b/src/sys6.bu4 @@ -0,0 +1,17 @@ +@@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +/(g) + (x) + / + [x] + [g] +[YC] +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +(_) + / + [ID] + [YC] +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +(_) + / < (x) /[x][SC] > [SC] +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""