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"
|
||||
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("fn*"): None,
|
||||
})
|
||||
|
@ -110,13 +110,14 @@ class Analyzer:
|
|||
def _analyze(e):
|
||||
return self.analyze(module, e)
|
||||
|
||||
if isinstance(expr, (int, float, str, Keyword)):
|
||||
match expr:
|
||||
case int() | float() | str():
|
||||
return ConstExpr(expr)
|
||||
|
||||
if isinstance(expr, Vec):
|
||||
return ListExpr([self.analyze(module, e) for e in expr])
|
||||
case Keyword():
|
||||
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])):
|
||||
match target:
|
||||
case Symbol("if*", BOOTSTRAP):
|
||||
|
@ -134,7 +135,11 @@ class Analyzer:
|
|||
# FIXME: Macros go here? Or is macroexpansion separate?
|
||||
|
||||
case _:
|
||||
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)})")
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""A syntax analyzer for Shogoth."""
|
||||
|
|
@ -15,7 +15,7 @@ mapping: "{" kv* "}"
|
|||
kv: expr 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
|
||||
|
||||
keyword: ":" SYMBOL
|
||||
|
@ -30,9 +30,12 @@ STRING: /".*?"/
|
|||
pattern: PATTERN
|
||||
PATTERN: /\/.*?\//
|
||||
|
||||
true: /true/
|
||||
false: /false/
|
||||
nil: /nil/
|
||||
bool: TRUE | FALSE
|
||||
TRUE: /true/
|
||||
FALSE: /false/
|
||||
|
||||
nil: NIL
|
||||
NIL: /nil/
|
||||
|
||||
// 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