[NO TESTS] WIP

This commit is contained in:
Reid D. 'arrdem' McKenzie 2022-05-31 09:36:47 -06:00
parent a65702be42
commit 65091be070
12 changed files with 264 additions and 57 deletions

View file

@ -2,6 +2,8 @@ use <steel-shelving.scad>
use <Z4M.scad> use <Z4M.scad>
use <tire-loft.scad> use <tire-loft.scad>
use <printer-rack.scad> use <printer-rack.scad>
use <worktop.scad>
use <worktop2.scad>
// Note 16" stud spacing // Note 16" stud spacing
// Note 2.5" downgrade over run of garage; height to minimums // Note 2.5" downgrade over run of garage; height to minimums
@ -136,9 +138,8 @@ translate([4, 232 - 37 - 1 - toolchest_size, 0]) {
//// worktop //// worktop
let(tops=2) let(tops=2)
translate([24 + 1, 232 - 1 - 37 - 1 - toolchest_size - 1- (48 * tops), 0]) { translate([4, 232 - 1 - 37 - 1 - toolchest_size - 1- (48 * tops), 0]) {
rotate([0, 0, 90]) worktop2();
shelving(48 * tops, 24, 32, shelves=3);
label("Worktop", pos=[-12, (48*tops)/2, 32]); label("Worktop", pos=[-12, (48*tops)/2, 32]);
} }

View file

@ -1,14 +1,16 @@
use <box.scad> use <box.scad>
use <casters.scad> use <casters.scad>
padding = 1.5; function mm2in(x) = x * 0.0393701;
mdf_thickness = 0.75; mdf_thickness = 0.75;
padding = mdf_thickness;
pin_inset = 3; pin_inset = 3;
function padded(dims, padding) = [for(e=dims) e+padding]; function padded(dims, padding) = [for(e=dims) e+padding];
// dimensions for things // dimensions for things
prusa = padded([31.5, 31.5, 35.5], padding); prusa = padded([mm2in(180 * 2 + 504), mm2in(115 + 660), mm2in(910)], padding);
cr10 = padded([19, 27, 25], padding); cr10 = padded([19, 27, 25], padding);
repbox = padded([19, 12.5, 12.5], padding); repbox = padded([19, 12.5, 12.5], padding);
cart = [prusa.x, prusa.y, 6.75 + mdf_thickness]; cart = [prusa.x, prusa.y, 6.75 + mdf_thickness];
@ -35,14 +37,15 @@ module printer_rack() {
translate([0, 0, 0]) translate([0, 0, 0])
cart(); cart();
//translate([0, 0, cart.z])
//box(prusa, mdf_thickness);
translate([0, 0, cart.z]) translate([0, 0, cart.z])
box(prusa, mdf_thickness);
translate([0, 0, cart.z + prusa.z])
box([prusa.x, prusa.y, repbox.z], mdf_thickness); box([prusa.x, prusa.y, repbox.z], mdf_thickness);
translate([0, 0, cart.z + repbox.z + mdf_thickness * 2]) translate([0, 0, cart.z + prusa.z + repbox.z + mdf_thickness * 2])
box([prusa.x, prusa.y, cr10.z], mdf_thickness); box([prusa.x, prusa.y, cr10.z], mdf_thickness);
} }
printer_rack(); printer_rack();
echo(prusa);

View file

@ -7,5 +7,6 @@ py_project(
], ],
lib_deps = [ lib_deps = [
py_requirement("lark"), py_requirement("lark"),
py_requirement("flask"),
], ],
) )

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python3
"""A syntax analyzer for Shogoth."""
from .impl import *

View file

