parser extensions
This commit is contained in:
parent
03d46253c5
commit
e3ca0d396d
@ -18,7 +18,7 @@ class LLambda(Linked):
|
|||||||
|
|
||||||
def evaluable(self, env: envtype) -> Evaluable:
|
def evaluable(self, env: envtype) -> Evaluable:
|
||||||
return ELambda(
|
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.name,
|
||||||
self.value
|
self.value
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
# Copyright (c) PARRRATE T&V 2021. All rights reserved.
|
# Copyright (c) PARRRATE T&V 2021. All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('AbstractParser',)
|
__all__ = ('AbstractParser',)
|
||||||
|
|
||||||
|
|
||||||
class AbstractParser:
|
class AbstractParser:
|
||||||
def read(self) -> int:
|
def read(self) -> int:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def parse_name(self) -> bytes:
|
||||||
|
raise NotImplementedError
|
||||||
|
23
bu4/parsing/extensions/extension.py
Normal file
23
bu4/parsing/extensions/extension.py
Normal file
@ -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
|
15
bu4/parsing/extensions/xcall.py
Normal file
15
bu4/parsing/extensions/xcall.py
Normal file
@ -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
|
25
bu4/parsing/extensions/xmake.py
Normal file
25
bu4/parsing/extensions/xmake.py
Normal file
@ -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
|
16
bu4/parsing/extensions/xname.py
Normal file
16
bu4/parsing/extensions/xname.py
Normal file
@ -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
|
16
bu4/parsing/extensions/xnull.py
Normal file
16
bu4/parsing/extensions/xnull.py
Normal file
@ -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
|
16
bu4/parsing/extensions/xqopn.py
Normal file
16
bu4/parsing/extensions/xqopn.py
Normal file
@ -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
|
16
bu4/parsing/extensions/xquot.py
Normal file
16
bu4/parsing/extensions/xquot.py
Normal file
@ -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
|
13
bu4/parsing/extensions/xskip.py
Normal file
13
bu4/parsing/extensions/xskip.py
Normal file
@ -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
|
@ -1,34 +1,35 @@
|
|||||||
# Copyright (c) PARRRATE T&V 2021. All rights reserved.
|
# Copyright (c) PARRRATE T&V 2021. All rights reserved.
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from typing import Iterable, Type
|
||||||
|
|
||||||
from bu4.parsing.abstractparser import AbstractParser
|
from bu4.parsing.abstractparser import AbstractParser
|
||||||
from bu4.parsing.codes import *
|
|
||||||
from bu4.parsing.constructs.parsed import Parsed
|
from bu4.parsing.constructs.parsed import Parsed
|
||||||
from bu4.parsing.constructs.plambda import PLambda
|
from bu4.parsing.extensions.extension import Extension
|
||||||
from bu4.parsing.constructs.pname import PName
|
|
||||||
from bu4.parsing.constructs.pnull import PNull
|
|
||||||
from bu4.parsing.states.parsestate import ParseState
|
from bu4.parsing.states.parsestate import ParseState
|
||||||
from bu4.parsing.states.psafter import PSAfter
|
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.psfinal import PSFinal
|
||||||
from bu4.parsing.states.psread import PSRead
|
from bu4.parsing.states.psread import PSRead
|
||||||
from bu4.parsing.targets.pschain import PSChain
|
from bu4.parsing.targets.pschain import PSChain
|
||||||
from bu4.parsing.targets.psendswith import PSEndsWith
|
|
||||||
from bu4.parsing.targets.pslambda import PSLambda
|
from bu4.parsing.targets.pslambda import PSLambda
|
||||||
|
|
||||||
__all__ = ('Parser',)
|
__all__ = ('Parser',)
|
||||||
|
|
||||||
|
|
||||||
class Parser(AbstractParser):
|
class Parser(AbstractParser):
|
||||||
def __init__(self, source: bytes):
|
def __init__(self, source: bytes, extensions: Iterable[Type[Extension]]):
|
||||||
self.__source = BytesIO(source)
|
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:
|
def read(self) -> int:
|
||||||
bytes_ = self.__source.read(1)
|
bytes_ = self.__source.read(1)
|
||||||
return bytes_[0] if bytes_ else 0
|
return bytes_[0] if bytes_ else 0
|
||||||
|
|
||||||
def _parse_name(self) -> bytes:
|
def parse_name(self) -> bytes:
|
||||||
s = BytesIO()
|
s = BytesIO()
|
||||||
while True:
|
while True:
|
||||||
c = self.read()
|
c = self.read()
|
||||||
@ -37,29 +38,11 @@ class Parser(AbstractParser):
|
|||||||
s.write(bytes([c]))
|
s.write(bytes([c]))
|
||||||
|
|
||||||
def _state_read(self, state: PSAfter) -> ParseState:
|
def _state_read(self, state: PSAfter) -> ParseState:
|
||||||
c = self.read()
|
code = self.read()
|
||||||
if c == CODE_NULL:
|
extension = self.__extensions.get(code)
|
||||||
state.state = PSFinal(PNull())
|
if extension is None:
|
||||||
elif c == CODE_CALL:
|
raise ValueError(f'unknown control: {hex(code)}')
|
||||||
state.state = pscall()
|
state = extension.apply(state)
|
||||||
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)}")
|
|
||||||
return state
|
return state
|
||||||
|
|
||||||
def _state_next(self, state: PSAfter) -> ParseState:
|
def _state_next(self, state: PSAfter) -> ParseState:
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
# Copyright (c) PARRRATE T&V 2021. All rights reserved.
|
# Copyright (c) PARRRATE T&V 2021. All rights reserved.
|
||||||
|
|
||||||
|
from typing import Iterable, Type
|
||||||
|
|
||||||
from bu4.parsing.constructs.parsed import Parsed
|
from bu4.parsing.constructs.parsed import Parsed
|
||||||
|
from bu4.parsing.extensions.extension import Extension
|
||||||
from bu4.parsing.parser import Parser
|
from bu4.parsing.parser import Parser
|
||||||
|
|
||||||
__all__ = ('parse',)
|
__all__ = ('parse',)
|
||||||
|
|
||||||
|
|
||||||
def parse(source: bytes) -> Parsed:
|
def parse(source: bytes, extensions: Iterable[Type[Extension]]) -> Parsed:
|
||||||
return Parser(source).parse()
|
return Parser(
|
||||||
|
source,
|
||||||
|
extensions
|
||||||
|
).parse()
|
||||||
|
21
bu4/parsing/toolchain/stdext.py
Normal file
21
bu4/parsing/toolchain/stdext.py
Normal file
@ -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,
|
||||||
|
)
|
@ -4,6 +4,7 @@ from bu4.evaluation.constructs.evalue import EValue
|
|||||||
from bu4.evaluation.sync import sync
|
from bu4.evaluation.sync import sync
|
||||||
from bu4.linking.evaluable_from_parsed import evaluable_from_parsed
|
from bu4.linking.evaluable_from_parsed import evaluable_from_parsed
|
||||||
from bu4.parsing.toolchain.parse import parse
|
from bu4.parsing.toolchain.parse import parse
|
||||||
|
from bu4.parsing.toolchain.stdext import standard_extension
|
||||||
from bu4.parsing.toolchain.transply import transply
|
from bu4.parsing.toolchain.transply import transply
|
||||||
|
|
||||||
__all__ = ('synced',)
|
__all__ = ('synced',)
|
||||||
@ -11,6 +12,6 @@ __all__ = ('synced',)
|
|||||||
|
|
||||||
def synced(source: str) -> EValue:
|
def synced(source: str) -> EValue:
|
||||||
bsource = transply(source)
|
bsource = transply(source)
|
||||||
parsed = parse(bsource)
|
parsed = parse(bsource, standard_extension)
|
||||||
evaluable = evaluable_from_parsed({}, parsed)
|
evaluable = evaluable_from_parsed({}, parsed)
|
||||||
return sync(evaluable)
|
return sync(evaluable)
|
||||||
|
26
manual/extensions.md
Normal file
26
manual/extensions.md
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user