Initial deeply busted Lilith state for LangJam
This commit is contained in:
parent
b52e0488d7
commit
9514e9ddae
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