diff --git a/projects/garage/garage.scad b/projects/garage/garage.scad index 7f4f3bb..29b4277 100644 --- a/projects/garage/garage.scad +++ b/projects/garage/garage.scad @@ -2,6 +2,8 @@ use use use use +use +use // Note 16" stud spacing // Note 2.5" downgrade over run of garage; height to minimums @@ -136,9 +138,8 @@ translate([4, 232 - 37 - 1 - toolchest_size, 0]) { //// worktop let(tops=2) -translate([24 + 1, 232 - 1 - 37 - 1 - toolchest_size - 1- (48 * tops), 0]) { - rotate([0, 0, 90]) - shelving(48 * tops, 24, 32, shelves=3); +translate([4, 232 - 1 - 37 - 1 - toolchest_size - 1- (48 * tops), 0]) { + worktop2(); label("Worktop", pos=[-12, (48*tops)/2, 32]); } diff --git a/projects/garage/printer-rack.scad b/projects/garage/printer-rack.scad index f5faf7d..623d97e 100644 --- a/projects/garage/printer-rack.scad +++ b/projects/garage/printer-rack.scad @@ -1,14 +1,16 @@ use use -padding = 1.5; +function mm2in(x) = x * 0.0393701; + mdf_thickness = 0.75; +padding = mdf_thickness; pin_inset = 3; function padded(dims, padding) = [for(e=dims) e+padding]; // 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); repbox = padded([19, 12.5, 12.5], padding); cart = [prusa.x, prusa.y, 6.75 + mdf_thickness]; @@ -35,14 +37,15 @@ module printer_rack() { translate([0, 0, 0]) cart(); - //translate([0, 0, cart.z]) - //box(prusa, mdf_thickness); - 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); - 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); } -printer_rack(); \ No newline at end of file +printer_rack(); +echo(prusa); \ No newline at end of file diff --git a/projects/shogoth/BUILD b/projects/shogoth/BUILD index 355fc15..5c3407f 100644 --- a/projects/shogoth/BUILD +++ b/projects/shogoth/BUILD @@ -7,5 +7,6 @@ py_project( ], lib_deps = [ py_requirement("lark"), + py_requirement("flask"), ], ) diff --git a/projects/shogoth/src/python/shogoth/analyzer/__init__.py b/projects/shogoth/src/python/shogoth/analyzer/__init__.py new file mode 100644 index 0000000..c5b87bd --- /dev/null +++ b/projects/shogoth/src/python/shogoth/analyzer/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +"""A syntax analyzer for Shogoth.""" + +from .impl import * diff --git a/projects/shogoth/src/python/shogoth/analyzer/impl.py b/projects/shogoth/src/python/shogoth/analyzer/impl.py new file mode 100644 index 0000000..3da0b51 --- /dev/null +++ b/projects/shogoth/src/python/shogoth/analyzer/impl.py @@ -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)})") diff --git a/projects/shogoth/src/python/shogoth/reader/__init__.py b/projects/shogoth/src/python/shogoth/reader/__init__.py index 0f5eb24..05ebd3d 100644 --- a/projects/shogoth/src/python/shogoth/reader/__init__.py +++ b/projects/shogoth/src/python/shogoth/reader/__init__.py @@ -1,6 +1,3 @@ #!/usr/bin/env python3 -from .impl import Reader - - -__all__ = ["Reader"] +from .impl import * diff --git a/projects/shogoth/src/python/shogoth/reader/impl.py b/projects/shogoth/src/python/shogoth/reader/impl.py index 33c1908..c686e76 100644 --- a/projects/shogoth/src/python/shogoth/reader/impl.py +++ b/projects/shogoth/src/python/shogoth/reader/impl.py @@ -10,7 +10,7 @@ from typing import Any from lark import Token, Tree from shogoth.parser import parse -from shogoth.types import Keyword, Symbol +from shogoth.types import Keyword, Symbol, List, Vec # Monkeypatching for py3.10 matching @@ -42,10 +42,10 @@ class Reader(object): return self._read(children[0]) 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): - return [self._read(c) for c in 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) diff --git a/projects/shogoth/src/python/shogoth/repl/__main__.py b/projects/shogoth/src/python/shogoth/repl/__main__.py index 3a3da28..adbd570 100644 --- a/projects/shogoth/src/python/shogoth/repl/__main__.py +++ b/projects/shogoth/src/python/shogoth/repl/__main__.py @@ -1,7 +1,8 @@ """A testing REPL.""" - from shogoth.reader import Reader +from shogoth.types import Symbol +from shogoth.analyzer import Analyzer, SPECIALS, GLOBALS, Namespace from prompt_toolkit import ( print_formatted_text, @@ -24,12 +25,16 @@ STYLE = Style.from_dict( } ) -SPINNER = Spinner(["|", "/", "-", "\\"], 200) +SPINNER = Spinner("|/-\\", 200) def main(): reader = Reader() + analyzer = Analyzer(SPECIALS, GLOBALS) + ns = Namespace(Symbol("user"), {}) + session = PromptSession(history=FileHistory(".shogoth.history")) + while True: try: line = session.prompt([("class:prompt", ">>> ")], style=STYLE) @@ -40,9 +45,12 @@ def main(): with yaspin(SPINNER): read = reader.read(line) - print(read, type(read)) + with yaspin(SPINNER): + expr = analyzer.analyze(ns, read) + print('analyze ]', expr, type(expr)) + if __name__ == "__main__": main() diff --git a/projects/shogoth/src/python/shogoth/server/impl.py b/projects/shogoth/src/python/shogoth/server/impl.py new file mode 100644 index 0000000..bf2580c --- /dev/null +++ b/projects/shogoth/src/python/shogoth/server/impl.py @@ -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 diff --git a/projects/shogoth/src/python/shogoth/types/__init__.py b/projects/shogoth/src/python/shogoth/types/__init__.py index 91b3160..3490332 100644 --- a/projects/shogoth/src/python/shogoth/types/__init__.py +++ b/projects/shogoth/src/python/shogoth/types/__init__.py @@ -4,37 +4,37 @@ from .keyword import Keyword from .symbol import Symbol from abc import ABC -from typing import NamedTuple, List, Mapping, Any +import typing as t from uuid import UUID, uuid4 -class TypeVariable(NamedTuple): +class TypeVariable(t.NamedTuple): name: str id: UUID = uuid4() -class ArrayExpr(NamedTuple): - child: Any +class ArrayExpr(t.NamedTuple): + child: t.Any -class SumExpr(NamedTuple): - children: List[Any] +class SumExpr(t.NamedTuple): + children: t.List[t.Any] -class ProductExpr(NamedTuple): - children: Mapping[str, Any] +class ProductExpr(t.NamedTuple): + children: t.Mapping[str, t.Any] #################################################################################################### #################################################################################################### #################################################################################################### -class Function(NamedTuple): +class Function(t.NamedTuple): """The type of a function; a subset of its signature.""" -class FunctionRef(NamedTuple): +class FunctionRef(t.NamedTuple): raw: str type_params: list name: str @@ -55,3 +55,18 @@ class FunctionRef(NamedTuple): cls.parse_list(args), 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 diff --git a/projects/shogoth/src/python/shogoth/types/symbol.py b/projects/shogoth/src/python/shogoth/types/symbol.py index 19b3709..13cd9e9 100644 --- a/projects/shogoth/src/python/shogoth/types/symbol.py +++ b/projects/shogoth/src/python/shogoth/types/symbol.py @@ -1,5 +1,21 @@ -from typing import NamedTuple +import typing as t -class Symbol(NamedTuple): +class Symbol(t.NamedTuple): 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 diff --git a/projects/shogoth/src/python/shogoth/vm/isa.py b/projects/shogoth/src/python/shogoth/vm/isa.py index 92d0007..6ca8657 100644 --- a/projects/shogoth/src/python/shogoth/vm/isa.py +++ b/projects/shogoth/src/python/shogoth/vm/isa.py @@ -127,7 +127,7 @@ class Opcode: """ class CALLC(NamedTuple): - """(`CLOSURE<... A, ... B>`, ... A) -> (... B) + """(`CLOSURE<... A to ... B>`, ... A) -> (... B) Call [closure] Make a dynamic call to the closure at the top of stack. @@ -139,32 +139,32 @@ class Opcode: nargs: int = 0 - # #################################################################################################### - # # Structures - # #################################################################################################### - # class STRUCT(NamedTuple): - # """(*) -> (T) - # 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 + #################################################################################################### + # Structures + #################################################################################################### + class STRUCT(NamedTuple): + """(*) -> (T) + Consume the top N items of the stack, producing a struct of the type `structref`. - # class FLOAD(NamedTuple): - # """(A) -> (B) - # Consume the struct reference at the top of the stack, producing the value of the referenced field. - # """ - # - # fieldref: str + The name and module path of the current function MUST match the name and module path of `structref`. + """ - # class FSTORE(NamedTuple): - # """(A) -> (B) - # Consume the struct reference at the top of the stack, producing the value of the referenced field. - # """ - # - # fieldref: str + structref: str + nargs: int + + class FLOAD(NamedTuple): + """(A) -> (B) + 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