Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ianj-als/pcl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Johnson <ianj@wgrids.com>2013-10-16 20:26:03 +0400
committerIan Johnson <ianj@wgrids.com>2013-10-16 20:26:03 +0400
commit6451c37835ef255ec01cbe182094a11db889d6e0 (patch)
treef9c297f899d888deb096b259420e322c9bb5afd6
parentad4a1c1065086f4902b15e46e8b3a9013c00d419 (diff)
More resolution code
-rw-r--r--src/pclc/parser/expressions.py5
-rw-r--r--src/pclc/parser/pcl_parser.py2
-rw-r--r--src/pclc/visitors/first_pass_resolver_visitor.py143
-rw-r--r--src/pclc/visitors/second_pass_resolver_visitor.py17
-rw-r--r--src/pclc/visitors/third_pass_resolver_visitor.py28
-rw-r--r--src/runtime/pcl/os/path.py3
-rw-r--r--src/runtime/pcl/util/string.py3
7 files changed, 148 insertions, 53 deletions
diff --git a/src/pclc/parser/expressions.py b/src/pclc/parser/expressions.py
index b12411d..e2b0d73 100644
--- a/src/pclc/parser/expressions.py
+++ b/src/pclc/parser/expressions.py
@@ -16,8 +16,9 @@
# You should have received a copy of the GNU General Public License
# along with Pipeline Creation Language (PCL). If not, see <http://www.gnu.org/licenses/>.
#
-from entity import Entity
+import types
+from entity import Entity
from mappings import TopMapping, BottomMapping, LiteralMapping
from pypeline.core.types.just import Just
@@ -28,7 +29,7 @@ class Literal(Entity):
self.value = value
def __str__(self):
- return str(self.value)
+ return '"' + str(self.value) + '"' if type(self.value) in types.StringTypes else str(self.value)
def __repr__(self):
return "<Literal: value = [%s], entity = %s>" % \
diff --git a/src/pclc/parser/pcl_parser.py b/src/pclc/parser/pcl_parser.py
index 86a398f..08f8428 100644
--- a/src/pclc/parser/pcl_parser.py
+++ b/src/pclc/parser/pcl_parser.py
@@ -395,7 +395,7 @@ def p_function(p):
def p_opt_function_args(p):
'''opt_function_args : function_arg_list
| '''
- if len(p) > 0:
+ if len(p) > 1:
p[0] = p[1]
else:
p[0] = list()
diff --git a/src/pclc/visitors/first_pass_resolver_visitor.py b/src/pclc/visitors/first_pass_resolver_visitor.py
index 93e9609..37a47e9 100644
--- a/src/pclc/visitors/first_pass_resolver_visitor.py
+++ b/src/pclc/visitors/first_pass_resolver_visitor.py
@@ -125,7 +125,6 @@ class FirstPassResolverVisitor(ResolverVisitor):
python_module_interface = []
if declarations:
- print "\n".join(map(lambda d: str(d), declarations))
for decl in declarations:
# Count occurrences of declaration identifiers
try:
@@ -250,10 +249,21 @@ class FirstPassResolverVisitor(ResolverVisitor):
module_spec = {}
if imported_module:
funcs_and_specs = [(o[0], inspect.getargspec(o[1])) \
- for o in inspect.getmembers(imported_module) \
- if inspect.isfunction(o[1])]
+ for o in inspect.getmembers(imported_module, \
+ lambda t: inspect.isfunction(t))]
for k, v in funcs_and_specs:
- module_spec[k] = v
+ if v.keywords:
+ self._add_warnings("WARNING: %(filename)s at line %(lineno)d, " \
+ "dropping function %(func_name)s imported " \
+ "from module %(module_name)s since arguments " \
+ "are unsupported.",
+ [k],
+ lambda n: {'filename' : an_import.filename,
+ 'lineno' : an_import.lineno,
+ 'func_name' : k,
+ 'module_name' : an_import.module_name})
+ else:
+ module_spec[k] = v
return module_spec
@@ -782,17 +792,25 @@ class FirstPassResolverVisitor(ResolverVisitor):
@multimethod(Function)
def visit(self, function):
+ # Get the package alias and function name
package_alias, name = function.name.split(".")
+ # The imports
import_symbol_dict = self._module.resolution_symbols['imports']
- if not import_symbol_dict.has_key(package_alias):
- self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function package alias %(alias)s",
- [function],
- lambda f: {'filename' : f.filename,
- 'lineno' : f.lineno,
- 'alias' : package_alias})
- else:
- functions = import_symbol_dict[package_alias]
+
+ # This error is a little pointless.
+ #if not import_symbol_dict.has_key(package_alias):
+ # self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function package alias %(alias)s",
+ # [function],
+ # lambda f: {'filename' : f.filename,
+ # 'lineno' : f.lineno,
+ # 'alias' : package_alias})
+
+ # The key for the imports is an Identifier, so create an Identifier
+ # from the string package alias derived from the function call.
+ alias_identifier = Identifier(None, -1, package_alias)
+ if import_symbol_dict.has_key(alias_identifier):
+ functions = import_symbol_dict[alias_identifier]
if not functions.has_key(name):
self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function %(alias)s.%(name)s",
[function],
@@ -803,41 +821,72 @@ class FirstPassResolverVisitor(ResolverVisitor):
else:
# The argument spec from the imported module
function_arg_spec = functions[name]
-
- if len(function_arg_spec.args) != len(function.arguments):
- self._add_errors("ERROR: %(filename)s at line %(lineno)d, function %(name)s called with " \
- "%(given)d arguments, expected %(required)d",
- [function],
- lambda f: {'filename' : f.filename,
- 'lineno' : f.lineno,
- 'name' : f.name,
- 'given' : len(f.arguments),
- 'required' : len(function_arg_spec.args)})
-
- # Check that the arguments are either inputs, configuration or assignment
- for argument in function.arguments:
- if isinstance(argument, Identifier) and \
- argument not in self._module.definition.inputs and \
- argument not in self._module.resolution_symbols['assignment_table']:
- self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function argument %(arg_name)s",
+ no_defaults = len(function_arg_spec.defaults) if function_arg_spec.defaults else 0
+ min_no_args = len(function_arg_spec.args) - no_defaults
+
+ # If there are no argument default values and no var-args, then if the
+ # the number of arguments does *not* match the function's definition
+ # then that is an error.
+ if no_defaults < 1 and not function_arg_spec.varargs:
+ # The number of expected arguments *is* the length of the ArgSpec.args
+ if len(function_arg_spec.args) != len(function.arguments):
+ self._add_errors("ERROR: %(filename)s at line %(lineno)d, function %(name)s called with " \
+ "%(given)d arguments, expected %(required)d",
[function],
lambda f: {'filename' : f.filename,
'lineno' : f.lineno,
- 'arg_name' : argument})
- elif isinstance(argument, StateIdentifier) and \
- argument not in self._module.resolution_symbols['configuration']:
- self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function argument %(arg_name)s",
+ 'name' : f.name,
+ 'given' : len(f.arguments),
+ 'required' : len(function_arg_spec.args)})
+ elif no_defaults > 0 or function_arg_spec.varargs:
+ # If we have at least one default argument value or at least one var-args then
+ # if the minimum number of arguments expected for this function is less than
+ # the number of arguments in the function call then this is an error.
+ if len(function.arguments) < min_no_args:
+ self._add_errors("ERROR: %(filename)s at line %(lineno)d, function %(name)s called with " \
+ "%(given)d arguments, expected at least %(required)d",
[function],
lambda f: {'filename' : f.filename,
'lineno' : f.lineno,
- 'arg_name' : argument})
+ 'name' : f.name,
+ 'given' : len(f.arguments),
+ 'required' : min_no_args})
- # Mark the import as used
- self._module.resolution_symbols['used_imports'][package_alias] = (self._module.resolution_symbols['used_imports'][package_alias][0],
- True)
+ # Mark the import as used
+ self._module.resolution_symbols['used_imports'][alias_identifier] = (self._module.resolution_symbols['used_imports'][alias_identifier][0],
+ True)
+
+ # Check that the arguments are either inputs, configuration or assignment
+ for argument in function.arguments:
+ if isinstance(argument, StateIdentifier):
+ if argument not in self._module.resolution_symbols['configuration']:
+ self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function argument %(arg_name)s",
+ [function],
+ lambda f: {'filename' : f.filename,
+ 'lineno' : f.lineno,
+ 'arg_name' : argument})
+ else:
+ # Mark the configuration as used
+ try:
+ self._module.resolution_symbols['unused_configuration'].remove(argument)
+ except KeyError:
+ # We may well have removed this state identifier already
+ pass
+ elif isinstance(argument, Identifier):
+ if argument not in self._module.definition.inputs and \
+ argument not in self._module.resolution_symbols['assignment_table']:
+ self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function argument %(arg_name)s TITZ",
+ [function],
+ lambda f: {'filename' : f.filename,
+ 'lineno' : f.lineno,
+ 'arg_name' : argument})
@multimethod(Command)
def visit(self, command):
+ # If no assignment then we don't need to do anything.
+ if not command.identifier:
+ return
+
# Check the assigned variable is *not* an input. Inputs are immutable.
if command.identifier in self._module.definition.inputs:
self._add_errors("ERROR: %(filename)s at line %(lineno)d, attempt to write read-only input %(input)s",
@@ -858,16 +907,19 @@ class FirstPassResolverVisitor(ResolverVisitor):
# generate all code to provide the previous use!
if command.identifier in self._module.resolution_symbols['assignment_table']:
self._add_warnings("WARNING: %(filename)s at line %(lineno)d, duplicate assignment variable %(var_name)s",
- [command],
+ [command.identifier],
lambda c: {'filename' : c.filename,
'lineno' : c.lineno,
- 'var_name' : c.identifier})
-
- # Record the assignment and the command
- self._module.resolution_symbols['assignment_table'][command.identifier] = command
+ 'var_name' : c})
+ else:
+ # Record the assignment and the command
+ self._module.resolution_symbols['assignment_table'][command.identifier] = command
@multimethod(Return)
def visit(self, ret):
+ if not ret.mappings:
+ return
+
# Duplicate 'to' maps
duplicate_to = dict()
missing_froms = list()
@@ -898,6 +950,13 @@ class FirstPassResolverVisitor(ResolverVisitor):
'lineno' : t.lineno,
'missing' : str(t)})
+ # Unknown 'to' maps
+ self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown output in return %(unknown)s",
+ frozenset([mapping.to for mapping in ret.mappings if mapping.to not in self._module.definition.outputs]),
+ lambda m: {'filename' : m.filename,
+ 'lineno' : m.lineno,
+ 'unknown' : m})
+
# Missing 'from' maps
self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown variable in return %(unknown)s",
missing_froms,
diff --git a/src/pclc/visitors/second_pass_resolver_visitor.py b/src/pclc/visitors/second_pass_resolver_visitor.py
index 00bde81..3923191 100644
--- a/src/pclc/visitors/second_pass_resolver_visitor.py
+++ b/src/pclc/visitors/second_pass_resolver_visitor.py
@@ -18,6 +18,7 @@
#
from multimethod import multimethod, multimethodclass
from parser.import_spec import Import
+from parser.command import Function, Command, Return, IfCommand
from parser.component import Component
from parser.conditional_expressions import ConditionalExpression, \
UnaryConditionalExpression, \
@@ -125,3 +126,19 @@ class SecondPassResolverVisitor(FirstPassResolverVisitor):
@multimethod(TerminalConditionalExpression)
def visit(self, term_cond_expr):
pass
+
+ @multimethod(Function)
+ def visit(self, function):
+ pass
+
+ @multimethod(Command)
+ def visit(self, command):
+ pass
+
+ @multimethod(Return)
+ def visit(self, ret):
+ pass
+
+ @multimethod(IfCommand)
+ def visit(self, if_command):
+ pass
diff --git a/src/pclc/visitors/third_pass_resolver_visitor.py b/src/pclc/visitors/third_pass_resolver_visitor.py
index 21e4f9c..f04442a 100644
--- a/src/pclc/visitors/third_pass_resolver_visitor.py
+++ b/src/pclc/visitors/third_pass_resolver_visitor.py
@@ -49,6 +49,9 @@ class ThirdPassResolverVisitor(SecondPassResolverVisitor):
@multimethod(object)
def visit(self, nowt):
+ if self._module.definition.is_leaf:
+ return
+
# Root expression, so get inputs and outpus
# from module defined inputs and outputs clauses
expr = self._module.definition.definition
@@ -157,11 +160,20 @@ class ThirdPassResolverVisitor(SecondPassResolverVisitor):
'filename' : e.filename,
'lineno' : e.lineno})
elif isinstance(terminal, Identifier):
- self._current_if_expr.resolution_symbols['inputs'] >= (lambda s: self._add_errors("ERROR: %(filename)s at line %(lineno)d, " \
- "identifier %(entity)s not defined in " \
- "if inputs",
- [terminal],
- lambda e: {'entity' : e,
- 'filename' : e.filename,
- 'lineno' : e.lineno}) \
- if terminal not in s else None)
+ if self._module.definition.is_leaf:
+ self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown identifier %(identifier)s",
+ [terminal] if terminal not in self._module.definition.inputs and \
+ terminal not in self._module.resolution_symbols['assignment_table'] \
+ else [],
+ lambda t: {'filename' : t.filename,
+ 'lineno' : t.lineno,
+ 'identifier' : t})
+ else:
+ self._current_if_expr.resolution_symbols['inputs'] >= (lambda s: self._add_errors("ERROR: %(filename)s at line %(lineno)d, " \
+ "identifier %(entity)s not defined in " \
+ "if inputs",
+ [terminal],
+ lambda e: {'entity' : e,
+ 'filename' : e.filename,
+ 'lineno' : e.lineno}) \
+ if terminal not in s else None)
diff --git a/src/runtime/pcl/os/path.py b/src/runtime/pcl/os/path.py
index b5a9349..2dd1092 100644
--- a/src/runtime/pcl/os/path.py
+++ b/src/runtime/pcl/os/path.py
@@ -26,3 +26,6 @@ join = lambda *args: os.path.join(*args)
# mkdir :: String -> ()
mkdir = lambda p: os.mkdir(p)
+
+# basename :: String -> String
+basename = lambda s: os.path.basename(s)
diff --git a/src/runtime/pcl/util/string.py b/src/runtime/pcl/util/string.py
index 120c9c6..7ba1859 100644
--- a/src/runtime/pcl/util/string.py
+++ b/src/runtime/pcl/util/string.py
@@ -19,3 +19,6 @@
# split :: String -> String -> String
split = lambda s, ss: s.__str__().split(ss)
+
+# join :: [String] -> String -> String
+join = lambda l, s: s.join(l)