Initial deeply busted Lilith state for LangJam
This commit is contained in:
parent
2b101bf02c
commit
43bbcda050
7 changed files with 270 additions and 0 deletions
9
projects/lilith/BUILD
Normal file
9
projects/lilith/BUILD
Normal file
|
@ -0,0 +1,9 @@
|
|||
py_project(
|
||||
name = "lilith",
|
||||
lib_deps = [
|
||||
|
||||
],
|
||||
test_deps = [
|
||||
py_requirement("hypothesis"),
|
||||
]
|
||||
)
|
81
projects/lilith/README.md
Normal file
81
projects/lilith/README.md
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Lilith
|
||||
|
||||
> The theme of the jam is "first-class comments".
|
||||
|
||||
```c
|
||||
// foo bar baz
|
||||
```
|
||||
|
||||
``` clojure
|
||||
; text that the parser throws away
|
||||
|
||||
(def <name> <value>)
|
||||
(def ^{:doc "foo bar baz"} <name> <value>)
|
||||
```
|
||||
|
||||
``` python
|
||||
"""modules have __doc__ as a property defined by the FIRST heredoc-string in the file"""
|
||||
|
||||
def foo(a, b):
|
||||
"""Adds a and b.
|
||||
|
||||
>>> foo(1, 2)
|
||||
3
|
||||
"""
|
||||
|
||||
return a + b
|
||||
|
||||
```
|
||||
|
||||
``` clojure
|
||||
(defn foo [a b]
|
||||
"Adds a and b
|
||||
|
||||
>>> (foo 1 2)
|
||||
=> 3
|
||||
"
|
||||
(+ a b))
|
||||
```
|
||||
|
||||
- Object code
|
||||
- .... Other media?
|
||||
|
||||
``` clojure
|
||||
(defn foo [a b]
|
||||
(!doc "")
|
||||
(!doctest "")
|
||||
(!test "")
|
||||
|
||||
(!impl
|
||||
(+ a b)))
|
||||
```
|
||||
|
||||
``` clojure
|
||||
(defn foo [a b]
|
||||
"---
|
||||
doc: |
|
||||
Adds a and b
|
||||
|
||||
---
|
||||
doctest:
|
||||
- in: (foo 1 2)
|
||||
out: 3
|
||||
"
|
||||
)
|
||||
```
|
||||
|
||||
``` org
|
||||
#+TITLE: foo
|
||||
* my bullet
|
||||
#+BEGIN_SRC clojure
|
||||
(defn foo [a b] (+ a b))
|
||||
#+END_SRC
|
||||
```
|
||||
|
||||
`org-babel` `org-babel-tagle`
|
||||
|
||||
``` clojure
|
||||
(defn foo [] ..)
|
||||
```
|
||||
|
||||
####################################################################################################
|
20
projects/lilith/setup.py
Normal file
20
projects/lilith/setup.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="arrdem.lilith",
|
||||
# Package metadata
|
||||
version="0.0.0",
|
||||
license="MIT",
|
||||
# Package setup
|
||||
package_dir={"": "src/python"},
|
||||
packages=[
|
||||
"lilith",
|
||||
],
|
||||
install_requires=[
|
||||
"lark",
|
||||
],
|
||||
test_requires=[
|
||||
"pytest",
|
||||
"hypothesis",
|
||||
],
|
||||
)
|
47
projects/lilith/src/lilith/designdoc.lil
Normal file
47
projects/lilith/src/lilith/designdoc.lil
Normal file
|
@ -0,0 +1,47 @@
|
|||
!defscope[designdoc]
|
||||
!scope[designdoc]
|
||||
|
||||
!def[pitch]
|
||||
!frag[lang: md]
|
||||
|
||||
# The Lilith Pitch
|
||||
|
||||
Code is more than .. just code for the compiler.
|
||||
We write lots of artifacts related to our software
|
||||
- Design document
|
||||
- Formatted API listing
|
||||
- Formatted book which is MORE than just the API / source listing
|
||||
- A spec? OpenAPI example
|
||||
- Doctests? / inline test cases
|
||||
|
||||
Theory - most of your "program" isn't code.
|
||||
idea: the parser is going to IGNORE stuff by default.
|
||||
|
||||
Let's build an M-expression syntax with support for multiple co-equal languages and fragments/scopes with a meta-syntax allowing for references between them and consuming fragment-notation-defined blocks? (FIXME: better word?)
|
||||
|
||||
!def[sytnax]
|
||||
!frag[lang: md]
|
||||
|
||||
\![] bang-brackets is a meta-syntactic directive used both by the lil tangler and the lil object language.
|
||||
|
||||
Programs consist of many fragments, which can reference each-other and from which executable framents are woven.
|
||||
|
||||
Fragments define interpretations and scopes.
|
||||
|
||||
Interpretations control sub-languages, the bang-bracket notation is more of a meta-language than a target language.
|
||||
Interpretation is defined by the `lang:` key of a bang-bracket.
|
||||
|
||||
FIXME: how to define more langs?
|
||||
|
||||
FIXME: how do scopes get defined?
|
||||
\!defscope[name: <>]
|
||||
|
||||
FIXME: we need bare words, we need strings
|
||||
|
||||
FIXME: We need the object language
|
||||
|
||||
!def[main]
|
||||
!frag[lang: lil]
|
||||
; is importing a bang-operation?
|
||||
import[tagle]
|
||||
print[tangle[pitch, syntax]]
|
84
projects/lilith/src/python/lilith/parser.py
Normal file
84
projects/lilith/src/python/lilith/parser.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
"""
|
||||
Variously poor parsing for Lilith.
|
||||
"""
|
||||
|
||||
import typing as t
|
||||
import re
|
||||
|
||||
import lark
|
||||
|
||||
class Block(t.NamedTuple):
|
||||
tag: str
|
||||
args: list
|
||||
kwargs: list
|
||||
body_lines: list
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return "\n".join(self.body_lines)
|
||||
|
||||
|
||||
class TreeToTuples(lark.Transformer):
|
||||
def kwargs(self, args):
|
||||
d = {}
|
||||
key, val = args[0:2]
|
||||
if len(args) == 3:
|
||||
d.update(args[2])
|
||||
d[key.value] = val.value
|
||||
return d
|
||||
|
||||
def args(self, args):
|
||||
_args = [args[0].value]
|
||||
if len(args) == 2:
|
||||
_args = _args + args[1]
|
||||
return _args
|
||||
|
||||
def header(self, parse_args):
|
||||
print("Header", parse_args)
|
||||
tag = None
|
||||
args = None
|
||||
kwargs = None
|
||||
body = []
|
||||
|
||||
iargs = iter(parse_args[1])
|
||||
tag = parse_args[0]
|
||||
v = next(iargs, None)
|
||||
if isinstance(v, list):
|
||||
args = v
|
||||
v = next(iargs, None)
|
||||
if isinstance(v, dict):
|
||||
kwargs = v
|
||||
|
||||
return Block(tag, args, kwargs, body)
|
||||
|
||||
|
||||
block_grammar = lark.Lark("""
|
||||
%import common.WORD
|
||||
%import common.WS
|
||||
%ignore WS
|
||||
?start: header
|
||||
|
||||
args: WORD ("," args)?
|
||||
kwargs: WORD ":" WORD ("," kwargs)?
|
||||
arguments: args "," kwargs | args | kwargs
|
||||
header: "!" WORD "[" arguments? "]"
|
||||
""",
|
||||
parser='lalr',
|
||||
transformer=TreeToTuples())
|
||||
|
||||
|
||||
|
||||
def shotgun_parse(buff: str) -> t.List[object]:
|
||||
def _parse():
|
||||
block = None
|
||||
for line in buff.splitlines():
|
||||
if line.startswith("!"):
|
||||
if block:
|
||||
yield block
|
||||
block = [line]
|
||||
else:
|
||||
block.append(line)
|
||||
if block:
|
||||
yield block
|
||||
|
||||
return list(_parse())
|
3
projects/lilith/test/python/conftest.py
Normal file
3
projects/lilith/test/python/conftest.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Pytest fixtures.
|
||||
"""
|
26
projects/lilith/test/python/test_parser.py
Normal file
26
projects/lilith/test/python/test_parser.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""tests covering the Lilith parser."""
|
||||
|
||||
from lilith.parser import block_grammar, Block
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.mark.parametrize('example, result', [
|
||||
('!def[syntax]',
|
||||
Block('def', ['syntax'], None, [])),
|
||||
('!frag[lang: md]',
|
||||
Block('frag', None, {'lang': 'md'}, [])),
|
||||
('!frag[foo, lang: md]',
|
||||
Block('frag', ['foo'], {'lang': 'md'}, [])),
|
||||
])
|
||||
def test_parse_header(example, result):
|
||||
assert block_grammar.parse(example) == result
|
||||
|
||||
|
||||
(
|
||||
"""!def[designdoc]
|
||||
!frag[lang: md]
|
||||
# Designdoc
|
||||
|
||||
A design document""",
|
||||
None
|
||||
)
|
Loading…
Reference in a new issue