@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""The implementation of Shogoth's lexical analyzer."""
from abc import ABC
from dataclasses import dataclass
import typing as t
from shogoth.types import List, Vec, Keyword, Symbol
@dataclass
class Namespace:
name: Symbol
mappings: t.Mapping[Symbol, t.Any]
def resolve(self, name: Symbol) -> t.Optional[Symbol]:
if name in self.mappings:
return name.qualify(self.name.name)
def __contains__(self, item):
return item.namespace == self.name and item.unqualified() in self.mappings
def __getitem__(self, key: Symbol):
return self.mappings[key.unqualified()]
def get(self, key: Symbol, default=None):
if key in self:
return self[key]
else:
return default
class Expr(ABC):
pass
@dataclass
class ConstExpr(Expr):
val: t.Any
@dataclass
class ListExpr(Expr):
children: t.List[t.Any]
@dataclass
class MapExpr(Expr):
kvs: t.List[t.Tuple[t.Any, t.Any]]
@dataclass
class InvokeExpr(Expr):
target: Expr
args: t.List[Expr]
# FIXME: kwargs/star-args?
@dataclass
class IfExpr(Expr):
"""(if* test then else)"""
test: Expr
pos_branch: Expr
neg_branch: Expr
@dataclass
class LetExpr(Expr):
"""(let* [name binding name2 binding2] body)"""
bindings: t.List[t.Tuple[Symbol, Expr]]
body: Expr
@dataclass
class FnExpr(Expr):
"""(fn* args body)"""
args: t.List[Symbol]
body: Expr
BOOTSTRAP = "lang.shogoth.v0.bootstrap"
SPECIALS = Namespace(Symbol(BOOTSTRAP), {
Symbol("if*"): None,
Symbol("let*"): None,
Symbol("fn*"): None,
})
GLOBALS = Namespace(Symbol("lang.shogoth.v0.core"), {
})
# FIXME: This is really expand... isn't it.
class Analyzer:
def __init__(self, specials, globals):
self._specials = specials
self._globals = globals
def resolve(self, module, name: Symbol):
return module.resolve(name) or self._globals.resolve(name) or self._specials.resolve(name)
def analyze(self, module: Namespace, expr: t.Any):
def _analyze(e):
return self.analyze(module, e)
if isinstance(expr, (int, float, str, Keyword)):
return ConstExpr(expr)
if isinstance(expr, Vec):
return ListExpr([self.analyze(module, e) for e in expr])
if isinstance(expr, List) and len(expr) > 1 and isinstance(expr[0], Symbol):
if (target := self.resolve(module, expr[0])):
match target:
case Symbol("if*", BOOTSTRAP):
assert len(expr) == 4
return IfExpr(_analyze(expr[1]), _analyze(expr[2]), _analyze(expr[3]))
case Symbol("let*", BOOTSTRAP):
assert len(expr) == 3
return LetExpr([(name, _analyze(e)) for name, e in expr[1]], _analyze(expr[2]))
case Symbol("fn*", BOOTSTRAP):
assert len(expr) == 3
return FnExpr(expr[1], _analyze(expr[2]))
# FIXME: Macros go here? Or is macroexpansion separate?
case _:
return InvokeExpr(_analyze(target), [_analyze(e) for e in expr[1:]])
raise ValueError(f"Unable to analyze {expr!r} ({type(expr)})")

View file

@ -1,6 +1,3 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from .impl import Reader from .impl import *
__all__ = ["Reader"]

View file

@ -10,7 +10,7 @@ from typing import Any
from lark import Token, Tree from lark import Token, Tree
from shogoth.parser import parse from shogoth.parser import parse
from shogoth.types import Keyword, Symbol from shogoth.types import Keyword, Symbol, List, Vec
# Monkeypatching for py3.10 matching # Monkeypatching for py3.10 matching
@ -42,10 +42,10 @@ class Reader(object):
return self._read(children[0]) return self._read(children[0])
case Tree(Token("RULE", "plist"), children): case Tree(Token("RULE", "plist"), children):
return [self._read(c) for c in children] return List([self._read(c) for c in children])
case Tree(Token("RULE", "blist"), children): case Tree(Token("RULE", "blist"), children):
return [self._read(c) for c in children] return Vec([self._read(c) for c in children])
case Tree(Token("RULE", "mapping"), children): case Tree(Token("RULE", "mapping"), children):
return dict(self._read(c) for c in children) return dict(self._read(c) for c in children)

View file

@ -1,7 +1,8 @@
"""A testing REPL.""" """A testing REPL."""
from shogoth.reader import Reader from shogoth.reader import Reader
from shogoth.types import Symbol
from shogoth.analyzer import Analyzer, SPECIALS, GLOBALS, Namespace
from prompt_toolkit import ( from prompt_toolkit import (
print_formatted_text, print_formatted_text,
@ -24,12 +25,16 @@ STYLE = Style.from_dict(
} }
) )
SPINNER = Spinner(["|", "/", "-", "\\"], 200) SPINNER = Spinner("|/-\\", 200)
def main(): def main():
reader = Reader() reader = Reader()
analyzer = Analyzer(SPECIALS, GLOBALS)
ns = Namespace(Symbol("user"), {})
session = PromptSession(history=FileHistory(".shogoth.history")) session = PromptSession(history=FileHistory(".shogoth.history"))
while True: while True:
try: try:
line = session.prompt([("class:prompt", ">>> ")], style=STYLE) line = session.prompt([("class:prompt", ">>> ")], style=STYLE)
@ -40,9 +45,12 @@ def main():
with yaspin(SPINNER): with yaspin(SPINNER):
read = reader.read(line) read = reader.read(line)
print(read, type(read)) print(read, type(read))
with yaspin(SPINNER):
expr = analyzer.analyze(ns, read)
print('analyze ]', expr, type(expr))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -0,0 +1,26 @@
#!/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

