Somewhat working launcher with prelude!
This commit is contained in:
parent
d2309e1ac4
commit
8dd071625f
11 changed files with 284 additions and 41 deletions
|
@ -2,6 +2,8 @@ py_project(
|
||||||
name = "lib",
|
name = "lib",
|
||||||
lib_deps = [
|
lib_deps = [
|
||||||
py_requirement("lark"),
|
py_requirement("lark"),
|
||||||
|
py_requirement("pyyaml"),
|
||||||
|
py_requirement("markdown"),
|
||||||
],
|
],
|
||||||
test_deps = [
|
test_deps = [
|
||||||
py_requirement("hypothesis"),
|
py_requirement("hypothesis"),
|
||||||
|
@ -13,7 +15,10 @@ zapp_binary(
|
||||||
main = "src/python/lilith/__main__.py",
|
main = "src/python/lilith/__main__.py",
|
||||||
deps = [
|
deps = [
|
||||||
":lib",
|
":lib",
|
||||||
py_requirement("lark"), # FIXME: Absolutely a zapp bug
|
# FIXME: Due to zapp bug(s), replicating requirements here.
|
||||||
|
py_requirement("lark"),
|
||||||
|
py_requirement("pyyaml"),
|
||||||
|
py_requirement("markdown"),
|
||||||
py_requirement("prompt_toolkit"),
|
py_requirement("prompt_toolkit"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
!def[pitch, frag[lang: md]]
|
!def[pitch, md[]]
|
||||||
# The Lilith Pitch
|
# The Lilith Pitch
|
||||||
|
|
||||||
Code is more than .. just code for the compiler.
|
Code is more than .. just code for the compiler.
|
||||||
|
@ -14,8 +14,7 @@ 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?)
|
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]
|
!def[sytnax, md[]]
|
||||||
!frag[lang: md]
|
|
||||||
|
|
||||||
\![] bang-brackets is a meta-syntactic directive used both by the lil tangler and the lil object language.
|
\![] bang-brackets is a meta-syntactic directive used both by the lil tangler and the lil object language.
|
||||||
|
|
||||||
|
@ -35,10 +34,9 @@ FIXME: we need bare words, we need strings
|
||||||
|
|
||||||
FIXME: We need the object language
|
FIXME: We need the object language
|
||||||
|
|
||||||
!def[openapi, frag[lang: yaml]]
|
!def[openapi, yaml[]]
|
||||||
|
|
||||||
!def[main, frag[lang: lil]]
|
!def[main, lilith[]]
|
||||||
; is importing a bang-operation?
|
; is importing a bang-operation?
|
||||||
|
|
||||||
import[tagle]
|
print[str.join["", [pitch, syntax]]]
|
||||||
print[str.join["", list[pitch, syntax]]]
|
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
"""A simple Lilith shell."""
|
"""The Lilith runner."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from importlib.resources import read_text as resource_text
|
||||||
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from lilith.interpreter import Bindings, eval, Runtime
|
from lilith.interpreter import Bindings, eval, Runtime
|
||||||
from lilith.parser import Apply, Args, parse_expr
|
from lilith.parser import Apply, Args, parse_expr, Symbol
|
||||||
from lilith.reader import Module
|
from lilith.reader import Import, Module, read_buffer, read_file
|
||||||
|
|
||||||
from prompt_toolkit import print_formatted_text, prompt, PromptSession
|
from prompt_toolkit import print_formatted_text, prompt, PromptSession
|
||||||
from prompt_toolkit.formatted_text import FormattedText
|
from prompt_toolkit.formatted_text import FormattedText
|
||||||
from prompt_toolkit.history import FileHistory
|
from prompt_toolkit.history import FileHistory
|
||||||
from prompt_toolkit.styles import Style
|
from prompt_toolkit.styles import Style
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
STYLE = Style.from_dict(
|
STYLE = Style.from_dict(
|
||||||
|
@ -27,17 +30,13 @@ def print_(fmt, **kwargs):
|
||||||
print_formatted_text(FormattedText(fmt), **kwargs)
|
print_formatted_text(FormattedText(fmt), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def repl(opts, args, runtime):
|
||||||
|
"""Run an interactive Lilith repl, supporting only the Lilith language itself."""
|
||||||
|
|
||||||
session = PromptSession(history=FileHistory(".lilith.history"))
|
session = PromptSession(history=FileHistory(".lilith.history"))
|
||||||
runtime = Runtime("test", dict())
|
|
||||||
module = Module(
|
module = Module(
|
||||||
"__repl__",
|
"__repl__",
|
||||||
{
|
{},
|
||||||
"print": print,
|
|
||||||
"int": int,
|
|
||||||
"string": str,
|
|
||||||
"float": float,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -60,3 +59,88 @@ if __name__ == "__main__":
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def batch(opts, args, runtime):
|
||||||
|
"""Run a Lilith program 'for real' in non-interactive batch mode."""
|
||||||
|
|
||||||
|
if opts.file:
|
||||||
|
mod = read_file(opts.file)
|
||||||
|
main = "main"
|
||||||
|
elif opts.main:
|
||||||
|
mod, main = opts.main.split(":") if ":" in opts.main else opts.main, "main"
|
||||||
|
dirmod, rname = ".".join(mod.split(".")[:-1]), mod.split(".")[-1] + ".lil"
|
||||||
|
mod = read_buffer(resource_text(dirmod, rname), mod)
|
||||||
|
|
||||||
|
main = Symbol(main)
|
||||||
|
|
||||||
|
# Register
|
||||||
|
runtime.modules[mod.name] = mod
|
||||||
|
|
||||||
|
print("DEBUG: batch mode")
|
||||||
|
print(
|
||||||
|
yaml.dump(
|
||||||
|
{
|
||||||
|
"type": "runtime",
|
||||||
|
"name": runtime.name,
|
||||||
|
"modules": {
|
||||||
|
m.name: {
|
||||||
|
"defs": {dname.name: repr(d) for dname, d in m.defs.items()}
|
||||||
|
}
|
||||||
|
for m in runtime.modules.values()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if main in mod.defs:
|
||||||
|
eval(runtime, mod, Bindings(main, None), mod.defs.get(main))
|
||||||
|
else:
|
||||||
|
raise NameError(f"entry point {main} not found in {mod.name.name}")
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"-m", "--main", default="main", help="The name of an entry point eg <module>:<name>"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-p",
|
||||||
|
"--path",
|
||||||
|
default=[],
|
||||||
|
action="append",
|
||||||
|
help="Append something to the module path.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--prelude", default="lilith.prelude", help="Select a module prelude."
|
||||||
|
)
|
||||||
|
parser.add_argument("file", nargs="?", help="A file to start executing from")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
opts, args = parser.parse_known_args()
|
||||||
|
|
||||||
|
# Bash anything the user says is the path onto the PYTHONPATH so we can use importlib for our loading machinery.
|
||||||
|
for e in opts.path:
|
||||||
|
for _e in e.split(":"):
|
||||||
|
sys.path.insert(0, _e)
|
||||||
|
|
||||||
|
# Building up a bootstrap interface for going out to many Python features.
|
||||||
|
runtime = Runtime("__runtime__", Symbol(opts.prelude), {})
|
||||||
|
bootstrap = Module(
|
||||||
|
"lilith.bootstrap",
|
||||||
|
[],
|
||||||
|
{Symbol("py"): lambda *args, body=None, **kwargs: eval(body)},
|
||||||
|
)
|
||||||
|
runtime.modules[Symbol(bootstrap.name)] = bootstrap
|
||||||
|
prelude_mod = read_buffer(
|
||||||
|
resource_text(
|
||||||
|
".".join(opts.prelude.split(".")[:-1]), opts.prelude.split(".")[-1] + ".lil"
|
||||||
|
),
|
||||||
|
opts.prelude,
|
||||||
|
)
|
||||||
|
runtime.modules[prelude_mod.name] = prelude_mod
|
||||||
|
|
||||||
|
if (opts.path and opts.main) or opts.file:
|
||||||
|
batch(opts, args, runtime)
|
||||||
|
else:
|
||||||
|
repl(opts, args, runtime)
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
"""
|
"""
|
||||||
|
A quick and dirty recursive interpreter for Lilith.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from .parser import Apply, Block, Symbol
|
from lilith.parser import Apply, Block, Symbol
|
||||||
from .reader import Module
|
from lilith.reader import Module
|
||||||
|
|
||||||
|
|
||||||
class Runtime(t.NamedTuple):
|
class Runtime(t.NamedTuple):
|
||||||
name: str
|
name: str
|
||||||
modules: t.Dict[str, Module]
|
prelude: Symbol
|
||||||
|
modules: t.Dict[Symbol, Module]
|
||||||
|
|
||||||
|
|
||||||
class BindingNotFound(KeyError):
|
class BindingNotFound(KeyError):
|
||||||
|
|
129
projects/lilith/src/python/lilith/prelude.lil
Normal file
129
projects/lilith/src/python/lilith/prelude.lil
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
!import[lilith.bootstrap, [lang, py]]
|
||||||
|
!def[abs, lang[py]]
|
||||||
|
abs
|
||||||
|
!def[delattr, lang[py]]
|
||||||
|
delattr
|
||||||
|
!def[hash, lang[py]]
|
||||||
|
hash
|
||||||
|
!def[set, lang[py]]
|
||||||
|
set
|
||||||
|
!def[all, lang[py]]
|
||||||
|
all
|
||||||
|
!def[dict, lang[py]]
|
||||||
|
dict
|
||||||
|
!def[min, lang[py]]
|
||||||
|
min
|
||||||
|
!def[any, lang[py]]
|
||||||
|
any
|
||||||
|
!def[dir, lang[py]]
|
||||||
|
dir
|
||||||
|
!def[hex, lang[py]]
|
||||||
|
hex
|
||||||
|
!def[next, lang[py]]
|
||||||
|
next
|
||||||
|
!def[slice, lang[py]]
|
||||||
|
slice
|
||||||
|
!def[ascii, lang[py]]
|
||||||
|
ascii
|
||||||
|
!def[divmod, lang[py]]
|
||||||
|
divmod
|
||||||
|
!def[id, lang[py]]
|
||||||
|
id
|
||||||
|
!def[object, lang[py]]
|
||||||
|
object
|
||||||
|
!def[sorted, lang[py]]
|
||||||
|
sorted
|
||||||
|
!def[bin, lang[py]]
|
||||||
|
bin
|
||||||
|
!def[enumerate, lang[py]]
|
||||||
|
enumerate
|
||||||
|
!def[input, lang[py]]
|
||||||
|
input
|
||||||
|
!def[oct, lang[py]]
|
||||||
|
oct
|
||||||
|
!def[staticmethod, lang[py]]
|
||||||
|
staticmethod
|
||||||
|
!def[bool, lang[py]]
|
||||||
|
bool
|
||||||
|
!def[eval, lang[py]]
|
||||||
|
eval
|
||||||
|
!def[int, lang[py]]
|
||||||
|
int
|
||||||
|
!def[open, lang[py]]
|
||||||
|
open
|
||||||
|
!def[str, lang[py]]
|
||||||
|
str
|
||||||
|
!def[breakpoint, lang[py]]
|
||||||
|
breakpoint
|
||||||
|
!def[exec, lang[py]]
|
||||||
|
exec
|
||||||
|
!def[isinstance, lang[py]]
|
||||||
|
isinstance
|
||||||
|
!def[ord, lang[py]]
|
||||||
|
ord
|
||||||
|
!def[sum, lang[py]]
|
||||||
|
sum
|
||||||
|
!def[bytearray, lang[py]]
|
||||||
|
bytearray
|
||||||
|
!def[filter, lang[py]]
|
||||||
|
filter
|
||||||
|
!def[issubclass, lang[py]]
|
||||||
|
issubclass
|
||||||
|
!def[pow, lang[py]]
|
||||||
|
pow
|
||||||
|
!def[super, lang[py]]
|
||||||
|
super
|
||||||
|
!def[bytes, lang[py]]
|
||||||
|
bytes
|
||||||
|
!def[float, lang[py]]
|
||||||
|
float
|
||||||
|
!def[iter, lang[py]]
|
||||||
|
iter
|
||||||
|
!def[print, lang[py]]
|
||||||
|
print
|
||||||
|
!def[tuple, lang[py]]
|
||||||
|
tuple
|
||||||
|
!def[callable, lang[py]]
|
||||||
|
callable
|
||||||
|
!def[format, lang[py]]
|
||||||
|
format
|
||||||
|
!def[len, lang[py]]
|
||||||
|
len
|
||||||
|
!def[property, lang[py]]
|
||||||
|
property
|
||||||
|
!def[type, lang[py]]
|
||||||
|
type
|
||||||
|
!def[chr, lang[py]]
|
||||||
|
chr
|
||||||
|
!def[frozenset, lang[py]]
|
||||||
|
frozenset
|
||||||
|
!def[list, lang[py]]
|
||||||
|
list
|
||||||
|
!def[range, lang[py]]
|
||||||
|
range
|
||||||
|
!def[vars, lang[py]]
|
||||||
|
vars
|
||||||
|
!def[classmethod, lang[py]]
|
||||||
|
classmethod
|
||||||
|
!def[getattr, lang[py]]
|
||||||
|
getattr
|
||||||
|
!def[locals, lang[py]]
|
||||||
|
locals
|
||||||
|
!def[repr, lang[py]]
|
||||||
|
repr
|
||||||
|
!def[zip, lang[py]]
|
||||||
|
zip
|
||||||
|
!def[compile, lang[py]]
|
||||||
|
compile
|
||||||
|
!def[map, lang[py]]
|
||||||
|
map
|
||||||
|
!def[reversed, lang[py]]
|
||||||
|
reversed
|
||||||
|
!def[complex, lang[py]]
|
||||||
|
complex
|
||||||
|
!def[hasattr, lang[py]]
|
||||||
|
hasattr
|
||||||
|
!def[max, lang[py]]
|
||||||
|
max
|
||||||
|
!def[round, lang[py]]
|
||||||
|
round
|
|
@ -4,21 +4,30 @@ Lilith's reader takes parsed blocks and applies languages, building a module str
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import typing as t
|
import typing as t
|
||||||
from .parser import Block, Args, parse_buffer, Symbol
|
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
|
from lilith.parser import Args, Block, parse_buffer, Symbol
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Import(t.NamedTuple):
|
||||||
|
src: Symbol
|
||||||
|
names: t.Dict[Symbol, Symbol]
|
||||||
|
wild: bool = False
|
||||||
|
|
||||||
|
|
||||||
class Module(t.NamedTuple):
|
class Module(t.NamedTuple):
|
||||||
name: str
|
name: Symbol
|
||||||
|
imports: t.List[Import]
|
||||||
defs: t.Dict[str, Block]
|
defs: t.Dict[str, Block]
|
||||||
|
|
||||||
|
|
||||||
def read_buffer(buffer: str, name: str = "&buff") -> Module:
|
def read_buffer(buffer: str, name: str = "&buff") -> Module:
|
||||||
"""Read a module out of a string [or file]"""
|
"""Read a module out of a string [or file]"""
|
||||||
|
|
||||||
m = Module(name, {})
|
m = Module(Symbol(name), [], {})
|
||||||
for block in parse_buffer(buffer, name):
|
for block in parse_buffer(buffer, name):
|
||||||
if block.app.target == Symbol("def"):
|
if block.app.target == Symbol("def"):
|
||||||
if len(block.args.positionals) == 2:
|
if len(block.args.positionals) == 2:
|
||||||
|
@ -30,6 +39,20 @@ def read_buffer(buffer: str, name: str = "&buff") -> Module:
|
||||||
if block.args.kwargs:
|
if block.args.kwargs:
|
||||||
warn("!def[<kwargs>] are ignored")
|
warn("!def[<kwargs>] are ignored")
|
||||||
|
|
||||||
|
elif block.app.target == Symbol("import"):
|
||||||
|
# FIXME (arrdem 2021-08-21):
|
||||||
|
# This doesn't simplify imports as it goes.
|
||||||
|
# Multiple imports from the same source will wind up with multiple importlist entries.
|
||||||
|
iname = block.args.positionals[0]
|
||||||
|
wild = block.args.kwargs.get(Symbol("wild"), False)
|
||||||
|
rename = block.args.kwargs.get(Symbol("as"), {})
|
||||||
|
imports = (
|
||||||
|
block.args.positionals[1] if len(block.args.positionals) == 2 else []
|
||||||
|
)
|
||||||
|
m.imports.append(
|
||||||
|
Import(iname, {rename.get(i, i): i for i in imports}, wild)
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise SyntaxError(f"Unsupported block !{block.tag}[..]")
|
raise SyntaxError(f"Unsupported block !{block.tag}[..]")
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
Pytest fixtures.
|
Pytest fixtures.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from lilith.parser import Block, parser_with_transformer, GRAMMAR
|
from lilith.parser import Block, GRAMMAR, parser_with_transformer
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from lilith.interpreter import Bindings, Runtime, eval
|
from lilith.interpreter import Bindings, eval, Runtime
|
||||||
|
from lilith.parser import Apply, Args, Symbol
|
||||||
from lilith.reader import Module
|
from lilith.reader import Module
|
||||||
from lilith.parser import Args, Apply, Symbol
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def runtime():
|
||||||
|
return Runtime("test", None, {})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"expr, expected",
|
"expr, expected",
|
||||||
[
|
[
|
||||||
|
@ -17,11 +21,11 @@ import pytest
|
||||||
({"foo": "bar"}, {"foo": "bar"}),
|
({"foo": "bar"}, {"foo": "bar"}),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_eval(expr, expected):
|
def test_eval(runtime, expr, expected):
|
||||||
assert (
|
assert (
|
||||||
eval(
|
eval(
|
||||||
Runtime("test", dict()),
|
runtime,
|
||||||
Module("__repl__", dict()),
|
Module("__repl__", [], dict()),
|
||||||
Bindings("__root__", None),
|
Bindings("__root__", None),
|
||||||
expr,
|
expr,
|
||||||
)
|
)
|
||||||
|
@ -29,11 +33,11 @@ def test_eval(expr, expected):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_hello_world(capsys):
|
def test_hello_world(capsys, runtime):
|
||||||
assert (
|
assert (
|
||||||
eval(
|
eval(
|
||||||
Runtime("test", {}),
|
runtime,
|
||||||
Module("__repl__", {Symbol("print"): print}),
|
Module("__repl__", [], {Symbol("print"): print}),
|
||||||
Bindings("__root__", None),
|
Bindings("__root__", None),
|
||||||
Apply(Symbol("print"), Args(["hello, world"], {})),
|
Apply(Symbol("print"), Args(["hello, world"], {})),
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,6 @@ from lilith.parser import (
|
||||||
parser_with_transformer,
|
parser_with_transformer,
|
||||||
Symbol,
|
Symbol,
|
||||||
)
|
)
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
from lilith.parser import Apply, Args, Block, Symbol
|
from lilith.parser import Apply, Args, Block, Symbol
|
||||||
from lilith.reader import Module, read_buffer
|
from lilith.reader import Module, read_buffer
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +11,8 @@ import pytest
|
||||||
(
|
(
|
||||||
"""!def[main, lang[lil]]\nprint["hello, world"]\n""",
|
"""!def[main, lang[lil]]\nprint["hello, world"]\n""",
|
||||||
Module(
|
Module(
|
||||||
"&buff",
|
Symbol("&buff"),
|
||||||
|
[],
|
||||||
{
|
{
|
||||||
Symbol("main"): Block(
|
Symbol("main"): Block(
|
||||||
Apply(Symbol("lang"), Args([Symbol("lil")], {})),
|
Apply(Symbol("lang"), Args([Symbol("lil")], {})),
|
||||||
|
|
|
@ -27,6 +27,7 @@ lark==0.11.1
|
||||||
livereload==2.6.3
|
livereload==2.6.3
|
||||||
lxml==4.6.3
|
lxml==4.6.3
|
||||||
m2r==0.2.1
|
m2r==0.2.1
|
||||||
|
Markdown==3.3.4
|
||||||
MarkupSafe==2.0.1
|
MarkupSafe==2.0.1
|
||||||
meraki==1.7.2
|
meraki==1.7.2
|
||||||
mirakuru==2.4.1
|
mirakuru==2.4.1
|
||||||
|
|
Loading…
Reference in a new issue