mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[project] Generate syscalls from new interface DSL
This change adds a new interface DSL for specifying objects (with methods) and interfaces (that expose objects, and optionally have their own methods). Significant changes: - Add the new scripts/definitions Python module to parse the DSL - Add the new definitions directory containing DSL definition files - Use cog to generate syscall-related code in kernel and libj6 - Unify ordering of pointer + length pairs in interfaces
This commit is contained in:
23
scripts/definitions/types/__init__.py
Normal file
23
scripts/definitions/types/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
def _indent(x):
|
||||
from textwrap import indent
|
||||
return indent(str(x), ' ')
|
||||
|
||||
class Description(str): pass
|
||||
class Import(str): pass
|
||||
|
||||
class Options(set):
|
||||
def __str__(self):
|
||||
if not self: return ""
|
||||
return "[{}]".format(" ".join(self))
|
||||
|
||||
class UID(int):
|
||||
def __str__(self):
|
||||
return f"{self:016x}"
|
||||
|
||||
from .object import Object
|
||||
from .interface import Interface, Expose
|
||||
from .function import Function, Method, Param
|
||||
|
||||
from .type import Type
|
||||
from .primitive import get_primitive
|
||||
from .objref import ObjectRef
|
||||
49
scripts/definitions/types/function.py
Normal file
49
scripts/definitions/types/function.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from . import _indent
|
||||
from . import Options
|
||||
|
||||
def _hasopt(opt):
|
||||
def test(self):
|
||||
return opt in self.options
|
||||
return test
|
||||
|
||||
class Function:
|
||||
typename = "function"
|
||||
|
||||
def __init__(self, name, opts=Options(), desc="", children=tuple()):
|
||||
self.name = name
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
self.params = [c for c in children if isinstance(c, Param)]
|
||||
self.id = -1
|
||||
|
||||
def __str__(self):
|
||||
parts = ["{} {}".format(self.typename, self.name)]
|
||||
if self.desc:
|
||||
parts.append(_indent(self.desc))
|
||||
if self.options:
|
||||
parts.append(f" Options: {self.options}")
|
||||
parts.extend(map(_indent, self.params))
|
||||
return "\n".join(parts)
|
||||
|
||||
static = property(lambda x: True)
|
||||
constructor = property(lambda x: False)
|
||||
|
||||
|
||||
class Method(Function):
|
||||
typename = "method"
|
||||
|
||||
static = property(_hasopt("static"))
|
||||
constructor = property(_hasopt("constructor"))
|
||||
|
||||
|
||||
class Param:
|
||||
def __init__(self, name, typename, opts=Options(), desc=""):
|
||||
self.name = name
|
||||
self.type = typename
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
|
||||
def __str__(self):
|
||||
return "param {} {} {} {}".format(
|
||||
self.name, repr(self.type), self.options, self.desc or "")
|
||||
|
||||
43
scripts/definitions/types/interface.py
Normal file
43
scripts/definitions/types/interface.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from . import _indent
|
||||
from . import Options
|
||||
|
||||
class Expose(object):
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
|
||||
def __repr__(self):
|
||||
return f'expose {repr(self.type)}'
|
||||
|
||||
class Interface:
|
||||
def __init__(self, name, uid, opts=Options(), desc="", children=tuple()):
|
||||
from .function import Function
|
||||
|
||||
self.name = name
|
||||
self.uid = uid
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
|
||||
self.functions = [c for c in children if isinstance(c, Function)]
|
||||
self.exposes = [e.type for e in children if isinstance(e, Expose)]
|
||||
|
||||
def __str__(self):
|
||||
parts = [f"interface {self.name}: {self.uid}"]
|
||||
if self.desc:
|
||||
parts.append(_indent(self.desc))
|
||||
if self.options:
|
||||
parts.append(f" Options: {self.options}")
|
||||
parts.extend(map(_indent, self.exposes))
|
||||
parts.extend(map(_indent, self.functions))
|
||||
return "\n".join(parts)
|
||||
|
||||
def __methods(self):
|
||||
mm = [(i, None, self.functions[i]) for i in range(len(self.functions))]
|
||||
|
||||
base = len(mm)
|
||||
for o in [e.object for e in self.exposes]:
|
||||
mm.extend([(base + i, o, o.methods[i]) for i in range(len(o.methods))])
|
||||
base += len(o.methods)
|
||||
|
||||
return mm
|
||||
|
||||
methods = property(__methods)
|
||||
25
scripts/definitions/types/object.py
Normal file
25
scripts/definitions/types/object.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from . import _indent
|
||||
from . import Options
|
||||
|
||||
class Object:
|
||||
def __init__(self, name, uid, typename=None, opts=Options(), desc="", children=tuple()):
|
||||
self.name = name
|
||||
self.uid = uid
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
self.super = typename
|
||||
self.methods = children
|
||||
|
||||
from . import ObjectRef
|
||||
self.__ref = ObjectRef(name)
|
||||
|
||||
def __str__(self):
|
||||
parts = [f"object {self.name}: {self.uid}"]
|
||||
if self.desc:
|
||||
parts.append(_indent(self.desc))
|
||||
if self.options:
|
||||
parts.append(f" Options: {self.options}")
|
||||
parts.extend(map(_indent, self.methods))
|
||||
return "\n".join(parts)
|
||||
|
||||
reftype = property(lambda self: self.__ref)
|
||||
44
scripts/definitions/types/objref.py
Normal file
44
scripts/definitions/types/objref.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from .type import Type
|
||||
|
||||
class ObjectRef(Type):
|
||||
all_refs = {}
|
||||
|
||||
def __init__(self, name, filename=None):
|
||||
super().__init__(name)
|
||||
self.__c_type = "j6_handle_t"
|
||||
self.__object = None
|
||||
ObjectRef.all_refs[self] = filename
|
||||
|
||||
def __repr__(self):
|
||||
return f'ObjectRef({self.name})'
|
||||
|
||||
object = property(lambda self: self.__object)
|
||||
|
||||
def c_names(self, options):
|
||||
one = self.__c_type
|
||||
out = bool({"out", "inout"}.intersection(options))
|
||||
|
||||
if "list" in options:
|
||||
two = "size_t"
|
||||
if out:
|
||||
one = f"const {one} *"
|
||||
two += " *"
|
||||
else:
|
||||
one = f"{one} *"
|
||||
return ((one, ""), (two, "_count"))
|
||||
|
||||
else:
|
||||
if out:
|
||||
one += " *"
|
||||
return ((one, ""),)
|
||||
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
@classmethod
|
||||
def connect(cls, objects):
|
||||
for ref, filename in cls.all_refs.items():
|
||||
ref.__object = objects.get(ref.name)
|
||||
if ref.__object is None:
|
||||
from ..errors import UnknownTypeError
|
||||
raise UnknownTypeError(ref.name, filename)
|
||||
70
scripts/definitions/types/primitive.py
Normal file
70
scripts/definitions/types/primitive.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from .type import Type
|
||||
|
||||
class Primitive(Type):
|
||||
def __init__(self, name, c_type):
|
||||
super().__init__(name)
|
||||
self.c_type = c_type
|
||||
|
||||
def __repr__(self):
|
||||
return f'Primitive({self.name})'
|
||||
|
||||
def c_names(self, options=None):
|
||||
one = self.c_type
|
||||
if options and "out" in options or "inout" in options:
|
||||
one += " *"
|
||||
|
||||
return ((one, ""),)
|
||||
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
class PrimitiveRef(Primitive):
|
||||
def __init__(self, name, c_type, counted=False):
|
||||
super().__init__(name, c_type)
|
||||
self.__counted = counted
|
||||
|
||||
def c_names(self, options=None):
|
||||
one = f"{self.c_type} *"
|
||||
two = "size_t"
|
||||
|
||||
if options and "out" in options or "inout" in options:
|
||||
two += " *"
|
||||
else:
|
||||
one = "const " + one
|
||||
|
||||
if self.__counted:
|
||||
return ((one, ""), (two, "_len"))
|
||||
else:
|
||||
return ((one, ""),)
|
||||
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
_primitives = {
|
||||
"string": PrimitiveRef("string", "char"),
|
||||
"buffer": PrimitiveRef("buffer", "void", counted=True),
|
||||
|
||||
"int": Primitive("int", "int"),
|
||||
"uint": Primitive("uint", "unsigned"),
|
||||
"size": Primitive("size", "size_t"),
|
||||
"address": Primitive("address", "uintptr_t"),
|
||||
|
||||
"int8": Primitive("int8", "int8_t"),
|
||||
"uint8": Primitive("uint8", "uint8_t"),
|
||||
|
||||
"int16": Primitive("int16", "int16_t"),
|
||||
"uint16": Primitive("uint16", "uint16_t"),
|
||||
|
||||
"int32": Primitive("int32", "int32_t"),
|
||||
"uint32": Primitive("uint32", "uint32_t"),
|
||||
|
||||
"int64": Primitive("int64", "int64_t"),
|
||||
"uint64": Primitive("uint64", "uint64_t"),
|
||||
}
|
||||
|
||||
def get_primitive(name):
|
||||
p = _primitives.get(name)
|
||||
if not p:
|
||||
from ..errors import InvalidType
|
||||
raise InvalidType(name)
|
||||
return p
|
||||
12
scripts/definitions/types/type.py
Normal file
12
scripts/definitions/types/type.py
Normal file
@@ -0,0 +1,12 @@
|
||||
class Type:
|
||||
def __init__(self, name):
|
||||
self.__name = name
|
||||
|
||||
name = property(lambda self: self.__name)
|
||||
|
||||
def c_names(self, options):
|
||||
raise NotImplemented("Call to base Type.c_names")
|
||||
|
||||
def cxx_names(self, options):
|
||||
raise NotImplemented("Call to base Type.c_names")
|
||||
|
||||
Reference in New Issue
Block a user