Ditch the .impl pattern for now
This commit is contained in:
parent
7e5a558163
commit
6d9d9b3fd5
11 changed files with 170 additions and 160 deletions
|
@ -86,7 +86,7 @@ class FnExpr(Expr):
|
||||||
|
|
||||||
BOOTSTRAP = "lang.shoggoth.v0.bootstrap"
|
BOOTSTRAP = "lang.shoggoth.v0.bootstrap"
|
||||||
SPECIALS = Namespace(Symbol(BOOTSTRAP), {
|
SPECIALS = Namespace(Symbol(BOOTSTRAP), {
|
||||||
Symbol("if*"): None,
|
Symbol("if*"): None, # FIXME: This isn't really if anymore, it's MATCH or TEST. Something. Unless bool is in bootstrap.
|
||||||
Symbol("let*"): None,
|
Symbol("let*"): None,
|
||||||
Symbol("fn*"): None,
|
Symbol("fn*"): None,
|
||||||
})
|
})
|
||||||
|
@ -110,31 +110,36 @@ class Analyzer:
|
||||||
def _analyze(e):
|
def _analyze(e):
|
||||||
return self.analyze(module, e)
|
return self.analyze(module, e)
|
||||||
|
|
||||||
if isinstance(expr, (int, float, str, Keyword)):
|
match expr:
|
||||||
return ConstExpr(expr)
|
case int() | float() | str():
|
||||||
|
return ConstExpr(expr)
|
||||||
|
|
||||||
if isinstance(expr, Vec):
|
case Keyword():
|
||||||
return ListExpr([self.analyze(module, e) for e in expr])
|
raise ValueError()
|
||||||
|
|
||||||
if isinstance(expr, List) and len(expr) > 1 and isinstance(expr[0], Symbol):
|
case List() if len(expr) > 1 and isinstance(expr[0], Symbol):
|
||||||
if (target := self.resolve(module, expr[0])):
|
if (target := self.resolve(module, expr[0])):
|
||||||
match target:
|
match target:
|
||||||
case Symbol("if*", BOOTSTRAP):
|
case Symbol("if*", BOOTSTRAP):
|
||||||
assert len(expr) == 4
|
assert len(expr) == 4
|
||||||
return IfExpr(_analyze(expr[1]), _analyze(expr[2]), _analyze(expr[3]))
|
return IfExpr(_analyze(expr[1]), _analyze(expr[2]), _analyze(expr[3]))
|
||||||
|
|
||||||
case Symbol("let*", BOOTSTRAP):
|
case Symbol("let*", BOOTSTRAP):
|
||||||
assert len(expr) == 3
|
assert len(expr) == 3
|
||||||
return LetExpr([(name, _analyze(e)) for name, e in expr[1]], _analyze(expr[2]))
|
return LetExpr([(name, _analyze(e)) for name, e in expr[1]], _analyze(expr[2]))
|
||||||
|
|
||||||
case Symbol("fn*", BOOTSTRAP):
|
case Symbol("fn*", BOOTSTRAP):
|
||||||
assert len(expr) == 3
|
assert len(expr) == 3
|
||||||
return FnExpr(expr[1], _analyze(expr[2]))
|
return FnExpr(expr[1], _analyze(expr[2]))
|
||||||
|
|
||||||
# FIXME: Macros go here? Or is macroexpansion separate?
|
# FIXME: Macros go here? Or is macroexpansion separate?
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
return InvokeExpr(_analyze(target), [_analyze(e) for e in expr[1:]])
|
pass
|
||||||
|
|
||||||
|
return InvokeExpr(_analyze(target), [_analyze(e) for e in expr[1:]])
|
||||||
|
|
||||||
|
case List():
|
||||||
|
return ListExpr([self.analyze(module, e) for e in expr])
|
||||||
|
|
||||||
raise ValueError(f"Unable to analyze {expr!r} ({type(expr)})")
|
raise ValueError(f"Unable to analyze {expr!r} ({type(expr)})")
|
|
@ -1,4 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""A syntax analyzer for Shogoth."""
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ mapping: "{" kv* "}"
|
||||||
kv: expr expr
|
kv: expr expr
|
||||||
quote: "'" expr
|
quote: "'" expr
|
||||||
|
|
||||||
atom: keyword | pattern | num | string | true | false | nil | symbol
|
atom: keyword | pattern | num | string | bool | nil | symbol
|
||||||
num: hex | octal | bin | int | float
|
num: hex | octal | bin | int | float
|
||||||
|
|
||||||
keyword: ":" SYMBOL
|
keyword: ":" SYMBOL
|
||||||
|
@ -30,9 +30,12 @@ STRING: /".*?"/
|
||||||
pattern: PATTERN
|
pattern: PATTERN
|
||||||
PATTERN: /\/.*?\//
|
PATTERN: /\/.*?\//
|
||||||
|
|
||||||
true: /true/
|
bool: TRUE | FALSE
|
||||||
false: /false/
|
TRUE: /true/
|
||||||
nil: /nil/
|
FALSE: /false/
|
||||||
|
|
||||||
|
nil: NIL
|
||||||
|
NIL: /nil/
|
||||||
|
|
||||||
// NOTE: order-prescidence matters here, 0x, 0b, 0o etc. require lookahead
|
// NOTE: order-prescidence matters here, 0x, 0b, 0o etc. require lookahead
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
"""Published interface to the shoggoth parser."""
|
|
||||||
|
|
||||||
from .impl import parse
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["parse"]
|
|
97
projects/shoggoth/src/python/shoggoth/reader.py
Normal file
97
projects/shoggoth/src/python/shoggoth/reader.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""The shoggoth reader."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
assert sys.version_info > (3, 10, 0), "`match` support is required"
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from lark import Token, Tree
|
||||||
|
from shoggoth.parser import parse
|
||||||
|
from shoggoth.types import (
|
||||||
|
Keyword,
|
||||||
|
List,
|
||||||
|
Symbol,
|
||||||
|
Vec,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Monkeypatching for py3.10 matching
|
||||||
|
Tree.__match_args__ = ("data", "children")
|
||||||
|
Token.__match_args__ = ("type", "value")
|
||||||
|
|
||||||
|
|
||||||
|
class Reader(object):
|
||||||
|
"""An extension of parsing that produces meaningful trees. Can be extended with userland hooks."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _parse(self, buffer):
|
||||||
|
return parse(buffer)
|
||||||
|
|
||||||
|
def symbol(self, x):
|
||||||
|
return Symbol(x)
|
||||||
|
|
||||||
|
def keyword(self, x):
|
||||||
|
return Keyword(x)
|
||||||
|
|
||||||
|
def pattern(self, x):
|
||||||
|
return re.compile(x[1:-1].replace("\\/", "/"))
|
||||||
|
|
||||||
|
def _read(self, tree: Tree) -> Any:
|
||||||
|
match tree:
|
||||||
|
case Tree(Token("RULE", "expr"), children):
|
||||||
|
return self._read(children[0])
|
||||||
|
|
||||||
|
case Tree(Token("RULE", "plist"), children):
|
||||||
|
return List([self._read(c) for c in children])
|
||||||
|
|
||||||
|
case Tree(Token("RULE", "blist"), children):
|
||||||
|
return Vec([self._read(c) for c in children])
|
||||||
|
|
||||||
|
case Tree(Token("RULE", "mapping"), children):
|
||||||
|
return dict(self._read(c) for c in children)
|
||||||
|
|
||||||
|
case Tree(Token("RULE", "kv"), [k, v]):
|
||||||
|
return (self._read(k), self._read(v))
|
||||||
|
|
||||||
|
# Fallback case of handling rules
|
||||||
|
case Tree(Token("RULE", _), [a]):
|
||||||
|
return self._read(a)
|
||||||
|
|
||||||
|
# Handling individual tokens
|
||||||
|
case Token("INT", x):
|
||||||
|
return int(x)
|
||||||
|
|
||||||
|
case Token("FLOAT", x):
|
||||||
|
return float(x)
|
||||||
|
|
||||||
|
case Token("PATTERN", x):
|
||||||
|
return self.pattern(x)
|
||||||
|
|
||||||
|
case Token("TRUE", _):
|
||||||
|
return True
|
||||||
|
|
||||||
|
case Token("FALSE", _):
|
||||||
|
return False
|
||||||
|
|
||||||
|
case Token("NIL", _):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Symbol is very much the catch-all of the grammar
|
||||||
|
case Token("SYMBOL", x):
|
||||||
|
return self.symbol(x)
|
||||||
|
|
||||||
|
case Token("KEYWORD", x):
|
||||||
|
return self.keyword(x)
|
||||||
|
|
||||||
|
case _:
|
||||||
|
return tree
|
||||||
|
|
||||||
|
def read(self, buffer):
|
||||||
|
return self._read(self._parse(buffer))
|
|
@ -1,2 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""The shoggoth reader."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
assert sys.version_info > (3, 10, 0), "`match` support is required"
|
|
||||||
|
|
||||||
import re
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from lark import Token, Tree
|
|
||||||
from shoggoth.parser import parse
|
|
||||||
from shoggoth.types import (
|
|
||||||
Keyword,
|
|
||||||
List,
|
|
||||||
Symbol,
|
|
||||||
Vec,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Monkeypatching for py3.10 matching
|
|
||||||
Tree.__match_args__ = ("data", "children")
|
|
||||||
Token.__match_args__ = ("type", "value")
|
|
||||||
|
|
||||||
|
|
||||||
class Reader(object):
|
|
||||||
"""An extension of parsing that produces meaningful trees. Can be extended with userland hooks."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _parse(self, buffer):
|
|
||||||
return parse(buffer)
|
|
||||||
|
|
||||||
def symbol(self, x):
|
|
||||||
return Symbol(x)
|
|
||||||
|
|
||||||
def keyword(self, x):
|
|
||||||
return Keyword(x)
|
|
||||||
|
|
||||||
def pattern(self, x):
|
|
||||||
return re.compile(x[1:-1].replace("\\/", "/"))
|
|
||||||
|
|
||||||
def _read(self, tree: Tree) -> Any:
|
|
||||||
match tree:
|
|
||||||
case Tree(Token("RULE", "expr"), children):
|
|
||||||
return self._read(children[0])
|
|
||||||
|
|
||||||
case Tree(Token("RULE", "plist"), children):
|
|
||||||
return List([self._read(c) for c in children])
|
|
||||||
|
|
||||||
case Tree(Token("RULE", "blist"), children):
|
|
||||||
return Vec([self._read(c) for c in children])
|
|
||||||
|
|
||||||
case Tree(Token("RULE", "mapping"), children):
|
|
||||||
return dict(self._read(c) for c in children)
|
|
||||||
|
|
||||||
case Tree(Token("RULE", "kv"), [k, v]):
|
|
||||||
return (self._read(k), self._read(v))
|
|
||||||
|
|
||||||
case Tree(Token("RULE", "atom"), [a]):
|
|
||||||
return self._read(a)
|
|
||||||
|
|
||||||
case Tree(Token("RULE", "num"), [a]):
|
|
||||||
return self._read(a)
|
|
||||||
|
|
||||||
case Token("INT", x):
|
|
||||||
return int(x)
|
|
||||||
|
|
||||||
case Token("FLOAT", x):
|
|
||||||
return float(x)
|
|
||||||
|
|
||||||
case Token("KEYWORD", x):
|
|
||||||
return self.keyword(x)
|
|
||||||
|
|
||||||
case Token("PATTERN", x):
|
|
||||||
return self.pattern(x)
|
|
||||||
|
|
||||||
case Token("TRUE", _):
|
|
||||||
return True
|
|
||||||
|
|
||||||
case Token("FALSE", _):
|
|
||||||
return False
|
|
||||||
|
|
||||||
case Token("NIL", _):
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Symbol is very much the catch-all of the grammar
|
|
||||||
case Token("SYMBOL", x):
|
|
||||||
return self.symbol(x)
|
|
||||||
|
|
||||||
case _:
|
|
||||||
return tree
|
|
||||||
|
|
||||||
def read(self, buffer):
|
|
||||||
return self._read(self._parse(buffer))
|
|
|
@ -1,26 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""The implementation of a Shogoth evaluation server."""
|
|
||||||
|
|
||||||
import flask
|
|
||||||
|
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
|
||||||
|
|
||||||
@app.route("/api/v0/login", method=["POST"])
|
|
||||||
def login():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/v0/logout", method=["POST"])
|
|
||||||
def logout():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/v0/session", method=["GET"])
|
|
||||||
def get_session():
|
|
||||||
pass
|
|
||||||
|
|
||||||
@app.route("/api/v0/session", method=["POST"])
|
|
||||||
def make_session():
|
|
||||||
pass
|
|
41
projects/shoggoth/src/python/shoggoth/types.py
Normal file
41
projects/shoggoth/src/python/shoggoth/types.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Symbol(object):
|
||||||
|
name: str
|
||||||
|
namespace: "Symbol" = None
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
def qualify(self, name: str) -> "Symbol":
|
||||||
|
return Symbol(
|
||||||
|
name = self.name,
|
||||||
|
namespace = Symbol(name)
|
||||||
|
)
|
||||||
|
|
||||||
|
def unqualified(self):
|
||||||
|
return Symbol(self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class Keyword(Symbol):
|
||||||
|
|
||||||
|
def qualify(self, name: str) -> "Keyword":
|
||||||
|
return Keyword(
|
||||||
|
name = self.name,
|
||||||
|
namespace = Keyword(name)
|
||||||
|
)
|
||||||
|
|
||||||
|
def unqualified(self):
|
||||||
|
return Keyword(self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class List(list):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Vec(list):
|
||||||
|
pass
|
Loading…
Reference in a new issue