hissp.compiler module¶
The Hissp data-structure language compiler and associated helper functions.
- hissp.compiler.NS = <ContextVar name='NS' default=()>¶
Sometimes macros need the current namespace when expanding, instead of its defining namespace. Rather than pass in an implicit argument to all macros, it’s available here.
readerless
uses this automatically.
- exception hissp.compiler.CompileError¶
Bases:
SyntaxError
Catch-all exception for compilation failures.
- exception hissp.compiler.PostCompileWarning¶
Bases:
Warning
Form compiled to Python, but execution of it failed.
- class hissp.compiler.Compiler(qualname='__main__', ns=None, evaluate=True)¶
Bases:
object
The Hissp recursive-descent compiler.
Translates the Hissp data-structure language into a functional subset of Python.
- static new_ns(name, doc=None, package=None)¶
Creates and initializes a dict namespace like a module, with the given
__name__
,__doc__
, and__package__
;__builtins__
; an empty__annotations__
; and whatever else Python currently adds to new module objects.
- form(form) str ¶
Compile Hissp form to the equivalent Python code in a string.
tuple
andstr
have special evaluation rules, otherwise it’s anatom
that represents itself.
- special(form: Tuple) str ¶
Try to compile as special form, else
invocation
.
- function(form: Tuple) str ¶
Compile the anonymous function special form.
- (lambda (<parameters>)
<body>)
The parameters tuple is divided into (<single> : <paired>)
Parameter types are the same as Python’s. For example,
>>> readerless( ... ('lambda', ('a',':/','b', ... ':', 'e',1, 'f',2, ... ':*','args', 'h',4, 'i',':?', 'j',1, ... ':**','kwargs',), ... 42,), ... ) '(lambda a,/,b,e=(1),f=(2),*args,h=(4),i,j=(1),**kwargs:(42))'
The special control words
:*
and:**
designate the remainder of the positional and keyword parameters, respectively. Note this body evaluates expressions in sequence, for side effects.>>> print(readerless( ... ('lambda', (':',':*','args',':**','kwargs',), ... ('print','args',), ... ('print','kwargs',),), ... )) (lambda *args,**kwargs:( print( args), print( kwargs))[-1])
You can omit the right of a pair with
:?
(except the final**kwargs
). Also note that the body can be empty.>>> readerless( ... ('lambda', (':','a',1, ':/',':?', ':*',':?', 'b',':?', 'c',2,),), ... ) '(lambda a=(1),/,*,b,c=(2):())'
The
:
may be omitted if there are no paired parameters.>>> readerless(('lambda', ('a','b','c',':',),),) '(lambda a,b,c:())' >>> readerless(('lambda', ('a','b','c',),),) '(lambda a,b,c:())' >>> readerless(('lambda', (':',),),) '(lambda :())' >>> readerless(('lambda', (),),) '(lambda :())'
The
:
is required if there are any paired parameters, even if there are no single parameters:>>> readerless(('lambda', (':',':**','kwargs',),),) '(lambda **kwargs:())'
- call(form: Iterable) str ¶
Compile call form.
Any tuple that is not quoted,
()
, or aspecial
form ormacro
is a run-time call.Like Python, it has three parts: (<callable> <args> : <kwargs>). For example:
>>> print(readerless( ... ('print',1,2,3, ... ':','sep',('quote',":",), 'end',('quote',"\n\n",),) ... )) print( (1), (2), (3), sep=':', end='\n\n')
Either <args> or <kwargs> may be empty:
>>> readerless(('foo',':',),) 'foo()' >>> print(readerless(('foo','bar',':',),)) foo( bar) >>> print(readerless(('foo',':','bar','baz',),)) foo( bar=baz)
The
:
is optional if the <kwargs> part is empty:>>> readerless(('foo',),) 'foo()' >>> print(readerless(('foo','bar',),),) foo( bar)
The <kwargs> part has implicit pairs; there must be an even number.
Use the control words
:*
and:**
for iterable and mapping unpacking:>>> print(readerless( ... ('print',':',':*',[1,2], 'a',3, ':*',[4], ':**',{'sep':':','end':'\n\n'},), ... )) print( *[1, 2], a=(3), *[4], **{'sep': ':', 'end': '\n\n'})
Unlike other control words, these can be repeated, but (as in Python) a ‘*’ is not allowed to follow ‘**’.
Method calls are similar to function calls: (.<method name> <object> <args> : <kwargs>) Like Clojure, a method on the first object is assumed if the function name starts with a dot:
>>> readerless(('.conjugate', 1j,),) '(1j).conjugate()' >>> eval(_) -1j >>> readerless(('.decode', b'\xfffoo', ':', 'errors',('quote','ignore',),),) "b'\\xfffoo'.decode(\n errors='ignore')" >>> eval(_) 'foo'
- str(code: str) str ¶
Compile code strings. Expands qualified identifiers and module literals into imports. Otherwise, injects as raw Python directly into the output.
- qualified_identifier(code)¶
Compile qualified identifier into import and attribute.
- module_identifier(code)¶
Compile module identifier to import.
- atom(form) hissp.compiler.Compiler.str ¶
Compile forms that evaluate to themselves.
Emits a literal if possible, otherwise falls back to
pickle
:>>> readerless(-4.2j) '((-0-4.2j))' >>> print(readerless(float('nan'))) __import__('pickle').loads( # nan b'Fnan\n.' ) >>> readerless([{'foo':2},(),1j,2.0,{3}]) "[{'foo': 2}, (), 1j, 2.0, {3}]" >>> spam = [] >>> spam.append(spam) # ref cycle can't be a literal >>> print(readerless(spam)) __import__('pickle').loads( # [[...]] b'(lp0\ng0\na.' ) >>> spam = [[]] * 3 # duplicated refs >>> print(readerless(spam)) __import__('pickle').loads( # [[], [], []] b'(l(lp0\nag0\nag0\na.' )
- pickle(form) hissp.compiler.Compiler.str ¶
Compile to
pickle.loads
. The final fallback foratom
.
- eval(form: hissp.compiler.Compiler.str) Tuple[hissp.compiler.Compiler.str, ...] ¶
Execute compiled form, but only if evaluate mode is enabled.