diff --git a/bu4/linking/constructs/llambda.py b/bu4/linking/constructs/llambda.py index 5bc3aa4..67bec92 100644 --- a/bu4/linking/constructs/llambda.py +++ b/bu4/linking/constructs/llambda.py @@ -18,7 +18,7 @@ class LLambda(Linked): def evaluable(self, env: envtype) -> Evaluable: return ELambda( - {name: container for name, container in env.items() if name in self.future}, + {name: env[name] for name in self.future}, self.name, self.value ) diff --git a/bu4/parsing/abstractparser.py b/bu4/parsing/abstractparser.py index 85f0b8d..6e0738b 100644 --- a/bu4/parsing/abstractparser.py +++ b/bu4/parsing/abstractparser.py @@ -1,8 +1,12 @@ # Copyright (c) PARRRATE T&V 2021. All rights reserved. + __all__ = ('AbstractParser',) class AbstractParser: def read(self) -> int: raise NotImplementedError + + def parse_name(self) -> bytes: + raise NotImplementedError diff --git a/bu4/parsing/extensions/extension.py b/bu4/parsing/extensions/extension.py new file mode 100644 index 0000000..499390b --- /dev/null +++ b/bu4/parsing/extensions/extension.py @@ -0,0 +1,23 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.abstractparser import AbstractParser +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter + +__all__ = ('Extension',) + + +class Extension: + code: int + + def __init_subclass__(cls, /, code: int = None, **kwargs): + super().__init_subclass__(**kwargs) + if code is None: + raise TypeError + cls.code = code + + def __init__(self, parser: AbstractParser): + self.parser = parser + + def apply(self, state: PSAfter) -> ParseState: + raise NotImplementedError diff --git a/bu4/parsing/extensions/xcall.py b/bu4/parsing/extensions/xcall.py new file mode 100644 index 0000000..503ae20 --- /dev/null +++ b/bu4/parsing/extensions/xcall.py @@ -0,0 +1,15 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_CALL +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter +from bu4.parsing.states.pscall import pscall + +__all__ = ('XCall',) + + +class XCall(Extension, code=CODE_CALL): + def apply(self, state: PSAfter) -> ParseState: + state.state = pscall() + return state diff --git a/bu4/parsing/extensions/xmake.py b/bu4/parsing/extensions/xmake.py new file mode 100644 index 0000000..4afe2d9 --- /dev/null +++ b/bu4/parsing/extensions/xmake.py @@ -0,0 +1,25 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_MAKE +from bu4.parsing.constructs.plambda import PLambda +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter +from bu4.parsing.states.psfinal import PSFinal +from bu4.parsing.states.psread import PSRead + +__all__ = ('XMake',) + +from bu4.parsing.targets.pslambda import PSLambda + + +class XMake(Extension, code=CODE_MAKE): + def apply(self, state: PSAfter) -> ParseState: + name = self.parser.parse_name() + state.state = PSAfter( + PSRead(), + PSLambda( + lambda value: PSFinal(PLambda(name, value)) + ) + ) + return state diff --git a/bu4/parsing/extensions/xname.py b/bu4/parsing/extensions/xname.py new file mode 100644 index 0000000..c9cf95a --- /dev/null +++ b/bu4/parsing/extensions/xname.py @@ -0,0 +1,16 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_NAME +from bu4.parsing.constructs.pname import PName +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter +from bu4.parsing.states.psfinal import PSFinal + +__all__ = ('XName',) + + +class XName(Extension, code=CODE_NAME): + def apply(self, state: PSAfter) -> ParseState: + state.state = PSFinal(PName(self.parser.parse_name())) + return state diff --git a/bu4/parsing/extensions/xnull.py b/bu4/parsing/extensions/xnull.py new file mode 100644 index 0000000..2120359 --- /dev/null +++ b/bu4/parsing/extensions/xnull.py @@ -0,0 +1,16 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_NULL +from bu4.parsing.constructs.pnull import PNull +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter +from bu4.parsing.states.psfinal import PSFinal + +__all__ = ('XNull',) + + +class XNull(Extension, code=CODE_NULL): + def apply(self, state: PSAfter) -> ParseState: + state.state = PSFinal(PNull()) + return state diff --git a/bu4/parsing/extensions/xqopn.py b/bu4/parsing/extensions/xqopn.py new file mode 100644 index 0000000..87e5fbb --- /dev/null +++ b/bu4/parsing/extensions/xqopn.py @@ -0,0 +1,16 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_QOPN, CODE_QCLS +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter +from bu4.parsing.targets.pschain import PSChain +from bu4.parsing.targets.psendswith import PSEndsWith + +__all__ = ('XQopn',) + + +class XQopn(Extension, code=CODE_QOPN): + def apply(self, state: PSAfter) -> ParseState: + state.target = PSChain(PSEndsWith(self.parser, CODE_QCLS, 'quot expected'), state.target) + return state diff --git a/bu4/parsing/extensions/xquot.py b/bu4/parsing/extensions/xquot.py new file mode 100644 index 0000000..ae40053 --- /dev/null +++ b/bu4/parsing/extensions/xquot.py @@ -0,0 +1,16 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_QUOT +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter +from bu4.parsing.targets.pschain import PSChain +from bu4.parsing.targets.psendswith import PSEndsWith + +__all__ = ('XQuot',) + + +class XQuot(Extension, code=CODE_QUOT): + def apply(self, state: PSAfter) -> ParseState: + state.target = PSChain(PSEndsWith(self.parser, CODE_QUOT, 'quot expected'), state.target) + return state diff --git a/bu4/parsing/extensions/xskip.py b/bu4/parsing/extensions/xskip.py new file mode 100644 index 0000000..997fca9 --- /dev/null +++ b/bu4/parsing/extensions/xskip.py @@ -0,0 +1,13 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.codes import CODE_SKIP +from bu4.parsing.extensions.extension import Extension +from bu4.parsing.states.parsestate import ParseState +from bu4.parsing.states.psafter import PSAfter + +__all__ = ('XSkip',) + + +class XSkip(Extension, code=CODE_SKIP): + def apply(self, state: PSAfter) -> ParseState: + return state diff --git a/bu4/parsing/parser.py b/bu4/parsing/parser.py index 3b519a3..a26f3c5 100644 --- a/bu4/parsing/parser.py +++ b/bu4/parsing/parser.py @@ -1,34 +1,35 @@ # Copyright (c) PARRRATE T&V 2021. All rights reserved. from io import BytesIO +from typing import Iterable, Type from bu4.parsing.abstractparser import AbstractParser -from bu4.parsing.codes import * from bu4.parsing.constructs.parsed import Parsed -from bu4.parsing.constructs.plambda import PLambda -from bu4.parsing.constructs.pname import PName -from bu4.parsing.constructs.pnull import PNull +from bu4.parsing.extensions.extension import Extension from bu4.parsing.states.parsestate import ParseState from bu4.parsing.states.psafter import PSAfter -from bu4.parsing.states.pscall import pscall from bu4.parsing.states.psfinal import PSFinal from bu4.parsing.states.psread import PSRead from bu4.parsing.targets.pschain import PSChain -from bu4.parsing.targets.psendswith import PSEndsWith from bu4.parsing.targets.pslambda import PSLambda __all__ = ('Parser',) class Parser(AbstractParser): - def __init__(self, source: bytes): + def __init__(self, source: bytes, extensions: Iterable[Type[Extension]]): self.__source = BytesIO(source) + self.__extensions: dict[int, Extension] = {} + for extension in extensions: + if extension.code in self.__extensions: + raise ValueError('code overload') + self.__extensions[extension.code] = extension(self) def read(self) -> int: bytes_ = self.__source.read(1) return bytes_[0] if bytes_ else 0 - def _parse_name(self) -> bytes: + def parse_name(self) -> bytes: s = BytesIO() while True: c = self.read() @@ -37,29 +38,11 @@ class Parser(AbstractParser): s.write(bytes([c])) def _state_read(self, state: PSAfter) -> ParseState: - c = self.read() - if c == CODE_NULL: - state.state = PSFinal(PNull()) - elif c == CODE_CALL: - state.state = pscall() - elif c == CODE_MAKE: - name = self._parse_name() - state.state = PSAfter( - PSRead(), - PSLambda( - lambda value: PSFinal(PLambda(name, value)) - ) - ) - elif c == CODE_NAME: - state.state = PSFinal(PName(self._parse_name())) - elif c == CODE_SKIP: - pass - elif c == CODE_QUOT: - state.target = PSChain(PSEndsWith(self, CODE_QUOT, "quot expected"), state.target) - elif c == CODE_QOPN: - state.target = PSChain(PSEndsWith(self, CODE_QCLS, "qcls expected"), state.target) - else: - raise ValueError(f"unknown control: {hex(c)}") + code = self.read() + extension = self.__extensions.get(code) + if extension is None: + raise ValueError(f'unknown control: {hex(code)}') + state = extension.apply(state) return state def _state_next(self, state: PSAfter) -> ParseState: diff --git a/bu4/parsing/toolchain/parse.py b/bu4/parsing/toolchain/parse.py index e2ce877..0f60b3e 100644 --- a/bu4/parsing/toolchain/parse.py +++ b/bu4/parsing/toolchain/parse.py @@ -1,10 +1,16 @@ # Copyright (c) PARRRATE T&V 2021. All rights reserved. +from typing import Iterable, Type + from bu4.parsing.constructs.parsed import Parsed +from bu4.parsing.extensions.extension import Extension from bu4.parsing.parser import Parser __all__ = ('parse',) -def parse(source: bytes) -> Parsed: - return Parser(source).parse() +def parse(source: bytes, extensions: Iterable[Type[Extension]]) -> Parsed: + return Parser( + source, + extensions + ).parse() diff --git a/bu4/parsing/toolchain/stdext.py b/bu4/parsing/toolchain/stdext.py new file mode 100644 index 0000000..6342008 --- /dev/null +++ b/bu4/parsing/toolchain/stdext.py @@ -0,0 +1,21 @@ +# Copyright (c) PARRRATE T&V 2021. All rights reserved. + +from bu4.parsing.extensions.xcall import XCall +from bu4.parsing.extensions.xmake import XMake +from bu4.parsing.extensions.xname import XName +from bu4.parsing.extensions.xnull import XNull +from bu4.parsing.extensions.xqopn import XQopn +from bu4.parsing.extensions.xquot import XQuot +from bu4.parsing.extensions.xskip import XSkip + +__all__ = ('standard_extension',) + +standard_extension = ( + XNull, + XCall, + XMake, + XName, + XSkip, + XQuot, + XQopn, +) diff --git a/bu4/synced.py b/bu4/synced.py index 2ecb827..b402005 100644 --- a/bu4/synced.py +++ b/bu4/synced.py @@ -4,6 +4,7 @@ from bu4.evaluation.constructs.evalue import EValue from bu4.evaluation.sync import sync from bu4.linking.evaluable_from_parsed import evaluable_from_parsed from bu4.parsing.toolchain.parse import parse +from bu4.parsing.toolchain.stdext import standard_extension from bu4.parsing.toolchain.transply import transply __all__ = ('synced',) @@ -11,6 +12,6 @@ __all__ = ('synced',) def synced(source: str) -> EValue: bsource = transply(source) - parsed = parse(bsource) + parsed = parse(bsource, standard_extension) evaluable = evaluable_from_parsed({}, parsed) return sync(evaluable) diff --git a/manual/extensions.md b/manual/extensions.md new file mode 100644 index 0000000..2ab5143 --- /dev/null +++ b/manual/extensions.md @@ -0,0 +1,26 @@ +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