Produce valid Cocopacks

As of this change -- just a few minor fixes -- CocoPy produces real Cocopacks!

	$ cocopy index.py | coco pack info -a -
	// Basic example of an AWS web server accessible over HTTP.
	package "webserver" {
	    dependencies [
	        aws: "*"
	    ]
	    module "index.py" {
	        imports [
	            "coconut/aws"
	        ]
	        exports [
	            ".init" -> ".init"
	            ".main" -> ".main"
	            "_Name" -> "__name__"
	            "main" -> "main"
	        ]
	        method ".init": ()
	        method ".main": ()
	        property "_Name": dynamic
	        method "main": (): dynamic
	    }
	}

The contents of various tokens aren't quite right yet (they must be
fully qualified), meaning the pack still won't verify and load.

But we're getting close...
This commit is contained in:
joeduffy 2017-04-07 13:32:12 -07:00
parent f1c1684d6f
commit b2f2c4adb1
4 changed files with 34 additions and 28 deletions

View file

@ -216,7 +216,7 @@ class Function(Definition):
def __init__(self, kind, name, parameters=None, return_type=None, body=None, loc=None):
assert isinstance(kind, basestring)
assert isinstance(name, Identifier)
assert (parameters is none or
assert (parameters is None or
(isinstance(parameters, list) and all(isinstance(node, LocalVariable) for node in parameters)))
assert return_type is None or isinstance(return_type, TypeToken)
assert body is None or isinstance(body, Block)
@ -229,7 +229,7 @@ class ModuleMethod(Function, ModuleMember):
"""A module method is just a function defined at the module scope."""
def __init__(self, name, parameters=None, return_type=None, body=None, loc=None):
assert isinstance(name, Identifier)
assert (parameters is none or
assert (parameters is None or
(isinstance(parameters, list) and all(isinstance(node, LocalVariable) for node in parameters)))
assert return_type is None or isinstance(return_type, TypeToken)
assert body is None or isinstance(body, Block)
@ -240,7 +240,7 @@ class ClassMethod(Function, ClassMember):
def __init__(self, name, parameters=None, return_type=None, body=None,
access=None, static=None, sealed=None, abstract=None, loc=None):
assert isinstance(name, Identifier)
assert (parameters is none or
assert (parameters is None or
(isinstance(parameters, list) and all(isinstance(node, LocalVariable) for node in parameters)))
assert return_type is None or isinstance(return_type, TypeToken)
assert body is None or isinstance(body, Block)
@ -438,21 +438,21 @@ class BoolLiteral(Literal):
"""A `bool`-typed literal (`true` or `false`)."""
def __init__(self, value, loc=None):
assert isinstance(value, bool)
super(BoolLiteral, self).__init__("BoolLiteral", loc)
super(BoolLiteral, self).__init__("BoolLiteral", loc=loc)
self.value = value
class NumberLiteral(Literal):
"""A `number`-typed literal (floating point IEEE 754)."""
def __init__(self, value, loc=None):
assert isinstance(value, int) or isinstance(value, long) or isinstance(value, float)
super(NumberLiteral, self).__init__("NumberLiteral", loc)
super(NumberLiteral, self).__init__("NumberLiteral", loc=loc)
self.value = value
class StringLiteral(Literal):
"""A `string`-typed literal."""
def __init__(self, value, loc=None):
assert isinstance(value, basestring)
super(StringLiteral, self).__init__("StringLiteral", loc)
super(StringLiteral, self).__init__("StringLiteral", loc=loc)
self.value = value
class ArrayLiteral(Literal):

View file

@ -104,9 +104,11 @@ class Transformer:
var_modname = "__name__"
members[var_modname] = ast.ModuleProperty(
self.ident(var_modname), self.type_token(tokens.type_dynamic))
modname_init_expr = ast.BinaryOperatorExpression(
ast.LoadLocationExpression(var_modname), ast.binop_assign, ast.StringLiteral("__main__"))
initstmts.append(ast.ExpressionStatement(modname_init_expr))
modname_init = ast.BinaryOperatorExpression(
ast.LoadLocationExpression(ast.Token(var_modname)),
ast.binop_assign,
ast.StringLiteral("__main__"))
initstmts.append(ast.ExpressionStatement(modname_init))
# Enumerate the top-level statements and put them in the right place. This transformation is subtle because
# loose code (arbitrary statements) aren't supported in CocoIL. Instead, we must elevate top-level definitions
@ -125,7 +127,7 @@ class Transformer:
else:
# For all other statement nodes, simply accumulate them for the module initializer.
initstmt = self.transform_stmt(stmt)
assert initstmt.is_statement()
assert isinstance(initstmt, ast.Statement)
initstmts.append(initstmt)
# If any top-level statements spilled over, add them to the initializer.
@ -148,7 +150,8 @@ class Transformer:
# By default, Python exports everything, so add all declarations to the list.
exports = dict()
for name in members:
exports[name] = name # TODO: this needs to be a full qualified token.
# TODO: this needs to be a full qualified token.
exports[name] = ast.Export(self.ident(name), ast.Token(name))
return ast.Module(self.ident(name), list(imports), exports, members)
@ -206,7 +209,7 @@ class Transformer:
assert False, "Unrecognized statement node: {}".format(type(node).__name__)
# Check that the return is good and then return it.
assert isinstance(stmt, ast.Node) and stmt.is_statement(), \
assert isinstance(stmt, ast.Statement), \
"Expected PyAST node {} to produce a statement; got {}".format(
type(node).__name__, type(stmt).__name__)
return stmt
@ -218,19 +221,21 @@ class Transformer:
stmts.append(self.transform_stmt(node))
# Propagate location information based on the inner statements.
loc = None
if len(stmts) > 0:
file = None
start = None
end = None
if stmts[0].loc:
file = stmts[0].loc.file
start = stmts[0].loc.start
if stmts[len(stmts)-1].loc:
if not start:
file = stmts[len(stmts)-1].file
start = stmts[len(stmts)-1].loc.start
end = stmts[len(stmts)-1].loc.end
loc = ast.Location(file, start, end)
firstloc = stmts[0].loc
lastloc = stmts[len(stmts)-1].loc
file, start, end = None, None, None
if firstloc:
file = firstloc.file
start = firstloc.start
if lastloc:
if not firstloc:
file = lastloc.file
start = loastloc.start
end = lastloc.end
if file and start:
loc = ast.Location(file, start, end)
return ast.Block(stmts, loc)
@ -411,7 +416,7 @@ class Transformer:
assert False, "Unrecognized statement node: {}".format(type(node).__name__)
# Check that the return is good and then return it.
assert isinstance(expr, ast.Node) and expr.is_expression(), \
assert isinstance(expr, ast.Expression), \
"Expected PyAST node {} to produce an expression; got {}".format(
type(node).__name__, type(expr).__name__)
return expr
@ -419,7 +424,7 @@ class Transformer:
def transform_Attribute(self, node):
assert not node.ctx
obj = self.transform_expr(node.value)
return ast.LoadDynamicExpression(node.attr, obj, loc=self.loc_from(node))
return ast.LoadDynamicExpression(ast.StringLiteral(node.attr), obj, loc=self.loc_from(node))
def transform_BinOp(self, node):
self.not_yet_implemented(node) # left, op, right
@ -487,7 +492,7 @@ class Transformer:
def transform_Name(self, node):
assert not node.ctx
return ast.LoadLocationExpression(node.id, loc=self.loc_from(node))
return ast.LoadLocationExpression(ast.Token(node.id), loc=self.loc_from(node))
def transform_NameID(self, node):
assert not node.ctx

View file

@ -51,7 +51,7 @@ def to_serializable_dict(m, opts=None):
def to_serializable_value(v, opts=None):
"""This routine converts a singular value into its JSON-serializable equivalent."""
if (isinstance(v, str) or isinstance(v, unicode) or
if (isinstance(v, basestring) or
isinstance(v, int) or isinstance(v, long) or isinstance(v, float) or
isinstance(v, bool) or v is None):
# Simple serializable values can be stored without any translation.

View file

@ -34,4 +34,5 @@ typemod_func_sep = ")" # the separator between parameters and return of
acc_public = "public"
acc_private = "private"
acc_protected = "protected"
accs = set([ acc_public, acc_private, acc_protected ])