View
@@ -0,0 +1,199 @@
#!/usr/bin/python
"""
meta.py
Meta-Object model for ASDL. This is available at runtime.
asdl_.py could be renamed asdl_parse.py. It has a parser and an AST.
or parse.py
And then we need a compile_.py that resolves the types and creates a
meta-object model.
repr() of these types should correspond to Python code. So we can print it!
TODO: Look at ASDL in ASDL from the original paper.
Serialization algorithm:
- They all start out empty, token = ProductType(), word = SumType(),
word.Compound = ConsType(), etc.
- And then you iterate over fields and do
osh_asdl.py
from asdl import meta
meta_word = meta.SumType()
meta_word.CompoundWord = meta.ConsType()
meta_word.CompoundWord.fields['a'] = meta_word_part ?
class word_e(object):
TokenWord = 1
CompoundWord = 2
BracedWordTree = 3
StringWord = 4
class word(asdl_base.CompoundObj):
ASDL_TYPE = meta_word # instance of meta.SumType
class CompoundWord(word):
ASDL_TYPE = meta_word.CompoundWord
So for each ASDL type, we have two Python classes
1. INSTANCE: One used to instantiate it
2. TYPE: A meta_ instance, instance of meta.Type(), that is used for
reflection. It is the ASDL_TYPE class attribute of the first type.
asdl_base.Instance() # base class for all instances
asdl_meta.Type() # base class for all types
w.ASDL_META ? That might be a better name.
NOTE: How would you represent this in C? By a big pointer graph? With oheap?
Yes oheap would be nice for this!
Could we generate C code?
It would be very compact.
So then the first half of osh_asdl.py is the META_word and META_word_part, etc.
And META_INT = meta.IntType()
And then the second half is all the instances
word, word_part_e, etc.
So I need to generate them dynamically first, and then do pretty printing.
"""
class Obj(object):
"""Base class for all ASDL instances (not types)."""
# instead of DESCRIPTOR
#
ASDL_TYPE = None # instance of meta.Type
UNDEFINED, BOOL, INT, STR, SUM, PRODUCT, CONS = range(7)
class Type(object):
tag = UNDEFINED # invalid value
def __repr__(self):
raise NotImplementedError
class BoolType(Type):
tag = BOOL
def __repr__(self):
return 'BoolType()'
class IntType(Type):
tag = INT
def __repr__(self):
return 'IntType()'
class StrType(Type):
tag = STR
def __repr__(self):
return 'StrType()'
class CompoundType(Type):
tag = UNDEFINED # abstract type
def __init__(self):
# name -> meta.Type
#
# The compilation process resolves names and fills this out.
self.fields = {}
def GetFieldType(self, name):
# get field type
pass
def GetFieldTypes(self):
# Iterate over (name, meta.Type() instances?)
pass
class ProductType(CompoundType):
tag = PRODUCT
def __repr__(self):
# It should
# How to refer to the fields?
# I guess you can have
# token = ProductType()
#
# word_part = SumType()
#
# word.Compound = ConsType()
#
# word.Compound.fields['parts'] = word_part
# word.Compound.fields['parts'] = word_part
#
#
# word.String = ConsType()
# word.String.fields['id'] = Id # Oh this is the generated type
# word.String.fields['s'] = STR_TYPE # singleton
return 'ProductType(TODO)'
# Usage:
#
# w = word.Compound(...)
# tag = w.ASDL_TYPE.GetTag()
# Not sure if I need this?
# tag = w.ASDL_TYPE.GetFieldType('name')
#
# I guess this is used for generic pretty printing?
# meta_tag = w.ASDL_TYPE.tag
#
# or should I use __type and __tag ?
# or TYPE and TAG
# ASDL_TYPE and ASDL_TAG? I think that makes it more meta
# asdl_type and asdl_tag
class ConsType(CompoundType):
tag = CONS
def GetTag(self):
# e.g. word.Compound or word.Token
pass
def __repr__(self):
return 'ConsType(TODO)'
class SumType():
tag = SUM
# TODO: Does this need to know about its children types for pretty printing?
# For type checking?
def __repr__(self):
return 'SumType(TODO)'
View
@@ -235,19 +235,20 @@ def __repr__(self):
return s
def _MakeFieldDescriptors(module, fields, app_types, add_spids=True):
def _MakeFieldDescriptors(module, fields, type_lookup, add_spids=True):
"""
Args:
module: asdl.Module
It must have a types attribute for lookup.
fields: list of asdl.Field instances
app_types: {string: <app-specific type}
add_spids: TODO this should be replaced by attributes?
"""
desc_lookup = {}
for f in fields:
# look up type by name
primitive_desc = asdl.DESCRIPTORS_BY_NAME.get(f.type)
app_desc = app_types.get(f.type)
# Lookup order: primitive, defined in the ASDL file, passed by the app
desc = primitive_desc or module.types.get(f.type) or app_desc
# It's either a primitive type or sum type
if primitive_desc is None and app_desc is None:
assert (isinstance(desc, asdl.Sum) or
isinstance(desc, asdl.Product)), 'field %s has descriptor %s' % (f, desc)
desc = type_lookup.Get(f.type)
# Wrap descriptor here. Then we can type check.
# And then encode too.
@@ -274,13 +275,12 @@ def _MakeFieldDescriptors(module, fields, app_types, add_spids=True):
return class_attr
def MakeTypes(module, root, app_types=None):
def MakeTypes(module, root, type_lookup):
"""
Args:
module: asdl.Module
root: an object/package to add types to
"""
app_types = app_types or {}
for defn in module.dfns:
typ = defn.value
@@ -304,10 +304,12 @@ def MakeTypes(module, root, app_types=None):
cls = type(class_name, (SimpleObj, ), class_attr)
setattr(root, class_name, cls)
# TODO: cons needs ASDL_TYPE?
for i, cons in enumerate(sum_type.types):
enum_id = i + 1
name = cons.name
val = cls(enum_id, cons.name) # Instantiate SimpleObj subtype
# Set a static attribute like op_id.Plus, op_id.Minus.
setattr(cls, name, val)
else:
@@ -324,8 +326,11 @@ def MakeTypes(module, root, app_types=None):
tag_num[cons.name] = tag # for enum
# Add 'int* spids' to every constructor.
class_attr = _MakeFieldDescriptors(module, cons.fields, app_types)
class_attr = _MakeFieldDescriptors(module, cons.fields, type_lookup)
class_attr['ASDL_TYPE'] = cons # asdl.Constructor
# TODO: remove these
class_attr['DESCRIPTOR'] = cons # asdl.Constructor
class_attr['tag'] = tag
@@ -338,7 +343,10 @@ def MakeTypes(module, root, app_types=None):
setattr(root, enum_name, tag_enum)
elif isinstance(typ, asdl.Product):
class_attr = _MakeFieldDescriptors(module, typ.fields, app_types)
class_attr = _MakeFieldDescriptors(module, typ.fields, type_lookup)
class_attr['ASDL_TYPE'] = typ
# TODO: remove
class_attr['DESCRIPTOR'] = typ
cls = type(defn.name, (CompoundObj, ), class_attr)
@@ -349,6 +357,7 @@ def MakeTypes(module, root, app_types=None):
def AssignTypes(src_module, dest_module):
"""For generated code."""
for name in dir(src_module):
if not name.startswith('__'):
v = getattr(src_module, name)
View
@@ -33,8 +33,15 @@ asdl-py() {
asdl/asdl_demo.py py $schema
}
repr() {
asdl/asdl_demo.py repr
smoke-test() {
# Print Schema (asdl_.py, py_meta.py)
asdl-py asdl/arith.asdl
# Parse real values and pretty print (format.py)
asdl-arith-format '1+2*3'
# encode.py
arith-demo
}
asdl-cpp() {
@@ -123,15 +130,15 @@ arith-demo() {
local data=_tmp/${name}.bin
# Write a binary
asdl-arith-encode '7 * 9' $data
asdl-arith-encode '1 + 2 * 3' $data
local bin=_tmp/${name}_demo
build-demo asdl/arith.asdl
set -x
gdb-trace $bin $data
#$bin $data
#gdb-trace $bin $data
$bin $data
}
# TODO: How big is oheap vs. the virtual memory size?
View
@@ -17,11 +17,12 @@ def _ParseAndMakeTypes(f, root):
module = asdl.parse(f)
app_types = {'id': asdl.UserType(Id)}
type_lookup = asdl.ResolveTypes(module, app_types)
# Check for type errors
if not asdl.check(module, app_types):
raise AssertionError('ASDL file is invalid')
py_meta.MakeTypes(module, root, app_types)
py_meta.MakeTypes(module, root, type_lookup)
f = util.GetResourceLoader().open('core/runtime.asdl')
View
@@ -132,7 +132,8 @@ def LoadSchema(f):
if not asdl.check(asdl_module, app_types):
raise AssertionError('ASDL file is invalid')
return asdl_module, app_types
type_lookup = asdl.ResolveTypes(asdl_module, app_types)
return asdl_module, type_lookup
# TODO: This should be the only lines in this module?
@@ -141,8 +142,8 @@ def LoadSchema(f):
root = sys.modules[__name__]
if 1:
f = util.GetResourceLoader().open('osh/osh.asdl')
asdl_module, app_types = LoadSchema(f)
py_meta.MakeTypes(asdl_module, root, app_types)
asdl_module, type_lookup = LoadSchema(f)
py_meta.MakeTypes(asdl_module, root, type_lookup)
f.close()
else:
# Get the types from elsewhere