[cig-commits] r5070 - cs/merlin/trunk/merlin
leif at geodynamics.org
leif at geodynamics.org
Fri Oct 20 11:39:09 PDT 2006
Author: leif
Date: 2006-10-20 11:39:09 -0700 (Fri, 20 Oct 2006)
New Revision: 5070
Added:
cs/merlin/trunk/merlin/builtins.cfg
cs/merlin/trunk/merlin/classes.py
cs/merlin/trunk/merlin/defaults.cfg
cs/merlin/trunk/merlin/objects.py
cs/merlin/trunk/merlin/parser.py
cs/merlin/trunk/merlin/properties.py
Modified:
cs/merlin/trunk/merlin/__init__.py
Log:
More work on the Merlin project parser. I don't know if this will be
ready in a reasonable time frame (and I'm not sure I even like .cfg
syntax for project files), so I'm going to punt Merlin's advanced
Pythonic project parser technology for now.
Modified: cs/merlin/trunk/merlin/__init__.py
===================================================================
--- cs/merlin/trunk/merlin/__init__.py 2006-10-20 08:19:58 UTC (rev 5069)
+++ cs/merlin/trunk/merlin/__init__.py 2006-10-20 18:39:09 UTC (rev 5070)
@@ -42,331 +42,14 @@
#
-### don't forget, .svn metadata gone in tarball; need gen manifest/sources
-### imports.people? Requires decoration
-### how to tie to filesystem? svn:metadata?
-### implicit roots for traits
-####### style: requires-imports?
-### exports, (freeze-and-?)include in project
-
-### food for thought:
-###### projects have implicit "root = ${thisfile}.${directory}"
-###### "mounting" stuff... almost nothing has to be hard-coded in merlin itself
-
-
-__inUserSpace__ = False
-
-
-class Node(object):
-
- class UserAttributes(object):
-
- def __init__(self):
- self._factory = None
- self._frozen = False
-
- def __getattribute__(self, attr):
- xgetattr = super(Node.UserAttributes, self).__getattribute__
- if (attr.startswith('_') or
- self._frozen or
- self._factory is None):
- value = xgetattr(attr)
- else:
- try:
- value = xgetattr(attr)
- except AttributeError:
- value = self._createNewAttribute(attr)
- return value
-
- def _createNewAttribute(self, attr):
- value = self._factory(attr)
- setattr(self, attr, value)
- value._name = attr
- return value
-
- isContextual = False
-
- def __init__(self, name):
- super(Node, self).__init__()
- self.name = name
- self.attrs = Node.UserAttributes()
- self.attrs._factory = Node
-
- def __getattribute__(self, attr):
- global __inUserSpace__, __lookups__
- if __inUserSpace__:
- __lookups__.append(attr)
- return self.getUserAttribute(attr)
- return super(Node, self).__getattribute__(attr)
-
- def getFactory(self): return self.attrs._factory
- def setFactory(self, value): self.attrs._factory = value
- factory = property(getFactory, setFactory)
-
- def __repr__(self):
- return self.__class__.__name__
-
- def getUserAttribute(self, attr): return getattr(self.attrs, attr)
-
- def setUserAttribute(self, attr, value): setattr(self.attrs, attr, value)
-
- def freeze(self):
- self.attrs._frozen = True
- for name, child in self.children():
- child.freeze()
- return
-
- def resolveAll(self, context):
- context.push(self)
- self.resolve(context)
- context.pop()
-
- def resolve(self, context):
- for name, child in self.children():
- child.resolveAll(context)
- return
-
- def populateNamespace(self, namespace):
- for name, child in self.children():
- namespace[name] = child
- return namespace
-
- def dump(self, indent = 0):
- i = indent * " "
- print "%s%s(%r):" % (i, self.name, self)
- for name, child in self.children():
- child.dump(indent + 1)
- return
-
- def children(self):
- for k,v in self.attrs.__dict__.iteritems():
- if k.startswith('_'):
- continue
- yield k,v
- return
-
- def find(self, name):
- return self.follow(name.split('.'))
-
- def follow(self, path):
- node = self
- for attr in path:
- node = node.getUserAttribute(attr)
- return node
-
-
-class LeafNode(Node):
- def dump(self, indent = 0):
- i = indent * " "
- print "%s%s = %r" % (i, self.name, self)
-
-
-class Literal(LeafNode):
-
- def __init__(self, name, value):
- super(Literal, self).__init__(name)
- self._value = value
-
- def __repr__(self):
- return repr(self._value)
-
-
-class SymbolicExpr(LeafNode):
-
- def __init__(self, name, expr):
- super(SymbolicExpr, self).__init__(name)
- self._expr = expr
- self._value = None
-
- def __repr__(self):
- return repr(self._expr)
-
- def resolve(self, context):
- self._value = context.resolve(self)
-
-
-class ErrorNode(LeafNode):
-
- def __init__(self, name, exception):
- super(ErrorNode, self).__init__(name)
- self._exception = exception
-
- def __repr__(self):
- return repr(self._exception)
-
-
-class Object(Node): pass
-
-
-class Action(Object): pass
-class Component(Object): pass
-class Program(Object): pass
-class Library(Object): pass
-
-class Person(Object): pass
-
-
-class Project(Object):
-
- isContextual = True
-
- def __init__(self, name):
- super(Project, self).__init__(name)
- self.attrs.actions.factory = Action
- self.attrs.components.factory = Component
- self.attrs.programs.factory = Program
- self.attrs.libraries.factory = Library
-
-
-class Root(Object):
- isContextual = True
- def __init__(self, name="root"):
- super(Root, self).__init__(name)
-
-
-class Context(object):
-
- def __init__(self, root):
- super(Context, self).__init__()
- self.path = []
- self.root = root
- self.errors = False
- self.namespaceStack = [dict()] # pre-root namespace
-
- def push(self, node):
- self.path.append(node)
- if node.isContextual:
- new = dict(self.currentNamespace())
- node.populateNamespace(new)
- self.namespaceStack.append(new)
- return
-
- def pop(self):
- node = self.path.pop()
- if node.isContextual:
- self.namespaceStack.pop()
- return node
-
- def currentPathName(self): return '.'.join([node.name for node in self.path[1:]])
-
- def resolveAll(self):
- self.root.resolveAll(self)
-
- def resolve(self, symbol):
- value, exception = self.lookup(symbol._expr)
- if exception:
- value = [ErrorNode(symbol.name, exception)]
- self.unresolvedSymbol(symbol, exception)
- return value
-
- def lookup(self, identifier):
- value = None
- exception = None
- userSpace = dict(globals())
- userSpace['__inUserSpace__'] = True
- userSpace['__lookups__'] = []
- try:
- value = eval(identifier, self.currentNamespace(), userSpace)
- except Exception, e:
- exception = e
- print '@@@@', userSpace['__lookups__']
- return value, exception
-
- def currentNamespace(self): return self.namespaceStack[-1]
-
- def unresolvedSymbol(self, symbol, exception):
- import sys
-
- self.errors = True
- name = self.currentPathName()
-
- print >> sys.stderr, name
- print >> sys.stderr, " error: cannot resolve '%s':" % symbol._expr
- print >> sys.stderr, " %s" % exception
- return
-
-
-def parseSpell(filename = 'project.cfg'):
-
- from os.path import expanduser, join
- from ConfigParser import ConfigParser
-
- # initialize
- spell = ConfigParser()
- #spell.readfp(open('defaults.cfg')) # pkg
- spell.read(expanduser(join('~', '.merlin', 'etc', 'merlin', 'preferences.cfg')))
-
- # parse the spell file
- spell.readfp(open(filename))
-
- return spell
-
-
-def parseSpellSyntaxTree(spell):
- """Return an abstract syntax tree for the raw syntax tree <spell>.
-
- """
-
- # This function is, in essence, a "tree parser" in the ANTLR
- # sense.
-
- from curses.ascii import isalpha
-
- ast = Root()
- ast.attrs.projects.factory = Project
- ast.attrs.people.factory = Person
-
- for section in spell.sections():
-
- # Walk the path given by the section name, starting at the
- # root.
- current = ast.find(section)
-
- # Walk the path given by each item name, starting at the
- # current node.
- items = spell.items(section)
- for name, value in items:
- path = name.split('.')
- node = current.follow(path[:-1])
- attr = path[-1]
-
- if len(value) and (isalpha(value[0]) or value[0] == '['):
- # These will be evaluated later.
- value = SymbolicExpr(attr, value)
- else:
- value = eval(value)
- if isinstance(value, basestring):
- value = ' '.join(value.splitlines())
- value = value.strip()
- value = Literal(attr, value)
- node.setUserAttribute(attr, value)
-
- return ast
-
-
def setup():
- from setuptools import setup, find_packages, Extension
+ from parser import parse
+ root = parse()
+ return
- spell = parseSpell()
- ast = parseSpellSyntaxTree(spell)
-
- ast.dump()
+def oldsetup():
- # The spell has been cast; no magic node creation beyond this
- # point. Perhaps we need some Latin here or something, instead of
- # 'freeze'.
- ast.freeze()
-
- # Fix-up symbolic references.
- context = Context(ast)
- context.resolveAll()
- if context.errors:
- import sys
- sys.exit("merlin: error(s) loading project")
-
- return
-
name = config.get('project', 'name')
version = config.get('project', 'version')
extensions = config.get('project', 'extensions')
Added: cs/merlin/trunk/merlin/builtins.cfg
===================================================================
--- cs/merlin/trunk/merlin/builtins.cfg 2006-10-20 08:19:58 UTC (rev 5069)
+++ cs/merlin/trunk/merlin/builtins.cfg 2006-10-20 18:39:09 UTC (rev 5070)
@@ -0,0 +1,39 @@
+# merlin builtins
+
+
+# modifiers -- people
+
+[modifiers.author]
+[modifiers.original]
+[modifiers.primary]
+
+
+# modifiers -- components
+
+[modifiers.experimental]
+[modifiers.legacy]
+[modifiers.native]
+[modifiers.optional]
+[modifiers.primitive]
+[modifiers.private]
+[modifiers.scriptable]
+
+
+# PyPI classifiers
+
+[classifiers.Topic.SciEng.Physics]
+python.distutils.setup.classifier = 'Topic :: Scientific/Engineering :: Physics'
+
+
+# licenses
+
+[licenses.BSD]
+python.distutils.setup.license = 'BSD'
+python.distutils.setup.classifier = 'License :: OSI Approved :: BSD License'
+
+[licenses.GPL]
+python.distutils.setup.license = 'GPL'
+python.distutils.setup.classifier = 'License :: OSI Approved :: GNU General Public License (GPL)'
+
+
+# end of file
Added: cs/merlin/trunk/merlin/classes.py
===================================================================
--- cs/merlin/trunk/merlin/classes.py 2006-10-20 08:19:58 UTC (rev 5069)
+++ cs/merlin/trunk/merlin/classes.py 2006-10-20 18:39:09 UTC (rev 5070)
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# merlin
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# * Neither the name of the California Institute of Technology nor
+# the names of its contributors may be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from properties import Property
+
+
+class UserObjectClass(type):
+
+
+ def __init__(cls, name, bases, dct):
+ type.__init__(name, bases, dct)
+
+ properties = {}
+
+ if False:
+ bases = list(bases)
+ bases.reverse()
+ for base in bases:
+ try:
+ properties.update(base._properties)
+ except AttributeError:
+ pass
+
+ for name, prop in [kv for kv in dct.iteritems()
+ if isinstance(kv[1], Property)]:
+ prop.name = name
+ properties[prop.name] = prop
+
+ cls._properties = properties
+
+ return
+
+
+# end of file
Added: cs/merlin/trunk/merlin/defaults.cfg
===================================================================
--- cs/merlin/trunk/merlin/defaults.cfg 2006-10-20 08:19:58 UTC (rev 5069)
+++ cs/merlin/trunk/merlin/defaults.cfg 2006-10-20 18:39:09 UTC (rev 5070)
@@ -0,0 +1,2 @@
+# defaults for merlin
+
Added: cs/merlin/trunk/merlin/objects.py
===================================================================
--- cs/merlin/trunk/merlin/objects.py 2006-10-20 08:19:58 UTC (rev 5069)
+++ cs/merlin/trunk/merlin/objects.py 2006-10-20 18:39:09 UTC (rev 5070)
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# merlin
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# * Neither the name of the California Institute of Technology nor
+# the names of its contributors may be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from properties import *
+from classes import UserObjectClass
+
+
+class UserObject(object):
+
+ __metaclass__ = UserObjectClass
+
+ modifiers = ModifierList()
+
+
+class Contact(UserObject):
+
+ email = EMail()
+
+
+class Person(Contact):
+
+ name = String()
+
+
+class MailingList(Contact): pass
+
+
+class Project(UserObject):
+
+ version = String()
+
+ summary = String()
+ description = String()
+
+ classifiers = Classifiers()
+ license = License()
+
+ homepage = URL()
+ contact = Contact()
+
+
+class Target(UserObject):
+
+ requires = TargetList()
+
+
+class Library(Target): pass
+class ImportLibrary(Library): pass
+
+
+class ProjectLibrary(Library):
+
+ authors = PersonList()
+
+ #source = SourceCode()
+
+
+# end of file
Copied: cs/merlin/trunk/merlin/parser.py (from rev 5058, cs/merlin/trunk/merlin/__init__.py)
===================================================================
--- cs/merlin/trunk/merlin/__init__.py 2006-10-17 09:11:50 UTC (rev 5058)
+++ cs/merlin/trunk/merlin/parser.py 2006-10-20 18:39:09 UTC (rev 5070)
@@ -0,0 +1,488 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# merlin
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# * Neither the name of the California Institute of Technology nor
+# the names of its contributors may be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+### don't forget, .svn metadata gone in tarball; need gen manifest/sources
+### imports.people? Requires decoration
+### how to tie to filesystem? svn:metadata?
+### exports, (freeze-and-?)include in project
+
+
+__inUserSpace__ = False
+
+
+root = None
+
+
+class Node(object):
+
+ class UserAttributes(object):
+
+ def __init__(self):
+ self._factory = None
+ self._frozen = False
+
+ def __getattribute__(self, attr):
+ xgetattr = super(Node.UserAttributes, self).__getattribute__
+ if (attr.startswith('_') or
+ self._frozen or
+ self._factory is None):
+ value = xgetattr(attr)
+ else:
+ try:
+ value = xgetattr(attr)
+ except AttributeError:
+ value = self._createNewAttribute(attr)
+ return value
+
+ def _createNewAttribute(self, attr):
+ value = self._factory(attr)
+ setattr(self, attr, value)
+ value._name = attr
+ return value
+
+ isContextual = False
+
+ def __init__(self, name):
+ super(Node, self).__init__()
+ self.name = name
+ self.implicitNamespaceNodes = []
+ self._ = self.UserAttributes()
+ self._._factory = Node
+
+ def __getattribute__(self, attr):
+ global __inUserSpace__
+ if __inUserSpace__:
+ # return self.getUserAttribute(attr)
+ return getattr(super(Node, self).__getattribute__('_'), attr)
+ return super(Node, self).__getattribute__(attr)
+
+ def __repr__(self):
+ return self.__class__.__name__
+
+ def getUserAttribute(self, attr): return getattr(self._, attr)
+
+ def setUserAttribute(self, attr, value): setattr(self._, attr, value)
+
+ def parseAttributeValue(self, attr, value):
+ from curses.ascii import isalpha
+ if len(value) == 0:
+ value = Literal(attr, "")
+ elif isalpha(value[0]) or value[0] == '[':
+ # These will be evaluated later.
+ value = SymbolicExpr(attr, value)
+ else:
+ value = eval(value)
+ if isinstance(value, basestring):
+ value = ' '.join(value.splitlines())
+ value = value.strip()
+ value = Literal(attr, value)
+ self.setUserAttribute(attr, value)
+ return
+
+ def bless(self, attr, value): pass
+
+ def freeze(self):
+ self._._frozen = True
+ for name, child in self.children():
+ child.freeze()
+ return
+
+ def resolveAll(self, context):
+ context.push(self)
+ self.resolve(context)
+ context.pop()
+
+ def resolve(self, context):
+ for name, child in self.children():
+ self.bless(name, child) # bless the child
+ child.resolveAll(context)
+ return
+
+ def populateNamespace(self, namespace):
+ for name, child in self.children():
+ namespace[name] = child
+ return namespace
+
+ def prepareNamespace(self, namespace):
+ for node in self.implicitNamespaceNodes:
+ for name, child in node.children():
+ namespace[name] = child
+ return
+
+ def addToImplicitNamespace(self, node): self.implicitNamespaceNodes.append(node)
+
+ def dump(self, indent = 0):
+ i = indent * " "
+ print "%s%s(%r):" % (i, self.name, self)
+ for name, child in self.children():
+ child.dump(indent + 1)
+ return
+
+ def children(self):
+ for k,v in self._.__dict__.iteritems():
+ if k.startswith('_'):
+ continue
+ yield k,v
+ return
+
+ def find(self, name):
+ return self.follow(name.split('.'))
+
+ def follow(self, path):
+ node = self
+ for attr in path:
+ node = node.getUserAttribute(attr)
+ return node
+
+
+class LeafNode(Node):
+ def dump(self, indent = 0):
+ i = indent * " "
+ print "%s%s = %r" % (i, self.name, self)
+
+
+class Literal(LeafNode):
+
+ def __init__(self, name, value):
+ super(Literal, self).__init__(name)
+ self._value = value
+
+ def __repr__(self):
+ return repr(self._value)
+
+
+class SymbolicExpr(LeafNode):
+
+ def __init__(self, name, expr):
+ super(SymbolicExpr, self).__init__(name)
+ self._expr = expr
+ self._value = None
+
+ def __repr__(self):
+ return repr(self._expr)
+
+ def resolve(self, context):
+ self._value = context.resolve(self)
+
+
+class Modifier(LeafNode):
+
+ def __init__(self, name):
+ super(Modifier, self).__init__(name)
+
+
+class ErrorNode(LeafNode):
+
+ def __init__(self, name, exception):
+ super(ErrorNode, self).__init__(name)
+ self._exception = exception
+
+ def __repr__(self):
+ return repr(self._exception)
+
+
+class Object(Node):
+
+ def bless(self, attr, value):
+ super(Object, self).bless(attr, value)
+ if attr == 'modifiers':
+ value.addToImplicitNamespace(root._.modifiers)
+ elif attr == 'classifiers':
+ value.addToImplicitNamespace(root._.classifiers)
+ return
+
+
+class PersonNode(Object): pass
+class ActionNode(Object): pass
+
+
+class SourceFileList(Object):
+ def __init__(self, name, filenames):
+ super(SourceFileList, self).__init__(name)
+ self.filenames = filenames
+
+
+class SourceDirectory(Object):
+ def __init__(self, name, directory):
+ super(SourceDirectory, self).__init__(name)
+ self.directory = directory
+
+
+class SourceFilesNode(Object):
+
+ def parseAttributeValue(self, attr, value):
+ value = SourceFileList(value.split())
+ self.setUserAttribute(attr, value)
+
+
+class SourceNode(Node):
+
+ def parseAttributeValue(self, attr, value):
+ if attr == 'directory':
+ value = SourceDirectory(attr, value)
+ elif attr == 'files':
+ value = SourceFileList(attr, value)
+ else:
+ super(SourceNode, self).parseAttributeValue(attr, value)
+ self.setUserAttribute(attr, value)
+
+
+class ComponentNode(Object):
+
+ def __init__(self, name):
+ super(ComponentNode, self).__init__(name)
+ self._.source = SourceNode('source')
+
+ def bless(self, attr, value):
+ super(ComponentNode, self).bless(attr, value)
+ if attr == 'authors':
+ value.addToImplicitNamespace(root._.people)
+ return
+
+
+class ProgramNode(ComponentNode): pass
+class LibraryNode(ComponentNode): pass
+
+
+class ProjectNode(Object):
+
+ isContextual = True
+
+ def __init__(self, name):
+ super(ProjectNode, self).__init__(name)
+
+ #from objects import Component, Program, Library
+ self._.actions._._factory = ActionNode
+ self._.components._._factory = ComponentNode #NodeFactory(Component)
+ self._.programs._._factory = ProgramNode #NodeFactory(Program)
+ self._.libraries._._factory = LibraryNode #NodeFactory(Library)
+
+
+class FSNode(Node):
+
+ def __init__(self, pathname):
+ super(FSNode, self).__init__(pathname)
+ self.pathname = pathname
+ self._._factory = FSNodeFactory(pathname)
+
+
+class FSNodeFactory(object):
+
+ def __init__(self, pathname):
+ super(FSNodeFactory, self).__init__()
+ from os.path import exists
+ assert exists(pathname)
+ self.pathname = pathname
+
+ def __call__(self, name):
+ from os.path import exists, join
+ childPathname = join(self.pathname, name)
+ assert exists(childPathname)
+ return FSNode(childPathname)
+
+
+class Root(Object):
+ isContextual = True
+ def __init__(self, name='root'):
+ super(Root, self).__init__(name)
+
+
+class Context(object):
+
+ def __init__(self, root):
+ super(Context, self).__init__()
+ self.path = []
+ self.root = root
+ self.errors = False
+ self.namespaceStack = [dict()] # pre-root namespace
+
+ def push(self, node):
+ self.path.append(node)
+ if node.isContextual:
+ new = dict(self.currentNamespace())
+ node.populateNamespace(new)
+ self.namespaceStack.append(new)
+ return
+
+ def pop(self):
+ node = self.path.pop()
+ if node.isContextual:
+ self.namespaceStack.pop()
+ return node
+
+ def currentPathName(self): return '.'.join([node.name for node in self.path[1:]])
+
+ def resolveAll(self):
+ self.root.resolveAll(self)
+
+ def resolve(self, symbol):
+ namespace = dict(self.currentNamespace())
+ symbol.prepareNamespace(namespace)
+ value, exception = self.lookup(symbol._expr, namespace)
+ if exception:
+ value = [ErrorNode(symbol.name, exception)]
+ self.unresolvedSymbol(symbol, exception)
+ return value
+
+ def lookup(self, expr, namespace):
+ value = None
+ exception = None
+
+ # For some reason I don't understand, passing
+ # '__inUserSpace__' into 'eval' as a part of a 'globals'
+ # dictionary doesn't work. So for now, use the time honored
+ # technique of save & restore.
+ global __inUserSpace__
+ __inUserSpace__ = True
+
+ try:
+ value = eval(expr, namespace)
+ except Exception, e:
+ exception = e
+
+ __inUserSpace__ = False
+
+ return value, exception
+
+ def currentNamespace(self): return self.namespaceStack[-1]
+
+ def unresolvedSymbol(self, symbol, exception):
+ import sys
+
+ self.errors = True
+ name = self.currentPathName()
+
+ print >> sys.stderr, name
+ print >> sys.stderr, " error: cannot resolve '%s':" % symbol._expr
+ print >> sys.stderr, " %s" % exception
+ return
+
+
+def parseSpell(filename = 'project.cfg'):
+
+ from os.path import expanduser, join
+ from ConfigParser import ConfigParser
+ from pkg_resources import resource_stream
+
+ # initialize
+ spell = ConfigParser()
+ for name in ["builtins", "defaults"]:
+ fp = resource_stream(__name__, "%s.cfg" % name)
+ spell.readfp(fp)
+ spell.read(expanduser(join('~', '.merlin', 'etc', 'merlin', 'preferences.cfg')))
+
+ # parse the spell file
+ spell.readfp(open(filename))
+
+ return spell
+
+
+def parseSpellSyntaxTree(spell):
+ """Return an abstract syntax tree for the raw syntax tree <spell>.
+
+ """
+
+ # This function is, in essence, a "tree parser" in the ANTLR
+ # sense.
+
+ import os
+
+ ast = Root()
+ ast._.projects._._factory = ProjectNode
+ ast._.people._._factory = PersonNode
+
+ ast._.filesystem._.srcdir._._factory = FSNodeFactory(os.getcwd())
+ ast._.filesystem._._factory = FSNodeFactory('/') # this must be last
+
+ for section in spell.sections():
+
+ # Walk the path given by the section name, starting at the
+ # root.
+ current = ast.find(section)
+
+ items = spell.items(section)
+
+ for name, value in items:
+
+ # Walk the path given by each item name, starting at the
+ # current node.
+ path = name.split('.')
+ node = current.follow(path[:-1])
+ attr = path[-1]
+
+ # Parse each value.
+ node.parseAttributeValue(attr, value)
+
+ return ast
+
+
+def parse():
+ spell = parseSpell()
+
+ ast = parseSpellSyntaxTree(spell)
+
+ #ast.dump()
+
+ # The spell has been cast; no magic node creation beyond this
+ # point. Perhaps we need some Latin here or something, instead of
+ # 'freeze'.
+ ast.freeze()
+
+ # The tree is becoming less abstract...
+ global root
+ root = ast
+
+ # Fix-up symbolic references.
+ context = Context(root)
+ context.resolveAll()
+
+ if context.errors:
+ import sys
+ sys.exit("merlin: error(s) loading project")
+
+ # The tree is no longer abstract.
+ del ast
+
+
+# end of file
Added: cs/merlin/trunk/merlin/properties.py
===================================================================
--- cs/merlin/trunk/merlin/properties.py 2006-10-20 08:19:58 UTC (rev 5069)
+++ cs/merlin/trunk/merlin/properties.py 2006-10-20 18:39:09 UTC (rev 5070)
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# merlin
+#
+# Copyright (c) 2006, California Institute of Technology
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# * Neither the name of the California Institute of Technology nor
+# the names of its contributors may be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+class Property(object):
+
+ def __init__(self, name=None):
+ self.name = name
+
+ def __get__(self, instance, cls=None):
+ if instance is None:
+ return self
+ return getattr(instance, self.name)
+
+ def __set__(self, instance, value):
+ setattr(instance, self.name, value)
+
+ def parseValue(self, value):
+ return eval(value)
+
+ def resolveValue(self, value, context): pass
+
+
+# strings
+
+class String(Property):
+
+ def parseValue(self, value):
+ value = ' '.join(value.splitlines())
+ return value.strip()
+
+class EMail(String): pass
+class URL(String): pass
+
+
+# symbol lists
+
+class SymbolList(Property):
+
+ def parseValue(self, value):
+ return value.split()
+
+ def resolveValue(self, value, context):
+ return context.resolve(self)
+
+class Classifiers(SymbolList): pass
+class License(SymbolList): pass
+class ModifierList(SymbolList): pass
+class TargetList(SymbolList): pass
+class PersonList(SymbolList): pass
+
+
+# end of file
More information about the cig-commits
mailing list