View file

@ -4,37 +4,37 @@ from .keyword import Keyword
from .symbol import Symbol from .symbol import Symbol
from abc import ABC from abc import ABC
from typing import NamedTuple, List, Mapping, Any import typing as t
from uuid import UUID, uuid4 from uuid import UUID, uuid4
class TypeVariable(NamedTuple): class TypeVariable(t.NamedTuple):
name: str name: str
id: UUID = uuid4() id: UUID = uuid4()
class ArrayExpr(NamedTuple): class ArrayExpr(t.NamedTuple):
child: Any child: t.Any
class SumExpr(NamedTuple): class SumExpr(t.NamedTuple):
children: List[Any] children: t.List[t.Any]
class ProductExpr(NamedTuple): class ProductExpr(t.NamedTuple):
children: Mapping[str, Any] children: t.Mapping[str, t.Any]
#################################################################################################### ####################################################################################################
#################################################################################################### ####################################################################################################
#################################################################################################### ####################################################################################################
class Function(NamedTuple): class Function(t.NamedTuple):
"""The type of a function; a subset of its signature.""" """The type of a function; a subset of its signature."""
class FunctionRef(NamedTuple): class FunctionRef(t.NamedTuple):
raw: str raw: str
type_params: list type_params: list
name: str name: str
@ -55,3 +55,18 @@ class FunctionRef(NamedTuple):
cls.parse_list(args), cls.parse_list(args),
cls.parse_list(ret) cls.parse_list(ret)
) )
class Closure(t.NamedTuple):
target: t.Union["Closure", FunctionRef]
args: t.List[t.Any]
# FIXME (arrdem 2022-05-30):
# Find a better name for this
class Vec(list):
pass
class List(list):
pass

View file

@ -1,5 +1,21 @@
from typing import NamedTuple import typing as t
class Symbol(NamedTuple): class Symbol(t.NamedTuple):
name: str name: str
namespace: t.Optional[str] = None
def qualify(self, ns: str):
return Symbol(self.name, ns)
def unqualified(self):
if not self.namespace:
return self
else:
return Symbol(self.name)
def __str__(self):
if self.namespace:
return f"{self.namespace}/{self.name}"
else:
return self.name

View file

@ -127,7 +127,7 @@ class Opcode:
""" """
class CALLC(NamedTuple): class CALLC(NamedTuple):
"""(`CLOSURE<... A, ... B>`, ... A) -> (... B) """(`CLOSURE<... A to ... B>`, ... A) -> (... B)
Call [closure] Call [closure]
Make a dynamic call to the closure at the top of stack. Make a dynamic call to the closure at the top of stack.
@ -139,32 +139,32 @@ class Opcode:
nargs: int = 0 nargs: int = 0
# #################################################################################################### ####################################################################################################
# # Structures # Structures
# #################################################################################################### ####################################################################################################
# class STRUCT(NamedTuple): class STRUCT(NamedTuple):
# """(*) -> (T) """(*) -> (T)
# Consume the top N items of the stack, producing a struct of the type `structref`. Consume the top N items of the stack, producing a struct of the type `structref`.
#
# The name and module path of the current function MUST match the name and module path of `structref`.
# """
#
# structref: str
# nargs: int
# class FLOAD(NamedTuple): The name and module path of the current function MUST match the name and module path of `structref`.
# """(A) -> (B) """
# Consume the struct reference at the top of the stack, producing the value of the referenced field.
# """
#
# fieldref: str
# class FSTORE(NamedTuple): structref: str
# """(A) -> (B) nargs: int
# Consume the struct reference at the top of the stack, producing the value of the referenced field.
# """ class FLOAD(NamedTuple):
# """(A) -> (B)
# fieldref: str Consume the struct reference at the top of the stack, producing the value of the referenced field.
"""
fieldref: str
class FSTORE(NamedTuple):
"""(A) -> (B)
Consume the struct reference at the top of the stack, producing the value of the referenced field.
"""
fieldref: str
#################################################################################################### ####################################################################################################
# Arrays # Arrays