(puppetlabs/puppet) (#11331) A whole new parser and some iteration goodness

178 views
Skip to first unread message

weareth...@puppetlabs.com

unread,
Apr 5, 2013, 5:45:12 PM4/5/13
to puppet...@googlegroups.com

Please review pull request #1584: (#11331) A whole new parser and some iteration goodness opened by (zaphod42)

Description:

This implements a new parser that provides an implementation of many parts of ARM-2
as well as some better error reporting.

  • Opened: Fri Apr 05 21:42:05 UTC 2013
  • Based on: puppetlabs:master (44e79d0f9c30d68201eee85cdbf288431be3572a)
  • Requested merge: puppetlabs:feature/master/future-parser (5773a20886c84c93fdb5383301c767c27e2f496c)

Diff follows:

diff --git a/Gemfile b/Gemfile
index 5de4d54..88cee31 100644
--- a/Gemfile
+++ b/Gemfile
@@ -15,8 +15,10 @@ platforms :ruby do
   gem 'pry', :group => :development
   gem 'yard', :group => :development
   gem 'redcarpet', :group => :development
+  gem "racc", "~> 1.4", :group => :development
 end
 
+
 group(:development, :test) do
   gem "puppet", *location_for('file://.')
   gem "facter", *location_for(ENV['FACTER_LOCATION'] || '~> 1.6')
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index a83fc4b..f73960f 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -1505,7 +1505,43 @@ module Puppet
 in order to upgrade, and can rename your variables at your leisure. Please
 revert it to `false` after you have renamed all affected variables.
 EOT
+    },
+    :parser => {
+      :default => "current",
+      :desc => <<-'EOT'
+Selects the parser to use for parsing puppet manifests (in puppet DSL language/'.pp' files).
+Available choices are 'current' (the default), and 'future'.
+
+The 'curent' parser means that the released version of the parser should be used.
+
+The 'future' parser is a "time travel to the future" allowing early exposure to new language features.
+What these fatures are will vary from release to release and they may be invididually configurable.
+
+Available Since Puppet 3.2. 
+EOT
+    },
+   :max_errors => {
+     :default => 10,
+     :desc => <<-'EOT'
+Sets the max number of logged/displayed parser validation errors in case multiple errors have been detected.
+A value of 0 is the same as value 1. The count is per manifest.
+EOT
+   },
+   :max_warnings => {
+     :default => 10,
+     :desc => <<-'EOT'
+Sets the max number of logged/displayed parser validation warnings in case multiple errors have been detected.
+A value of 0 is the same as value 1. The count is per manifest.
+EOT
+     },
+  :max_deprecations => {
+    :default => 10,
+    :desc => <<-'EOT'
+Sets the max number of logged/displayed parser validation deprecation warnings in case multiple errors have been detected.
+A value of 0 is the same as value 1. The count is per manifest.
+EOT
     }
+
   )
   define_settings(:puppetdoc,
     :document_all => {
diff --git a/lib/puppet/error.rb b/lib/puppet/error.rb
index dceeea3..2e49863 100644
--- a/lib/puppet/error.rb
+++ b/lib/puppet/error.rb
@@ -12,18 +12,29 @@ module ExternalFileError
     # This module implements logging with a filename and line number. Use this
     # for errors that need to report a location in a non-ruby file that we
     # parse.
-    attr_accessor :line, :file
+    attr_accessor :line, :file, :pos
 
-    def initialize(message, file=nil, line=nil, original=nil)
+    # May be called with 3 arguments for message, file, line, and exception, or
+    # 4 args including the position on the line.
+    #
+    def initialize(message, file=nil, line=nil, pos=nil, original=nil)
+      if pos.kind_of? Exception
+        original = pos
+        pos = nil
+      end
       super(message, original)
       @file = file
       @line = line
+      @pos = pos
     end
-
     def to_s
       msg = super
-      if @file and @line
+      if @file and @line and @pos
+        "#{msg} at #{@file}:#{@line}:#{@pos}"
+      elsif @file and @line
         "#{msg} at #{@file}:#{@line}"
+      elsif @line and @pos
+          "#{msg} at line #{@line}:#{@pos}"
       elsif @line
         "#{msg} at line #{@line}"
       elsif @file
diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb
index 25e429a..087cd5c 100644
--- a/lib/puppet/node/environment.rb
+++ b/lib/puppet/node/environment.rb
@@ -1,6 +1,7 @@
 require 'puppet/util'
 require 'puppet/util/cacher'
 require 'monitor'
+require 'puppet/parser/parser_factory'
 
 # Just define it, so this class has fewer load dependencies.
 class Puppet::Node
@@ -217,7 +218,8 @@ def validate_dirs(dirs)
 
   def perform_initial_import
     return empty_parse_result if Puppet.settings[:ignoreimport]
-    parser = Puppet::Parser::Parser.new(self)
+#    parser = Puppet::Parser::Parser.new(self)
+    parser = Puppet::Parser::ParserFactory.parser(self)
     if code = Puppet.settings.uninterpolated_value(:code, name.to_s) and code != ""
       parser.string = code
     else
diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
index 3cc1a8e..438cb49 100644
--- a/lib/puppet/parser/ast.rb
+++ b/lib/puppet/parser/ast.rb
@@ -14,7 +14,7 @@ class Puppet::Parser::AST
   include Puppet::Util::MethodHelper
   include Puppet::Util::Docs
 
-  attr_accessor :parent, :scope, :file, :line
+  attr_accessor :parent, :scope, :file, :line, :pos
 
   def inspect
     "( #{self.class} #{self.to_s} #{@children.inspect} )"
@@ -110,8 +110,10 @@ def evaluate_match(value, scope)
 require 'puppet/parser/ast/hostclass'
 require 'puppet/parser/ast/ifstatement'
 require 'puppet/parser/ast/in_operator'
+require 'puppet/parser/ast/lambda'
 require 'puppet/parser/ast/leaf'
 require 'puppet/parser/ast/match_operator'
+require 'puppet/parser/ast/method_call'
 require 'puppet/parser/ast/minus'
 require 'puppet/parser/ast/node'
 require 'puppet/parser/ast/nop'
diff --git a/lib/puppet/parser/ast/arithmetic_operator.rb b/lib/puppet/parser/ast/arithmetic_operator.rb
index 68cf021..9b4fe76 100644
--- a/lib/puppet/parser/ast/arithmetic_operator.rb
+++ b/lib/puppet/parser/ast/arithmetic_operator.rb
@@ -11,23 +11,67 @@ def each
       [@lval,@rval,@operator].each { |child| yield child }
     end
 
-    # Returns a boolean which is the result of the boolean operation
-    # of lval and rval operands
+    # Produces an object which is the result of the applying the operator to the of lval and rval operands.
+    # * Supports +, -, *, /, %, and <<, >> on numeric strings.
+    # * Supports + on arrays (concatenate), and hashes (merge)
+    # * Supports << on arrays (append)
+    #
     def evaluate(scope)
       # evaluate the operands, should return a boolean value
-      lval = @lval.safeevaluate(scope)
-      lval = Puppet::Parser::Scope.number?(lval)
-      if lval == nil
-        raise ArgumentError, "left operand of #{@operator} is not a number"
+      left = @lval.safeevaluate(scope)
+      right = @rval.safeevaluate(scope)
+
+      if left.is_a?(Array) || right.is_a?(Array)
+        eval_array(left, right)
+      elsif left.is_a?(Hash) || right.is_a?(Hash)
+        eval_hash(left, right)
+      else
+        eval_numeric(left, right)
       end
-      rval = @rval.safeevaluate(scope)
-      rval = Puppet::Parser::Scope.number?(rval)
-      if rval == nil
-        raise ArgumentError, "right operand of #{@operator} is not a number"
+    end
+
+    # Concatenates (+) two arrays, or appends (<<) any object to a newly created array.
+    #
+    def eval_array(left, right)
+      assert_concatenation_supported()
+
+      raise ArgumentError, "operator #{@operator} is not applicable when one of the operands is an Array." unless %w{+ <<}.include?(@operator)
+      raise ArgumentError, "left operand of #{@operator} must be an Array" unless left.is_a?(Array)
+      if @operator == '+'
+        raise ArgumentError, "right operand of #{@operator} must be an Array when left is an Array." unless right.is_a?(Array)
+        return left + right
       end
+      # only append case remains, left asserted to be an array, and right may be any object
+      # wrapping right in an array and adding it ensures a new copy (operator << mutates).
+      #
+      left + [right]
+    end
+
+    # Merges two hashes.
+    #
+    def eval_hash(left, right)
+      assert_concatenation_supported()
+
+      raise ArgumentError, "operator #{@operator} is not applicable when one of the operands is an Hash." unless @operator == '+'
+      raise ArgumentError, "left operand of #{@operator} must be an Hash" unless left.is_a?(Hash)
+      raise ArgumentError, "right operand of #{@operator} must be an Hash" unless right.is_a?(Hash)
+      # merge produces a merged copy
+      left.merge(right)
+    end
+
+    def eval_numeric(left, right)
+      left = Puppet::Parser::Scope.number?(left)
+      right = Puppet::Parser::Scope.number?(right)
+      raise ArgumentError, "left operand of #{@operator} is not a number" unless left != nil
+      raise ArgumentError, "right operand of #{@operator} is not a number" unless right != nil
 
       # compute result
-      lval.send(@operator, rval)
+      left.send(@operator, right)
+    end
+
+    def assert_concatenation_supported
+      return if Puppet[:parser] == 'future'
+      raise ParseError.new("Unsupported Operation: Array concatenation available with '--parser future' setting only.")
     end
 
     def initialize(hash)
diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb
index 7283a1f..42a47a1 100644
--- a/lib/puppet/parser/ast/astarray.rb
+++ b/lib/puppet/parser/ast/astarray.rb
@@ -9,7 +9,7 @@ class Puppet::Parser::AST
   class ASTArray < Branch
     include Enumerable
 
-    # Return a child by index.  Probably never used.
+    # Return a child by index. Used (at least) by tests.
     def [](index)
       @children[index]
     end
diff --git a/lib/puppet/parser/ast/block_expression.rb b/lib/puppet/parser/ast/block_expression.rb
new file mode 100644
index 0000000..7f75ceb
--- /dev/null
+++ b/lib/puppet/parser/ast/block_expression.rb
@@ -0,0 +1,41 @@
+require 'puppet/parser/ast/branch'
+
+class Puppet::Parser::AST
+  class BlockExpression < Branch
+    include Enumerable
+
+    # Evaluate contained expressions, produce result of the last
+    def evaluate(scope)
+      result = nil
+      @children.each do |child|
+        # Skip things that respond to :instantiate (classes, nodes,
+        # and definitions), because they have already been
+        # instantiated.
+        if !child.respond_to?(:instantiate)
+          result = child.safeevaluate(scope)
+        end
+      end
+      result
+    end
+
+    # Return a child by index.
+    def [](index)
+      @children[index]
+    end
+
+    def push(*ary)
+      ary.each { |child|
+        #Puppet.debug "adding %s(%s) of type %s to %s" %
+        #    [child, child.object_id, child.class.to_s.sub(/.+::/,''),
+        #    self.object_id]
+        @children.push(child)
+      }
+
+      self
+    end
+
+    def to_s
+      "[" + @children.collect { |c| c.to_s }.join(', ') + "]"
+    end
+  end
+end
diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb
index f6916b7..5d2dc6f 100644
--- a/lib/puppet/parser/ast/function.rb
+++ b/lib/puppet/parser/ast/function.rb
@@ -6,7 +6,7 @@ class Function < AST::Branch
 
     associates_doc
 
-    attr_accessor :name, :arguments
+    attr_accessor :name, :arguments, :pblock
 
     def evaluate(scope)
       # Make sure it's a defined function
@@ -16,8 +16,16 @@ def evaluate(scope)
       case @ftype
       when :rvalue
         raise Puppet::ParseError, "Function '#{@name}' does not return a value" unless Puppet::Parser::Functions.rvalue?(@name)
+
       when :statement
-        if Puppet::Parser::Functions.rvalue?(@name)
+        # It is harmless to produce an ignored rvalue, the alternative is to mark functions
+        # as appropriate for both rvalue and statements
+        # Keeping the old behavior when a pblock is not present. This since it is not known
+        # if the lambda contains a statement or not (at least not without a costly search).
+        # The purpose of the check is to protect a user for producing a meaningless rvalue where the
+        # operation has no side effects.
+        #
+        if !pblock && Puppet::Parser::Functions.rvalue?(@name)
           raise Puppet::ParseError,
             "Function '#{@name}' must be the value of a statement"
         end
@@ -28,6 +36,9 @@ def evaluate(scope)
       # We don't need to evaluate the name, because it's plaintext
       args = @arguments.safeevaluate(scope).map { |x| x == :undef ? '' : x }
 
+      # append a puppet lambda (unevaluated) if it is defined
+      args << pblock if pblock
+
       scope.send("function_#{@name}", args)
     end
 
diff --git a/lib/puppet/parser/ast/lambda.rb b/lib/puppet/parser/ast/lambda.rb
new file mode 100644
index 0000000..00c0e86
--- /dev/null
+++ b/lib/puppet/parser/ast/lambda.rb
@@ -0,0 +1,107 @@
+require 'puppet/parser/ast/block_expression'
+
+class Puppet::Parser::AST
+  # A block of statements/expressions with additional parameters
+  # Requires scope to contain the values for the defined parameters when evaluated
+  # If evaluated without a prepared scope, the lambda will behave like its super class.
+  #
+  class Lambda < AST::BlockExpression
+
+    # The lambda parameters.
+    # These are encoded as an array where each entry is an array of one or two object. The first
+    # is the parameter name, and the optional second object is the value expression (that will
+    # be evaluated when bound to a scope).
+    # The value expression is the default value for the parameter. All default values must be
+    # at the end of the parameter list.
+    #
+    # @return [Array<Array<String,String>>] list of parameter names with optional value expression
+    attr_accessor :parameters
+    # Evaluates each expression/statement and produce the last expression evaluation result
+    # @return [Object] what the last expression evaluated to
+    def evaluate(scope)
+      if @children.is_a? Puppet::Parser::AST::ASTArray
+        result = nil
+        @children.each {|expr| result = expr.evaluate(scope) }
+        result
+      else
+        @children.evaluate(scope)
+      end
+    end
+
+    # Calls the lambda.
+    # Assigns argument values in a nested local scope that should be used to evaluate the lambda
+    # and then evaluates the lambda.
+    # @param scope [Puppet::Scope] the calling scope
+    # @return [Object] the result of evaluating the expression(s) in the lambda
+    #
+    def call(scope, *args)
+      raise Puppet::ParseError, "Too many arguments: #{args.size} for #{parameters.size}" unless args.size <= parameters.size
+      merged = parameters.zip(args)
+      missing = merged.select { |e| !e[1] && e[0].size == 1 }
+      unless missing.empty?
+        optional = parameters.count { |p| p.size == 2 }
+        raise Puppet::ParseError, "Too few arguments; #{args.size} for #{optional > 0 ? ' min ' : ''}#{parameters.size - optional}"
+      end
+
+      evaluated = merged.collect do |m|
+        # Ruby 1.8.7 zip seems to produce a different result than Ruby 1.9.3 in some situations
+        n = m[0].is_a?(Array) ? m[0][0] : m[0]
+        v = m[1] || (m[0][1]).safeevaluate(scope) # given value or default expression value
+        [n, v]
+      end
+
+      # Store the evaluated name => value associations in a new inner/local/ephemeral scope
+      # (This is made complicated due to the fact that the implementation of scope is overloaded with
+      # functionality and an inner ephemeral scope must be used (as opposed to just pushing a local scope
+      # on a scope "stack").
+      begin
+        elevel = scope.ephemeral_level
+        scope.ephemeral_from(Hash[evaluated], file, line)
+        result = safeevaluate(scope)
+      ensure
+        scope.unset_ephemeral_var(elevel)
+        result ||= nil
+      end
+      result
+    end
+
+    # Validates the lambda.
+    # Validation checks if parameters with default values are at the end of the list. (It is illegal
+    # to have a parameter with default value followed by one without).
+    #
+    # @raise [Puppet::ParseError] if a parameter with a default comes before a parameter without default value
+    #
+    def validate
+      params = parameters || []
+      defaults = params.drop_while {|p| p.size < 2 }
+      trailing = defaults.drop_while {|p| p.size == 2 }
+      raise Puppet::ParseError, "Lambda parameters with default values must be placed last" unless trailing.empty?
+    end
+
+    # Returns the number of parameters (required and optional)
+    # @return [Integer] the total number of accepted parameters
+    def parameter_count
+      @parameters.size
+    end
+
+    # Returns the number of optional parameters.
+    # @return [Integer] the number of optional accepted parameters
+    def optional_parameter_count
+      @parameters.count {|p| p.size == 2 }
+    end
+
+    def initialize(options)
+      super(options)
+      # ensure there is an empty parameters structure if not given by creator
+      @parameters = [] unless options[:parameters]
+      validate
+    end
+
+    def to_s
+      result = ["{|"]
+      result += @parameters.collect {|p| "#{p[0]}" + (p.size == 2 && p[1]) ? p[1].to_s() : '' }.join(', ')
+      result << "| ... }"
+      result.join('')
+    end
+  end
+end
diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb
index 2371ae7..ce5441d 100644
--- a/lib/puppet/parser/ast/leaf.rb
+++ b/lib/puppet/parser/ast/leaf.rb
@@ -148,7 +148,6 @@ def array_index_or_key(object, key)
     def evaluate(scope)
       object = evaluate_container(scope)
       accesskey = evaluate_key(scope)
-
       raise Puppet::ParseError, "#{variable} is not an hash or array when accessing it with #{accesskey}" unless object.is_a?(Hash) or object.is_a?(Array)
 
       object[array_index_or_key(object, accesskey)] || :undef
diff --git a/lib/puppet/parser/ast/method_call.rb b/lib/puppet/parser/ast/method_call.rb
new file mode 100644
index 0000000..d613674
--- /dev/null
+++ b/lib/puppet/parser/ast/method_call.rb
@@ -0,0 +1,77 @@
+require 'puppet/parser/ast/branch'
+require 'puppet/parser/methods'
+
+class Puppet::Parser::AST
+  # An AST object to call a method
+  class MethodCall < AST::Branch
+
+    associates_doc
+
+    # An AST that evaluates to the object the method is applied to
+    # @return [Puppet::Parser::AST]
+    attr_accessor :receiver
+
+    # The name of the method
+    # @return [String]
+    attr_accessor :name
+
+    # The arguments to evaluate as arguments to the method.
+    # @return [Array<Puppet::Parser::AST>]
+    attr_accessor :arguments
+
+    # An optional lambda/block that will be yielded to by the called method (if it supports this)
+    # @return [Puppet::Parser::AST::Lambda]
+    attr_accessor :lambda
+
+    # Evaluates the method call and returns what the called method/function returns.
+    # The evaluation evaluates all arguments in the calling scope and then delegates
+    # to a "method" instance produced by Puppet::Parser::Methods for this method call.
+    # @see Puppet::Parser::Methods
+    # @return [Object] what the called method/function returns
+    def evaluate(scope)
+      # Make sure it's a defined method for the receiver
+      r = @receiver.evaluate(scope)
+      raise Puppet::ParseError, "No object to apply method #{@name} to" unless r
+      m = Puppet::Parser::Methods.find_method(scope, r, @name)
+      raise Puppet::ParseError, "Unknown method #{@name} for #{r}" unless m
+
+      # Now check if rvalue is required (in expressions)
+      case @ftype
+      when :rvalue
+        raise Puppet::ParseError, "Method '#{@name}' does not return a value" unless m.is_rvalue?
+      when :statement
+        # When used as a statement, ignore if it produces a rvalue (it is simply not used)
+      else
+        raise Puppet::DevError, "Invalid method type #{@ftype.inspect}"
+      end
+
+      # Evaluate arguments
+      args = @arguments ? @arguments.safeevaluate(scope).map { |x| x == :undef ? '' : x } : []
+
+      # There is no need to evaluate the name, since it is a literal ruby string
+
+      # call the method (it is already bound to the receiver and name)
+      m.invoke(scope, args, @lambda)
+    end
+
+    def initialize(hash)
+      @ftype = hash[:ftype] || :rvalue
+      hash.delete(:ftype) if hash.include? :ftype
+
+      super(hash)
+
+      # Lastly, check the parity
+    end
+
+    # Sets this method call in statement mode where a produced rvalue is ignored.
+    # @return [void]
+    def ignore_rvalue
+      @ftype = :statement
+    end
+
+    def to_s
+      args = arguments.is_a?(ASTArray) ? arguments.to_s.gsub(/\[(.*)\]/,'\1') : arguments
+      "#{@receiver.to_s}.#{name} (#{args})" + (@lambda ? " #{@lambda.to_s}" : '')
+    end
+  end
+end
diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb
index 1374c73..07999f3 100644
--- a/lib/puppet/parser/ast/vardef.rb
+++ b/lib/puppet/parser/ast/vardef.rb
@@ -21,6 +21,13 @@ def evaluate(scope)
           scope.setvar(name,value, :file => file, :line => line, :append => @append)
         end
       end
+      if @append
+        # Produce resulting value from append operation
+        scope[name]
+      else
+        # Produce assigned value
+        value
+      end
     end
 
     def each
diff --git a/lib/puppet/parser/e_parser_adapter.rb b/lib/puppet/parser/e_parser_adapter.rb
new file mode 100644
index 0000000..beec147
--- /dev/null
+++ b/lib/puppet/parser/e_parser_adapter.rb
@@ -0,0 +1,120 @@
+require 'puppet/pops'
+
+module Puppet; module Parser; end; end;
+# Adapts an egrammar/eparser to respond to the public API of the classic parser
+#
+class Puppet::Parser::EParserAdapter
+
+  def initialize(classic_parser)
+    @classic_parser = classic_parser
+    @file = ''
+    @string = ''
+    @use = :undefined
+  end
+
+  def file=(file)
+    @classic_parser.file = file
+    @file = file
+    @use = :file
+  end
+
+  def parse(string = nil)
+    if @file =~ /\.rb$/
+      return parse_ruby_file
+    else
+      self.string= string if string
+      parser = Puppet::Pops::Parser::Parser.new()
+      parse_result = if @use == :string
+        parser.parse_string(@string)
+      else
+        parser.parse_file(@file)
+      end
+      # Compute the source_file to set in created AST objects (it was either given, or it may be unknown
+      # if caller did not set a file and the present a string.
+      #
+      source_file = @file || "unknown-source-location"
+
+      # Validate
+      validate(parse_result)
+
+      # Transform the result, but only if not nil
+      parse_result = Puppet::Pops::Model::AstTransformer.new(source_file, @classic_parser).transform(parse_result) if parse_result
+      if parse_result && !parse_result.is_a?(Puppet::Parser::AST::BlockExpression)
+        # Need to transform again, if result is not wrapped in something iterable when handed off to
+        # a new Hostclass as its code.
+        parse_result = Puppet::Parser::AST::BlockExpression.new(:children => [parse_result]) if parse_result
+      end
+    end
+
+    Puppet::Parser::AST::Hostclass.new('', :code => parse_result)
+  end
+
+  def validate(parse_result)
+    # TODO: This is too many hoops to jump through... ugly API
+    # could reference a ValidatorFactory.validator_3_1(acceptor) instead.
+    # and let the factory abstract the rest.
+    #
+    return unless parse_result
+
+    acceptor  = Puppet::Pops::Validation::Acceptor.new
+    validator = Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor)
+    validator.validate(parse_result)
+
+    max_errors = Puppet[:max_errors]
+    max_warnings = Puppet[:max_warnings] + 1
+    max_deprecations = Puppet[:max_deprecations] + 1
+
+    # If there are warnings output them
+    warnings = acceptor.warnings
+    if warnings.size > 0
+      formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new
+      emitted_w = 0
+      emitted_dw = 0
+      acceptor.warnings.each {|w|
+        if w.severity == :deprecation
+          # Do *not* call Puppet.deprecation_warning it is for internal deprecation, not
+          # deprecation of constructs in manifests! (It is not designed for that purpose even if
+          # used throughout the code base).
+          #
+          Puppet.warning(formatter.format(w)) if emitted_dw < max_deprecations
+          emitted_dw += 1
+        else
+          Puppet.warning(formatter.format(w)) if emitted_w < max_warnings
+          emitted_w += 1
+        end
+        break if emitted_w > max_warnings && emitted_dw > max_deprecations # but only then
+      }
+    end
+
+    # If there were errors, report the first found. Use a puppet style formatter.
+    errors = acceptor.errors
+    if errors.size > 0
+      formatter = Puppet::Pops::Validation::DiagnosticFormatterPuppetStyle.new
+      if errors.size == 1 || max_errors <= 1
+        # raise immediately
+        raise Puppet::ParseError.new(formatter.format(errors[0]))
+      end
+      emitted = 0
+      errors.each do |e|
+        Puppet.err(formatter.format(e))
+        emitted += 1
+        break if emitted >= max_errors
+      end
+      warnings_message = warnings.size > 0 ? ", and #{warnings.size} warnings" : ""
+      giving_up_message = "Found #{errors.size} errors#{warnings_message}. Giving up"
+      exception = Puppet::ParseError.new(giving_up_message)
+      exception.file = errors[0].file
+      raise exception
+    end
+  end
+
+  def string=(string)
+    @classic_parser.string = string
+    @string = string
+    @use = :string
+  end
+
+  def parse_ruby_file
+    @classic_parser.parse
+  end
+end
diff --git a/lib/puppet/parser/files.rb b/lib/puppet/parser/files.rb
index 18bae6f..b0239f2 100644
--- a/lib/puppet/parser/files.rb
+++ b/lib/puppet/parser/files.rb
@@ -1,11 +1,12 @@
 require 'puppet/module'
-require 'puppet/parser/parser'
+#require 'puppet/parser/parser'
 
 # This is a silly central module for finding
 # different kinds of files while parsing.  This code
 # doesn't really belong in the Puppet::Module class,
 # but it doesn't really belong anywhere else, either.
-module Puppet::Parser::Files
+module Puppet; module Parser; module Files
+  
   module_function
 
   # Return a list of manifests (as absolute filenames) that match +pat+
@@ -85,4 +86,4 @@ def split_file_path(path)
     path.split(File::SEPARATOR, 2) unless path == "" or Puppet::Util.absolute_path?(path)
   end
 
-end
+end; end; end
diff --git a/lib/puppet/parser/functions/collect.rb b/lib/puppet/parser/functions/collect.rb
new file mode 100644
index 0000000..e0b27d4
--- /dev/null
+++ b/lib/puppet/parser/functions/collect.rb
@@ -0,0 +1,41 @@
+Puppet::Parser::Functions::newfunction(
+:collect,
+:type => :rvalue,
+:arity => 2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each element in a sequence of entries from the first
+  argument and returns an array with the result of each invocation of the parameterized block.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+  a parameterized block as produced by the puppet syntax:
+
+    $a.collect |$x| { ... }
+
+  When the first argument is an Array, the block is called with each entry in turn. When the first argument
+  is a hash the entry is an array with `[key, value]`.
+
+  *Examples*
+
+    # Turns hash into array of values
+    $a.collect |$x|{ $x[1] }
+
+    # Turns hash into array of keys
+    $a.collect |$x| { $x[0] }
+
+  Since 3.2
+  ENDHEREDOC
+
+  require 'puppet/parser/ast/lambda'
+  raise ArgumentError, ("collect(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2
+  receiver = args[0]
+  pblock = args[1]
+  raise ArgumentError, ("collect(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+  case receiver
+  when Array
+  when Hash
+  else
+    raise ArgumentError, ("collect(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+
+  receiver.collect {|x| pblock.call(self, x) }
+end
diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb
new file mode 100644
index 0000000..1c089e5
--- /dev/null
+++ b/lib/puppet/parser/functions/each.rb
@@ -0,0 +1,96 @@
+Puppet::Parser::Functions::newfunction(
+:each,
+:type => :rvalue,
+:arity => 2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each element in a sequence of selected entries from the first
+  argument and returns the first argument.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+  a parameterized block as produced by the puppet syntax:
+
+    $a.each {|$x| ... }
+
+  When the first argument is an Array, the parameterized block should define one or two block parameters.
+  For each application of the block, the next element from the array is selected, and it is passed to
+  the block if the block has one parameter. If the block has two parameters, the first is the elements
+  index, and the second the value. The index starts from 0.
+
+    $a.each {|$index, $value| ... }
+
+  When the first argument is a Hash, the parameterized block should define one or two parameters.
+  When one parameter is defined, the iteration is performed with each entry as an array of `[key, value]`,
+  and when two parameters are defined the iteration is performed with key and value.
+
+    $a.each {|$entry|       ..."key ${$entry[0]}, value ${$entry[1]}" }
+    $a.each {|$key, $value| ..."key ${key}, value ${value}" }
+
+  Since 3.2
+  ENDHEREDOC
+  require 'puppet/parser/ast/lambda'
+
+  def foreach_Array(o, scope, pblock)
+    return nil unless pblock
+
+    serving_size = pblock.parameter_count
+    if serving_size == 0
+      raise ArgumentError, "Block must define at least one parameter; value."
+    end
+    if serving_size > 2
+      raise ArgumentError, "Block must define at most two parameters; index, value"
+    end
+    enumerator = o.each
+    index = 0
+    result = nil
+    if serving_size == 1
+      (o.size).times do
+        pblock.call(scope, enumerator.next)
+      end
+    else
+      (o.size).times do
+        pblock.call(scope, index, enumerator.next)
+        index = index +1
+      end
+    end
+    o
+  end
+
+  def foreach_Hash(o, scope, pblock)
+    return nil unless pblock
+    serving_size = pblock.parameter_count
+    case serving_size
+    when 0
+      raise ArgumentError, "Block must define at least one parameter (for hash entry key)."
+    when 1
+    when 2
+    else
+      raise ArgumentError, "Block must define at most two parameters (for hash entry key and value)."
+    end
+    enumerator = o.each_pair
+    result = nil
+    if serving_size == 1
+      (o.size).times do
+        pblock.call(scope, enumerator.next)
+      end
+    else
+      (o.size).times do
+        pblock.call(scope, *enumerator.next)
+      end
+    end
+    o
+  end
+
+  raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2
+  receiver = args[0]
+  pblock = args[1]
+  raise ArgumentError, ("each(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+
+  case receiver
+  when Array
+    foreach_Array(receiver, self, pblock)
+  when Hash
+    foreach_Hash(receiver, self, pblock)
+  else
+    raise ArgumentError, ("each(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+end
diff --git a/lib/puppet/parser/functions/foreach.rb b/lib/puppet/parser/functions/foreach.rb
new file mode 100644
index 0000000..1307209
--- /dev/null
+++ b/lib/puppet/parser/functions/foreach.rb
@@ -0,0 +1,96 @@
+Puppet::Parser::Functions::newfunction(
+:foreach,
+:type => :rvalue,
+:arity => 2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each element in a sequence of selected entries from the first
+  argument and returns the first argument.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+  a parameterized block as produced by the puppet syntax:
+
+    $a.foreach {|$x| ... }
+
+  When the first argument is an Array, the parameterized block should define one or two block parameters.
+  For each application of the block, the next element from the array is selected, and it is passed to
+  the block if the block has one parameter. If the block has two parameters, the first is the elements
+  index, and the second the value. The index starts from 0.
+
+    $a.foreach {|$index, $value| ... }
+
+  When the first argument is a Hash, the parameterized block should define one or two parameters.
+  When one parameter is defined, the iteration is performed with each entry as an array of `[key, value]`,
+  and when two parameters are defined the iteration is performed with key and value.
+
+    $a.foreach {|$entry|       ..."key ${$entry[0]}, value ${$entry[1]}" }
+    $a.foreach {|$key, $value| ..."key ${key}, value ${value}" }
+
+  Since 3.2
+  ENDHEREDOC
+  require 'puppet/parser/ast/lambda'
+
+  def foreach_Array(o, scope, pblock)
+    return nil unless pblock
+
+    serving_size = pblock.parameter_count
+    if serving_size == 0
+      raise ArgumentError, "Block must define at least one parameter; value."
+    end
+    if serving_size > 2
+      raise ArgumentError, "Block must define at most two parameters; index, value"
+    end
+    enumerator = o.each
+    index = 0
+    result = nil
+    if serving_size == 1
+      (o.size).times do
+        pblock.call(scope, enumerator.next)
+      end
+    else
+      (o.size).times do
+        pblock.call(scope, index, enumerator.next)
+        index = index +1
+      end
+    end
+    o
+  end
+
+  def foreach_Hash(o, scope, pblock)
+    return nil unless pblock
+    serving_size = pblock.parameter_count
+    case serving_size
+    when 0
+      raise ArgumentError, "Block must define at least one parameter (for hash entry key)."
+    when 1
+    when 2
+    else
+      raise ArgumentError, "Block must define at most two parameters (for hash entry key and value)."
+    end
+    enumerator = o.each_pair
+    result = nil
+    if serving_size == 1
+      (o.size).times do
+        pblock.call(scope, enumerator.next)
+      end
+    else
+      (o.size).times do
+        pblock.call(scope, *enumerator.next)
+      end
+    end
+    o
+  end
+
+  raise ArgumentError, ("foreach(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2
+  receiver = args[0]
+  pblock = args[1]
+  raise ArgumentError, ("foreach(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+
+  case receiver
+  when Array
+    foreach_Array(receiver, self, pblock)
+  when Hash
+    foreach_Hash(receiver, self, pblock)
+  else
+    raise ArgumentError, ("foreach(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+end
diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb
new file mode 100644
index 0000000..0cc22bb
--- /dev/null
+++ b/lib/puppet/parser/functions/reduce.rb
@@ -0,0 +1,74 @@
+Puppet::Parser::Functions::newfunction(
+:reduce,
+:type => :rvalue,
+:arity => -2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each element in a sequence of entries from the first
+  argument (_the collection_) and returns the last result of the invocation of the parameterized block.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the last
+  a parameterized block as produced by the puppet syntax:
+
+    $a.reduce |$memo, $x| { ... }
+
+  When the first argument is an Array, the block is called with each entry in turn. When the first argument
+  is a hash each entry is converted to an array with `[key, value]` before being fed to the block. An optional
+  'start memo' value may be supplied as an argument between the array/hash and mandatory block.
+
+  If no 'start memo' is given, the first invocation of the parameterized block will be given the first and second
+  elements of the collection, and if the collection has fewer than 2 elements, the first
+  element is produced as the result of the reduction without invocation of the block.
+
+  On each subsequent invocations, the produced value of the invoked parameterized block is given as the memo in the
+  next invocation.
+
+  *Examples*
+
+    # Reduce an array
+    $a = [1,2,3]
+    $a.reduce |$memo, $entry| { $memo + $entry }
+    #=> 6
+
+    # Reduce hash values
+    $a = {a => 1, b => 2, c => 3}
+    $a.reduce |$memo, $entry| { [sum, $memo[1]+$entry[1]] }
+    #=> [sum, 6]
+
+  It is possible to provide a starting 'memo' as an argument.
+
+  *Examples*
+    # Reduce an array
+    $a = [1,2,3]
+    $a.reduce(4) |$memo, $entry| { $memo + $entry }
+    #=> 10
+
+    # Reduce hash values
+    $a = {a => 1, b => 2, c => 3}
+    $a.reduce([na, 4]) |$memo, $entry| { [sum, $memo[1]+$entry[1]] }
+    #=> [sum, 10]
+
+  Since 3.2
+  ENDHEREDOC
+
+  require 'puppet/parser/ast/lambda'
+  case args.length
+  when 2
+    pblock = args[1]
+  when 3
+    pblock = args[2]
+  else
+    raise ArgumentError, ("reduce(): wrong number of arguments (#{args.length}; must be 2 or 3)")
+  end
+  unless pblock.is_a? Puppet::Parser::AST::Lambda
+    raise ArgumentError, ("reduce(): wrong argument type (#{args[1].class}; must be a parameterized block.")
+  end
+  receiver = args[0]
+  unless [Array, Hash].include?(receiver.class)
+    raise ArgumentError, ("collect(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+  if args.length == 3
+    receiver.reduce(args[1]) {|memo, x| pblock.call(self, memo, x) }
+  else
+    receiver.reduce {|memo, x| pblock.call(self, memo, x) }
+  end
+end
diff --git a/lib/puppet/parser/functions/reject.rb b/lib/puppet/parser/functions/reject.rb
new file mode 100644
index 0000000..0297bff
--- /dev/null
+++ b/lib/puppet/parser/functions/reject.rb
@@ -0,0 +1,39 @@
+Puppet::Parser::Functions::newfunction(
+:reject,
+:type => :rvalue,
+:arity => 2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each element in a sequence of entries from the first
+  argument and returns an array with the entires for which the block did *not* evaluate to true.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+  a parameterized block as produced by the puppet syntax:
+
+    $a.reject |$x| { ... }
+
+  When the first argument is an Array, the block is called with each entry in turn. When the first argument
+  is a hash the entry is an array with `[key, value]`.
+
+  *Examples*
+
+    # selects all that does not end with berry
+    $a = ["rasberry", "blueberry", "orange"]
+    $a.reject |$x| { $x =~ /berry$/ }
+
+  Since 3.2
+  ENDHEREDOC
+
+  require 'puppet/parser/ast/lambda'
+  raise ArgumentError, ("reject(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2
+  receiver = args[0]
+  pblock = args[1]
+  raise ArgumentError, ("reject(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+  case receiver
+  when Array
+  when Hash
+  else
+    raise ArgumentError, ("reject(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+
+  receiver.reject {|x| pblock.call(self, x) }
+end
diff --git a/lib/puppet/parser/functions/select.rb b/lib/puppet/parser/functions/select.rb
new file mode 100644
index 0000000..03884d3
--- /dev/null
+++ b/lib/puppet/parser/functions/select.rb
@@ -0,0 +1,39 @@
+Puppet::Parser::Functions::newfunction(
+:select,
+:type => :rvalue,
+:arity => 2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each element in a sequence of entries from the first
+  argument and returns an array with the entires for which the block evaluates to true.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+  a parameterized block as produced by the puppet syntax:
+
+    $a.select |$x| { ... }
+
+  When the first argument is an Array, the block is called with each entry in turn. When the first argument
+  is a hash the entry is an array with `[key, value]`.
+
+  *Examples*
+
+    # selects all that end with berry
+    $a = ["raspberry", "blueberry", "orange"]
+    $a.select |$x| { $x =~ /berry$/ }
+
+  Since 3.2
+  ENDHEREDOC
+
+  require 'puppet/parser/ast/lambda'
+  raise ArgumentError, ("select(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2
+  receiver = args[0]
+  pblock = args[1]
+  raise ArgumentError, ("select(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+  case receiver
+  when Array
+  when Hash
+  else
+    raise ArgumentError, ("select(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+
+  receiver.select {|x| pblock.call(self, x) }
+end
diff --git a/lib/puppet/parser/functions/slice.rb b/lib/puppet/parser/functions/slice.rb
new file mode 100644
index 0000000..41b693d
--- /dev/null
+++ b/lib/puppet/parser/functions/slice.rb
@@ -0,0 +1,96 @@
+Puppet::Parser::Functions::newfunction(
+:slice,
+:type => :rvalue,
+:arity => -2,
+:doc => <<-'ENDHEREDOC') do |args|
+  Applies a parameterized block to each _slice_ of elements in a sequence of selected entries from the first
+  argument and returns the first argument, or if no block is given returns a new array with a concatenation of
+  the slices.
+
+  This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+  the number of elements to include in each slice. The optional third argument should be a
+  a parameterized block as produced by the puppet syntax:
+
+      |$x| { ... }
+
+  The parameterized block should have either one parameter (receiving an array with the slice), or the same number
+  of parameters as specified by the slice size (each parameter receiving its part of the slice).
+  In case there are fewer remaining elements than the slice size for the last slice it will contain the remaining
+  elements. When the block has multiple parameters, excess parameters are set to :undef for an array, and to
+  empty arrays for a Hash.
+
+      $a.slice(2) |$first, $second| { ... }
+
+  When the first argument is a Hash, each key,value entry is counted as one, e.g, a slice size of 2 will produce
+  an array of two arrays with key, value.
+
+      $a.slice(2) |$entry|          { notice "first ${$entry[0]}, second ${$entry[1]}" }
+      $a.slice(2) |$first, $second| { notice "first ${first}, second ${second}" }
+
+  When called without a block, the function produces a concatenated result of the slices.
+
+      slice($[1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]]
+
+  Since 3.2
+  ENDHEREDOC
+  require 'puppet/parser/ast/lambda'
+  require 'puppet/parser/scope'
+
+  def each_Common(o, slice_size, filler, scope, pblock)
+    serving_size = pblock ? pblock.parameter_count : 1
+    if serving_size == 0
+      raise ArgumentError, "Block must define at least one parameter."
+    end
+    unless serving_size == 1 || serving_size == slice_size
+      raise ArgumentError, "Block must define one parameter, or the same number of parameters as the given size of the slice (#{slice_size})."
+    end
+    enumerator = o.each_slice(slice_size)
+    result = []
+    if serving_size == 1
+      ((o.size.to_f / slice_size).ceil).times do
+        if pblock
+          pblock.call(scope, enumerator.next)
+        else
+          result << enumerator.next
+        end
+      end
+    else
+      ((o.size.to_f / slice_size).ceil).times do
+        a = enumerator.next
+        if a.size < serving_size
+          a = a.dup.fill(filler, a.length...serving_size)
+        end
+        pblock.call(scope, *a)
+      end
+    end
+    if pblock
+      o
+    else
+      result
+    end
+  end
+  raise ArgumentError, ("slice(): wrong number of arguments (#{args.length}; must be 2 or 3)") unless args.length == 2 || args.length == 3
+  if args.length >= 2
+    begin
+      slice_size = Puppet::Parser::Scope.number?(args[1])
+    rescue
+      raise ArgumentError, ("slice(): wrong argument type (#{args[1]}; must be number.")
+    end
+  end
+  raise ArgumentError, ("slice(): wrong argument type (#{args[1]}; must be number.") unless slice_size
+  raise ArgumentError, ("slice(): wrong argument value: #{slice_size}; is not an positive integer number > 0") unless slice_size.is_a?(Fixnum) && slice_size > 0
+  receiver = args[0]
+
+  # the block is optional, ok if nil, function then produces an array
+  pblock = args[2]
+  raise ArgumentError, ("slice(): wrong argument type (#{args[2].class}; must be a parameterized block.") unless pblock.is_a?(Puppet::Parser::AST::Lambda) || args.length == 2
+
+  case receiver
+  when Array
+    each_Common(receiver, slice_size, :undef, self, pblock)
+  when Hash
+    each_Common(receiver, slice_size, [], self, pblock)
+  else
+    raise ArgumentError, ("slice(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
+  end
+end
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index a809a14..509f36f 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -32,7 +32,7 @@ program:    statements_and_declarations
   | nil
 
   statements_and_declarations:   statement_or_declaration {
-    result = ast AST::ASTArray, :children => (val[0] ? [val[0]] : [])
+    result = ast AST::BlockExpression, :children => (val[0] ? [val[0]] : [])
   }
   | statements_and_declarations statement_or_declaration {
     if val[1]
@@ -553,7 +553,7 @@ caseopt:        casevalues COLON LBRACE statements RBRACE {
     AST::CaseOpt,
     :value => val[0],
 
-    :statements => ast(AST::ASTArray)
+    :statements => ast(AST::BlockExpression)
   )
 }
 
diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
index 6ae4870..cfaa34d 100644
--- a/lib/puppet/parser/lexer.rb
+++ b/lib/puppet/parser/lexer.rb
@@ -20,6 +20,15 @@ class Puppet::Parser::Lexer
   attr_accessor :line, :indefine
   alias :indefine? :indefine
 
+  # Returns the position on the line.
+  # This implementation always returns nil. It is here for API reasons in Puppet::Error
+  # which needs to support both --parser current, and --parser future.
+  #
+  def pos
+    # Make the lexer comply with newer API. It does not produce a pos...
+    nil
+  end
+  
   def lex_error msg
     raise Puppet::LexError.new(msg)
   end
diff --git a/lib/puppet/parser/methods.rb b/lib/puppet/parser/methods.rb
new file mode 100644
index 0000000..af19b2a
--- /dev/null
+++ b/lib/puppet/parser/methods.rb
@@ -0,0 +1,69 @@
+require 'puppet/util/autoload'
+require 'puppet/parser/scope'
+require 'puppet/parser/functions'
+require 'monitor'
+
+# A module for handling finding and invoking methods (functions invokable as a method).
+# A method call on the form:
+#
+#  $a.meth(1,2,3] {|...| ...}
+#
+# will lookup a function called 'meth' and call it with the arguments ($a, 1, 2, 3, <lambda>)
+#
+# @see Puppet::Parser::AST::Lambda
+# @see Puppet::Parser::AST::MethodCall
+#
+# @api public
+# @since 3.2
+#
+module Puppet::Parser::Methods
+  Environment = Puppet::Node::Environment
+  # Represents an invokable method configured to be invoked for a given object.
+  #
+  class Method
+    def initialize(receiver, obj, method_name, rvalue)
+      @receiver = receiver
+      @o = obj
+      @method_name = method_name
+      @rvalue = rvalue
+    end
+
+    # Invoke this method's function in the given scope with the given arguments and parameterized block.
+    # A method call on the form:
+    #
+    #  $a.meth(1,2,3) {|...| ...}
+    #
+    # results in the equivalent:
+    #
+    #  meth($a, 1, 2, 3, {|...| ... })
+    #
+    # @param scope [Puppet::Parser::Scope] the scope the call takes place in
+    # @param args [Array<Object>] arguments 1..n to pass to the function
+    # @param pblock [Puppet::Parser::AST::Lambda] optional parameterized block to pass as the last argument
+    #   to the called function
+    #
+    def invoke(scope, args=[], pblock=nil)
+      arguments = [@o] + args
+      arguments << pblock if pblock
+      @receiver.send(@method_name, arguments)
+    end
+
+    # @return [Boolean] whether the method function produces an rvalue or not.
+    def is_rvalue?
+      @rvalue
+    end
+  end
+
+  class << self
+    include Puppet::Util
+  end
+
+  # Finds a function and returns an instance of Method configured to perform invocation.
+  # @return [Method, nil] configured method or nil if method not found
+  def self.find_method(scope, receiver, name)
+    fname = Puppet::Parser::Functions.function(name)
+    rvalue = Puppet::Parser::Functions.rvalue?(name)
+    return Method.new(scope, receiver, fname, rvalue) if fname
+    nil
+  end
+end
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
index c756701..4a472c9 100644
--- a/lib/puppet/parser/parser.rb
+++ b/lib/puppet/parser/parser.rb
@@ -34,104 +34,109 @@ class Parser < Racc::Parser
 ##### State transition tables begin ###
 
 clist = [
-'9,15,199,200,204,181,-130,104,-112,106,301,372,91,291,370,168,172,181',
-'300,-197,-184,180,107,180,163,165,169,171,39,205,47,1,332,10,13,180',
-'21,25,35,331,44,48,2,9,15,16,103,164,167,34,334,174,175,157,158,160',
-'161,340,166,170,31,-130,-130,-130,-130,159,8,162,9,15,311,39,104,47',
-'1,348,10,13,330,21,25,35,376,44,48,2,9,15,16,-178,377,292,34,397,282',
-'319,54,304,305,120,55,168,172,31,282,307,185,16,320,8,163,165,169,171',
-'39,-123,47,1,345,10,13,180,21,25,35,358,44,48,2,9,15,16,315,164,167',
-'34,395,174,175,157,158,160,161,314,166,170,31,9,15,304,305,159,8,162',
-'187,372,312,39,370,47,1,257,10,13,212,21,25,35,212,44,48,2,9,15,16,211',
-'299,329,34,211,214,82,54,294,214,185,55,168,172,31,80,81,282,366,336',
-'8,163,165,169,171,39,290,47,1,289,10,13,54,21,25,35,55,44,48,2,9,15',
-'16,31,164,167,34,354,174,175,157,158,160,161,31,166,170,31,9,15,166',
-'170,159,8,162,166,170,159,39,-185,47,1,159,10,13,121,21,25,35,342,44',
-'48,2,9,15,16,-179,-96,343,34,384,58,-180,212,212,25,236,-178,-129,48',
-'31,9,15,16,211,211,8,34,54,214,214,39,55,47,1,353,10,13,31,21,25,35',
-'259,44,48,2,9,15,16,166,170,-177,34,278,58,350,159,303,25,236,304,305',
-'48,31,9,15,16,308,257,8,34,282,307,256,39,255,47,1,253,10,13,31,21,25',
-'35,249,44,48,2,9,15,16,247,312,361,34,322,58,241,93,363,25,236,321,220',
-'48,31,282,283,16,285,106,8,34,282,283,208,39,369,47,1,-122,10,13,31',
-'21,25,35,122,44,48,2,9,15,16,121,66,97,34,373,99,346,157,158,-183,282',
-'307,166,170,31,9,15,-181,-179,159,8,162,375,249,31,149,151,154,125,128',
-'58,134,135,139,141,145,380,152,155,381,9,15,16,257,138,143,146,394,58',
-'93,-182,-180,25,236,-177,-184,48,31,9,15,16,385,127,130,34,82,122,93',
-'39,386,47,1,121,10,13,31,21,25,35,-228,44,48,2,9,15,16,120,97,388,34',
-'99,58,54,-179,390,25,196,-180,44,48,31,110,393,16,108,97,8,34,99,31',
-'100,39,57,47,1,56,10,13,31,21,25,35,399,44,48,2,245,31,16,400,,,34,',
-',168,172,85,86,87,83,,,31,163,165,169,171,,8,174,175,157,158,,,,166',
-'170,-38,-38,-38,-38,,159,,162,164,167,172,,174,175,157,158,160,161,163',
-'166,170,246,-40,-40,-40,-40,159,,162,,168,172,85,86,87,83,,,,163,165',
-'169,171,,,174,175,157,158,160,161,,166,170,-44,-44,-44,-44,,159,,162',
-'164,167,,,174,175,157,158,160,161,,166,170,173,,,,,159,,162,,168,172',
-',,,,,,,163,165,169,171,,,174,175,157,158,,9,15,166,170,66,,72,,,159',
-',162,164,167,,,174,175,157,158,160,161,59,166,170,9,15,,,114,159,-197',
-'162,58,,,,25,69,,,48,,,,16,64,113,,34,9,15,77,,66,,72,,58,,31,,25,69',
-',,48,9,15,,16,66,59,72,34,,,,,,,,,58,,31,,25,69,59,,48,,,,16,64,,,34',
-'58,,77,,25,69,,,48,,31,,16,64,9,15,34,,66,77,72,,,,9,15,,31,66,,72,',
-',,,,59,,,,,,,,157,158,59,58,,166,170,25,69,,,48,159,58,162,16,64,25',
-'69,34,,48,77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31,66,,72,,,,9,15',
-'59,287,66,,72,,,,,,59,58,,,,25,69,,,48,59,58,,16,64,25,69,34,,48,77',
-'58,,16,64,25,69,34,31,48,77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31',
-'66,,72,,,,9,15,59,,66,,72,,,,,,59,58,,,,25,69,,,48,59,58,,16,64,25,69',
-'34,,48,77,58,,16,64,25,69,34,31,48,77,,,16,64,9,15,34,31,66,77,72,,',
-',9,15,,31,66,,72,,,,9,15,59,,66,179,72,,,,,,59,58,,,,25,69,,,48,59,58',
-',16,64,25,69,34,,48,77,58,,16,64,25,69,34,31,48,77,,,16,64,9,15,34,31',
-'66,77,72,,,,9,15,,31,66,,72,,,,9,15,59,,66,,72,,,,,,59,58,,,,25,69,',
-',48,59,58,,16,64,25,69,34,,48,77,58,,16,64,25,69,34,31,48,77,,,16,64',
-'9,15,34,31,66,77,72,,,,9,15,,31,66,,72,,,,9,15,59,,66,,72,,,,,,59,58',
-',,,25,69,,,48,59,58,,16,64,25,69,34,,48,77,58,,16,64,25,69,34,31,48',
-'77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31,66,,72,,,,9,15,59,,66,,72',
-',,,,,59,58,,,,25,69,,,48,59,58,,16,64,25,69,34,,48,77,58,,16,64,25,69',
-'34,31,48,77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31,66,,72,,,,9,15,59',
-',66,,72,,,,,,59,58,,,,25,69,,,48,59,58,,16,64,25,69,34,,48,77,58,,16',
-'64,25,69,34,31,48,77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31,66,,72',
-',,,,,59,,,,,,,,,,59,58,9,15,,25,69,,,48,,58,,16,64,25,69,34,,48,77,',
-',16,64,,,34,31,,77,,,,,58,54,,31,25,196,,44,48,9,15,,16,66,,72,34,,',
-'9,15,,,66,,72,,31,,9,15,59,,66,,,,,,9,15,59,58,66,,,25,69,,,48,,58,',
-'16,64,25,69,34,,48,77,58,,16,64,25,69,34,31,48,77,58,,16,,25,69,34,31',
-'48,9,15,,16,66,,72,34,31,,9,15,,,66,,72,,31,,9,15,59,,66,,72,,,,,,59',
-'58,,,,25,69,,,48,59,58,,16,64,25,69,34,,48,77,58,,16,64,25,69,34,31',
-'48,77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31,66,,72,,,,9,15,59,,,,238',
-',,,,,59,58,,,,25,69,,,48,,58,,16,64,25,69,34,,48,77,58,,16,64,25,236',
-'34,31,48,77,,,16,,9,15,34,31,66,,72,,,,,,,31,,,,9,15,,,66,59,72,,,,',
-',,,,,58,,,,25,69,59,243,48,9,15,,16,64,,,34,58,,77,,25,69,,,48,,31,',
-'16,64,,,34,9,15,77,,66,179,72,,58,,31,,25,236,,,48,9,15,,16,66,59,72',
-'34,,,,,,,,,58,,31,,25,69,59,,48,,,,16,64,,,34,58,,77,,25,69,,,48,,31',
-',16,64,9,15,34,,66,77,72,,,,9,15,,31,66,,72,,,,9,15,59,,66,,72,,,,,',
-'59,58,,,,25,69,,,48,59,58,,16,64,25,69,34,,48,77,58,,16,64,25,69,34',
-'31,48,77,,,16,64,9,15,34,31,66,77,72,,,,9,15,,31,66,,174,175,157,158',
-'160,161,59,166,170,,,,,,159,,162,58,,,,25,69,,,48,,58,,16,64,25,236',
-'34,,48,77,168,172,16,,,,34,31,,163,165,169,171,,,,,31,,,,,,,,,,,,,,',
-'164,167,,,174,175,157,158,160,161,,166,170,168,172,,,,159,,162,,163',
-'165,169,171,,,,,168,172,,,,,,,,163,165,169,171,,,164,167,,,174,175,157',
-'158,160,161,,166,170,,,,,,159,,162,174,175,157,158,160,161,,166,170',
-'168,172,,,,159,,162,,163,165,169,171,,,,,,,,,168,172,,,,,,,,163,165',
-'169,171,,,174,175,157,158,160,161,,166,170,,,172,,,159,,162,164,167',
-'163,,174,175,157,158,160,161,,166,170,168,172,,,,159,,162,,163,165,169',
-'171,,,,174,175,157,158,160,161,,166,170,,,,,,159,,162,167,,,174,175',
-'157,158,160,161,,166,170,168,172,,,,159,,162,,163,165,169,171,174,175',
-'157,158,160,161,,166,170,,172,,,,159,,162,,163,164,167,,,174,175,157',
-'158,160,161,,166,170,168,172,,,,159,,162,,163,165,169,171,174,175,157',
-'158,160,161,,166,170,,172,,,,159,,162,,163,164,167,,,174,175,157,158',
-'160,161,,166,170,168,172,,,,159,,162,,163,165,169,171,174,175,157,158',
-'160,161,,166,170,,,,,,159,,162,,,164,167,,,174,175,157,158,160,161,',
-'166,170,168,172,,,,159,,162,,163,165,169,171,,,,,,,,,,,,,,,,,,,,164',
-'167,,,174,175,157,158,160,161,,166,170,168,172,,,,159,,162,260,163,165',
-'169,171,,,,,,,,,,,,,,,,,,,,164,167,,,174,175,157,158,160,161,,166,170',
-'168,172,,,,159,,162,,163,165,169,171,,,,,168,172,,,,,,,,163,165,169',
-'171,,,164,167,,,174,175,157,158,160,161,,166,170,,,,,,159,,162,174,175',
-'157,158,160,161,,166,170,168,172,,,,159,,162,,163,165,169,171,,,,,,',
-',,,,,,,,,,,,,164,167,,,174,175,157,158,160,161,,166,170,,,,,,159,,162',
-'149,151,154,125,128,,134,135,139,222,230,,152,225,,,,,,138,143,224,149',
-'151,154,125,128,,134,135,139,222,223,,152,225,,127,130,,,138,143,224',
-'149,151,154,125,128,,134,135,139,222,230,,152,225,,127,130,,,138,143',
-'224,149,151,154,125,128,,134,135,139,222,223,,152,225,,127,130,,,138',
-'143,224,149,151,154,125,128,,134,135,139,222,223,,152,225,,127,130,',
-',138,143,224,149,151,154,125,128,,134,135,139,222,230,,152,225,,127',
-'130,,,138,143,224,,,,,,,,,,,,,,,,127,130' ]
-        racc_action_table = arr = ::Array.new(2533, nil)
+'35,36,199,198,246,159,-130,86,-112,82,277,357,361,379,356,215,210,159',
+'276,-197,360,158,85,158,211,213,212,214,39,248,48,49,267,33,50,158,51',
+'37,26,-129,40,46,30,35,36,32,84,217,216,31,398,203,204,206,205,208,209',
+'-122,201,202,52,-130,-130,-130,-130,200,38,207,35,36,278,39,86,48,49',
+'350,33,50,90,51,37,26,89,40,46,30,35,36,32,-178,94,253,31,257,280,257',
+'364,274,273,92,93,215,210,52,257,255,226,336,62,38,211,213,212,214,39',
+'338,48,49,254,33,50,339,51,37,26,347,40,46,30,35,36,32,-180,217,216',
+'31,310,203,204,206,205,208,209,341,201,202,52,35,36,274,273,200,38,207',
+'223,303,-178,39,304,48,49,-177,33,50,185,51,37,26,95,40,46,30,35,36',
+'32,190,-184,281,31,397,189,344,90,201,202,226,89,215,210,52,200,357',
+'250,32,356,38,211,213,212,214,39,-179,48,49,268,33,50,90,51,37,26,89',
+'40,46,30,35,36,32,185,217,216,31,395,203,204,206,205,208,209,190,201',
+'202,52,119,189,201,202,200,38,207,201,202,200,39,119,48,49,200,33,50',
+'185,51,37,26,267,40,46,30,35,36,32,190,185,90,31,392,189,89,119,265',
+'375,118,337,190,120,52,257,280,189,302,-96,38,118,257,263,120,39,-185',
+'48,49,52,33,50,52,51,37,26,-123,40,46,30,35,36,32,52,103,118,31,252',
+'120,279,206,205,251,257,280,201,202,52,250,243,243,262,200,38,207,257',
+'263,52,137,135,139,134,136,80,132,140,141,177,168,240,131,162,35,36',
+'82,32,180,142,130,163,271,94,-184,274,273,203,204,206,205,-183,52,-181',
+'201,202,62,138,144,-180,353,200,39,207,48,49,354,33,50,330,51,37,26',
+'-182,40,46,30,35,36,32,-177,52,-179,31,367,203,204,206,205,157,368,370',
+'201,202,52,35,36,371,372,200,38,207,122,327,326,39,110,48,49,109,33',
+'50,267,51,37,26,334,40,46,30,35,36,32,91,81,62,31,377,80,90,323,-179',
+'37,128,382,40,46,52,35,36,32,383,-180,38,31,385,61,-228,39,387,48,49',
+'388,33,50,52,51,37,26,323,40,46,30,35,36,32,319,110,393,31,308,80,90',
+'317,305,37,128,158,40,46,52,53,399,32,400,,38,31,,,,39,,48,49,,33,50',
+'52,51,37,26,,40,46,30,230,,32,,,,31,,,215,210,56,57,58,59,,,52,211,213',
+'212,214,,38,203,204,206,205,208,209,,201,202,56,57,58,59,,200,,207,217',
+'216,210,,203,204,206,205,208,209,211,201,202,228,-38,-38,-38,-38,200',
+',207,,215,210,-44,-44,-44,-44,,,,211,213,212,214,,,203,204,206,205,208',
+'209,,201,202,-40,-40,-40,-40,,200,,207,217,216,,,203,204,206,205,208',
+'209,,201,202,229,,,,,200,,207,,215,210,206,205,,,,201,202,211,213,212',
+'214,,200,,207,,,,35,36,,,103,,104,,,,,,217,216,,,203,204,206,205,208',
+'209,102,201,202,35,36,,,103,200,104,207,80,,,,37,77,,,46,,,,32,101,102',
+',31,35,36,100,,103,,104,,80,,52,,37,77,,,46,,,,32,101,102,,31,35,36',
+'100,,103,,104,,80,,52,,37,77,,,46,,,,32,101,102,,31,35,36,100,,103,',
+'104,,80,,52,,37,77,,,46,,,,32,101,102,,31,35,36,100,,103,,104,,80,,52',
+',37,77,,,46,,,,32,101,102,,31,35,36,100,,103,,104,,80,,52,,37,77,,,46',
+',,,32,101,102,,31,35,36,100,,103,,104,,80,,52,,37,77,,,46,,,,32,101',
+'102,,31,35,36,100,,103,,104,,80,,52,,37,77,,,46,,,,32,101,102,,31,35',
+'36,100,,103,,104,,80,,52,,37,77,,,46,,,,32,101,102,,31,35,36,100,,103',
+',104,,80,,52,,37,77,,,46,,,,32,101,102,,31,35,36,100,,103,,104,,80,',
+'52,,37,77,,,46,,,,32,101,102,,31,35,36,100,,103,,104,,80,,52,,37,77',
+',,46,35,36,,32,101,102,155,31,,,100,,,35,36,,80,103,52,,37,77,,,46,',
+',,32,101,35,36,31,80,103,100,104,37,231,,,46,,52,,32,80,,,31,37,77,102',
+',46,35,36,,32,103,52,104,31,80,,35,36,37,77,,,46,358,52,,32,101,102',
+',31,,,100,35,36,,,,80,,52,,37,77,,,46,,80,,32,101,37,231,31,,46,100',
+',,32,,,,31,52,80,,,,37,231,,,46,52,,,32,35,36,,31,103,,104,,,,,,,,52',
+',,35,36,,,103,102,104,,,,,,,,,,80,,,,37,77,102,,46,,,,32,101,,,31,80',
+',100,,37,77,,,46,,52,,32,101,35,36,31,,103,100,104,,,,35,36,,52,103',
+',104,,,,35,36,102,,103,161,104,,,,,,102,80,,,,37,77,,,46,102,80,,32',
+'101,37,77,31,,46,100,80,,32,101,37,77,31,52,46,100,,,32,101,35,36,31',
+'52,103,100,104,,,,35,36,,52,103,,104,,,,35,36,102,,103,,104,,,,,,102',
+'80,,,,37,77,,,46,102,80,,32,101,37,77,31,,46,100,80,,32,101,37,77,31',
+'52,46,100,,,32,101,35,36,31,52,103,100,104,,,,35,36,,52,78,,-197,,,',
+'35,36,102,,103,,104,,,,,,63,80,,,,37,77,,,46,102,80,,32,101,37,77,31',
+',46,100,80,,32,,37,77,31,52,46,,,,32,101,35,36,31,52,103,100,104,,,',
+'35,36,,52,103,,104,,,,35,36,102,,103,,104,,,,,,102,80,,,,37,77,,,46',
+'102,80,,32,101,37,77,31,,46,100,80,,32,101,37,77,31,52,46,100,,,32,101',
+'35,36,31,52,103,100,104,,,,35,36,,52,103,,104,,,,35,36,102,,103,,104',
+',,,,,102,80,,,,37,77,,,46,102,80,,32,101,37,77,31,,46,100,80,,32,101',
+'37,77,31,52,46,100,,,32,101,35,36,31,52,103,100,104,,,,35,36,,52,103',
+',104,,,,35,36,102,,103,,104,,,,,,102,80,,,,37,77,,,46,102,80,,32,101',
+'37,77,31,,46,100,80,,32,101,37,77,31,52,46,100,,,32,101,35,36,31,52',
+'103,100,104,,,,35,36,,52,103,161,104,,,,35,36,102,,103,,104,,,,,,102',
+'80,,,,37,77,,,46,102,80,,32,101,37,77,31,,46,100,80,,32,101,37,77,31',
+'52,46,100,,,32,101,35,36,31,52,103,100,104,,,,35,36,,52,,,,,,,35,36',
+'102,,,,234,,,,35,36,,80,103,,,37,77,,,46,,80,,32,101,37,231,31,,46,100',
+'80,,32,,37,231,31,52,46,,80,,32,,37,231,31,52,46,35,36,,32,103,,104',
+'31,52,,35,36,,,103,,104,,52,,35,36,102,,,,,,,,,,102,80,,,,37,77,,,46',
+',80,,32,101,37,77,31,,46,100,80,,32,101,37,231,31,52,46,100,,,32,,35',
+'36,31,52,103,,104,,,,,,,52,,,,35,36,,,103,102,104,,,,,,,,,,80,,,,37',
+'77,102,260,46,,35,36,32,101,103,,31,80,,100,,37,77,,,46,,52,,32,101',
+'35,36,31,,103,100,104,,,,,,80,52,,,37,77,,,46,,102,,32,,210,,31,,,,',
+'80,211,,,37,77,52,,46,,,,32,101,215,210,31,,,100,,,,211,213,212,214',
+'52,203,204,206,205,208,209,,201,202,210,,,,,200,,207,211,217,216,,,203',
+'204,206,205,208,209,,201,202,215,210,,,,200,,207,,211,213,212,214,203',
+'204,206,205,208,209,,201,202,,210,,,,200,,207,,211,217,216,,,203,204',
+'206,205,208,209,,201,202,215,210,,,,200,,207,,211,213,212,214,203,204',
+'206,205,208,209,,201,202,,,,,,200,,207,,,217,216,,,203,204,206,205,208',
+'209,,201,202,215,210,,,,200,,207,,211,213,212,214,203,204,206,205,208',
+'209,,201,202,,,,,,200,,207,,,217,216,,,203,204,206,205,208,209,,201',
+'202,215,210,,,,200,,207,301,211,213,212,214,,,,,215,210,,,,,,,,211,213',
+'212,214,,,217,216,,,203,204,206,205,208,209,,201,202,,,,,,200,,207,203',
+'204,206,205,208,209,,201,202,215,210,,,,200,,207,,211,213,212,214,,',
+',,,,,,215,210,,,,,,,,211,213,212,214,,,203,204,206,205,208,209,,201',
+'202,,,,,,200,,207,217,216,,,203,204,206,205,208,209,,201,202,215,210',
+',,,200,,207,,211,213,212,214,,,,,,,,,,,,,,,,,,,,,216,,,203,204,206,205',
+'208,209,,201,202,215,210,,,,200,,207,,211,213,212,214,,,,,,,,,,,,,,',
+',,,,,217,216,,,203,204,206,205,208,209,,201,202,215,210,,,,200,,207',
+',211,213,212,214,,,,,215,210,,,,,,,,211,213,212,214,,,217,216,,,203',
+'204,206,205,208,209,,201,202,,,,,,200,,207,203,204,206,205,208,209,',
+'201,202,215,210,,,,200,,207,,211,213,212,214,,,,,,,,,,,,,,,,,,,,217',
+'216,,,203,204,206,205,208,209,,201,202,215,210,,,,200,,207,,211,213',
+'212,214,,,,,,,,,,,,,,,,,,,,217,216,,,203,204,206,205,208,209,,201,202',
+',,,,,200,,207,137,135,139,134,136,,132,140,141,148,179,,131,133,,,,',
+',142,130,143,137,135,139,134,136,,132,140,141,148,146,,131,133,,138',
+'144,,,142,130,143,137,135,139,134,136,,132,140,141,148,146,,131,133',
+',138,144,,,142,130,143,137,135,139,134,136,,132,140,141,148,179,,131',
+'133,,138,144,,,142,130,143,137,135,139,134,136,,132,140,141,148,179',
+',131,133,,138,144,,,142,130,143,137,135,139,134,136,,132,140,141,148',
+'146,,131,133,,138,144,,,142,130,143,,,,,,,,,,,,,,,,138,144' ]
+        racc_action_table = arr = ::Array.new(2588, nil)
         idx = 0
         clist.each do |str|
           str.split(',', -1).each do |i|
@@ -141,113 +146,113 @@ class Parser < Racc::Parser
         end
 
 clist = [
-'0,0,89,89,95,69,321,27,145,27,210,322,18,184,322,89,89,196,210,196,18',
-'145,27,69,89,89,89,89,0,95,0,0,251,0,0,196,0,0,0,250,0,0,0,253,253,0',
-'27,89,89,0,253,89,89,89,89,89,89,258,89,89,0,321,321,321,321,89,0,89',
-'289,289,226,253,68,253,253,289,253,253,249,253,253,253,326,253,253,253',
-'393,393,253,68,326,191,253,393,242,242,26,226,226,247,26,191,191,253',
-'286,286,289,26,242,253,191,191,191,191,393,243,393,393,286,393,393,236',
-'393,393,393,302,393,393,393,390,390,393,235,191,191,393,390,191,191',
-'191,191,191,191,230,191,191,393,72,72,302,302,191,393,191,72,373,228',
-'390,373,390,390,221,390,390,306,390,390,390,212,390,390,390,277,277',
-'390,306,206,248,390,212,306,10,1,198,212,72,1,248,248,390,10,10,317',
-'317,254,390,248,248,248,248,277,182,277,277,182,277,277,13,277,277,277',
-'13,277,277,277,299,299,277,170,248,248,277,299,248,248,248,248,248,248',
-'166,248,248,277,376,376,262,262,248,277,248,266,266,262,299,155,299',
-'299,266,299,299,153,299,299,299,279,299,299,299,350,350,299,148,146',
-'283,299,350,376,144,103,107,376,376,142,141,376,299,367,367,376,103',
-'107,299,376,294,103,107,350,294,350,350,294,350,350,376,350,350,350',
-'140,350,350,350,173,173,350,261,261,137,350,173,367,293,261,217,367',
-'367,217,217,367,350,246,246,367,219,133,350,367,219,219,132,173,126',
-'173,173,123,173,173,367,173,173,173,122,173,173,173,245,245,173,119',
-'309,310,173,245,246,111,205,313,246,246,244,105,246,173,244,244,246',
-'177,101,173,246,177,177,100,245,319,245,245,320,245,245,246,245,245',
-'245,88,245,245,245,57,57,245,79,57,205,245,323,205,288,280,280,78,288',
-'288,280,280,245,324,324,75,74,280,245,280,324,332,205,57,57,57,57,57',
-'57,57,57,57,57,57,333,57,57,335,383,383,57,338,57,57,57,383,324,204',
-'71,70,324,324,65,63,324,57,84,84,324,355,57,57,324,58,53,21,383,360',
-'383,383,50,383,383,324,383,383,383,366,383,383,383,30,30,383,47,204',
-'368,383,204,84,84,40,372,84,84,36,84,84,383,33,377,84,28,21,383,84,21',
-'204,24,30,3,30,30,2,30,30,84,30,30,30,396,30,30,30,116,21,30,398,,,30',
-',,116,116,11,11,11,11,,,30,116,116,116,116,,30,265,265,265,265,,,,265',
-'265,52,52,52,52,,265,,265,116,116,275,,116,116,116,116,116,116,275,116',
-'116,117,7,7,7,7,116,,116,,117,117,19,19,19,19,,,,117,117,117,117,,,275',
-'275,275,275,275,275,,275,275,17,17,17,17,,275,,275,117,117,,,117,117',
-'117,117,117,117,,117,117,62,,,,,117,,117,,62,62,,,,,,,,62,62,62,62,',
-',264,264,264,264,,160,160,264,264,160,,160,,,264,,264,62,62,,,62,62',
-'62,62,62,62,160,62,62,35,35,,,35,62,35,62,160,,,,160,160,,,160,,,,160',
-'160,35,,160,181,181,160,,181,,181,,35,,160,,35,35,,,35,39,39,,35,39',
-'181,39,35,,,,,,,,,181,,35,,181,181,39,,181,,,,181,181,,,181,39,,181',
-',39,39,,,39,,181,,39,39,370,370,39,,370,39,370,,,,44,44,,39,44,,44,',
-',,,,370,,,,,,,,281,281,44,370,,281,281,370,370,,,370,281,44,281,370',
-'370,44,44,370,,44,370,,,44,44,180,180,44,370,180,44,180,,,,175,175,',
-'44,175,,175,,,,174,174,180,180,174,,174,,,,,,175,180,,,,180,180,,,180',
-'174,175,,180,180,175,175,180,,175,180,174,,175,175,174,174,175,180,174',
-'175,,,174,174,15,15,174,175,15,174,15,,,,59,59,,174,59,,59,,,,199,199',
-'15,,199,,199,,,,,,59,15,,,,15,15,,,15,199,59,,15,15,59,59,15,,59,15',
-'199,,59,59,199,199,59,15,199,59,,,199,199,64,64,199,59,64,199,64,,,',
-'172,172,,199,172,,172,,,,66,66,64,,66,66,66,,,,,,172,64,,,,64,64,,,64',
-'66,172,,64,64,172,172,64,,172,64,66,,172,172,66,66,172,64,66,172,,,66',
-'66,8,8,66,172,8,66,8,,,,171,171,,66,171,,171,,,,169,169,8,,169,,169',
-',,,,,171,8,,,,8,8,,,8,169,171,,8,8,171,171,8,,171,8,169,,171,171,169',
-'169,171,8,169,171,,,169,169,168,168,169,171,168,169,168,,,,167,167,',
-'169,167,,167,,,,330,330,168,,330,,330,,,,,,167,168,,,,168,168,,,168',
-'330,167,,168,168,167,167,168,,167,168,330,,167,167,330,330,167,168,330',
-'167,,,330,330,165,165,330,167,165,330,165,,,,77,77,,330,77,,77,,,,164',
-'164,165,,164,,164,,,,,,77,165,,,,165,165,,,165,164,77,,165,165,77,77',
-'165,,77,165,164,,77,77,164,164,77,165,164,77,,,164,164,80,80,164,77',
-'80,164,80,,,,81,81,,164,81,,81,,,,82,82,80,,82,,82,,,,,,81,80,,,,80',
-'80,,,80,82,81,,80,80,81,81,80,,81,80,82,,81,81,82,82,81,80,82,81,,,82',
-'82,163,163,82,81,163,82,163,,,,91,91,,82,91,,91,,,,,,163,,,,,,,,,,91',
-'163,92,92,,163,163,,,163,,91,,163,163,91,91,163,,91,163,,,91,91,,,91',
-'163,,91,,,,,92,92,,91,92,92,,92,92,162,162,,92,162,,162,92,,,161,161',
-',,161,,161,,92,,315,315,162,,315,,,,,,241,241,161,162,241,,,162,162',
-',,162,,161,,162,162,161,161,162,,161,162,315,,161,161,315,315,161,162',
-'315,161,241,,315,,241,241,315,161,241,104,104,,241,104,,104,241,315',
-',314,314,,,314,,314,,241,,159,159,104,,159,,159,,,,,,314,104,,,,104',
-'104,,,104,159,314,,104,104,314,314,104,,314,104,159,,314,314,159,159',
-'314,104,159,314,,,159,159,158,158,159,314,158,159,158,,,,157,157,,159',
-'157,,157,,,,110,110,158,,,,110,,,,,,157,158,,,,158,158,,,158,,157,,158',
-'158,157,157,158,,157,158,110,,157,157,110,110,157,158,110,157,,,110',
-',259,259,110,157,259,,259,,,,,,,110,,,,113,113,,,113,259,113,,,,,,,',
-',,259,,,,259,259,113,113,259,238,238,,259,259,,,259,113,,259,,113,113',
-',,113,,259,,113,113,,,113,114,114,113,,114,114,114,,238,,113,,238,238',
-',,238,284,284,,238,284,114,284,238,,,,,,,,,114,,238,,114,114,284,,114',
-',,,114,114,,,114,284,,114,,284,284,,,284,,114,,284,284,121,121,284,',
-'121,284,121,,,,291,291,,284,291,,291,,,,301,301,121,,301,,301,,,,,,291',
-'121,,,,121,121,,,121,301,291,,121,121,291,291,121,,291,121,301,,291',
-'291,301,301,291,121,301,291,,,301,301,300,300,301,291,300,301,300,,',
-',255,255,,301,255,,276,276,276,276,276,276,300,276,276,,,,,,276,,276',
-'300,,,,300,300,,,300,,255,,300,300,255,255,300,,255,300,378,378,255',
-',,,255,300,,378,378,378,378,,,,,255,,,,,,,,,,,,,,,378,378,,,378,378',
-'378,378,378,378,,378,378,189,189,,,,378,,378,,189,189,189,189,,,,,357',
-'357,,,,,,,,357,357,357,357,,,189,189,,,189,189,189,189,189,189,,189',
-'189,,,,,,189,,189,357,357,357,357,357,357,,357,357,356,356,,,,357,,357',
-',356,356,356,356,,,,,,,,,349,349,,,,,,,,349,349,349,349,,,356,356,356',
-'356,356,356,,356,356,,,269,,,356,,356,349,349,269,,349,349,349,349,349',
-'349,,349,349,268,268,,,,349,,349,,268,268,268,268,,,,269,269,269,269',
-'269,269,,269,269,,,,,,269,,269,268,,,268,268,268,268,268,268,,268,268',
-'364,364,,,,268,,268,,364,364,364,364,267,267,267,267,267,267,,267,267',
-',273,,,,267,,267,,273,364,364,,,364,364,364,364,364,364,,364,364,178',
-'178,,,,364,,364,,178,178,178,178,273,273,273,273,273,273,,273,273,,272',
-',,,273,,273,,272,178,178,,,178,178,178,178,178,178,,178,178,190,190',
-',,,178,,178,,190,190,190,190,272,272,272,272,272,272,,272,272,,,,,,272',
-',272,,,190,190,,,190,190,190,190,190,190,,190,190,202,202,,,,190,,190',
-',202,202,202,202,,,,,,,,,,,,,,,,,,,,202,202,,,202,202,202,202,202,202',
-',202,202,156,156,,,,202,,202,156,156,156,156,156,,,,,,,,,,,,,,,,,,,',
-'156,156,,,156,156,156,156,156,156,,156,156,341,341,,,,156,,156,,341',
-'341,341,341,,,,,271,271,,,,,,,,271,271,271,271,,,341,341,,,341,341,341',
-'341,341,341,,341,341,,,,,,341,,341,271,271,271,271,271,271,,271,271',
-'344,344,,,,271,,271,,344,344,344,344,,,,,,,,,,,,,,,,,,,,344,344,,,344',
-'344,344,344,344,344,,344,344,,,,,,344,,344,108,108,108,108,108,,108',
-'108,108,108,108,,108,108,,,,,,108,108,108,106,106,106,106,106,,106,106',
-'106,106,106,,106,106,,108,108,,,106,106,106,312,312,312,312,312,,312',
-'312,312,312,312,,312,312,,106,106,,,312,312,312,257,257,257,257,257',
-',257,257,257,257,257,,257,257,,312,312,,,257,257,257,256,256,256,256',
-'256,,256,256,256,256,256,,256,256,,257,257,,,256,256,256,220,220,220',
-'220,220,,220,220,220,220,220,,220,220,,256,256,,,220,220,220,,,,,,,',
-',,,,,,,,220,220' ]
-        racc_action_check = arr = ::Array.new(2533, nil)
+'0,0,97,97,115,77,262,28,168,28,186,354,313,345,354,97,97,128,186,128',
+'313,168,28,77,97,97,97,97,0,115,0,0,178,0,0,128,0,0,0,177,0,0,0,391',
+'391,0,28,97,97,0,391,97,97,97,97,97,97,254,97,97,0,262,262,262,262,97',
+'0,97,304,304,191,391,68,391,391,304,391,391,49,391,391,391,49,391,391',
+'391,2,2,391,68,33,153,391,259,259,315,315,191,191,33,33,153,153,391',
+'154,154,304,259,175,391,153,153,153,153,2,263,2,2,154,2,2,264,2,2,2',
+'275,2,2,2,229,229,2,173,153,153,2,229,153,153,153,153,153,153,266,153',
+'153,2,104,104,275,275,153,2,153,104,222,171,229,222,229,229,170,229',
+'229,84,229,229,229,34,229,229,229,383,383,229,84,34,195,229,383,84,269',
+'29,288,288,104,29,195,195,229,288,310,270,29,310,229,195,195,195,195',
+'383,169,383,383,166,383,383,50,383,383,383,50,383,383,383,382,382,383',
+'185,195,195,383,382,195,195,195,195,195,195,185,195,195,383,51,185,290',
+'290,195,383,195,289,289,290,382,248,382,382,289,382,382,272,382,382',
+'382,165,382,382,382,372,372,382,272,85,326,382,372,272,326,246,164,326',
+'51,261,85,51,382,261,261,85,221,163,382,248,221,221,248,372,162,372',
+'372,201,372,372,51,372,372,372,155,372,372,372,81,81,372,248,81,246',
+'372,149,246,192,287,287,146,192,192,287,287,372,145,114,113,160,287',
+'372,287,160,160,246,81,81,81,81,81,81,81,81,81,81,81,112,81,81,306,306',
+'87,81,83,81,81,81,181,80,79,181,181,292,292,292,292,76,81,75,292,292',
+'73,81,81,71,307,292,306,292,306,306,309,306,306,249,306,306,306,69,306',
+'306,306,319,319,306,67,202,66,306,319,291,291,291,291,64,320,321,291',
+'291,306,55,55,323,324,291,306,291,53,245,244,319,48,319,319,41,319,319',
+'343,319,319,319,255,319,319,319,327,327,319,30,27,25,319,327,55,55,243',
+'23,55,55,357,55,55,319,60,60,55,360,22,319,55,362,21,364,327,366,327',
+'327,369,327,327,55,327,327,327,370,327,327,327,228,228,327,241,240,376',
+'327,228,60,60,235,225,60,60,231,60,60,327,1,394,60,396,,327,60,,,,228',
+',228,228,,228,228,60,228,228,228,,228,228,228,108,,228,,,,228,,,108',
+'108,20,20,20,20,,,228,108,108,108,108,,228,294,294,294,294,294,294,',
+'294,294,19,19,19,19,,294,,294,108,108,296,,108,108,108,108,108,108,296',
+'108,108,105,5,5,5,5,108,,108,,105,105,9,9,9,9,,,,105,105,105,105,,,296',
+'296,296,296,296,296,,296,296,7,7,7,7,,296,,296,105,105,,,105,105,105',
+'105,105,105,,105,105,107,,,,,105,,105,,107,107,286,286,,,,286,286,107',
+'107,107,107,,286,,286,,,,204,204,,,204,,204,,,,,,107,107,,,107,107,107',
+'107,107,107,204,107,107,36,36,,,36,107,36,107,204,,,,204,204,,,204,',
+',,204,204,36,,204,38,38,204,,38,,38,,36,,204,,36,36,,,36,,,,36,36,38',
+',36,39,39,36,,39,,39,,38,,36,,38,38,,,38,,,,38,38,39,,38,40,40,38,,40',
+',40,,39,,38,,39,39,,,39,,,,39,39,40,,39,214,214,39,,214,,214,,40,,39',
+',40,40,,,40,,,,40,40,214,,40,213,213,40,,213,,213,,214,,40,,214,214',
+',,214,,,,214,214,213,,214,212,212,214,,212,,212,,213,,214,,213,213,',
+',213,,,,213,213,212,,213,211,211,213,,211,,211,,212,,213,,212,212,,',
+'212,,,,212,212,211,,212,210,210,212,,210,,210,,211,,212,,211,211,,,211',
+',,,211,211,210,,211,209,209,211,,209,,209,,210,,211,,210,210,,,210,',
+',,210,210,209,,210,356,356,210,,356,,356,,209,,210,,209,209,,,209,,',
+',209,209,356,,209,63,63,209,,63,,63,,356,,209,,356,356,,,356,361,361',
+',356,356,63,63,356,,,356,,,317,317,,63,317,356,,63,63,,,63,,,,63,63',
+'208,208,63,361,208,63,208,361,361,,,361,,63,,361,317,,,361,317,317,208',
+',317,207,207,,317,207,361,207,317,208,,311,311,208,208,,,208,311,317',
+',208,208,207,,208,,,208,363,363,,,,207,,208,,207,207,,,207,,311,,207',
+'207,311,311,207,,311,207,,,311,,,,311,207,363,,,,363,363,,,363,311,',
+',363,305,305,,363,305,,305,,,,,,,,363,,,206,206,,,206,305,206,,,,,,',
+',,,305,,,,305,305,206,,305,,,,305,305,,,305,206,,305,,206,206,,,206',
+',305,,206,206,205,205,206,,205,206,205,,,,215,215,,206,215,,215,,,,78',
+'78,205,,78,78,78,,,,,,215,205,,,,205,205,,,205,78,215,,205,205,215,215',
+'205,,215,205,78,,215,215,78,78,215,205,78,215,,,78,78,203,203,78,215',
+'203,78,203,,,,371,371,,78,371,,371,,,,200,200,203,,200,,200,,,,,,371',
+'203,,,,203,203,,,203,200,371,,203,203,371,371,203,,371,203,200,,371',
+'371,200,200,371,203,200,371,,,200,200,199,199,200,371,199,200,199,,',
+',26,26,,200,26,,26,,,,251,251,199,,251,,251,,,,,,26,199,,,,199,199,',
+',199,251,26,,199,199,26,26,199,,26,199,251,,26,,251,251,26,199,251,',
+',,251,251,86,86,251,26,86,251,86,,,,252,252,,251,252,,252,,,,92,92,86',
+',92,,92,,,,,,252,86,,,,86,86,,,86,92,252,,86,86,252,252,86,,252,86,92',
+',252,252,92,92,252,86,92,252,,,92,92,93,93,92,252,93,92,93,,,,94,94',
+',92,94,,94,,,,95,95,93,,95,,95,,,,,,94,93,,,,93,93,,,93,95,94,,93,93',
+'94,94,93,,94,93,95,,94,94,95,95,94,93,95,94,,,95,95,216,216,95,94,216',
+'95,216,,,,100,100,,95,100,,100,,,,101,101,216,,101,,101,,,,,,100,216',
+',,,216,216,,,216,101,100,,216,216,100,100,216,,100,216,101,,100,100',
+'101,101,100,216,101,100,,,101,101,102,102,101,100,102,101,102,,,,103',
+'103,,101,103,103,103,,,,256,256,102,,256,,256,,,,,,103,102,,,,102,102',
+',,102,256,103,,102,102,103,103,102,,103,102,256,,103,103,256,256,103',
+'102,256,103,,,256,256,217,217,256,103,217,256,217,,,,234,234,,256,,',
+',,,,109,109,217,,,,109,,,,265,265,,217,265,,,217,217,,,217,,234,,217',
+'217,234,234,217,,234,217,109,,234,,109,109,234,217,109,,265,,109,,265',
+'265,109,234,265,276,276,,265,276,,276,265,109,,277,277,,,277,,277,,265',
+',230,230,276,,,,,,,,,,277,276,,,,276,276,,,276,,277,,276,276,277,277',
+'276,,277,276,230,,277,277,230,230,277,276,230,277,,,230,,159,159,230',
+'277,159,,159,,,,,,,230,,,,158,158,,,158,159,158,,,,,,,,,,159,,,,159',
+'159,158,158,159,,157,157,159,159,157,,159,158,,159,,158,158,,,158,,159',
+',158,158,62,62,158,,62,158,62,,,,,,157,158,,,157,157,,,157,,62,,157',
+',298,,157,,,,,62,298,,,62,62,157,,62,,,,62,62,335,335,62,,,62,,,,335',
+'335,335,335,62,298,298,298,298,298,298,,298,298,295,,,,,298,,298,295',
+'335,335,,,335,335,335,335,335,335,,335,335,390,390,,,,335,,335,,390',
+'390,390,390,295,295,295,295,295,295,,295,295,,297,,,,295,,295,,297,390',
+'390,,,390,390,390,390,390,390,,390,390,156,156,,,,390,,390,,156,156',
+'156,156,297,297,297,297,297,297,,297,297,,,,,,297,,297,,,156,156,,,156',
+'156,156,156,156,156,,156,156,194,194,,,,156,,156,,194,194,194,194,293',
+'293,293,293,293,293,,293,293,,,,,,293,,293,,,194,194,,,194,194,194,194',
+'194,194,,194,194,220,220,,,,194,,194,220,220,220,220,220,,,,,348,348',
+',,,,,,,348,348,348,348,,,220,220,,,220,220,220,220,220,220,,220,220',
+',,,,,220,,220,348,348,348,348,348,348,,348,348,349,349,,,,348,,348,',
+'349,349,349,349,,,,,,,,,333,333,,,,,,,,333,333,333,333,,,349,349,349',
+'349,349,349,,349,349,,,,,,349,,349,333,333,,,333,333,333,333,333,333',
+',333,333,300,300,,,,333,,333,,300,300,300,300,,,,,,,,,,,,,,,,,,,,,300',
+',,300,300,300,300,300,300,,300,300,352,352,,,,300,,300,,352,352,352',
+'352,,,,,,,,,,,,,,,,,,,,352,352,,,352,352,352,352,352,352,,352,352,196',
+'196,,,,352,,352,,196,196,196,196,,,,,299,299,,,,,,,,299,299,299,299',
+',,196,196,,,196,196,196,196,196,196,,196,196,,,,,,196,,196,299,299,299',
+'299,299,299,,299,299,193,193,,,,299,,299,,193,193,193,193,,,,,,,,,,',
+',,,,,,,,,193,193,,,193,193,193,193,193,193,,193,193,332,332,,,,193,',
+'193,,332,332,332,332,,,,,,,,,,,,,,,,,,,,332,332,,,332,332,332,332,332',
+'332,,332,332,,,,,,332,,332,268,268,268,268,268,,268,268,268,268,268',
+',268,268,,,,,,268,268,268,180,180,180,180,180,,180,180,180,180,180,',
+'180,180,,268,268,,,180,180,180,61,61,61,61,61,,61,61,61,61,61,,61,61',
+',180,180,,,61,61,61,267,267,267,267,267,,267,267,267,267,267,,267,267',
+',61,61,,,267,267,267,82,82,82,82,82,,82,82,82,82,82,,82,82,,267,267',
+',,82,82,82,250,250,250,250,250,,250,250,250,250,250,,250,250,,82,82',
+',,250,250,250,,,,,,,,,,,,,,,,250,250' ]
+        racc_action_check = arr = ::Array.new(2588, nil)
         idx = 0
         clist.each do |str|
           str.split(',', -1).each do |i|
@@ -257,326 +262,312 @@ class Parser < Racc::Parser
         end
 
 racc_action_pointer = [
-    -2,   146,   461,   494,   nil,   nil,   nil,   508,  1023,   nil,
-   175,   464,   nil,   171,   nil,   889,   nil,   540,    -3,   518,
-   nil,   455,   nil,   nil,   500,   nil,    60,     1,   486,   nil,
-   471,   nil,   nil,   468,   nil,   676,   464,   nil,   nil,   718,
-   460,   nil,   nil,   nil,   775,   nil,   nil,   474,   nil,   nil,
-   456,   nil,   486,   432,   nil,   nil,   nil,   385,   449,   899,
-   nil,   nil,   616,   422,   956,   421,   976,   nil,    66,    -1,
-   418,   417,   145,   nil,   385,   384,   nil,  1167,   376,   384,
-  1224,  1234,  1244,   nil,   446,   nil,   nil,   nil,   359,    -2,
-   nil,  1301,  1325,   nil,   nil,    -8,   nil,   nil,   nil,   nil,
-   371,   358,   nil,   244,  1439,   351,  2376,   245,  2354,   nil,
-  1526,   341,   nil,  1590,  1640,   nil,   508,   562,   nil,   335,
-   nil,  1704,   305,   325,   nil,   nil,   289,   nil,   nil,   nil,
-   nil,   nil,   308,   311,   nil,   nil,   nil,   283,   nil,   nil,
-   286,   250,   249,   nil,   244,    -3,   239,   nil,   238,   nil,
-   nil,   nil,   nil,   244,   nil,   221,  2207,  1516,  1506,  1459,
-   651,  1377,  1367,  1291,  1177,  1157,   167,  1100,  1090,  1043,
-   156,  1033,   966,   299,   842,   832,   nil,   358,  2072,   nil,
-   822,   701,   192,   nil,     2,   nil,   nil,   nil,   nil,  1854,
-  2117,    84,   nil,   nil,   nil,   nil,    11,   nil,   146,   909,
-   nil,   nil,  2162,   nil,   437,   352,   168,   nil,   nil,   nil,
-    -8,   nil,   144,   nil,   nil,   nil,   nil,   266,   nil,   315,
-  2464,   149,   nil,   nil,   nil,   nil,    48,   nil,   144,   nil,
-    91,   nil,   nil,   nil,   nil,   121,    97,   nil,  1615,   nil,
-   nil,  1397,    83,    92,   351,   342,   317,    97,   170,    63,
-    14,    20,   nil,    41,   185,  1781,  2442,  2420,    48,  1573,
-   nil,   244,   175,   nil,   595,   487,   180,  2004,  1982,  1962,
-   nil,  2269,  2094,  2049,   nil,   541,  1736,   170,   nil,   245,
-   342,   742,   nil,   256,  1657,   nil,    93,   nil,   389,    66,
-   nil,  1714,   nil,   302,   247,   nil,   nil,   nil,   nil,   213,
-  1771,  1724,   100,   nil,   nil,   nil,   140,   nil,   nil,   336,
-   340,   nil,  2398,   346,  1449,  1387,   nil,   181,   nil,   348,
-   353,    -2,   -20,   385,   403,   nil,    70,   nil,   nil,   nil,
-  1110,   nil,   378,   401,   nil,   420,   nil,   nil,   421,   nil,
-   nil,  2252,   nil,   nil,  2314,   nil,   nil,   nil,   nil,  1937,
-   256,   nil,   nil,   nil,   nil,   442,  1916,  1871,   nil,   nil,
-   450,   nil,   nil,   nil,  2027,   nil,   460,   274,   469,   nil,
-   765,   nil,   476,   124,   nil,   nil,   231,   484,  1809,   nil,
-   nil,   nil,   nil,   428,   nil,   nil,   nil,   nil,   nil,   nil,
-   127,   nil,   nil,    84,   nil,   nil,   503,   nil,   510,   nil,
+    -2,   490,    84,   nil,   nil,   507,   nil,   539,   nil,   517,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   485,
+   463,   447,   428,   417,   nil,   428,  1304,   425,     1,   146,
+   388,   nil,   nil,    84,   153,   nil,   675,   nil,   700,   725,
+   750,   395,   nil,   nil,   nil,   nil,   nil,   nil,   413,    42,
+   171,   231,   nil,   411,   nil,   402,   nil,   nil,   nil,   nil,
+   445,  2453,  1832,   950,   386,   nil,   368,   366,    66,   359,
+   nil,   345,   nil,   359,   nil,   339,   337,    -1,  1180,   330,
+   346,   299,  2497,   339,   140,   238,  1361,   337,   nil,   nil,
+   nil,   nil,  1381,  1428,  1438,  1448,   nil,    -2,   nil,   nil,
+  1505,  1515,  1562,  1572,   145,   561,   nil,   615,   507,  1649,
+   nil,   nil,   328,   297,   296,    -8,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    11,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   307,   261,   nil,   nil,   297,
+   nil,   nil,   nil,    84,    93,   274,  1954,  1811,  1785,  1768,
+   315,   nil,   264,   257,   228,   242,   184,   nil,    -3,   178,
+   138,   133,   nil,   109,   nil,   102,   nil,    16,    20,   nil,
+  2431,   305,   nil,   nil,   nil,   194,    -8,   nil,   nil,   nil,
+   nil,    48,   303,  2324,  1999,   170,  2262,   nil,   nil,  1294,
+  1247,   228,   328,  1227,   650,  1160,  1113,  1022,   997,   900,
+   875,   850,   825,   800,   775,  1170,  1495,  1629,   nil,   nil,
+  2044,   272,   146,   nil,   nil,   472,   nil,   nil,   470,   127,
+  1721,   462,   nil,   nil,  1639,   471,   nil,   nil,   nil,   nil,
+   474,   467,   nil,   404,   376,   404,   266,   nil,   242,   369,
+  2519,  1314,  1371,   nil,    34,   400,  1582,   nil,   nil,    82,
+   nil,   265,    -2,   108,   112,  1659,   134,  2475,  2409,   172,
+   180,   nil,   226,   nil,   nil,   100,  1701,  1711,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   579,   256,   123,   180,
+   175,   341,   303,  1976,   486,  1886,   540,  1931,  1842,  2279,
+  2172,   nil,   nil,   nil,    66,  1096,   341,   360,   nil,   366,
+   160,  1032,   nil,     0,   nil,    84,   nil,   980,   nil,   384,
+   374,   388,   nil,   391,   399,   nil,   227,   427,   nil,   nil,
+   nil,   nil,  2369,  2127,   nil,  1864,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   409,   nil,     4,   nil,   nil,  2061,  2106,
+   nil,   nil,  2217,   nil,   -20,   nil,   925,   435,   nil,   nil,
+   442,   967,   445,  1050,   447,   nil,   449,   nil,   nil,   436,
+   433,  1237,   256,   nil,   nil,   nil,   468,   nil,   nil,   nil,
+   nil,   nil,   213,   170,   nil,   nil,   nil,   nil,   nil,   nil,
+  1909,    41,   nil,   nil,   482,   nil,   484,   nil,   nil,   nil,
    nil ]
 
 racc_action_default = [
-  -206,  -241,   -74,  -241,   -18,    -7,   -19,    -8,  -241,  -124,
-  -221,   -20,    -9,  -198,  -186,  -241,   -98,   -10,  -239,  -241,
-   -11,  -241,  -177,   -12,  -241,  -129,  -241,  -178,   -39,   -13,
-    -1,  -229,  -182,  -241,   -96,   -97,   -41,   -14,    -2,  -241,
-   -42,   -15,    -3,  -181,  -241,   -43,   -16,  -241,  -185,  -183,
-   -45,   -17,    -6,  -206,  -198,  -197,   -75,  -109,  -221,  -241,
-  -142,  -141,  -241,  -239,  -241,   -59,  -241,   -66,   -60,   -97,
-   -63,   -61,  -241,   -64,   -58,   -67,   -62,  -241,   -68,   -65,
-  -241,  -241,  -241,   -49,  -241,   -46,   -47,   -48,  -206,  -241,
-  -125,  -241,  -241,  -203,  -205,  -206,  -199,  -202,  -201,  -204,
-  -241,  -241,   -73,  -206,  -241,   -77,  -109,  -206,  -119,    -4,
-  -241,   -53,   -54,  -241,  -241,  -134,  -241,  -241,  -188,  -190,
-  -187,  -241,  -207,  -241,  -208,   -25,   -94,   -29,   -27,   -92,
-   -35,  -113,  -241,  -227,   -23,   -31,  -110,  -100,   -33,   -32,
-  -241,  -114,  -101,   -21,  -103,   -97,   -34,  -104,   -99,   -28,
-  -102,   -26,   -22,  -105,   -30,   -24,  -241,  -241,  -241,  -241,
+  -206,  -241,    -1,    -2,    -3,    -6,    -7,    -8,    -9,   -10,
+   -11,   -12,   -13,   -14,   -15,   -16,   -17,   -18,   -19,   -20,
+  -241,   -39,   -41,   -42,   -43,   -45,   -97,  -241,  -178,  -241,
+   -74,   -96,   -98,  -221,  -239,  -124,  -241,  -129,  -241,  -241,
+  -241,  -241,  -177,  -181,  -182,  -183,  -185,  -186,  -241,  -241,
+  -198,  -241,  -229,  -241,    -4,  -241,   -46,   -47,   -48,   -49,
+  -241,  -119,  -241,  -241,   -53,   -54,   -58,   -59,   -60,   -61,
+   -62,   -63,   -64,   -65,   -66,   -67,   -68,   -97,  -241,  -239,
+  -221,  -109,  -109,   -77,  -206,  -206,  -241,  -241,   -73,  -197,
+  -198,   -75,  -241,  -241,  -241,  -241,  -125,  -241,  -141,  -142,
+  -241,  -241,  -241,  -241,  -241,  -241,  -134,  -241,  -241,  -241,
+  -187,  -188,  -190,  -206,  -206,  -206,  -199,  -201,  -202,  -203,
+  -204,  -205,   401,   -37,   -38,   -39,   -40,   -44,   -97,   -36,
+   -21,   -22,   -23,   -24,   -25,   -26,   -27,   -28,   -29,   -30,
+   -31,   -32,   -33,   -34,   -35,  -227,  -112,  -113,  -114,  -241,
+  -117,  -118,  -120,  -241,  -241,   -52,   -56,  -241,  -241,  -241,
+  -241,  -224,   -24,   -34,   -94,  -227,  -241,   -92,   -97,   -99,
+  -100,  -101,  -102,  -103,  -104,  -105,  -110,  -114,  -227,  -112,
+  -119,  -241,   -80,   -81,   -83,  -206,  -241,   -89,   -90,   -97,
+  -221,  -241,  -241,  -106,  -108,  -241,  -107,  -126,  -127,  -241,
   -241,  -241,  -241,  -241,  -241,  -241,  -241,  -241,  -241,  -241,
-  -241,  -241,  -241,  -241,  -241,  -241,  -160,  -241,   -56,  -224,
-  -241,  -241,  -241,  -233,  -241,  -236,  -237,  -232,  -153,  -106,
-  -108,  -241,   -40,   -44,   -37,   -39,   -97,   -38,  -206,  -241,
-  -127,  -126,  -107,   -36,  -241,  -241,  -241,  -215,   401,   -83,
-  -241,  -221,  -206,   -90,   -97,   -89,   -80,  -241,   -81,  -241,
-  -119,  -227,  -114,  -112,   -34,   -24,  -241,  -117,  -227,  -118,
-  -112,  -120,  -184,  -178,  -180,  -241,   -97,  -179,  -241,  -171,
-  -172,  -241,  -241,   -52,  -241,  -241,  -241,  -241,  -241,  -214,
-  -241,  -227,  -211,  -241,  -241,   -95,  -109,  -228,  -241,  -241,
-  -163,  -149,  -148,  -143,  -151,  -152,  -150,  -155,  -162,  -157,
-  -144,  -161,  -159,  -156,  -145,  -158,  -154,    -5,  -133,  -241,
-  -146,  -147,  -225,  -226,  -241,  -222,  -241,  -123,  -241,  -241,
-  -230,  -241,  -238,  -241,  -241,  -217,  -128,  -200,  -216,  -241,
-  -241,  -241,  -241,   -78,   -86,   -85,  -241,  -226,  -131,  -227,
-  -241,   -79,  -228,  -241,  -241,  -241,  -174,  -227,   -55,  -226,
-   -50,  -222,  -137,  -241,  -241,  -165,  -241,  -169,  -189,  -240,
-  -241,  -209,  -228,  -241,  -192,  -241,   -69,   -93,  -227,  -111,
-   -70,  -115,  -132,  -223,   -57,  -122,  -130,  -234,  -231,  -235,
-  -241,  -219,  -218,  -220,  -196,  -241,   -87,   -88,   -84,   -82,
-  -241,   -71,  -121,   -72,  -116,  -176,  -226,  -241,  -241,   -51,
-  -241,  -136,  -241,  -137,  -166,  -164,  -241,  -241,  -213,  -212,
-  -210,  -191,   -91,  -241,  -194,  -195,   -76,  -175,  -173,  -138,
-  -241,  -135,  -170,  -241,  -193,  -140,  -241,  -168,  -241,  -139,
+  -241,  -241,  -241,  -241,  -241,  -241,  -241,  -241,  -153,  -160,
+  -241,  -241,  -241,  -232,  -233,  -241,  -236,  -237,  -241,  -241,
+  -241,   -97,  -171,  -172,  -241,  -241,  -178,  -179,  -180,  -184,
+  -241,  -241,  -208,  -207,  -206,  -241,  -241,  -215,  -241,  -241,
+  -228,  -241,  -241,  -240,   -50,  -226,  -241,  -225,   -55,  -241,
+  -123,  -241,  -222,  -226,  -241,   -95,  -241,  -228,  -109,  -241,
+  -227,   -78,  -241,   -85,   -86,  -241,  -241,  -241,   -79,  -131,
+  -226,  -238,  -128,  -143,  -144,  -145,  -146,  -147,  -148,  -149,
+  -150,  -151,  -152,  -154,  -155,  -156,  -157,  -158,  -159,  -161,
+  -162,  -163,  -222,  -230,  -241,  -241,    -5,  -241,  -133,  -241,
+  -137,  -241,  -165,  -241,  -169,  -227,  -174,  -241,  -189,  -241,
+  -241,  -227,  -211,  -214,  -241,  -217,  -241,  -241,  -200,  -216,
+   -72,  -121,  -116,  -115,   -51,   -57,  -122,  -130,  -223,   -69,
+   -93,   -70,  -111,  -227,   -71,  -241,   -82,   -84,   -87,   -88,
+  -231,  -234,  -235,  -132,  -137,  -136,  -241,  -241,  -164,  -166,
+  -241,  -241,  -241,  -241,  -226,  -176,  -241,  -192,  -209,  -241,
+  -228,  -241,  -241,  -218,  -219,  -220,  -241,  -196,   -91,   -76,
+  -135,  -138,  -241,  -241,  -170,  -173,  -175,  -191,  -210,  -212,
+  -213,  -241,  -194,  -195,  -241,  -140,  -241,  -168,  -193,  -139,
   -167 ]
 
 racc_goto_table = [
-    27,    30,   112,   115,    22,   109,   279,    53,   235,   252,
-   183,   133,   240,    50,   118,   147,   227,    90,   371,    88,
-   258,   228,    94,   129,    96,   325,   101,   177,   123,   194,
-    27,   192,    38,    84,    22,   102,   217,   203,    32,   192,
-   226,    92,    43,    50,   193,   105,   324,   362,   359,   239,
-   201,   317,   193,   254,   119,   126,   293,   142,    95,   206,
-   221,   137,   367,   198,   298,   219,   250,   251,    32,   391,
-   153,   111,    43,   352,   242,   244,   182,    24,   323,   nil,
-   nil,   nil,   nil,   nil,    27,   124,   335,    18,    22,   nil,
-   nil,   nil,    27,   197,   nil,    32,    22,    50,   nil,    43,
-   nil,   197,   nil,   374,   nil,    50,   nil,   213,   310,   nil,
-   233,   213,   nil,   nil,    22,   313,   nil,    18,   nil,   nil,
-   124,   nil,    32,   nil,   nil,    28,    43,   207,   227,   nil,
-    32,   nil,   355,   309,    43,   nil,   235,   nil,   333,   nil,
-   316,   286,   288,   nil,   327,   302,   nil,   nil,    32,   nil,
-   nil,   nil,    43,   nil,   nil,    28,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   339,   nil,   270,   nil,   nil,
-   nil,   274,   nil,    27,   nil,   nil,   nil,    22,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,    50,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   360,   232,   nil,   nil,
-   nil,   296,   nil,   nil,   368,    94,    94,   297,   318,   195,
-   338,    32,   nil,   147,   328,    43,   213,   195,   nil,   379,
-   227,   337,   327,   396,   nil,   382,   398,   347,   nil,   nil,
-   295,   nil,   nil,   nil,   nil,   nil,   nil,    45,   233,   nil,
-   nil,   nil,    22,   nil,   nil,    27,   233,   nil,   nil,    22,
-    22,   nil,   109,    27,   nil,   142,   nil,    22,    50,   137,
-    18,   nil,   nil,   nil,   nil,   235,    50,    45,   153,   387,
-   nil,   nil,   nil,   nil,   392,   nil,    32,    27,   nil,   nil,
-    43,    22,   365,    32,    32,   nil,   nil,    43,    43,   nil,
-    50,    32,   nil,    32,   150,    43,   nil,    43,    28,    27,
-   351,   nil,   nil,    22,   nil,   nil,   nil,   nil,   nil,   nil,
-   213,   nil,    50,   nil,   nil,    32,   nil,   nil,   nil,    43,
-   nil,    45,   nil,   nil,   233,   232,   nil,   nil,    22,    45,
-   nil,   nil,    18,   232,   389,   nil,   nil,    32,   nil,   nil,
-    18,    43,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-    27,   383,   nil,   nil,    22,    40,   nil,   nil,   109,    36,
-   nil,   nil,    32,    50,    18,   nil,    43,   233,   nil,   nil,
-    28,    22,   nil,   nil,   nil,   nil,   233,   nil,    28,   nil,
-    22,   nil,   nil,    27,   nil,    40,    18,    22,    32,    36,
-    27,   nil,    43,    27,    22,   nil,    50,    22,   nil,   nil,
-   nil,   nil,    28,    50,   nil,    32,    50,   nil,   nil,    43,
-    45,   232,   148,   nil,    32,    49,   144,   nil,    43,   nil,
-   nil,    32,   nil,   nil,    28,    43,   nil,   186,    32,   nil,
-   nil,    32,    43,   nil,   nil,    43,   nil,    18,   nil,    40,
-   nil,   nil,   nil,    36,   nil,    49,   nil,    40,   nil,   nil,
-   nil,    36,   nil,   nil,   232,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   215,   232,   nil,   237,   215,   nil,   nil,   234,
-    18,   nil,    49,   nil,   nil,    28,   nil,    18,   nil,   nil,
-    18,   nil,    45,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-    45,   nil,   150,   nil,   nil,   nil,   nil,   nil,   nil,    49,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,    49,    28,   nil,
-   nil,   nil,   nil,   nil,    45,    28,   nil,   nil,    28,   nil,
-   nil,   nil,   nil,   nil,   nil,    49,   nil,   nil,    40,   nil,
-   nil,   nil,    36,   nil,   nil,   nil,    45,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    28,     2,    28,    42,   224,    42,    54,    65,   106,   233,
+   113,   114,   235,   121,   116,    44,   174,    44,    43,   111,
+    43,   249,   167,    96,   307,   309,     3,   322,   145,    87,
+    25,    88,    25,   165,   178,   176,   176,    83,   312,   355,
+   123,   266,   181,   191,   311,   129,   127,   331,   126,   154,
+    24,   127,    24,   126,   269,    28,   346,   124,    42,   232,
+    28,   197,   124,    42,   160,    55,    60,   241,   244,   315,
+    44,   264,   192,    43,   112,    44,   164,   324,    43,   115,
+   245,   171,   363,   380,   170,    25,   329,   188,   188,   221,
+    25,   320,   321,    64,   373,   222,    44,     1,   nil,    43,
+   nil,   nil,   nil,   nil,   nil,    24,   nil,   nil,   nil,   236,
+    24,   175,    42,   nil,   nil,   366,   nil,   nil,    34,   359,
+    34,   nil,   nil,   376,    44,   nil,   nil,    43,   nil,   nil,
+   nil,   172,   nil,   314,   316,   nil,   nil,   235,   258,   242,
+   242,   247,   nil,   275,   259,   261,   345,   270,   nil,   nil,
+   nil,   nil,   nil,   nil,   389,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   284,   285,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   394,   396,
+   nil,   nil,   nil,   nil,   nil,   nil,   282,   nil,   188,   nil,
+   nil,   362,   nil,   nil,   nil,   nil,   nil,   369,   nil,   nil,
+   174,   nil,   nil,   nil,   351,   nil,   340,   nil,   121,   328,
+   121,   318,   nil,   nil,   314,   nil,   nil,   nil,   nil,   378,
+   343,   342,   176,   nil,   nil,   nil,   nil,   239,    28,    28,
+   236,    42,    42,    42,   236,   nil,   nil,    42,   nil,   nil,
+   nil,   nil,   nil,    44,    44,    44,    43,    43,    43,    44,
+   nil,   nil,    43,   nil,   nil,   nil,   nil,   nil,    25,    25,
+   nil,   nil,   nil,   386,   384,   171,   235,   nil,   170,   nil,
+   325,   nil,   nil,   nil,   nil,   188,   nil,   nil,    24,    24,
+    44,   nil,   nil,    43,    23,   nil,    23,   374,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   175,   nil,   nil,   365,    45,
+   nil,    45,   nil,   nil,   nil,   nil,    28,   nil,   nil,    42,
+    54,   236,   nil,   nil,    42,   172,   nil,   nil,   nil,    28,
+   nil,    44,    42,   nil,    43,   381,    44,    28,   nil,    43,
+    42,   nil,   nil,   nil,    44,   nil,    25,    43,   nil,    23,
+   nil,   nil,    44,   nil,    23,    43,    34,    34,   239,    25,
+   nil,   nil,   239,   nil,    45,   nil,    24,    25,   nil,    45,
+   nil,   236,   nil,   236,    42,   169,    42,   nil,    22,    24,
+    22,   nil,    28,   391,   nil,    42,    44,    24,    44,    43,
+    45,    43,    28,    28,   nil,    42,    42,    44,   227,   nil,
+    43,    28,   nil,   237,    42,    54,   nil,    44,    44,   nil,
+    43,    43,    25,    21,   nil,    21,    44,   nil,    45,    43,
+   nil,   nil,    25,    25,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,    25,    24,    22,    34,   nil,   nil,   nil,    22,   239,
+   nil,   nil,    24,    24,   nil,   nil,   nil,    34,   nil,   nil,
+   nil,    24,   nil,   nil,   nil,    34,   nil,   nil,   nil,   173,
+   nil,   nil,   187,   187,   nil,   nil,   nil,   nil,   125,   nil,
+   nil,   nil,   nil,   125,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   238,   nil,   239,
+   nil,   239,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    34,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    34,    34,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    34,
+   nil,   nil,    23,    23,   237,   nil,   nil,   nil,   237,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,    45,    45,    45,
+   nil,   nil,   nil,    45,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   169,
+   nil,   nil,   nil,   187,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,    45,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   215,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,    45,    49,   nil,
-   nil,   nil,   nil,   237,   nil,   nil,   nil,   234,   nil,   nil,
-    40,   237,   nil,   nil,    36,   234,   nil,   nil,    40,   nil,
-   148,   nil,    36,   nil,   144,   nil,   nil,   nil,   nil,   nil,
-    45,   nil,   nil,   nil,   nil,   nil,   nil,    45,   nil,   nil,
-    45,   nil,    40,   nil,   nil,   nil,    36,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   186,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,    49,    40,   nil,   nil,   nil,    36,   nil,
-    49,    49,   nil,   nil,   nil,   215,   nil,   nil,    49,   nil,
-    49,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   237,
-   nil,    62,   nil,   234,   nil,   nil,   nil,   nil,    89,   nil,
-   nil,   nil,    49,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,    40,   nil,   nil,   nil,    36,
-   nil,   nil,   116,   nil,    49,   nil,   nil,   117,   nil,   nil,
-   nil,   nil,   237,   nil,   nil,   nil,   234,   nil,   nil,   nil,
-   nil,   237,   156,   nil,   nil,   234,   nil,   176,    40,    49,
-   nil,   nil,    36,   nil,   nil,    40,   nil,   nil,    40,    36,
-   188,   nil,    36,   189,   190,   191,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   202,    49,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   227,   nil,
+    23,   nil,   nil,   nil,   nil,   237,    22,    22,   238,   nil,
+   nil,   nil,   238,    23,   nil,    45,   nil,   nil,   nil,   nil,
+    45,    23,   nil,   nil,   nil,   nil,   nil,   nil,    45,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,    45,   nil,   nil,   nil,
+   nil,    21,    21,   173,   nil,    97,   nil,   105,   107,   108,
+   187,   nil,   nil,   nil,   nil,   237,   nil,   237,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,    23,   nil,   nil,   nil,
+    45,   153,    45,   nil,   nil,   nil,    23,    23,   nil,   nil,
+   nil,    45,   nil,   nil,    22,    23,   nil,   nil,   nil,   238,
+   nil,    45,    45,   nil,   nil,   nil,   nil,    22,   nil,   nil,
+    45,   193,   194,   195,   196,    22,   nil,   nil,   nil,   218,
+   219,   220,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    21,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,    49,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,    49,   nil,   nil,   248,   nil,   nil,   nil,    49,   nil,
-   nil,   nil,   nil,   nil,   nil,    49,   nil,   nil,    49,   nil,
+   nil,   nil,    21,   nil,   nil,   nil,   nil,   nil,   nil,   238,
+    21,   238,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    22,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    22,    22,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    22,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    21,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    21,    21,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,    21,   nil,   nil,   nil,    97,   283,
+   nil,   nil,   286,   287,   288,   289,   290,   291,   292,   293,
+   294,   295,   296,   297,   298,   299,   300,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   261,   262,   263,   264,   265,   266,   267,   268,   269,   nil,
-   271,   272,   273,   nil,   275,   276,   nil,   280,   281,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,    89,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   332,   333,   nil,   nil,   nil,   335,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   348,   349,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   352,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   341,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   344,   nil,   nil,
-   nil,   nil,   nil,   nil,   349,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   356,   357,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   364,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   378,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   107,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   116 ]
+   390 ]
 
 racc_goto_check = [
-    35,     2,    31,    62,    34,     4,     5,    40,    69,    83,
-    86,    43,    71,    28,    73,    37,    56,    60,    63,    40,
-    44,    45,    65,    53,    79,    67,    35,    29,    75,    22,
-    35,     8,     3,    23,    34,     6,    48,    22,    36,     8,
-    48,    23,    38,    28,    10,    47,    66,    59,    49,    70,
-    61,    72,    10,    42,    74,    41,    76,    35,    77,    78,
-    43,    34,    33,    75,    80,    29,    81,    82,    36,    63,
-    28,    30,    38,    84,    29,    29,    85,     1,     5,   nil,
-   nil,   nil,   nil,   nil,    35,     3,     5,    55,    34,   nil,
-   nil,   nil,    35,     6,   nil,    36,    34,    28,   nil,    38,
-   nil,     6,   nil,    67,   nil,    28,   nil,    34,    44,   nil,
-    35,    34,   nil,   nil,    34,    44,   nil,    55,   nil,   nil,
-     3,   nil,    36,   nil,   nil,    24,    38,     3,    56,   nil,
-    36,   nil,     5,    45,    38,   nil,    69,   nil,    44,   nil,
-    71,    29,    29,   nil,    69,    48,   nil,   nil,    36,   nil,
-   nil,   nil,    38,   nil,   nil,    24,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,    56,   nil,    65,   nil,   nil,
-   nil,    65,   nil,    35,   nil,   nil,   nil,    34,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,    28,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,    44,    55,   nil,   nil,
-   nil,    60,   nil,   nil,    44,    65,    65,    79,    31,    24,
-    43,    36,   nil,    37,    73,    38,    34,    24,   nil,    83,
-    56,    53,    69,     5,   nil,    44,     5,    86,   nil,   nil,
-     3,   nil,   nil,   nil,   nil,   nil,   nil,    27,    35,   nil,
-   nil,   nil,    34,   nil,   nil,    35,    35,   nil,   nil,    34,
-    34,   nil,     4,    35,   nil,    35,   nil,    34,    28,    34,
-    55,   nil,   nil,   nil,   nil,    69,    28,    27,    28,    71,
-   nil,   nil,   nil,   nil,    69,   nil,    36,    35,   nil,   nil,
-    38,    34,    31,    36,    36,   nil,   nil,    38,    38,   nil,
-    28,    36,   nil,    36,    27,    38,   nil,    38,    24,    35,
-    40,   nil,   nil,    34,   nil,   nil,   nil,   nil,   nil,   nil,
-    34,   nil,    28,   nil,   nil,    36,   nil,   nil,   nil,    38,
-   nil,    27,   nil,   nil,    35,    55,   nil,   nil,    34,    27,
-   nil,   nil,    55,    55,    62,   nil,   nil,    36,   nil,   nil,
-    55,    38,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-    35,     2,   nil,   nil,    34,    26,   nil,   nil,     4,    25,
-   nil,   nil,    36,    28,    55,   nil,    38,    35,   nil,   nil,
-    24,    34,   nil,   nil,   nil,   nil,    35,   nil,    24,   nil,
-    34,   nil,   nil,    35,   nil,    26,    55,    34,    36,    25,
-    35,   nil,    38,    35,    34,   nil,    28,    34,   nil,   nil,
-   nil,   nil,    24,    28,   nil,    36,    28,   nil,   nil,    38,
-    27,    55,    26,   nil,    36,    39,    25,   nil,    38,   nil,
-   nil,    36,   nil,   nil,    24,    38,   nil,    26,    36,   nil,
-   nil,    36,    38,   nil,   nil,    38,   nil,    55,   nil,    26,
-   nil,   nil,   nil,    25,   nil,    39,   nil,    26,   nil,   nil,
-   nil,    25,   nil,   nil,    55,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,    25,    55,   nil,    26,    25,   nil,   nil,    25,
-    55,   nil,    39,   nil,   nil,    24,   nil,    55,   nil,   nil,
-    55,   nil,    27,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-    27,   nil,    27,   nil,   nil,   nil,   nil,   nil,   nil,    39,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,    39,    24,   nil,
-   nil,   nil,   nil,   nil,    27,    24,   nil,   nil,    24,   nil,
-   nil,   nil,   nil,   nil,   nil,    39,   nil,   nil,    26,   nil,
-   nil,   nil,    25,   nil,   nil,   nil,    27,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    35,     2,    35,    34,    86,    34,     4,    31,    62,    71,
+    40,    40,    69,    65,    79,    36,    37,    36,    38,    73,
+    38,    44,    53,    60,     5,     5,     3,    83,    45,    35,
+    28,     6,    28,    43,    43,    56,    56,    47,    67,    63,
+    22,    44,    48,    48,    66,    22,    10,    59,     8,    29,
+    27,    10,    27,     8,    44,    35,    49,     6,    34,    70,
+    35,    61,     6,    34,    29,    23,    23,    75,    75,    72,
+    36,    42,    29,    38,    74,    36,    41,    76,    38,    77,
+    78,    35,    33,    63,    34,    28,    80,    34,    34,    29,
+    28,    81,    82,    30,    84,    85,    36,     1,   nil,    38,
+   nil,   nil,   nil,   nil,   nil,    27,   nil,   nil,   nil,    35,
+    27,    28,    34,   nil,   nil,     5,   nil,   nil,    55,    67,
+    55,   nil,   nil,     5,    36,   nil,   nil,    38,   nil,   nil,
+   nil,    27,   nil,    69,    71,   nil,   nil,    69,    31,     3,
+     3,     3,   nil,    48,    29,    29,    44,    45,   nil,   nil,
+   nil,   nil,   nil,   nil,    83,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,    65,    65,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,     5,     5,
+   nil,   nil,   nil,   nil,   nil,   nil,    60,   nil,    34,   nil,
+   nil,    44,   nil,   nil,   nil,   nil,   nil,    44,   nil,   nil,
+    37,   nil,   nil,   nil,    86,   nil,    53,   nil,    65,    79,
+    65,    73,   nil,   nil,    69,   nil,   nil,   nil,   nil,    44,
+    43,    56,    56,   nil,   nil,   nil,   nil,    55,    35,    35,
+    35,    34,    34,    34,    35,   nil,   nil,    34,   nil,   nil,
+   nil,   nil,   nil,    36,    36,    36,    38,    38,    38,    36,
+   nil,   nil,    38,   nil,   nil,   nil,   nil,   nil,    28,    28,
+   nil,   nil,   nil,    71,    69,    35,    69,   nil,    34,   nil,
+     3,   nil,   nil,   nil,   nil,    34,   nil,   nil,    27,    27,
+    36,   nil,   nil,    38,    26,   nil,    26,    40,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    28,   nil,   nil,    31,    39,
+   nil,    39,   nil,   nil,   nil,   nil,    35,   nil,   nil,    34,
+     4,    35,   nil,   nil,    34,    27,   nil,   nil,   nil,    35,
+   nil,    36,    34,   nil,    38,    62,    36,    35,   nil,    38,
+    34,   nil,   nil,   nil,    36,   nil,    28,    38,   nil,    26,
+   nil,   nil,    36,   nil,    26,    38,    55,    55,    55,    28,
+   nil,   nil,    55,   nil,    39,   nil,    27,    28,   nil,    39,
+   nil,    35,   nil,    35,    34,    26,    34,   nil,    25,    27,
+    25,   nil,    35,     2,   nil,    34,    36,    27,    36,    38,
+    39,    38,    35,    35,   nil,    34,    34,    36,    26,   nil,
+    38,    35,   nil,    26,    34,     4,   nil,    36,    36,   nil,
+    38,    38,    28,    24,   nil,    24,    36,   nil,    39,    38,
+   nil,   nil,    28,    28,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,    28,    27,    25,    55,   nil,   nil,   nil,    25,    55,
+   nil,   nil,    27,    27,   nil,   nil,   nil,    55,   nil,   nil,
+   nil,    27,   nil,   nil,   nil,    55,   nil,   nil,   nil,    25,
+   nil,   nil,    25,    25,   nil,   nil,   nil,   nil,    24,   nil,
+   nil,   nil,   nil,    24,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,    25,   nil,    55,
+   nil,    55,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    55,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    55,    55,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    55,
+   nil,   nil,    26,    26,    26,   nil,   nil,   nil,    26,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,    39,    39,    39,
+   nil,   nil,   nil,    39,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    26,
+   nil,   nil,   nil,    25,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,    39,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    26,   nil,
+    26,   nil,   nil,   nil,   nil,    26,    25,    25,    25,   nil,
+   nil,   nil,    25,    26,   nil,    39,   nil,   nil,   nil,   nil,
+    39,    26,   nil,   nil,   nil,   nil,   nil,   nil,    39,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,    39,   nil,   nil,   nil,
+   nil,    24,    24,    25,   nil,    32,   nil,    32,    32,    32,
+    25,   nil,   nil,   nil,   nil,    26,   nil,    26,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,    26,   nil,   nil,   nil,
+    39,    32,    39,   nil,   nil,   nil,    26,    26,   nil,   nil,
+   nil,    39,   nil,   nil,    25,    26,   nil,   nil,   nil,    25,
+   nil,    39,    39,   nil,   nil,   nil,   nil,    25,   nil,   nil,
+    39,    32,    32,    32,    32,    25,   nil,   nil,   nil,    32,
+    32,    32,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    24,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,    25,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,    27,    39,   nil,
-   nil,   nil,   nil,    26,   nil,   nil,   nil,    25,   nil,   nil,
-    26,    26,   nil,   nil,    25,    25,   nil,   nil,    26,   nil,
-    26,   nil,    25,   nil,    25,   nil,   nil,   nil,   nil,   nil,
-    27,   nil,   nil,   nil,   nil,   nil,   nil,    27,   nil,   nil,
-    27,   nil,    26,   nil,   nil,   nil,    25,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,    26,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,    39,    26,   nil,   nil,   nil,    25,   nil,
-    39,    39,   nil,   nil,   nil,    25,   nil,   nil,    39,   nil,
-    39,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    26,
-   nil,    32,   nil,    25,   nil,   nil,   nil,   nil,    32,   nil,
-   nil,   nil,    39,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,    26,   nil,   nil,   nil,    25,
-   nil,   nil,    32,   nil,    39,   nil,   nil,    32,   nil,   nil,
-   nil,   nil,    26,   nil,   nil,   nil,    25,   nil,   nil,   nil,
-   nil,    26,    32,   nil,   nil,    25,   nil,    32,    26,    39,
-   nil,   nil,    25,   nil,   nil,    26,   nil,   nil,    26,    25,
-    32,   nil,    25,    32,    32,    32,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,    32,    39,   nil,   nil,   nil,   nil,
+   nil,   nil,    24,   nil,   nil,   nil,   nil,   nil,   nil,    25,
+    24,    25,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    25,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    25,    25,   nil,   nil,   nil,   nil,   nil,   nil,   nil,    25,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,    39,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,    39,   nil,   nil,    32,   nil,   nil,   nil,    39,   nil,
-   nil,   nil,   nil,   nil,   nil,    39,   nil,   nil,    39,   nil,
+   nil,   nil,   nil,   nil,   nil,    24,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    24,    24,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,    24,   nil,   nil,   nil,    32,    32,
+   nil,   nil,    32,    32,    32,    32,    32,    32,    32,    32,
+    32,    32,    32,    32,    32,    32,    32,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-    32,    32,    32,    32,    32,    32,    32,    32,    32,   nil,
-    32,    32,    32,   nil,    32,    32,   nil,    32,    32,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    32,    32,   nil,   nil,   nil,    32,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,    32,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,    32,    32,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,    32,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,    32,   nil,   nil,
    nil,   nil,   nil,   nil,    32,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,    32,    32,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,   nil,   nil,   nil,   nil,    32,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,    32,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   nil,    32 ]
+   nil,   nil,   nil,   nil,   nil,    32,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+    32 ]
 
 racc_goto_pointer = [
-   nil,    77,     1,    32,   -25,  -167,     9,   nil,   -53,   nil,
-   -40,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-   nil,   nil,   -55,    22,   125,   359,   355,   237,    13,   -39,
-    36,   -33,   673,  -255,     4,     0,    38,   -42,    42,   415,
-     6,    -2,   -73,   -46,  -113,   -87,   nil,    18,   -67,  -258,
-   nil,   nil,   nil,   -34,   nil,    87,   -92,   nil,   nil,  -265,
-     2,   -39,   -36,  -304,   nil,     1,  -200,  -221,   nil,  -102,
-   -61,   -98,  -187,   -33,     7,   -25,  -142,    37,   -36,     3,
-  -141,   -56,   -55,  -113,  -221,     4,   -62,   nil ]
+   nil,    97,     1,    26,     4,  -204,     2,   nil,    -7,   nil,
+    -9,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   -15,    46,   403,   368,   284,    50,    30,   -14,
+    67,   -19,   599,  -233,     3,     0,    15,   -65,    18,   299,
+   -39,    -5,   -93,   -48,  -124,   -33,   nil,     9,   -42,  -216,
+   nil,   nil,   nil,   -59,   nil,   118,   -46,   nil,   nil,  -203,
+   -13,   -36,   -31,  -271,   nil,   -38,  -186,  -192,   nil,   -97,
+   -50,  -100,  -165,   -29,    26,   -46,  -167,    28,   -35,   -37,
+  -162,  -152,  -151,  -216,  -232,    -9,  -100,   nil ]
 
 racc_goto_default = [
-   nil,   nil,   277,   216,    42,   nil,    52,     5,     7,    12,
-    17,    20,    23,    29,    37,    41,    46,    51,     4,     6,
-    11,   131,    19,   nil,    67,    70,    74,    76,    79,   nil,
-   nil,    61,   178,   284,    65,    68,    71,    73,    75,    78,
-     3,   nil,   nil,   nil,   nil,   nil,    26,   nil,   nil,   218,
-   306,   209,   210,   nil,   132,    63,   136,   140,   229,   231,
-   nil,   nil,   nil,   nil,    60,    14,   nil,   nil,   326,    33,
+   nil,   nil,   306,   182,     4,   nil,     5,     6,     7,     8,
+     9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
+    19,   147,    20,   nil,    74,    71,    66,    70,    73,   nil,
+   nil,    98,   156,   256,    67,    68,    69,    72,    75,    76,
+    27,   nil,   nil,   nil,   nil,   nil,    29,   nil,   nil,   183,
+   272,   184,   186,   nil,   166,    79,   150,   149,   151,   152,
+   nil,   nil,   nil,   nil,    99,    47,   nil,   nil,   313,    41,
    nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
-    98,   nil,   nil,   nil,   nil,   nil,   nil,   184 ]
+   117,   nil,   nil,   nil,   nil,   nil,   nil,   225 ]
 
 racc_reduce_table = [
   0, 0, :racc_error,
@@ -1089,7 +1080,7 @@ class Parser < Racc::Parser
 
 module_eval(<<'.,.,', 'grammar.ra', 34)
   def _reduce_3(val, _values, result)
-        result = ast AST::ASTArray, :children => (val[0] ? [val[0]] : [])
+        result = ast AST::BlockExpression, :children => (val[0] ? [val[0]] : [])
   
     result
   end
@@ -2083,7 +2074,7 @@ def _reduce_168(val, _values, result)
     AST::CaseOpt,
     :value => val[0],
 
-    :statements => ast(AST::ASTArray)
+    :statements => ast(AST::BlockExpression)
   )
 
     result
diff --git a/lib/puppet/parser/parser_factory.rb b/lib/puppet/parser/parser_factory.rb
new file mode 100644
index 0000000..bf80c12
--- /dev/null
+++ b/lib/puppet/parser/parser_factory.rb
@@ -0,0 +1,62 @@
+module Puppet; end
+
+module Puppet::Parser
+  # The ParserFactory makes selection of parser possible.
+  # Currently, it is possible to switch between two different parsers:
+  # * classic_parser, the parser in 3.1
+  # * eparser, the Expression Based Parser
+  #
+  class ParserFactory
+    # Produces a parser instance for the given environment
+    def self.parser(environment)
+      case Puppet[:parser]
+      when 'future'
+        eparser(environment)
+      else
+        classic_parser(environment)
+      end
+    end
+
+    # Creates an instance of the classic parser.
+    #
+    def self.classic_parser(environment)
+      require 'puppet/parser'
+      Puppet::Parser::Parser.new(environment)
+    end
+
+    # Creates an instance of the expression based parser 'eparser'
+    #
+    def self.eparser(environment)
+      # Since RGen is optional, test that it is installed
+      @@asserted ||= false
+      assert_rgen_installed() unless @asserted
+      require 'puppet/parser'
+      require 'puppet/parser/e_parser_adapter'
+      EParserAdapter.new(Puppet::Parser::Parser.new(environment))
+    end
+
+    private
+
+    def self.assert_rgen_installed
+      begin
+        require 'rgen/metamodel_builder'
+      rescue LoadError
+        raise Puppet::DevError.new("The gem 'rgen' version >= 0.6.1 is required when using the setting '--parser future'. Please install 'rgen'.")
+      end
+      # Since RGen is optional, there is nothing specifying its version.
+      # It is not installed in any controlled way, so not possible to use gems to check (it may be installed some other way).
+      # Instead check that "eContainer, and eContainingFeature" has been installed.
+      require 'puppet/pops'
+      begin
+        litstring = Puppet::Pops::Model::LiteralString.new();
+        container = Puppet::Pops::Model::ArithmeticExpression.new();
+        container.left_expr = litstring
+        raise "no eContainer" if litstring.eContainer() != container
+        raise "no eContainingFeature" if litstring.eContainingFeature() != :left_expr
+      rescue =>e
+        raise Puppet::DevError.new("The gem 'rgen' version >= 0.6.1 is required when using '--parser future'. An older version is installed, please update.")
+      end
+    end
+  end
+
+end
diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb
index c7c0057..69226a5 100644
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@ -38,6 +38,11 @@ def aryfy(arg)
     ast AST::ASTArray, :children => [arg]
   end
 
+  # Create an AST block containing a single element
+  def block(arg)
+    ast AST::BlockExpression, :children => [arg]
+  end
+
   # Create an AST object, and automatically add the file and line information if
   # available.
   def ast(klass, hash = {})
@@ -146,9 +151,10 @@ def parse(string = nil)
       rescue Puppet::ParseError => except
         except.line ||= @lexer.line
         except.file ||= @lexer.file
+        except.pos ||= @lexer.pos
         raise except
       rescue => except
-        raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, except)
+        raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, nil, except)
       end
     end
     # Store the results as the top-level class.
@@ -166,6 +172,6 @@ def parse_ruby_file
     main_object.instance_eval(File.read(self.file))
 
     # Then extract any types that were created.
-    Puppet::Parser::AST::ASTArray.new :children => main_object.instance_eval { @__created_ast_objects__ }
+    Puppet::Parser::AST::BlockExpression.new :children => main_object.instance_eval { @__created_ast_objects__ }
   end
 end
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index de6abbb..d6fade4 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -2,7 +2,7 @@
 # such.
 require 'forwardable'
 
-require 'puppet/parser/parser'
+require 'puppet/parser'
 require 'puppet/parser/templatewrapper'
 
 require 'puppet/resource/type_collection_helper'
@@ -42,9 +42,10 @@ class Puppet::Parser::Scope
   class Ephemeral
     extend Forwardable
 
-    def initialize(parent=nil)
+    def initialize(parent=nil, local=false)
       @symbols = {}
       @parent = parent
+      @local_scope = local
     end
 
     def_delegators :@symbols, :include?, :delete, :[]=
@@ -56,6 +57,13 @@ def [](name)
         @parent[name]
       end
     end
+    def is_local_scope?
+      @local_scope
+    end
+    # @return [Ephemeral, Hash, nil]
+    def parent
+      @parent
+    end
   end
 
   # Initialize a new scope suitable for parser function testing.  This method
@@ -171,7 +179,8 @@ def initialize(compiler, options = {})
     # for $0..$xy capture variables of regexes
     # this is actually implemented as a stack, with each ephemeral scope
     # shadowing the previous one
-    @ephemeral = [ Ephemeral.new ]
+    # TODO: UPDATE YARDOC
+    @ephemeral = [ Ephemeral.new(@symtable) ]
 
     # All of the defaults set for types.  It's a hash of hashes,
     # with the first key being the type, then the second key being
@@ -259,7 +268,11 @@ def lookupvar(name, options = {})
 
     # Save the originating scope for the request
     options[:origin] = self unless options[:origin]
-    table = ephemeral?(name) ? @ephemeral.last : @symtable
+
+    # TODO: Cleanup comments/dead code, new design, the ephemeral refers to @symtable as "parent"
+    #
+    table = @ephemeral.last
+    # table = ephemeral?(name) ? @ephemeral.last : @symtable
 
     if name =~ /^(.*)::(.+)$/
       begin
@@ -270,9 +283,16 @@ def lookupvar(name, options = {})
         nil
       end
     # If the value is present and either we are top/node scope or originating scope...
-    elsif (ephemeral_include?(name) or table.include?(name)) and (compiler and self == compiler.topscope or (resource and resource.type == "Node") or self == options[:origin])
+    # TODO: This is F* up. If the name is an ephemeral name, table = the last ephemeral, otherwise it
+    # is the @symtable. This means that it ephmeral_include and table.include will produce the same answer
+    # unless it is possible to manually assign $0, $1 etc.
+    # ephemeral_include searches all ephemerals, but does not lookup in symtable, whereas [] on ephemeral
+    # searches up the ephemeral chain (including the scode's @symtable).
+    # TODO: change and/or use to && / || since and/or evaluates lhs and rhs
+    #
+    elsif (ephemeral_include?(name) || @symtable.include?(name)) && (compiler && self == compiler.topscope || (resource && resource.type == "Node") || self == options[:origin])
       table[name]
-    elsif resource and resource.type == "Class" and parent_type = resource.resource_type.parent
+    elsif resource and resource.type == "Class" && parent_type = resource.resource_type.parent
       qualified_scope(parent_type).lookupvar(name, options.merge({:origin => nil}))
     elsif parent
       parent.lookupvar(name, options)
@@ -361,11 +381,20 @@ def define_settings(type, params)
   #   It's preferred that you use self[]= instead of this; only use this
   # when you need to set options.
   def setvar(name, value, options = {})
+    if name =~ /^[0-9]+$/
+      raise Puppet::ParseError.new("Cannot assign to a numeric match result variable '$#{name}'") unless options[:ephemeral]
+    end
     unless name.is_a? String
       raise Puppet::DevError, "Scope variable name is a #{name.class}, not a string"
     end
-
-    table = options[:ephemeral] ? @ephemeral.last : @symtable
+    # TODO: consider removing possibility to pass :ephemeral option (only used within this class)
+    #
+    table = effective_symtable options[:ephemeral]
+    # table = options[:ephemeral] || @ephemeral.last.is_local_scope?() ? @ephemeral.last : @symtable
+
+    # TODO: F* up design, this may write in the shadow of other ephemeral scopes...
+    #   this because :ephemeral an be given as option to write in topmost/last ephemeral while
+    #   the real scope to write in is a local ephemeral or the scope's symtable.
     if table.include?(name)
       if options[:append]
         error = Puppet::ParseError.new("Cannot append, variable #{name} is defined in this scope")
@@ -379,9 +408,26 @@ def setvar(name, value, options = {})
 
     if options[:append]
       table[name] = append_value(undef_as('', self[name]), value)
-    else 
+    else
       table[name] = value
     end
+    table[name]
+  end
+
+  # Return the effective "table" for setting variables.
+  # This method returns the first ephemeral "table" that acts as a local scope, or this
+  # scope's symtable. If the parameter `use_ephemeral` is true, the "top most" ephemeral "table"
+  # will be returned (irrespective of it being a match scope or a local scope).
+  #
+  # @param [Boolean] whether the top most ephemeral (of any kind) should be used or not
+  def effective_symtable use_ephemeral
+    s = @ephemeral.last
+    return s if use_ephemeral
+
+    while s && !(s.is_a?(Hash) || s.is_local_scope?())
+      s = s.parent
+    end
+    s ? s : @symtable
   end
 
   # Sets the variable value of the name given as an argument to the given value. The value is
@@ -407,7 +453,7 @@ def append_value(bound_value, new_value)
       bound_value.merge(new_value)
     else
       if bound_value.is_a?(Hash)
-        raise ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported" 
+        raise ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported"
       end
       bound_value + new_value
     end
@@ -425,7 +471,7 @@ def to_s
   # remove ephemeral scope up to level
   def unset_ephemeral_var(level=:all)
     if level == :all
-      @ephemeral = [ Ephemeral.new ]
+      @ephemeral = [ Ephemeral.new(@symtable)]
     else
       # If we ever drop 1.8.6 and lower, this should be replaced by a single
       # pop-with-a-count - or if someone more ambitious wants to monkey-patch
@@ -436,32 +482,41 @@ def unset_ephemeral_var(level=:all)
     end
   end
 
-  # check if name exists in one of the ephemeral scope.
+  # check if name exists in one of the ephemeral scopes.
   def ephemeral_include?(name)
     @ephemeral.any? {|eph| eph.include?(name) }
   end
 
-  # is name an ephemeral variable?
+  # Checks whether the variable should be processed in the ephemeral scope or not.
+  # All numerical variables are processed in ephemeral scope at all times, and all other
+  # variables when the ephemeral scope is a local scope.
+  #
   def ephemeral?(name)
-    name =~ /^\d+$/
+    @ephemeral.last.is_local_scope? || name =~ /^\d+$/
   end
 
   def ephemeral_level
     @ephemeral.size
   end
 
-  def new_ephemeral
-    @ephemeral.push(Ephemeral.new(@ephemeral.last))
+  def new_ephemeral(local_scope = false)
+    @ephemeral.push(Ephemeral.new(@ephemeral.last, local_scope))
   end
 
   def ephemeral_from(match, file = nil, line = nil)
-    raise(ArgumentError,"Invalid regex match data") unless match.is_a?(MatchData)
-
-    new_ephemeral
-
-    setvar("0", match[0], :file => file, :line => line, :ephemeral => true)
-    match.captures.each_with_index do |m,i|
-      setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true)
+    case match
+    when Hash
+      # Create local scope ephemeral and set all values from hash
+      new_ephemeral true
+      match.each {|k,v| setvar(k, v, :file => file, :line => line, :ephemeral => true) }
+    else
+      raise(ArgumentError,"Invalid regex match data. Got a #{match.class}") unless match.is_a?(MatchData)
+      # Create a match ephemeral and set values from match data
+      new_ephemeral false
+      setvar("0", match[0], :file => file, :line => line, :ephemeral => true)
+      match.captures.each_with_index do |m,i|
+        setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true)
+      end
     end
   end
 
diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb
index fdb6ced..852d474 100644
--- a/lib/puppet/parser/type_loader.rb
+++ b/lib/puppet/parser/type_loader.rb
@@ -1,6 +1,7 @@
 require 'find'
 require 'forwardable'
 require 'puppet/node/environment'
+require 'puppet/parser/parser_factory'
 
 class Puppet::Parser::TypeLoader
   extend  Forwardable
@@ -137,7 +138,8 @@ def try_load_fqname(type, fqname)
 
   def parse_file(file)
     Puppet.debug("importing '#{file}' in environment #{environment}")
-    parser = Puppet::Parser::Parser.new(environment)
+#    parser = Puppet::Parser::Parser.new(environment)
+    parser = Puppet::Parser::ParserFactory.parser(environment)
     parser.file = file
     return parser.parse
   end
diff --git a/lib/puppet/pops.rb b/lib/puppet/pops.rb
new file mode 100644
index 0000000..b0a36aa
--- /dev/null
+++ b/lib/puppet/pops.rb
@@ -0,0 +1,40 @@
+module Puppet
+  module Pops
+    require 'puppet/pops/patterns'
+    require 'puppet/pops/utils'
+
+    require 'puppet/pops/adaptable'
+    require 'puppet/pops/adapters'
+
+    require 'puppet/pops/visitable'
+    require 'puppet/pops/visitor'
+
+    require 'puppet/pops/containment'
+
+    require 'puppet/pops/issues'
+    require 'puppet/pops/label_provider'
+    require 'puppet/pops/validation'
+
+    require 'puppet/pops/model/model'
+
+    module Model
+      require 'puppet/pops/model/tree_dumper'
+      require 'puppet/pops/model/ast_transformer'
+      require 'puppet/pops/model/ast_tree_dumper'
+      require 'puppet/pops/model/factory'
+      require 'puppet/pops/model/model_tree_dumper'
+      require 'puppet/pops/model/model_label_provider'
+    end
+
+    module Parser
+      require 'puppet/pops/parser/eparser'
+      require 'puppet/pops/parser/parser_support'
+      require 'puppet/pops/parser/lexer'
+    end
+
+    module Validation
+      require 'puppet/pops/validation/checker3_1'
+      require 'puppet/pops/validation/validator_factory_3_1'
+    end
+  end
+end
diff --git a/lib/puppet/pops/adaptable.rb b/lib/puppet/pops/adaptable.rb
new file mode 100644
index 0000000..86cc97a
--- /dev/null
+++ b/lib/puppet/pops/adaptable.rb
@@ -0,0 +1,190 @@
+# Adaptable is a mix-in module that adds adaptability to a class.
+# This means that an adapter can
+# associate itself with an instance of the class and store additional data/have behavior.
+#
+# This mechanism should be used when there is a desire to keep implementation concerns separate.
+# In Ruby it is always possible to open and modify a class or instance to teach it new tricks, but it
+# is however not possible to do this for two different versions of some service at the same time.
+# The Adaptable pattern is also good when only a few of the objects of some class needs to have extra
+# information (again possible in Ruby by adding instance variables dynamically). In fact, the implementation
+# of Adaptable does just that; it adds an instance variable named after the adapter class and keeps an
+# instance of this class in this slot.
+#
+# @note the implementation details; the fact that an instance variable is used to keep the adapter
+#   instance data should not
+#   be exploited as the implementation of _being adaptable_ may change in the future.
+# @api private
+#
+module Puppet::Pops::Adaptable
+  # Base class for an Adapter.
+  #
+  # A typical adapter just defines some accessors.
+  #
+  # A more advanced adapter may need to setup the adapter based on the object it is adapting.
+  # @example Making Duck adaptable
+  #   class Duck
+  #     include Puppet::Pops::Adaptable
+  #   end
+  # @example Giving a Duck a nick name
+  #   class NickNameAdapter < Puppet::Pops::Adaptable::Adapter
+  #     attr_accessor :nick_name
+  #   end
+  #   d = Duck.new
+  #   NickNameAdapter.adapt(d).nick_name = "Daffy"
+  #   NickNameAdapter.get(d).nick_name # => "Daffy"
+  #
+  # @example Giving a Duck a more elaborate nick name
+  #   class NickNameAdapter < Puppet::Pops::Adaptable::Adapter
+  #     attr_accessor :nick_name, :object
+  #     def initialize o
+  #       @object = o
+  #       @nick_name = "Yo"
+  #     end
+  #     def nick_name
+  #       "#{@nick_name}, the #{o.class.name}"
+  #     end
+  #     def NickNameAdapter.create_adapter(o)
+  #       x = new o
+  #       x
+  #     end
+  #   end
+  #   d = Duck.new
+  #   n = NickNameAdapter.adapt(d)
+  #   n.nick_name # => "Yo, the Duck"
+  #   n.nick_name = "Daffy"
+  #   n.nick_name # => "Daffy, the Duck"
+  # @example Using a block to set values
+  #   NickNameAdapter.adapt(o) { |a| a.nick_name = "Buddy!" }
+  #   NickNameAdapter.adapt(o) { |a, o| a.nick_name = "You're the best #{o.class.name} I met."}
+  #
+  class Adapter
+    # Returns an existing adapter for the given object, or nil, if the object is not
+    # adapted.
+    #
+    # @param o [Adaptable] object to get adapter from
+    # @return [Adapter<self>] an adapter of the same class as the receiver of #get
+    # @return [nil] if the given object o has not been adapted by the receiving adapter
+    # @raise [ArgumentError] if the object is not adaptable
+    #
+    def self.get(o)
+      attr_name = :"@#{instance_var_name(self.name)}"
+      if existing = o.instance_variable_defined?(attr_name)
+        o.instance_variable_get(attr_name)
+      else
+        nil
+      end
+    end
+
+    # Returns an existing adapter for the given object, or creates a new adapter if the
+    # object has not been adapted, or the adapter has been cleared.
+    #
+    # @example Using a block to set values
+    #   NickNameAdapter.adapt(o) { |a| a.nick_name = "Buddy!" }
+    #   NickNameAdapter.adapt(o) { |a, o| a.nick_name = "Your the best #{o.class.name} I met."}
+    # @overload adapt(o)
+    # @overload adapt(o, {|adapter| block})
+    # @overload adapt(o, {|adapter, o| block})
+    # @param o [Adaptable] object to add adapter to
+    # @yieldparam adapter [Adapter<self>] the created adapter
+    # @yieldparam o [Adaptable] optional, the given adaptable
+    # @param block [Proc] optional, evaluated in the context of the adapter (existing or new)
+    # @return [Adapter<self>] an adapter of the same class as the receiver of the call
+    # @raise [ArgumentError] if the given object o is not adaptable
+    #
+    def self.adapt(o, &block)
+      attr_name = :"@#{instance_var_name(self.name)}"
+      adapter = if existing = o.instance_variable_defined?(attr_name) && value = o.instance_variable_get(attr_name)
+        value
+      else
+        associate_adapter(create_adapter(o), o)
+      end
+      if block_given?
+        case block.arity
+          when 1
+            block.call(adapter)
+          else
+            block.call(adapter, o)
+        end
+      end
+      adapter
+    end
+
+    # Creates a new adapter, associates it with the given object and returns the adapter.
+    #
+    # @example Using a block to set values
+    #   NickNameAdapter.adapt_new(o) { |a| a.nick_name = "Buddy!" }
+    #   NickNameAdapter.adapt_new(o) { |a, o| a.nick_name = "Your the best #{o.class.name} I met."}
+    # This is used when a fresh adapter is wanted instead of possible returning an
+    # existing adapter as in the case of {Adapter.adapt}.
+    # @overload adapt_new(o)
+    # @overload adapt_new(o, {|adapter| block})
+    # @overload adapt_new(o, {|adapter, o| block})
+    # @yieldparam adapter [Adapter<self>] the created adapter
+    # @yieldparam o [Adaptable] optional, the given adaptable
+    # @param o [Adaptable] object to add adapter to
+    # @param block [Proc] optional, evaluated in the context of the new adapter
+    # @return [Adapter<self>] an adapter of the same class as the receiver of the call
+    # @raise [ArgumentError] if the given object o is not adaptable
+    #
+    def self.adapt_new(o, &block)
+      adapter = associate_adapter(create_adapter(o), o)
+      if block_given?
+        case block.arity
+        when 1
+          block.call(adapter)
+        else
+          block.call(adapter, o)
+        end
+      end
+      adapter
+    end
+
+    # Clears the adapter set in the given object o. Returns any set adapter or nil.
+    # @param o [Adaptable] the object where the adapter should be cleared
+    # @return [Adapter] if an adapter was set
+    # @return [nil] if the adapter has not been set
+    #
+    def self.clear(o)
+      attr_name = :"@#{instance_var_name(self.name)}"
+      if o.instance_variable_defined?(attr_name)
+        o.send(:remove_instance_variable, attr_name)
+      else
+        nil
+      end
+    end
+
+    # This base version creates an instance of the class (i.e. an instance of the concrete subclass
+    # of Adapter). A Specialization may want to create an adapter instance specialized for the given target
+    # object.
+    # @param o [Adaptable] The object to adapt. This implementation ignores this variable, but a
+    #   specialization may want to initialize itself differently depending on the object it is adapting.
+    # @return [Adapter<self>] instance of the subclass of Adapter receiving the call
+    #
+    def self.create_adapter(o)
+      new
+    end
+
+    # Associates the given adapter with the given target object
+    # @param adapter [Adapter] the adapter to associate with the given object _o_
+    # @param o [Adaptable] the object to adapt
+    # @return [adapter] the given adapter
+    #
+    def self.associate_adapter(adapter, o)
+      attr_name = :"@#{instance_var_name(adapter.class.name)}"
+      o.instance_variable_set(attr_name, adapter)
+      adapter
+    end
+
+    # Returns a suitable instance variable name given a class name.
+    # The returned string is the fully qualified name of a class with '::' replaced by '_' since
+    # '::' is not allowed in an instance variable name.
+    # @param name [String] the fully qualified name of a class
+    # @return [String] the name with all '::' replaced by '_'
+    # @api private
+    # @private
+    #
+    def self.instance_var_name(name)
+      name.split("::").join('_')
+    end
+  end
+end
diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb
new file mode 100644
index 0000000..07b3a1c
--- /dev/null
+++ b/lib/puppet/pops/adapters.rb
@@ -0,0 +1,65 @@
+# The Adapters module contains adapters for Documentation, Origin, SourcePosition, and Loader.
+#
+module Puppet::Pops::Adapters
+  # A documentation adapter adapts an object with a documentation string.
+  # (The intended use is for a source text parser to extract documentation and store this
+  # in DocumentationAdapter instances).
+  #
+  class DocumentationAdapter < Puppet::Pops::Adaptable::Adapter
+    # @return [String] The documentation associated with an object
+    attr_accessor :documentation
+  end
+
+  # An origin adapter adapts an object with where it came from. This origin
+  # describes the resource (a file, etc.) where source text originates.
+  # Instances of SourcePosAdapter is then used on other objects in a model to
+  # describe their relative position versus the origin.
+  #
+  # @see Puppet::Pops::Utils#find_adapter
+  #
+  class OriginAdapter < Puppet::Pops::Adaptable::Adapter
+    # @return [String] the origin of the adapted (usually a filename)
+    attr_accessor :origin
+  end
+
+  # A SourcePosAdapter describes a position relative to an origin. (Typically an {OriginAdapter} is
+  # associated with the root of a model. This origin has a URI to the resource, and a line number.
+  # The offset in the SourcePosAdapter is then relative to this origin.
+  # (This somewhat complex structure makes it possible to correctly refer to a source position
+  # in source that is embedded in some resource; a parser only sees the embedded snippet of source text
+  # and does not know where it was embedded).
+  #
+  # @see Puppet::Pops::Utils#find_adapter
+  #
+  class SourcePosAdapter < Puppet::Pops::Adaptable::Adapter
+    # @return [Fixnum] The start line in source starting from 1
+    attr_accessor :line
+
+    # @return [Fixnum] The position on the start_line (in characters) starting from 0
+    attr_accessor :pos
+
+    # @return [Fixnum] The (start) offset of source text characters
+    #   (starting from 0) representing the adapted object.
+    #   Value may be nil
+    attr_accessor :offset
+
+    # @return [Fixnum] The length (count) of characters of source text
+    #   representing the adapted object from the origin. Not including any
+    #   trailing whitespace.
+    attr_accessor :length
+  end
+
+  # A LoaderAdapter adapts an object with a {Puppet::Pops::Loader}. This is used to make further loading from the
+  # perspective of the adapted object take place in the perspective of this Loader.
+  #
+  # It is typically enough to adapt the root of a model as a search is made towards the root of the model
+  # until a loader is found, but there is no harm in duplicating this information provided a contained
+  # object is adapted with the correct loader.
+  #
+  # @see Puppet::Pops::Utils#find_adapter
+  #
+  class LoaderAdapter < Puppet::Pops::Adaptable::Adapter
+    # @return [Puppet::Pops::Loader] the loader
+    attr_accessor :loader
+  end
+end
diff --git a/lib/puppet/pops/containment.rb b/lib/puppet/pops/containment.rb
new file mode 100644
index 0000000..a019044
--- /dev/null
+++ b/lib/puppet/pops/containment.rb
@@ -0,0 +1,37 @@
+# FIXME: This module should be updated when a newer version of RGen (>0.6.2) adds required meta model "e-method" supports.
+#
+module Puppet::Pops::Containment
+  # Returns Enumerable, thus allowing
+  # some_element.eAllContents each {|contained| }
+  # This is a depth first enumeration where parent appears before children.
+  # @note the top-most object itself is not included in the enumeration, only what it contains.
+  def eAllContents
+    EAllContentsEnumerator.new(self)
+  end
+
+  class EAllContentsEnumerator
+    include Enumerable
+    def initialize o
+      @element = o
+    end
+
+    def each &block
+      if block_given?
+        eAllContents(@element, &block)
+        @element
+      else
+        self
+      end
+    end
+
+    def eAllContents(element, &block)
+      element.class.ecore.eAllReferences.select{|r| r.containment}.each do |r|
+        children = element.getGenericAsArray(r.name)
+        children.each do |c|
+          block.call(c)
+          eAllContents(c, &block)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb
new file mode 100644
index 0000000..1440c90
--- /dev/null
+++ b/lib/puppet/pops/issues.rb
@@ -0,0 +1,258 @@
+# Defines classes to deal with issues, and message formatting and defines constants with Issues.
+# @api public
+#
+module Puppet::Pops::Issues
+  # Describes an issue, and can produce a message for an occurrence of the issue.
+  #
+  class Issue
+    # The issue code
+    # @return [Symbol]
+    attr_reader :issue_code
+
+    # A block producing the message
+    # @return [Proc]
+    attr_reader :message_block
+
+    # Names that must be bound in an occurrence of the issue to be able to produce a message.
+    # These are the names in addition to requirements stipulated by the Issue formatter contract; i.e. :label`,
+    # and `:semantic`.
+    #
+    attr_reader :arg_names
+
+    # If this issue can have its severity lowered to :warning, :deprecation, or :ignored
+    attr_writer :demotable
+    # Configures the Issue with required arguments (bound by occurrence), and a block producing a message.
+    def initialize issue_code, *args, &block
+      @issue_code = issue_code
+      @message_block = block
+      @arg_names = args
+      @demotable = true
+    end
+
+    # Returns true if it is allowed to demote this issue
+    def demotable?
+      @demotable
+    end
+
+    # Formats a message for an occurrence of the issue with argument bindings passed in a hash.
+    # The hash must contain a LabelProvider bound to the key `label` and the semantic model element
+    # bound to the key `semantic`. All required arguments as specified by `arg_names` must be bound
+    # in the given `hash`.
+    # @api public
+    #
+    def format(hash ={})
+      # Create a Message Data where all hash keys become methods for convenient interpolation
+      # in issue text.
+      msgdata = MessageData.new(*arg_names)
+      # Evaluate the message block in the msg data's binding
+      msgdata.format(hash, &message_block)
+    end
+  end
+
+  # Provides a binding of arguments passed to Issue.format to method names available
+  # in the issue's message producing block.
+  # @api private
+  #
+  class MessageData
+    def initialize *argnames
+      singleton = class << self; self end
+      argnames.each do |name|
+        singleton.send(:define_method, name) do
+          @data[name]
+        end
+      end
+    end
+
+    def format(hash, &block)
+      @data = hash
+      instance_eval &block
+    end
+
+    # Returns the label provider given as a key in the hash passed to #format.
+    #
+    def label
+      raise "Label provider key :label must be set to produce the text of the message!" unless @data[:label]
+      @data[:label]
+    end
+
+    # Returns the label provider given as a key in the hash passed to #format.
+    #
+    def semantic
+      raise "Label provider key :semantic must be set to produce the text of the message!" unless @data[:semantic]
+      @data[:semantic]
+    end
+  end
+
+  # Defines an issue with the given `issue_code`, additional required parameters, and a block producing a message.
+  # The block is evaluated in the context of a MessageData which provides convenient access to all required arguments
+  # via accessor methods. In addition to accessors for specified arguments, these are also available:
+  # * `label` - a `LabelProvider` that provides human understandable names for model elements and production of article (a/an/the).
+  # * `semantic` - the model element for which the issue is reported
+  #
+  # @param issue_code [Symbol] the issue code for the issue used as an identifier, should be the same as the constant
+  #   the issue is bound to.
+  # @param *args [Symbol] required arguments that must be passed when formatting the message, may be empty
+  # @param &block [Proc] a block producing the message string, evaluated in a MessageData scope. The produced string
+  #   should not end with a period as additional information may be appended.
+  #
+  # @see MessageData
+  # @api public
+  #
+  def self.issue (issue_code, *args, &block)
+    Issue.new(issue_code, *args, &block)
+  end
+
+  # Creates a non demotable issue.
+  # @see Issue.issue
+  #
+  def self.hard_issue(issue_code, *args, &block)
+    result = Issue.new(issue_code, *args, &block)
+    result.demotable = false
+    result
+  end
+
+  # @comment Here follows definitions of issues. The intent is to provide a list from which yardoc can be generated
+  #   containing more detailed information / explanation of the issue.
+  #   These issues are set as constants, but it is unfortunately not possible for the created object to easily know which
+  #   name it is bound to. Instead the constant has to be repeated. (Alternatively, it could be done by instead calling
+  #   #const_set on the module, but the extra work required to get yardoc output vs. the extra effort to repeat the name
+  #   twice makes it not worth it (if doable at all, since there is no tag to artificially construct a constant, and
+  #   the parse tag does not produce any result for a constant assignment).
+
+  # This is allowed (3.1) and has not yet been deprecated.
+  # @todo configuration
+  #
+  NAME_WITH_HYPHEN = issue :NAME_WITH_HYPHEN, :name do
+    "#{label.a_an_uc(semantic)} may not have a name contain a hyphen. The name '#{name}' is not legal"
+  end
+
+  # When a variable name contains a hyphen and these are illegal.
+  # It is possible to control if a hyphen is legal in a name or not using the setting TODO
+  # @todo describe the setting
+  # @api public
+  # @todo configuration if this is error or warning
+  #
+  VAR_WITH_HYPHEN = issue :VAR_WITH_HYPHEN, :name do
+    "A variable name may not contain a hyphen. The name '#{name}' is not legal"
+  end
+
+  # A class, definition, or node may only appear at top level or inside other classes
+  # @todo Is this really true for nodes? Can they be inside classes? Isn't that too late?
+  # @api public
+  #
+  NOT_TOP_LEVEL = hard_issue :NOT_TOP_LEVEL do
+    "Classes, definitions, and nodes may only appear at toplevel or inside other classes"
+  end
+
+  CROSS_SCOPE_ASSIGNMENT = hard_issue :CROSS_SCOPE_ASSIGNMENT, :name do
+    "Illegal attempt to assign to '#{name}'. Cannot assign to variables in other namespaces"
+  end
+
+  # Assignment can only be made to certain types of left hand expressions such as variables.
+  ILLEGAL_ASSIGNMENT = hard_issue :ILLEGAL_ASSIGNMENT do
+    "Illegal attempt to assign to '#{label.a_an(semantic)}'. Not an assignable reference"
+  end
+
+  # Assignment cannot be made to numeric match result variables
+  ILLEGAL_NUMERIC_ASSIGNMENT = issue :ILLEGAL_NUMERIC_ASSIGNMENT, :varname do
+    "Illegal attempt to assign to the numeric match result variable '$#{varname}'. Numeric variables are not assignable"
+  end
+
+  # parameters cannot have numeric names, clashes with match result variables
+  ILLEGAL_NUMERIC_PARAMETER = issue :ILLEGAL_NUMERIC_PARAMETER, :name do
+    "The numeric parameter name '$#{varname}' cannot be used (clashes with numeric match result variables)"
+  end
+
+  # In certain versions of Puppet it may be allowed to assign to a not already assigned key
+  # in an array or a hash. This is an optional validation that may be turned on to prevent accidental
+  # mutation.
+  #
+  ILLEGAL_INDEXED_ASSIGNMENT = issue :ILLEGAL_INDEXED_ASSIGNMENT do
+    "Illegal attempt to assign via [index/key]. Not an assignable reference"
+  end
+
+  # When indexed assignment ($x[]=) is allowed, the leftmost expression must be
+  # a variable expression.
+  #
+  ILLEGAL_ASSIGNMENT_VIA_INDEX = hard_issue :ILLEGAL_ASSIGNMENT_VIA_INDEX do
+    "Illegal attempt to assign to #{label.a_an(semantic)} via [index/key]. Not an assignable reference"
+  end
+
+  # Some expressions/statements may not produce a value (known as right-value, or rvalue).
+  # This may vary between puppet versions.
+  #
+  NOT_RVALUE = issue :NOT_RVALUE do
+    "Invalid use of expression. #{label.a_an_uc(semantic)} does not produce a value"
+  end
+
+  # Appending to attributes is only allowed in certain types of resource expressions.
+  #
+  ILLEGAL_ATTRIBUTE_APPEND = hard_issue :ILLEGAL_ATTRIBUTE_APPEND, :name, :parent do
+    "Illegal +> operation on attribute #{name}. This operator can not be used in #{label.a_an(parent)}"
+  end
+
+  # In case a model is constructed programmatically, it must create valid type references.
+  #
+  ILLEGAL_CLASSREF = hard_issue :ILLEGAL_CLASSREF, :name do
+    "Illegal type reference. The given name '#{name}' does not conform to the naming rule"
+  end
+
+  # This is a runtime issue - storeconfigs must be on in order to collect exported. This issue should be
+  # set to :ignore when just checking syntax.
+  # @todo should be a :warning by default
+  #
+  RT_NO_STORECONFIGS = issue :RT_NO_STORECONFIGS do
+    "You cannot collect exported resources without storeconfigs being set; the collection will be ignored"
+  end
+
+  # This is a runtime issue - storeconfigs must be on in order to export a resource. This issue should be
+  # set to :ignore when just checking syntax.
+  # @todo should be a :warning by default
+  #
+  RT_NO_STORECONFIGS_EXPORT = issue :RT_NO_STORECONFIGS_EXPORT do
+    "You cannot collect exported resources without storeconfigs being set; the export is ignored"
+  end
+
+  # A hostname may only contain letters, digits, '_', '-', and '.'.
+  #
+  ILLEGAL_HOSTNAME_CHARS = hard_issue :ILLEGAL_HOSTNAME_CHARS, :hostname do
+    "The hostname '#{hostname}' contains illegal characters (only letters, digits, '_', '-', and '.' are allowed)"
+  end
+
+  # A hostname may only contain letters, digits, '_', '-', and '.'.
+  #
+  ILLEGAL_HOSTNAME_INTERPOLATION = hard_issue :ILLEGAL_HOSTNAME_INTERPOLATION do
+    "An interpolated expression is not allowed in a hostname of a node"
+  end
+
+  # Issues when an expression is used where it is not legal.
+  # E.g. an arithmetic expression where a hostname is expected.
+  #
+  ILLEGAL_EXPRESSION = hard_issue :ILLEGAL_EXPRESSION, :feature, :container do
+    "Illegal expression. #{label.a_an_uc(semantic)} is unacceptable as #{feature} in #{label.a_an(container)}"
+  end
+
+  # Issues when an expression is used illegaly in a query.
+  # query only supports == and !=, and not <, > etc.
+  #
+  ILLEGAL_QUERY_EXPRESSION = hard_issue :ILLEGAL_QUERY_EXPRESSION do
+    "Illegal query expression. #{label.a_an_uc(semantic)} cannot be used in a query"
+  end
+
+  # If an attempt is made to make a resource default virtual or exported.
+  #
+  NOT_VIRTUALIZEABLE = hard_issue :NOT_VIRTUALIZEABLE do
+    "Resource Defaults are not virtualizable"
+  end
+
+  # When an attempt is made to use multiple keys (to produce a range in Ruby - e.g. $arr[2,-1]).
+  # This is currently not supported, but may be in future versions
+  #
+  UNSUPPORTED_RANGE = issue :UNSUPPORTED_RANGE, :count do
+    "Attempt to use unsupported range in #{label.a_an(semantic)}, #{count} values given for max 1"
+  end
+
+  DEPRECATED_NAME_AS_TYPE = issue :DEPRECATED_NAME_AS_TYPE, :name do
+    "Resource references should now be capitalized. The given '#{name}' does not have the correct form"
+  end
+end
diff --git a/lib/puppet/pops/label_provider.rb b/lib/puppet/pops/label_provider.rb
new file mode 100644
index 0000000..e8a75a7
--- /dev/null
+++ b/lib/puppet/pops/label_provider.rb
@@ -0,0 +1,71 @@
+# Provides a label for an object.
+# This simple implementation calls #to_s on the given object, and handles articles 'a/an/the'.
+#
+class Puppet::Pops::LabelProvider
+  VOWELS = %w{a e i o u y}
+  SKIPPED_CHARACTERS = %w{" '}
+  A = "a"
+  AN = "an"
+
+  # Provides a label for the given object by calling `to_s` on the object.
+  # The intent is for this method to be overridden in concrete label providers.
+  def label o
+    o.to_s
+  end
+
+  # Produces a label for the given text with indefinite article (a/an)
+  def a_an o
+    text = label(o)
+    "#{article(text)} #{text}"
+  end
+
+  # Produces a label for the given text with indefinite article (A/An)
+  def a_an_uc o
+    text = label(o)
+    "#{article(text).capitalize} #{text}"
+  end
+
+  # Produces a label for the given text with *definitie article* (the).
+  def the o
+    "the #{label(o)}"
+  end
+
+  # Produces a label for the given text with *definitie article* (The).
+  def the_uc o
+    "The #{label(o)}"
+  end
+
+  private
+
+  # Produces an *indefinite article* (a/an) for the given text ('a' if
+  # it starts with a vowel) This is obviously flawed in the general
+  # sense as may labels have punctuation at the start and this method
+  # does not translate punctuation to English words. Also, if a vowel is
+  # pronounced as a consonant, the article should not be "an".
+  #
+  def article s
+    article_for_letter(first_letter_of(s))
+  end
+
+  def first_letter_of(string)
+    char = string[0,1]
+    if SKIPPED_CHARACTERS.include? char
+      char = string[1,1]
+    end
+
+    if char == ""
+      raise Puppet::DevError, "<#{string}> does not appear to contain a word"
+    end
+
+    char
+  end
+
+  def article_for_letter(letter)
+    downcased = letter.downcase
+    if VOWELS.include? downcased
+      AN
+    else
+      A
+    end
+  end
+end
diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb
new file mode 100644
index 0000000..a9ce994
--- /dev/null
+++ b/lib/puppet/pops/model/ast_transformer.rb
@@ -0,0 +1,636 @@
+require 'puppet/parser/ast'
+
+# The receiver of `import(file)` calls; once per imported file, or nil if imports are ignored
+#
+# Transforms a Pops::Model to classic Puppet AST.
+# TODO: Documentation is currently skipped completely (it is only used for Rdoc)
+#
+class Puppet::Pops::Model::AstTransformer
+  AST = Puppet::Parser::AST
+  Model = Puppet::Pops::Model
+
+  attr_reader :importer
+  def initialize(source_file = "unknown-file", importer=nil)
+    @@transform_visitor ||= Puppet::Pops::Visitor.new(nil,"transform",0,0)
+    @@query_transform_visitor ||= Puppet::Pops::Visitor.new(nil,"query",0,0)
+    @@hostname_transform_visitor ||= Puppet::Pops::Visitor.new(nil,"hostname",0,0)
+    @importer = importer
+    @source_file = source_file
+  end
+
+  # Initialize klass from o (location) and hash (options to created instance).
+  # The object o is used to compute a source location. It may be nil. Source position is merged into
+  # the given options (non surgically). If o is non-nil, the first found source position going up
+  # the containment hierarchy is set. I.e. callers should pass nil if a source position is not wanted
+  # or known to be unobtainable for the object.
+  #
+  # @param o [Object, nil] object from which source position / location is obtained, may be nil
+  # @param klass [Class<Puppet::Parser::AST>] the ast class to create an instance of
+  # @param hash [Hash] hash with options for the class to create
+  #
+  def ast(o, klass, hash={})
+    # create and pass hash with file and line information
+    klass.new(merge_location(hash, o))
+  end
+
+  def merge_location(hash, o)
+    if o
+      pos = {}
+      source_pos = Puppet::Pops::Utils.find_adapter(o, Puppet::Pops::Adapters::SourcePosAdapter)
+      if source_pos
+        pos[:line] = source_pos.line
+        pos[:pos]  = source_pos.pos
+      end
+      pos[:file] = @source_file if @source_file
+      hash = hash.merge(pos)
+    end
+    hash
+  end
+
+  # Transforms pops expressions into AST 3.1 statements/expressions
+  def transform(o)
+    @@transform_visitor.visit_this(self,o)
+  end
+
+  # Transforms pops expressions into AST 3.1 query expressions
+  def query(o)
+    @@query_transform_visitor.visit_this(self, o)
+  end
+
+  # Transforms pops expressions into AST 3.1 hostnames
+  def hostname(o)
+    @@hostname_transform_visitor.visit_this(self, o)
+  end
+
+  def transform_LiteralNumber(o)
+    s = case o.radix
+    when 10
+      o.value.to_s
+    when 8
+      "0%o" % o.value
+    when 16
+      "0x%X" % o.value
+    else
+      "bad radix:" + o.value.to_s
+    end
+
+    # Numbers are Names in the AST !! (Name a.k.a BareWord)
+    ast o, AST::Name, :value => s
+  end
+
+  # Transforms all literal values to string (override for those that should not be AST::String)
+  #
+  def transform_LiteralValue(o)
+    ast o, AST::String, :value => o.value.to_s
+  end
+
+  def transform_LiteralBoolean(o)
+    ast o, AST::Boolean, :value => o.value
+  end
+
+  def transform_Factory(o)
+    transform(o.current)
+  end
+
+  def transform_ArithmeticExpression(o)
+    ast o, AST::ArithmeticOperator, :lval => transform(o.left_expr), :rval=>transform(o.right_expr),
+    :operator => o.operator.to_s
+  end
+
+  def transform_Array(o)
+    ast nil, AST::ASTArray, :children => o.collect {|x| transform(x) }
+  end
+
+  # Puppet AST only allows:
+  # * variable[expression] => Hasharray Access
+  # * NAME [expressions] => Resource Reference(s)
+  # * type [epxressions] => Resource Reference(s)
+  # * HashArrayAccesses[expression] => HasharrayAccesses
+  #
+  # i.e. it is not possible to do `func()[3]`, `[1,2,3][$x]`, `{foo=>10, bar=>20}[$x]` etc. since
+  # LHS is not an expression
+  #
+  # Validation for 3.x semantics should validate the illegal cases. This transformation may fail,
+  # or ignore excess information if the expressions are not correct.
+  # This means that the transformation does not have to evaluate the lhs to detect the target expression.
+  #
+  # Hm, this seems to have changed, the LHS (variable) is evaluated if evaluateable, else it is used as is.
+  #
+  def transform_AccessExpression(o)
+    case o.left_expr
+    when Model::QualifiedName
+      ast o, AST::ResourceReference, :type => o.left_expr.value, :title => transform(o.keys)
+
+    when Model::QualifiedReference
+      ast o, AST::ResourceReference, :type => o.left_expr.value, :title => transform(o.keys)
+
+    when Model::VariableExpression
+      ast o, AST::HashOrArrayAccess, :variable => transform(o.left_expr), :key => transform(o.keys()[0])
+
+    else
+      ast o, AST::HashOrArrayAccess, :variable => transform(o.left_expr), :key => transform(o.keys()[0])
+    end
+  end
+
+  # Puppet AST has a complicated structure
+  # LHS can not be an expression, it must be a type (which is downcased).
+  # type = a downcased QualifiedName
+  #
+  def transform_CollectExpression(o)
+    raise "LHS is not a type" unless o.type_expr.is_a? Model::QualifiedReference
+    type = o.type_expr.value().downcase()
+    args = { :type => type }
+
+    # This somewhat peculiar encoding is used by the 3.1 AST.
+    query = transform(o.query)
+    if query.is_a? Symbol
+      args[:form] =  query
+    else
+      args[:form] = query.form
+      args[:query] = query
+      query.type = type
+    end
+
+    if o.operations.size > 0
+      args[:override] = transform(o.operations)
+    end
+    ast o, AST::Collection, args
+  end
+
+  def transform_ExportedQuery(o)
+    if is_nop?(o.expr)
+      result = :exported
+    else
+      result = query(o.expr)
+      result.form = :exported
+    end
+    result
+  end
+
+  def transform_VirtualQuery(o)
+    if is_nop?(o.expr)
+      result = :virtual
+    else
+      result = query(o.expr)
+      result.form = :virtual
+    end
+    result
+  end
+
+  # Ensures transformation fails if a 3.1 non supported object is encountered in a query expression
+  #
+  def query_Object(o)
+    raise "Not a valid expression in a collection query: "+o.class.name
+  end
+
+  # Puppet AST only allows == and !=, and left expr is restricted, but right value is an expression
+  #
+  def query_ComparisonExpression(o)
+    if [:'==', :'!='].include? o.operator
+      ast o, AST::CollExpr, :test1 => query(o.left_expr), :oper => o.operator.to_s, :test2 => transform(o.right_expr)
+    else
+      raise "Not a valid comparison operator in a collection query: " + o.operator.to_s
+    end
+  end
+
+  def query_AndExpression(o)
+    ast o, AST::CollExpr, :test1 => query(o.left_expr), :oper => 'and', :test2 => query(o.right_expr)
+  end
+
+  def query_OrExpression(o)
+    ast o, AST::CollExpr, :test1 => query(o.left_expr), :oper => 'or', :test2 => query(o.right_expr)
+  end
+
+  def query_ParenthesizedExpression(o)
+    result = query(o.expr) # produces CollExpr
+    result.parens = true
+    result
+  end
+
+  def query_VariableExpression(o)
+    transform(o)
+  end
+
+  def query_QualifiedName(o)
+    transform(o)
+  end
+
+  def query_LiteralNumber(o)
+    transform(o) # number to string in correct radix
+  end
+
+  def query_LiteralString(o)
+    transform(o)
+  end
+
+  def query_LiteralBoolean(o)
+    transform(o)
+  end
+
+  def transform_QualifiedName(o)
+    ast o, AST::Name, :value => o.value
+  end
+
+  def transform_QualifiedReference(o)
+    ast o, AST::Type, :value => o.value
+  end
+
+  def transform_ComparisonExpression(o)
+    ast o, AST::ComparisonOperator, :operator => o.operator.to_s, :lval => transform(o.left_expr), :rval => transform(o.right_expr)
+  end
+
+  def transform_AndExpression(o)
+    ast o, AST::BooleanOperator, :operator => 'and', :lval => transform(o.left_expr), :rval => transform(o.right_expr)
+  end
+
+  def transform_OrExpression(o)
+    ast o, AST::BooleanOperator, :operator => 'or', :lval => transform(o.left_expr), :rval => transform(o.right_expr)
+  end
+
+  def transform_InExpression(o)
+    ast o, AST::InOperator, :lval => transform(o.left_expr), :rval => transform(o.right_expr)
+  end
+
+  # This is a complex transformation from a modeled import to a Nop result (where the import took place),
+  # and calls to perform import/parsing etc. during the transformation.
+  # When testing syntax, the @importer does not have to be set, but it is not possible to check
+  # the actual import without inventing a new AST::ImportExpression with nop effect when evaluating.
+  def transform_ImportExpression(o)
+    if importer
+      o.files.each {|f|
+        unless f.is_a? Model::LiteralString
+          raise "Illegal import file expression. Must be a single quoted string"
+        end
+        importer.import(f.value)
+      }
+    end
+    # Crazy stuff
+    # Transformation of "import" needs to parse the other files at the time of transformation.
+    # Then produce a :nop, since nothing should be evaluated.
+    ast o, AST::Nop, {}
+  end
+
+  def transform_InstanceReferences(o)
+    ast o, AST::ResourceReference, :type => o.type_name.value, :title => transform(o.names)
+  end
+
+  # Assignment in AST 3.1 is to variable or hasharray accesses !!! See Bug #16116
+  def transform_AssignmentExpression(o)
+    args = {:value => transform(o.right_expr) }
+    args[:append] = true if o.operator == :'+='
+
+    args[:name] = case o.left_expr
+    when Model::VariableExpression
+      ast o, AST::Name, {:value => o.left_expr.expr.value }
+    when Model::AccessExpression
+      transform(o.left_expr)
+    else
+      raise "LHS is not an expression that can be assigned to"
+    end
+    ast o, AST::VarDef, args
+  end
+
+  # Produces (name => expr) or (name +> expr)
+  def transform_AttributeOperation(o)
+    args = { :value => transform(o.value_expr) }
+    args[:add] = true if o.operator == :'+>'
+    args[:param] = o.attribute_name
+    ast o, AST::ResourceParam, args
+  end
+
+  def transform_LiteralList(o)
+    # Uses default transform of Ruby Array to ASTArray
+    transform(o.values)
+  end
+
+  # Literal hash has strange behavior in Puppet 3.1. See Bug #19426, and this implementation is bug
+  # compatible
+  def transform_LiteralHash(o)
+    if o.entries.size == 0
+      ast o, AST::ASTHash, {:value=> {}}
+    else
+      value = {}
+      o.entries.each {|x| value.merge! transform(x) }
+      ast o, AST::ASTHash, {:value=> value}
+    end
+  end
+
+  # Transforms entry into a hash (they are later merged with strange effects: Bug #19426).
+  # Puppet 3.x only allows:
+  # * NAME
+  # * quotedtext
+  # As keys (quoted text can be an interpolated string which is compared as a key in a less than satisfactory way).
+  #
+  def transform_KeyedEntry(o)
+    value = transform(o.value)
+    key = case o.key
+    when Model::QualifiedName
+      o.key.value
+    when Model::LiteralString
+      transform o.key
+    when Model::LiteralNumber
+      transform o.key
+    when Model::ConcatenatedString
+      transform o.key
+    else
+      raise "Illegal hash key expression of type (#{o.key.class})"
+    end
+    {key => value}
+  end
+
+  def transform_MatchExpression(o)
+    ast o, AST::MatchOperator, :operator => o.operator.to_s, :lval => transform(o.left_expr), :rval => transform(o.right_expr)
+  end
+
+  def transform_LiteralString(o)
+    ast o, AST::String, :value => o.value
+  end
+
+  # Literal text in a concatenated string
+  def transform_LiteralText(o)
+    ast o, AST::String, :value => o.value
+  end
+
+  def transform_LambdaExpression(o)
+    astargs = { :parameters => o.parameters.collect {|p| transform(p) } }
+    astargs.merge!({ :children => transform(o.body) }) if o.body         # do not want children if it is nil/nop
+    ast o, AST::Lambda, astargs
+  end
+
+  def transform_LiteralDefault(o)
+    ast o, AST::Default, :value => :default
+  end
+
+  def transform_LiteralUndef(o)
+    ast o, AST::Undef, :value => :undef
+  end
+
+  def transform_LiteralRegularExpression(o)
+    ast o, AST::Regex, :value => o.value
+  end
+
+  def transform_Nop(o)
+    ast o, AST::Nop
+  end
+
+  # In the 3.1. grammar this is a hash that is merged with other elements to form a method call
+  # Also in 3.1. grammar there are restrictions on the LHS (that are only there for grammar issues).
+  #
+  def transform_NamedAccessExpression(o)
+    receiver = transform(o.left_expr)
+    name = o.right_expr
+    raise "Unacceptable function/method name" unless name.is_a? Model::QualifiedName
+    {:receiver => receiver, :name => name.value}
+  end
+
+  def transform_NilClass(o)
+    ast o, AST::Nop, {}
+  end
+
+  def transform_NotExpression(o)
+    ast o, AST::Not, :value => transform(o.expr)
+  end
+
+  def transform_VariableExpression(o)
+    # assumes the expression is a QualifiedName
+    ast o, AST::Variable, :value => o.expr.value
+  end
+
+  # In Puppet 3.1, the ConcatenatedString is responsible for the evaluation and stringification of
+  # expression segments. Expressions and Strings are kept in an array.
+  def transform_TextExpression(o)
+    transform(o.expr)
+  end
+
+  def transform_UnaryMinusExpression(o)
+    ast o, AST::Minus, :value => transform(o.expr)
+  end
+
+  # Puppet 3.1 representation of a BlockExpression is an AST::Array - this makes it impossible to differentiate
+  # between a LiteralArray and a Sequence. (Should it return the collected array, or the last expression?)
+  # (A BlockExpression has now been introduced in the AST to solve this).
+  #
+  def transform_BlockExpression(o)
+    children = []
+    # remove nops resulting from import
+    o.statements.each {|s| r = transform(s); children << r unless is_nop?(r) }
+    ast o, AST::BlockExpression, :children => children  # o.statements.collect {|s| transform(s) }
+  end
+
+  # Interpolated strings are kept in an array of AST (string or other expression).
+  def transform_ConcatenatedString(o)
+    ast o, AST::Concat, :value => o.segments.collect {|x| transform(x)}
+  end
+
+  def transform_HostClassDefinition(o)
+    parameters = o.parameters.collect {|p| transform(p) }
+    args = {
+      :arguments => parameters,
+      :parent => o.parent_class,
+    }
+    args[:code] = transform(o.body) unless is_nop?(o.body)
+    Puppet::Parser::AST::Hostclass.new(o.name, merge_location(args, o))
+  end
+
+  def transform_NodeDefinition(o)
+    # o.host_matches are expressions, and 3.1 AST requires special object AST::HostName
+    # where a HostName is one of NAME, STRING, DEFAULT or Regexp - all of these are strings except regexp
+    #
+    args = {
+      :code => transform(o.body)
+    }
+    args[:parent] = transform(o.parent) unless is_nop?(o.parent)
+    Puppet::Parser::AST::Node.new(hostname(o.host_matches), merge_location(args, o))
+  end
+
+  # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
+  def hostname_Array(o)
+    o.collect {|x| ast x, AST::HostName, :value => hostname(x) }
+  end
+
+  def hostname_LiteralValue(o)
+    return o.value
+  end
+
+  def hostname_QualifiedName(o)
+    return o.value
+  end
+
+  def hostname_LiteralNumber(o)
+    transform(o) # Number to string with correct radix
+  end
+
+  def hostname_LiteralDefault(o)
+    return 'default'
+  end
+
+  def hostname_LiteralRegularExpression(o)
+    ast o, AST::Regex, :value => o.value
+  end
+
+  def hostname_Object(o)
+    raise "Illegal expression - unacceptable as a node name"
+  end
+
+  def transform_RelationshipExpression(o)
+    Puppet::Parser::AST::Relationship.new(transform(o.left_expr), transform(o.right_expr), o.operator.to_s, merge_location({}, o))
+  end
+
+  def transform_ResourceTypeDefinition(o)
+    parameters = o.parameters.collect {|p| transform(p) }
+    args = { :arguments => parameters }
+    args[:code] = transform(o.body) unless is_nop?(o.body)
+
+    Puppet::Parser::AST::Definition.new(o.name, merge_location(args, o))
+  end
+
+  # Transformation of ResourceOverrideExpression is slightly more involved than a straight forward
+  # transformation.
+  # A ResourceOverrideExppression has "resources" which should be an AccessExpression
+  # on the form QualifiedName[expressions], or QualifiedReference[expressions] to be valid.
+  # It also has a set of attribute operations.
+  #
+  # The AST equivalence is an AST::ResourceOverride with a ResourceReference as its LHS, and
+  # a set of Parameters.
+  # ResourceReference has type as a string, and the expressions representing
+  # the "titles" to be an ASTArray.
+  #
+  def transform_ResourceOverrideExpression(o)
+    resource_ref = o.resources
+    raise "Unacceptable expression for resource override" unless resource_ref.is_a? Model::AccessExpression
+
+    type = case resource_ref.left_expr
+    when Model::QualifiedName
+      # This is deprecated "Resource references should now be capitalized" - this is caught elsewhere
+      resource_ref.left_expr.value
+    when Model::QualifiedReference
+      resource_ref.left_expr.value
+    else
+      raise "Unacceptable expression for resource override; need NAME or CLASSREF"
+    end
+
+    result_ref = ast o, AST::ResourceReference, :type => type, :title => transform(resource_ref.keys)
+
+    # title is one or more expressions, if more than one it should be an ASTArray
+    ast o, AST::ResourceOverride, :object => result_ref, :parameters => transform(o.operations)
+  end
+
+  # Parameter is a parameter in a definition of some kind.
+  # It is transformed to an array on the form `[name]´, or `[name, value]´.
+  def transform_Parameter(o)
+    if o.value
+      [o.name, transform(o.value)]
+    else
+      [o.name]
+    end
+  end
+
+  # For non query expressions, parentheses can be dropped in the resulting AST.
+  def transform_ParenthesizedExpression(o)
+    transform(o.expr)
+  end
+
+  def transform_IfExpression(o)
+    args = { :test => transform(o.test), :statements => transform(o.then_expr) }
+    args[:else] = transform(o.else_expr) # Tests say Nop should be there (unless is_nop? o.else_expr), probably not needed
+    result = ast o, AST::IfStatement, args
+  end
+
+  # Unless is not an AST object, instead an AST::IfStatement is used with an AST::Not around the test
+  #
+  def transform_UnlessExpression(o)
+    args = { :test => ast(o, AST::Not, :value => transform(o.test)),
+      :statements => transform(o.then_expr) }
+    # AST 3.1 does not allow else on unless in the grammar, but it is ok since unless is encoded as a if !x
+    args.merge!({:else => transform(o.else_expr)}) unless is_nop?(o.else_expr)
+    result = ast o, AST::IfStatement, args
+  end
+
+  # Puppet 3.1 AST only supports calling a function by name (it is not possible to produce a function
+  # that is then called).
+  # rval_required (for an expression)
+  # functor_expr (lhs - the "name" expression)
+  # arguments - list of arguments
+  #
+  def transform_CallNamedFunctionExpression(o)
+    name = o.functor_expr
+    raise "Unacceptable expression for name of function" unless name.is_a? Model::QualifiedName
+    args = {
+      :name => name.value,
+      :arguments => transform(o.arguments),
+      :ftype => o.rval_required ? :rvalue : :statement
+    }
+    args[:pblock] = transform(o.lambda) if o.lambda
+    ast o, AST::Function, args
+  end
+
+  # Transformation of CallMethodExpression handles a NamedAccessExpression functor and
+  # turns this into a 3.1 AST::MethodCall.
+  #
+  def transform_CallMethodExpression(o)
+    name = o.functor_expr
+    raise "Unacceptable expression for name of function" unless name.is_a? Model::NamedAccessExpression
+    # transform of NamedAccess produces a hash, add arguments to it
+    astargs = transform(name).merge(:arguments => transform(o.arguments))
+    astargs.merge!(:lambda => transform(o.lambda)) if o.lambda # do not want a Nop as the lambda
+    ast o, AST::MethodCall, astargs
+
+  end
+
+  def transform_CaseExpression(o)
+    # Expects expression, AST::ASTArray of AST
+    ast o, AST::CaseStatement, :test => transform(o.test), :options => transform(o.options)
+  end
+
+  def transform_CaseOption(o)
+    ast o, AST::CaseOpt, :value => transform(o.values), :statements => transform(o.then_expr)
+  end
+
+  def transform_ResourceBody(o)
+    # expects AST, AST::ASTArray of AST
+    ast o, AST::ResourceInstance, :title => transform(o.title), :parameters => transform(o.operations)
+  end
+
+  def transform_ResourceDefaultsExpression(o)
+    ast o, AST::ResourceDefaults, :type => o.type_ref.value, :parameters => transform(o.operations)
+  end
+
+  # Transformation of ResourceExpression requires calling a method on the resulting
+  # AST::Resource if it is virtual or exported
+  #
+  def transform_ResourceExpression(o)
+    raise "Unacceptable type name expression" unless o.type_name.is_a? Model::QualifiedName
+    resource = ast o, AST::Resource, :type => o.type_name.value, :instances => transform(o.bodies)
+    resource.send("#{o.form}=", true) unless o.form == :regular
+    resource
+  end
+
+  # Transformation of SelectorExpression is limited to certain types of expressions.
+  # This is probably due to constraints in the old grammar rather than any real concerns.
+  def transform_SelectorExpression(o)
+    case o.left_expr
+    when Model::CallNamedFunctionExpression
+    when Model::AccessExpression
+    when Model::VariableExpression
+    when Model::ConcatenatedString
+    else
+      raise "Unacceptable select expression" unless o.left_expr.kind_of? Model::Literal
+    end
+    ast o, AST::Selector, :param => transform(o.left_expr), :values => transform(o.selectors)
+  end
+
+  def transform_SelectorEntry(o)
+    ast o, AST::ResourceParam, :param => transform(o.matching_expr), :value => transform(o.value_expr)
+  end
+
+  def transform_Object(o)
+    raise "Unacceptable transform - found an Object without a rule: #{o.class}"
+  end
+
+  # Nil, nop
+  # Bee bopp a luh-lah, a bop bop boom.
+  #
+  def is_nop?(o)
+    o.nil? || o.is_a?(Model::Nop)
+  end
+end
diff --git a/lib/puppet/pops/model/ast_tree_dumper.rb b/lib/puppet/pops/model/ast_tree_dumper.rb
new file mode 100644
index 0000000..ffe8fd8
--- /dev/null
+++ b/lib/puppet/pops/model/ast_tree_dumper.rb
@@ -0,0 +1,378 @@
+require 'puppet/parser/ast'
+
+# Dumps a Pops::Model in reverse polish notation; i.e. LISP style
+# The intention is to use this for debugging output
+# TODO: BAD NAME - A DUMP is a Ruby Serialization
+#
+class Puppet::Pops::Model::AstTreeDumper < Puppet::Pops::Model::TreeDumper
+  AST = Puppet::Parser::AST
+  Model = Puppet::Pops::Model
+
+  def dump_LiteralNumber o
+    case o.radix
+    when 10
+      o.value.to_s
+    when 8
+      "0%o" % o.value
+    when 16
+      "0x%X" % o.value
+    else
+      "bad radix:" + o.value.to_s
+    end
+  end
+
+  def dump_Factory o
+    do_dump(o.current)
+  end
+
+  def dump_ArithmeticOperator o
+    [o.operator.to_s, do_dump(o.lval), do_dump(o.rval)]
+  end
+  def dump_Relationship o
+    [o.arrow.to_s, do_dump(o.left), do_dump(o.right)]
+  end
+
+  # Hostname is tricky, it is either a bare word, a string, or default, or regular expression
+  # Least evil, all strings except default are quoted
+  def dump_HostName o
+    result = do_dump o.value
+    unless o.value.is_a? AST::Regex
+      result = result == "default" ? ":default" : "'#{result}'"
+    end
+    result
+  end
+
+  # x[y] prints as (slice x y)
+  def dump_HashOrArrayAccess o
+    var = o.variable.is_a?(String) ? "$#{o.variable}" : do_dump(o.variable)
+    ["slice", var, do_dump(o.key)]
+  end
+
+  # The AST Collection knows about exported or virtual query, not the query.
+  def dump_Collection o
+    result = ["collect", do_dump(o.type), :indent, :break]
+    if o.form == :virtual
+      q = ["<| |>"]
+    else
+      q = ["<<| |>>"]
+    end
+    q << do_dump(o.query) unless is_nop?(o.query)
+    q <<  :indent
+    result << q
+    o.override do |ao|
+      result << :break << do_dump(ao)
+    end
+    result += [:dedent, :dedent ]
+    result
+  end
+
+  def dump_CollExpr o
+    operator = case o.oper
+    when 'and'
+      '&&'
+    when 'or'
+      '||'
+    else
+      o.oper
+    end
+    [operator, do_dump(o.test1), do_dump(o.test2)]
+  end
+
+  def dump_ComparisonOperator o
+    [o.operator.to_s, do_dump(o.lval), do_dump(o.rval)]
+  end
+
+  def dump_Boolean o
+    o.to_s
+  end
+
+  def dump_BooleanOperator o
+    operator = o.operator == 'and' ? '&&' : '||'
+    [operator, do_dump(o.lval), do_dump(o.rval)]
+  end
+
+  def dump_InOperator o
+    ["in", do_dump(o.lval), do_dump(o.rval)]
+  end
+
+  # $x = ...
+  # $x += ...
+  #
+  def dump_VarDef o
+    operator = o.append ? "+=" : "="
+    [operator, '$' + do_dump(o.name), do_dump(o.value)]
+  end
+
+  # Produces (name => expr) or (name +> expr)
+  def dump_ResourceParam o
+    operator = o.add ? "+>" : "=>"
+    [do_dump(o.param), operator, do_dump(o.value)]
+  end
+
+  def dump_Array o
+    o.collect {|e| do_dump(e) }
+  end
+
+  def dump_ASTArray o
+    ["[]"] + o.children.collect {|x| do_dump(x)}
+  end
+
+  def dump_ASTHash o
+    ["{}"] + o.value.sort_by{|k,v| k.to_s}.collect {|x| [do_dump(x[0]), do_dump(x[1])]}
+#    ["{}"] + o.value.collect {|x| [do_dump(x[0]), do_dump(x[1])]}
+  end
+
+  def dump_MatchOperator o
+    [o.operator.to_s, do_dump(o.lval), do_dump(o.rval)]
+  end
+
+  # Dump a Ruby String in single quotes unless it is a number.
+  def dump_String o
+
+    if o.is_a? String
+      o               # A Ruby String, not quoted
+    elsif n = Puppet::Pops::Utils.to_n(o.value)
+      o.value         # AST::String that is a number without quotes
+    else
+      "'#{o.value}'"  # AST::String that is not a number
+    end
+  end
+
+  def dump_Lambda o
+    result = ["lambda"]
+    result << ["parameters"] + o.parameters.collect {|p| _dump_ParameterArray(p) } if o.parameters.size() > 0
+    if o.children == []
+      result << [] # does not have a lambda body
+    else
+      result << do_dump(o.children)
+    end
+    result
+  end
+
+  def dump_Default o
+    ":default"
+  end
+
+  def dump_Undef o
+    ":undef"
+  end
+
+  # Note this is Regex (the AST kind), not Ruby Regexp
+  def dump_Regex o
+    "/#{o.value.source}/"
+  end
+
+  def dump_Nop o
+    ":nop"
+  end
+
+  def dump_NilClass o
+    "()"
+  end
+
+  def dump_Not o
+    ['!', dump(o.value)]
+  end
+
+  def dump_Variable o
+    "$#{dump(o.value)}"
+  end
+
+  def dump_Minus o
+    ['-', do_dump(o.value)]
+  end
+
+  def dump_BlockExpression o
+    ["block"] + o.children.collect {|x| do_dump(x) }
+  end
+
+  # Interpolated strings are shown as (cat seg0 seg1 ... segN)
+  def dump_Concat o
+    ["cat"] + o.value.collect {|x| x.is_a?(AST::String) ? " "+do_dump(x) : ["str", do_dump(x)]}
+  end
+
+  def dump_Hostclass o
+    # ok, this is kind of crazy stuff in the AST, information in a context instead of in AST, and
+    # parameters are in a Ruby Array with each parameter being an Array...
+    #
+    context = o.context
+    args = context[:arguments]
+    parent = context[:parent]
+    result = ["class", o.name]
+    result << ["inherits", parent] if parent
+    result << ["parameters"] + args.collect {|p| _dump_ParameterArray(p) } if args && args.size() > 0
+    if is_nop?(o.code)
+      result << []
+    else
+      result << do_dump(o.code)
+    end
+    result
+  end
+
+  def dump_Name o
+    o.value
+  end
+
+  def dump_Node o
+    context = o.context
+    parent = context[:parent]
+    code = context[:code]
+
+    result = ["node"]
+    result << ["matches"] + o.names.collect {|m| do_dump(m) }
+    result << ["parent", do_dump(parent)] if !is_nop?(parent)
+    if is_nop?(code)
+      result << []
+    else
+      result << do_dump(code)
+    end
+    result
+  end
+
+  def dump_Definition o
+    # ok, this is even crazier that Hostclass. The name of the define does not have an accessor
+    # and some things are in the context (but not the name). Parameters are called arguments and they
+    # are in a Ruby Array where each parameter is an array of 1 or 2 elements.
+    #
+    context = o.context
+    name = o.instance_variable_get("@name")
+    args = context[:arguments]
+    code = context[:code]
+    result = ["define", name]
+    result << ["parameters"] + args.collect {|p| _dump_ParameterArray(p) } if args && args.size() > 0
+    if is_nop?(code)
+      result << []
+    else
+      result << do_dump(code)
+    end
+    result
+  end
+
+  def dump_ResourceReference o
+    result = ["slice", do_dump(o.type)]
+    if o.title.children.size == 1
+      result << do_dump(o.title[0])
+    else
+      result << do_dump(o.title.children)
+    end
+    result
+  end
+
+  def dump_ResourceOverride o
+    result = ["override", do_dump(o.object), :indent]
+    o.parameters.each do |p|
+      result << :break << do_dump(p)
+    end
+    result << :dedent
+    result
+  end
+
+  # Puppet AST encodes a parameter as a one or two slot Array.
+  # This is not a polymorph dump method.
+  #
+  def _dump_ParameterArray o
+    if o.size == 2
+      ["=", o[0], do_dump(o[1])]
+    else
+      o[0]
+    end
+  end
+
+  def dump_IfStatement o
+    result = ["if", do_dump(o.test), :indent, :break,
+      ["then", :indent, do_dump(o.statements), :dedent]]
+    result +=
+    [:break,
+      ["else", :indent, do_dump(o.else), :dedent],
+      :dedent] unless is_nop? o.else
+    result
+  end
+
+  # Produces (invoke name args...) when not required to produce an rvalue, and
+  # (call name args ... ) otherwise.
+  #
+  def dump_Function o
+    # somewhat ugly as Function hides its "ftype" instance variable
+    result = [o.instance_variable_get("@ftype") == :rvalue ? "call" : "invoke", do_dump(o.name)]
+    o.arguments.collect {|a| result << do_dump(a) }
+    result << do_dump(o.pblock) if o.pblock
+    result
+  end
+
+  def dump_MethodCall o
+    # somewhat ugly as Method call (does the same as function) and hides its "ftype" instance variable
+    result = [o.instance_variable_get("@ftype") == :rvalue ? "call-method" : "invoke-method",
+      [".", do_dump(o.receiver), do_dump(o.name)]]
+    o.arguments.collect {|a| result << do_dump(a) }
+    result << do_dump(o.lambda) if o.lambda
+    result
+  end
+
+  def dump_CaseStatement o
+    result = ["case", do_dump(o.test), :indent]
+    o.options.each do |s|
+      result << :break << do_dump(s)
+    end
+    result << :dedent
+  end
+
+  def dump_CaseOpt o
+    result = ["when"]
+    result << o.value.collect {|x| do_dump(x) }
+    # A bit of trickery to get it into the same shape as Pops output
+    if is_nop?(o.statements)
+      result << ["then", []]  # Puppet AST has a nop if there is no body
+    else
+      result << ["then", do_dump(o.statements) ]
+    end
+    result
+  end
+
+  def dump_ResourceInstance o
+    result = [do_dump(o.title), :indent]
+    o.parameters.each do |p|
+      result << :break << do_dump(p)
+    end
+    result << :dedent
+    result
+  end
+
+  def dump_ResourceDefaults o
+    result = ["resource-defaults", do_dump(o.type), :indent]
+    o.parameters.each do |p|
+      result << :break << do_dump(p)
+    end
+    result << :dedent
+    result
+  end
+
+  def dump_Resource o
+    if o.exported
+      form = 'exported-'
+    elsif o.virtual
+      form = 'virtual-'
+    else
+      form = ''
+    end
+    result = [form+"resource", do_dump(o.type), :indent]
+    o.instances.each do |b|
+      result << :break << do_dump(b)
+    end
+    result << :dedent
+    result
+  end
+
+  def dump_Selector o
+    values = o.values
+    values = [values] unless values.instance_of? AST::ASTArray or values.instance_of? Array
+    ["?", do_dump(o.param)] + values.collect {|x| do_dump(x) }
+  end
+
+  def dump_Object o
+    ['dev-error-no-polymorph-dump-for:', o.class.to_s, o.to_s]
+  end
+
+  def is_nop? o
+    o.nil? || o.is_a?(Model::Nop) || o.is_a?(AST::Nop)
+  end
+end
diff --git a/lib/puppet/pops/model/factory.rb b/lib/puppet/pops/model/factory.rb
new file mode 100644
index 0000000..ec2b055
--- /dev/null
+++ b/lib/puppet/pops/model/factory.rb
@@ -0,0 +1,807 @@
+# Factory is a helper class that makes construction of a Pops Model
+# much more convenient. It can be viewed as a small internal DSL for model
+# constructions.
+# For usage see tests using the factory.
+#
+# @todo All those uppercase methods ... they look bad in one way, but stand out nicely in the grammar...
+#   decide if they should change into lower case names (some of the are lower case)...
+#
+class Puppet::Pops::Model::Factory
+  Model = Puppet::Pops::Model
+
+  attr_accessor :current
+
+  # Shared build_visitor, since there are many instances of Factory being used
+  @@build_visitor = Puppet::Pops::Visitor.new(self, "build")
+  # Initialize a factory with a single object, or a class with arguments applied to build of
+  # created instance
+  #
+  def initialize popsobj, *args
+    @current = to_ops(popsobj, *args)
+  end
+
+  # Polymorphic build
+  def build(o, *args)
+    begin
+      @@build_visitor.visit_this(self, o, *args)
+    rescue =>e
+      # require 'debugger'; debugger # enable this when in trouble...
+      raise e
+    end
+  end
+
+  # Building of Model classes
+
+  def build_ArithmeticExpression(o, op, a, b)
+    o.operator = op
+    build_BinaryExpression(o, a, b)
+  end
+
+  def build_AssignmentExpression(o, op, a, b)
+    o.operator = op
+    build_BinaryExpression(o, a, b)
+  end
+
+  def build_AttributeOperation(o, name, op, value)
+    o.operator = op
+    o.attribute_name = name.to_s # BOOLEAN is allowed in the grammar
+    o.value_expr = build(value)
+    o
+  end
+
+  def build_AccessExpression(o, left, *keys)
+    o.left_expr = to_ops(left)
+    keys.each {|expr| o.addKeys(to_ops(expr)) }
+    o
+  end
+
+  def build_BinaryExpression(o, left, right)
+    o.left_expr = to_ops(left)
+    o.right_expr = to_ops(right)
+    o
+  end
+
+  def build_BlockExpression(o, *args)
+    args.each {|expr| o.addStatements(to_ops(expr)) }
+    o
+  end
+
+  def build_CollectExpression(o, type_expr, query_expr, attribute_operations)
+    o.type_expr = to_ops(type_expr)
+    o.query = build(query_expr)
+    attribute_operations.each {|op| o.addOperations(build(op)) }
+    o
+  end
+
+  def build_ComparisonExpression(o, op, a, b)
+    o.operator = op
+    build_BinaryExpression(o, a, b)
+  end
+
+  def build_ConcatenatedString(o, *args)
+    args.each {|expr| o.addSegments(build(expr)) }
+    o
+  end
+
+  def build_CreateTypeExpression(o, name, super_name = nil)
+    o.name = name
+    o.super_name = super_name
+    o
+  end
+
+  def build_CreateEnumExpression(o, *args)
+    o.name = args.slice(0) if args.size == 2
+    o.values = build(args.last)
+    o
+  end
+
+  def build_CreateAttributeExpression(o, name, datatype_expr)
+    o.name = name
+    o.type = to_ops(datatype_expr)
+    o
+  end
+
+  # @param name [String] a valid classname
+  # @param parameters [Array<Model::Parameter>] may be empty
+  # @param parent_class_name [String, nil] a valid classname referencing a parent class, optional.
+  # @param body [Array<Expression>, Expression, nil] expression that constitute the body
+  # @return [Model::HostClassDefinition] configured from the parameters
+  #
+  def build_HostClassDefinition(o, name, parameters, parent_class_name, body)
+    build_NamedDefinition(o, name, parameters, body)
+    o.parent_class = parent_class_name if parent_class_name
+    o
+  end
+
+  #  # @param name [String] a valid classname
+  #  # @param parameters [Array<Model::Parameter>] may be empty
+  #  # @param body [Array<Expression>, Expression, nil] expression that constitute the body
+  #  # @return [Model::HostClassDefinition] configured from the parameters
+  #  #
+  #  def build_ResourceTypeDefinition(o, name, parameters, body)
+  #    build_NamedDefinition(o, name, parameters, body)
+  #    o.name = name
+  #    parameters.each {|p| o.addParameters(build(p)) }
+  #    b = f_build_body(body)
+  #    o.body = b.current if b
+  #    o
+  #  end
+
+  def build_ResourceOverrideExpression(o, resources, attribute_operations)
+    o.resources = build(resources)
+    attribute_operations.each {|ao| o.addOperations(build(ao)) }
+    o
+  end
+
+  def build_KeyedEntry(o, k, v)
+    o.key = build(k)
+    o.value = build(v)
+    o
+  end
+
+  def build_LiteralHash(o, *keyed_entries)
+    keyed_entries.each {|entry| o.addEntries build(entry) }
+    o
+  end
+
+  def build_LiteralList(o, *values)
+    values.each {|v| o.addValues build(v) }
+    o
+  end
+
+  def build_LiteralNumber(o, val, radix)
+    o.value = val
+    o.radix = radix
+    o
+  end
+
+  def build_InstanceReferences(o, type_name, name_expressions)
+    o.type_name = build(type_name)
+    name_expressions.each {|n| o.addNames(build(n)) }
+    o
+  end
+
+  def build_ImportExpression(o, files)
+    # The argument files has already been built
+    files.each {|f| o.addFiles(to_ops(f)) }
+    o
+  end
+
+  def build_IfExpression(o, t, ift, els)
+    o.test = build(t)
+    o.then_expr = build(ift)
+    o.else_expr= build(els)
+    o
+  end
+
+  def build_MatchExpression(o, op, a, b)
+    o.operator = op
+    build_BinaryExpression(o, a, b)
+  end
+
+  # Builds body :) from different kinds of input
+  # @param body [nil] unchanged, produces nil
+  # @param body [Array<Expression>] turns into a BlockExpression
+  # @param body [Expression] produces the given expression
+  # @param body [Object] produces the result of calling #build with body as argument
+  def f_build_body(body)
+    case body
+    when NilClass
+      nil
+    when Array
+      Puppet::Pops::Model::Factory.new(Model::BlockExpression, *body)
+    else
+      build(body)
+    end
+  end
+
+  def build_Definition(o, parameters, body)
+    parameters.each {|p| o.addParameters(build(p)) }
+    b = f_build_body(body)
+    o.body = b.current if b
+    o
+  end
+
+  def build_NamedDefinition(o, name, parameters, body)
+    build_Definition(o, parameters, body)
+    o.name = name
+    o
+  end
+
+  # @param o [Model::NodeDefinition]
+  # @param hosts [Array<Expression>] host matches
+  # @param parent [Expression] parent node matcher
+  # @param body [Object] see {#f_build_body}
+  def build_NodeDefinition(o, hosts, parent, body)
+    hosts.each {|h| o.addHost_matches(build(h)) }
+    o.parent = build(parent) if parent # no nop here
+    b = f_build_body(body)
+    o.body = b.current if b
+    o
+  end
+
+  def build_Parameter(o, name, expr)
+    o.name = name
+    o.value = build(expr) if expr # don't build a nil/nop
+    o
+  end
+
+  def build_QualifiedReference(o, name)
+    o.value = name.to_s.downcase
+    o
+  end
+
+  def build_RelationshipExpression(o, op, a, b)
+    o.operator = op
+    build_BinaryExpression(o, a, b)
+  end
+
+  def build_ResourceExpression(o, type_name, bodies)
+    o.type_name = build(type_name)
+    bodies.each {|b| o.addBodies(build(b)) }
+    o
+  end
+
+  def build_ResourceBody(o, title_expression, attribute_operations)
+    o.title = build(title_expression)
+    attribute_operations.each {|ao| o.addOperations(build(ao)) }
+    o
+  end
+
+  def build_ResourceDefaultsExpression(o, type_ref, attribute_operations)
+    o.type_ref = build(type_ref)
+    attribute_operations.each {|ao| o.addOperations(build(ao)) }
+    o
+  end
+
+  def build_SelectorExpression(o, left, *selectors)
+    o.left_expr = to_ops(left)
+    selectors.each {|s| o.addSelectors(build(s)) }
+    o
+  end
+
+  def build_SelectorEntry(o, matching, value)
+    o.matching_expr = build(matching)
+    o.value_expr = build(value)
+    o
+  end
+
+  def build_QueryExpression(o, expr)
+    ops = to_ops(expr)
+    o.expr = ops unless Puppet::Pops::Model::Factory.nop? ops
+    o
+  end
+
+  def build_UnaryExpression(o, expr)
+    ops = to_ops(expr)
+    o.expr = ops unless Puppet::Pops::Model::Factory.nop? ops
+    o
+  end
+
+  def build_QualifiedName(o, name)
+    o.value = name.to_s
+    o
+  end
+
+  # Puppet::Pops::Model::Factory helpers
+  def f_build_unary(klazz, expr)
+    Puppet::Pops::Model::Factory.new(build(klazz.new, expr))
+  end
+
+  def f_build_binary_op(klazz, op, left, right)
+    Puppet::Pops::Model::Factory.new(build(klazz.new, op, left, right))
+  end
+
+  def f_build_binary(klazz, left, right)
+    Puppet::Pops::Model::Factory.new(build(klazz.new, left, right))
+  end
+
+  def f_build_vararg(klazz, left, *arg)
+    Puppet::Pops::Model::Factory.new(build(klazz.new, left, *arg))
+  end
+
+  def f_arithmetic(op, r)
+    f_build_binary_op(Model::ArithmeticExpression, op, current, r)
+  end
+
+  def f_comparison(op, r)
+    f_build_binary_op(Model::ComparisonExpression, op, current, r)
+  end
+
+  def f_match(op, r)
+    f_build_binary_op(Model::MatchExpression, op, current, r)
+  end
+
+  # Operator helpers
+  def in(r)     f_build_binary(Model::InExpression, current, r);          end
+
+  def or(r)     f_build_binary(Model::OrExpression, current, r);          end
+
+  def and(r)    f_build_binary(Model::AndExpression, current, r);         end
+
+  def not();    f_build_unary(Model::NotExpression, self);                end
+
+  def minus();  f_build_unary(Model::UnaryMinusExpression, self);         end
+
+  def text();   f_build_unary(Model::TextExpression, self);               end
+
+  def var();    f_build_unary(Model::VariableExpression, self);           end
+
+  def [](*r);   f_build_vararg(Model::AccessExpression, current, *r);     end
+
+  def dot r;    f_build_binary(Model::NamedAccessExpression, current, r); end
+
+  def + r;      f_arithmetic(:+, r);                                      end
+
+  def - r;      f_arithmetic(:-, r);                                      end
+
+  def / r;      f_arithmetic(:/, r);                                      end
+
+  def * r;      f_arithmetic(:*, r);                                      end
+
+  def % r;      f_arithmetic(:%, r);                                      end
+
+  def << r;     f_arithmetic(:<<, r);                                     end
+
+  def >> r;     f_arithmetic(:>>, r);                                     end
+
+  def < r;      f_comparison(:<, r);                                      end
+
+  def <= r;     f_comparison(:<=, r);                                     end
+
+  def > r;      f_comparison(:>, r);                                      end
+
+  def >= r;     f_comparison(:>=, r);                                     end
+
+  def == r;     f_comparison(:==, r);                                     end
+
+  def ne r;     f_comparison(:'!=', r);                                   end
+
+  def =~ r;     f_match(:'=~', r);                                        end
+
+  def mne r;    f_match(:'!~', r);                                        end
+
+  def paren();  f_build_unary(Model::ParenthesizedExpression, current);   end
+
+  def relop op, r
+    f_build_binary_op(Model::RelationshipExpression, op.to_sym, current, r)
+  end
+
+  def select *args
+    Puppet::Pops::Model::Factory.new(build(Model::SelectorExpression, current, *args))
+  end
+
+  # For CaseExpression, setting the default for an already build CaseExpression
+  def default r
+    current.addOptions(Puppet::Pops::Model::Factory.WHEN(:default, r).current)
+    self
+  end
+
+  def lambda=(lambda)
+    current.lambda = lambda.current
+    self
+  end
+
+  # Assignment =
+  def set(r)
+    f_build_binary_op(Model::AssignmentExpression, :'=', current, r)
+  end
+
+  # Assignment +=
+  def plus_set(r)
+    f_build_binary_op(Model::AssignmentExpression, :'+=', current, r)
+  end
+
+  def attributes(*args)
+    args.each {|a| current.addAttributes(build(a)) }
+    self
+  end
+
+  # Catch all delegation to current
+  def method_missing(meth, *args, &block)
+    if current.respond_to?(meth)
+      current.send(meth, *args, &block)
+    else
+      super
+    end
+  end
+
+  def respond_to?(meth)
+    current.respond_to?(meth) || super
+  end
+
+  # Records the position (start -> end) and computes the resulting length.
+  #
+  def record_position(start_pos, end_pos)
+    unless start_pos.offset
+      require 'debugger'; debugger; 1
+    end
+    Puppet::Pops::Adapters::SourcePosAdapter.adapt(current) do |a|
+      a.line   = start_pos.line
+      a.offset = start_pos.offset
+      a.pos    = start_pos.pos
+      a.length = start_pos.length
+      if(end_pos.offset && end_pos.length)
+        a.length = end_pos.offset + end_pos.length - start_pos.offset
+      end
+    end
+    self
+  end
+
+  # Records the origin file of an element
+  # Does nothing if file is nil.
+  #
+  # @param file [String,nil] the file/path to the origin, may contain URI scheme of file: or some other URI scheme
+  # @returns [Factory] returns self
+  #
+  def record_origin(file)
+    return self unless file
+    Puppet::Pops::Adapters::OriginAdapter.adapt(current) do |a|
+       a.origin = file
+    end
+    self
+  end
+
+  # @return [Puppet::Pops::Adapters::SourcePosAdapter] with location information
+  def loc()
+    Puppet::Pops::Adapters::SourcePosAdapter.adapt(current)
+  end
+
+  # Returns documentation string, or nil if not available
+  # @return [String, nil] associated documentation if available
+  def doc()
+    a = Puppet::Pops::Adapters::SourcePosAdapter.adapt(current)
+    return a.documentation if a
+    nil
+  end
+
+  def doc=(doc_string)
+    a = Puppet::Pops::Adapters::SourcePosAdapter.adapt(current)
+    a.documentation = doc_string
+  end
+
+  # Returns symbolic information about a expected share of a resource expression given the LHS of a resource expr.
+  #
+  # * `name { }` => `:resource`,  create a resource of the given type
+  # * `Name { }` => ':defaults`, set defauls for the referenced type
+  # * `Name[] { }` => `:override`, ioverrides nstances referenced by LHS
+  # * _any other_ => ':error', all other are considered illegal
+  #
+  def self.resource_shape(expr)
+    expr = expr.current if expr.is_a?(Puppet::Pops::Model::Factory)
+    case expr
+    when Model::QualifiedName
+      :resource
+    when Model::QualifiedReference
+      :defaults
+    when Model::AccessExpression
+      :override
+    when 'class'
+      :class
+    else
+      :error
+    end
+  end
+  # Factory starting points
+
+  def self.literal(o);                   new(o);                                                 end
+
+  def self.minus(o);                     new(o).minus;                                           end
+
+  def self.var(o);                       new(o).var;                                             end
+
+  def self.block(*args);                 new(Model::BlockExpression, *args);                     end
+
+  def self.string(*args);                new(Model::ConcatenatedString, *args);                  end
+
+  def self.text(o);                      new(o).text;                                            end
+
+  def self.IF(test_e,then_e,else_e);     new(Model::IfExpression, test_e, then_e, else_e);       end
+
+  def self.UNLESS(test_e,then_e,else_e); new(Model::UnlessExpression, test_e, then_e, else_e);   end
+
+  def self.CASE(test_e,*options);        new(Model::CaseExpression, test_e, *options);           end
+
+  def self.WHEN(values_list, block);     new(Model::CaseOption, values_list, block);             end
+
+  def self.MAP(match, value);            new(Model::SelectorEntry, match, value);                end
+
+  def self.TYPE(name, super_name=nil);   new(Model::CreateTypeExpression, name, super_name);     end
+
+  def self.ATTR(name, type_expr=nil);    new(Model::CreateAttributeExpression, name, type_expr); end
+
+  def self.ENUM(*args);                  new(Model::CreateEnumExpression, *args);                end
+
+  def self.KEY_ENTRY(key, val);          new(Model::KeyedEntry, key, val);                       end
+
+  def self.HASH(entries);                new(Model::LiteralHash, *entries);                      end
+
+  def self.LIST(entries);                new(Model::LiteralList, *entries);                      end
+
+  def self.PARAM(name, expr=nil);        new(Model::Parameter, name, expr);                      end
+
+  def self.NODE(hosts, parent, body);    new(Model::NodeDefinition, hosts, parent, body);        end
+
+  # Creates a QualifiedName representation of o, unless o already represents a QualifiedName in which
+  # case it is returned.
+  #
+  def self.fqn(o)
+    o = o.current if o.is_a?(Puppet::Pops::Model::Factory)
+    o = new(Model::QualifiedName, o) unless o.is_a? Model::QualifiedName
+    o
+  end
+
+  # Creates a QualifiedName representation of o, unless o already represents a QualifiedName in which
+  # case it is returned.
+  #
+  def self.fqr(o)
+    o = o.current if o.is_a?(Puppet::Pops::Model::Factory)
+    o = new(Model::QualifiedReference, o) unless o.is_a? Model::QualifiedReference
+    o
+  end
+
+  def self.TEXT(expr)
+    new(Model::TextExpression, expr)
+  end
+
+  # TODO: This is the same a fqn factory method, don't know if callers to fqn and QNAME can live with the
+  # same result or not yet - refactor into one method when decided.
+  #
+  def self.QNAME(name)
+    new(Model::QualifiedName, name)
+  end
+
+  # Convert input string to either a qualified name, or a LiteralNumber with radix
+  #
+  def self.QNAME_OR_NUMBER(name)
+    if n_radix = Puppet::Pops::Utils.to_n_with_radix(name)
+      new(Model::LiteralNumber, *n_radix)
+    else
+      new(Model::QualifiedName, name)
+    end
+  end
+
+  def self.QREF(name)
+    new(Model::QualifiedReference, name)
+  end
+
+  def self.VIRTUAL_QUERY(query_expr)
+    new(Model::VirtualQuery, query_expr)
+  end
+
+  def self.EXPORTED_QUERY(query_expr)
+    new(Model::ExportedQuery, query_expr)
+  end
+
+  # Used by regular grammar, egrammar creates an AccessExpression instead, and evaluation determines
+  # if access is to instances or something else.
+  #
+  def self.INSTANCE(type_name, name_expressions)
+    new(Model::InstanceReferences, type_name, name_expressions)
+  end
+
+  def self.ATTRIBUTE_OP(name, op, expr)
+    new(Model::AttributeOperation, name, op, expr)
+  end
+
+  def self.CALL_NAMED(name, rval_required, argument_list)
+    unless name.kind_of?(Model::PopsObject)
+      name = Puppet::Pops::Model::Factory.fqn(name) unless name.is_a?(Puppet::Pops::Model::Factory)
+    end
+    new(Model::CallNamedFunctionExpression, name, rval_required, *argument_list)
+  end
+
+  def self.CALL_METHOD(functor, argument_list)
+    new(Model::CallMethodExpression, functor, true, nil, *argument_list)
+  end
+
+  def self.COLLECT(type_expr, query_expr, attribute_operations)
+    new(Model::CollectExpression, Puppet::Pops::Model::Factory.fqr(type_expr), query_expr, attribute_operations)
+  end
+
+  def self.IMPORT(files)
+    new(Model::ImportExpression, files)
+  end
+
+  def self.NAMED_ACCESS(type_name, bodies)
+    new(Model::NamedAccessExpression, type_name, bodies)
+  end
+
+  def self.RESOURCE(type_name, bodies)
+    new(Model::ResourceExpression, type_name, bodies)
+  end
+
+  def self.RESOURCE_DEFAULTS(type_name, attribute_operations)
+    new(Model::ResourceDefaultsExpression, type_name, attribute_operations)
+  end
+
+  def self.RESOURCE_OVERRIDE(resource_ref, attribute_operations)
+    new(Model::ResourceOverrideExpression, resource_ref, attribute_operations)
+  end
+
+  def self.RESOURCE_BODY(resource_title, attribute_operations)
+    new(Model::ResourceBody, resource_title, attribute_operations)
+  end
+
+  # Builds a BlockExpression if args size > 1, else the single expression/value in args
+  def self.block_or_expression(*args)
+    if args.size > 1
+      new(Model::BlockExpression, *args)
+    else
+      new(args[0])
+    end
+  end
+
+  def self.HOSTCLASS(name, parameters, parent, body)
+    new(Model::HostClassDefinition, name, parameters, parent, body)
+  end
+
+  def self.DEFINITION(name, parameters, body)
+    new(Model::ResourceTypeDefinition, name, parameters, body)
+  end
+
+  def self.LAMBDA(parameters, body)
+    new(Model::LambdaExpression, parameters, body)
+  end
+
+  def self.nop? o
+    o.nil? || o.is_a?(Puppet::Pops::Model::Nop)
+  end
+
+  # Transforms an array of expressions containing literal name expressions to calls if followed by an
+  # expression, or expression list. Also transforms a "call" to `import` into an ImportExpression.
+  #
+  def self.transform_calls(expressions)
+    expressions.reduce([]) do |memo, expr|
+      expr = expr.current if expr.is_a?(Puppet::Pops::Model::Factory)
+      name = memo[-1]
+      if name.is_a? Model::QualifiedName
+        if name.value() == 'import'
+          memo[-1] = Puppet::Pops::Model::Factory.IMPORT(expr.is_a?(Array) ? expr : [expr])
+        else
+          memo[-1] = Puppet::Pops::Model::Factory.CALL_NAMED(name, false, expr.is_a?(Array) ? expr : [expr])
+        end
+      else
+        memo << expr
+      end
+      if expr.is_a?(Model::CallNamedFunctionExpression)
+        # patch expression function call to statement style
+        # TODO: This is kind of meaningless, but to make it compatible...
+        expr.rval_required = false
+      end
+      memo
+    end
+
+  end
+
+  # Building model equivalences of Ruby objects
+  # Allows passing regular ruby objects to the factory to produce instructions
+  # that when evaluated produce the same thing.
+
+  def build_String(o)
+    x = Model::LiteralString.new
+    x.value = o;
+    x
+  end
+
+  def build_NilClass(o)
+    x = Model::Nop.new
+    x
+  end
+
+  def build_TrueClass(o)
+    x = Model::LiteralBoolean.new
+    x.value = o
+    x
+  end
+
+  def build_FalseClass(o)
+    x = Model::LiteralBoolean.new
+    x.value = o
+    x
+  end
+
+  def build_Fixnum(o)
+    x = Model::LiteralNumber.new
+    x.value = o;
+    x
+  end
+
+  def build_Float(o)
+    x = Model::LiteralNumber.new
+    x.value = o;
+    x
+  end
+
+  def build_Regexp(o)
+    x = Model::LiteralRegularExpression.new
+    x.value = o;
+    x
+  end
+
+  # If building a factory, simply unwrap the model oject contained in the factory.
+  def build_Factory(o)
+    o.current
+  end
+
+  # Creates a String literal, unless the symbol is one of the special :undef, or :default
+  # which instead creates a LiterlUndef, or a LiteralDefault.
+  def build_Symbol(o)
+    case o
+    when :undef
+      Model::LiteralUndef.new
+    when :default
+      Model::LiteralDefault.new
+    else
+      build_String(o.to_s)
+    end
+  end
+
+  # Creates a LiteralList instruction from an Array, where the entries are built.
+  def build_Array(o)
+    x = Model::LiteralList.new
+    o.each { |v| x.addValues(build(v)) }
+    x
+  end
+
+  # Create a LiteralHash instruction from a hash, where keys and values are built
+  # The hash entries are added in sorted order based on key.to_s
+  #
+  def build_Hash(o)
+    x = Model::LiteralHash.new
+    (o.sort_by {|k,v| k.to_s}).each {|k,v| x.addEntries(build(Model::KeyedEntry.new, k, v)) }
+    x
+  end
+
+  # @param rval_required [Boolean] if the call must produce a value
+  def build_CallExpression(o, functor, rval_required, *args)
+    o.functor_expr = to_ops(functor)
+    o.rval_required = rval_required
+    args.each {|x| o.addArguments(to_ops(x)) }
+    o
+  end
+
+  #  # @param rval_required [Boolean] if the call must produce a value
+  #  def build_CallNamedFunctionExpression(o, name, rval_required, *args)
+  #    build_CallExpression(o, name, rval_required, *args)
+  ##    o.functor_expr = build(name)
+  ##    o.rval_required = rval_required
+  ##    args.each {|x| o.addArguments(build(x)) }
+  #    o
+  #  end
+
+  def build_CallMethodExpression(o, functor, rval_required, lambda, *args)
+    build_CallExpression(o, functor, rval_required, *args)
+    o.lambda = lambda
+    o
+  end
+
+  def build_CaseExpression(o, test, *args)
+    o.test = build(test)
+    args.each {|opt| o.addOptions(build(opt)) }
+    o
+  end
+
+  def build_CaseOption(o, value_list, then_expr)
+    value_list = [value_list] unless value_list.is_a? Array
+    value_list.each { |v| o.addValues(build(v)) }
+    b = f_build_body(then_expr)
+    o.then_expr = to_ops(b) if b
+    o
+  end
+
+  # Build a Class by creating an instance of it, and then calling build on the created instance
+  # with the given arguments
+  def build_Class(o, *args)
+    build(o.new(), *args)
+  end
+
+  # Checks if the object is already a model object, or build it
+  def to_ops(o, *args)
+    if o.kind_of?(Model::PopsObject)
+      o
+    else
+      build(o, *args)
+    end
+  end
+end
diff --git a/lib/puppet/pops/model/model.rb b/lib/puppet/pops/model/model.rb
new file mode 100644
index 0000000..82a9490
--- /dev/null
+++ b/lib/puppet/pops/model/model.rb
@@ -0,0 +1,567 @@
+#
+# The Puppet Pops Metamodel
+#
+# This module contains a formal description of the Puppet Pops (*P*uppet *OP*eration instruction*S*).
+# It describes a Metamodel containing DSL instructions, a description of PuppetType and related
+# classes needed to evaluate puppet logic.
+# The metamodel resembles the existing AST model, but it is a semantic model of instructions and
+# the types that they operate on rather than a Abstract Syntax Tree, although closely related.
+#
+# The metamodel is anemic (has no behavior) except basic datatype and type
+# assertions and reference/containment assertions.
+# The metamodel is also a generalized description of the Puppet DSL to enable the
+# same metamodel to be used to express Puppet DSL models (instances) with different semantics as
+# the language evolves.
+#
+# The metamodel is concretized by a validator for a particular version of
+# the Puppet DSL language.
+#
+# This metamodel is expressed using RGen.
+#
+# TODO: Anonymous Enums - probably ok, but they can be named (don't know if that is meaningsful)
+
+require 'rgen/metamodel_builder'
+
+module Puppet::Pops::Model
+  # A base class for modeled objects that makes them Visitable, and Adaptable.
+  # @todo currently  includes Containment which will not be needed when the corresponding methods
+  #   are added to RGen (in some version after 0.6.2).
+  #
+  class PopsObject < RGen::MetamodelBuilder::MMBase
+    include Puppet::Pops::Visitable
+    include Puppet::Pops::Adaptable
+    include Puppet::Pops::Containment
+    abstract
+  end
+
+  # @abstract base class for expressions
+  class Expression < PopsObject
+    abstract
+  end
+
+  # A Nop - the "no op" expression.
+  # @note not really needed since the evaluator can evaluate nil with the meaning of NoOp
+  # @todo deprecate? May be useful if there is the need to differentiate between nil and Nop when transforming model.
+  #
+  class Nop < Expression
+  end
+
+  # A binary expression is abstract and has a left and a right expression. The order of evaluation
+  # and semantics are determined by the concrete subclass.
+  #
+  class BinaryExpression < Expression
+    abstract
+    #
+    # @!attribute [rw] left_expr
+    #   @return [Expression]
+    contains_one_uni 'left_expr', Expression, :lowerBound => 1
+    contains_one_uni 'right_expr', Expression, :lowerBound => 1
+  end
+
+  # An unary expression is abstract and contains one expression. The semantics are determined by
+  # a concrete subclass.
+  #
+  class UnaryExpression < Expression
+    abstract
+    contains_one_uni 'expr', Expression, :lowerBound => 1
+  end
+
+  # A class that simply evaluates to the contained expression.
+  # It is of value in order to preserve user entered parentheses in transformations, and
+  # transformations from model to source.
+  #
+  class ParenthesizedExpression < UnaryExpression; end
+
+  # An import of one or several files.
+  #
+  class ImportExpression < Expression
+    contains_many_uni 'files', Expression, :lowerBound => 1
+  end
+
+  # A boolean not expression, reversing the truth of the unary expr.
+  #
+  class NotExpression < UnaryExpression; end
+
+  # An arithmetic expression reversing the polarity of the numeric unary expr.
+  #
+  class UnaryMinusExpression < UnaryExpression; end
+
+  # An assignment expression assigns a value to the lval() of the left_expr.
+  #
+  class AssignmentExpression < BinaryExpression
+    has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'=', :'+=']), :lowerBound => 1
+  end
+
+  # An arithmetic expression applies an arithmetic operator on left and right expressions.
+  #
+  class ArithmeticExpression < BinaryExpression
+    has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'+', :'-', :'*', :'%', :'/', :'<<', :'>>' ]), :lowerBound => 1
+  end
+
+  # A relationship expression associates the left and right expressions
+  #
+  class RelationshipExpression < BinaryExpression
+    has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'->', :'<-', :'~>', :'<~']), :lowerBound => 1
+  end
+
+  # A binary expression, that accesses the value denoted by right in left. i.e. typically
+  # expressed concretely in a language as left[right].
+  #
+  class AccessExpression < Expression
+    contains_one_uni 'left_expr', Expression, :lowerBound => 1
+    contains_many_uni 'keys', Expression, :lowerBound => 1
+  end
+
+  # A comparison expression compares left and right using a comparison operator.
+  #
+  class ComparisonExpression < BinaryExpression
+    has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'==', :'!=', :'<', :'>', :'<=', :'>=' ]), :lowerBound => 1
+  end
+
+  # A match expression matches left and right using a matching operator.
+  #
+  class MatchExpression < BinaryExpression
+    has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'!~', :'=~']), :lowerBound => 1
+  end
+
+  # An 'in' expression checks if left is 'in' right
+  #
+  class InExpression < BinaryExpression; end
+
+  # A boolean expression applies a logical connective operator (and, or) to left and right expressions.
+  #
+  class BooleanExpression < BinaryExpression
+    abstract
+  end
+
+  # An and expression applies the logical connective operator and to left and right expression
+  # and does not evaluate the right expression if the left expression is false.
+  #
+  class AndExpression < BooleanExpression; end
+
+  # An or expression applies the logical connective operator or to the left and right expression
+  # and does not evaluate the right expression if the left expression is true
+  #
+  class OrExpression < BooleanExpression; end
+
+  # A literal list / array containing 0:M expressions.
+  #
+  class LiteralList < Expression
+    contains_many_uni 'values', Expression
+  end
+
+  # A Keyed entry has a key and a value expression. It it typically used as an entry in a Hash.
+  #
+  class KeyedEntry < PopsObject
+    contains_one_uni 'key', Expression, :lowerBound => 1
+    contains_one_uni 'value', Expression, :lowerBound => 1
+  end
+
+  # A literal hash is a collection of KeyedEntry objects
+  #
+  class LiteralHash < Expression
+    contains_many_uni 'entries', KeyedEntry
+  end
+
+  # A block contains a list of expressions
+  #
+  class BlockExpression < Expression
+    contains_many_uni 'statements', Expression
+  end
+
+  # A case option entry in a CaseStatement
+  #
+  class CaseOption < Expression
+    contains_many_uni 'values', Expression, :lowerBound => 1
+    contains_one_uni 'then_expr', Expression, :lowerBound => 1
+  end
+
+  # A case expression has a test, a list of options (multi values => block map).
+  # One CaseOption may contain a LiteralDefault as value. This option will be picked if nothing
+  # else matched.
+  #
+  class CaseExpression < Expression
+    contains_one_uni 'test', Expression, :lowerBound => 1
+    contains_many_uni 'options', CaseOption
+  end
+
+  # A query expression is an expression that is applied to some collection.
+  # The contained optional expression may contain different types of relational expressions depending
+  # on what the query is applied to.
+  #
+  class QueryExpression < Expression
+    abstract
+    contains_one_uni 'expr', Expression, :lowerBound => 0
+  end
+
+  # An exported query is a special form of query that searches for exported objects.
+  #
+  class ExportedQuery < QueryExpression
+  end
+
+  # A virtual query is a special form of query that searches for virtual objects.
+  #
+  class VirtualQuery < QueryExpression
+  end
+
+  # An attribute operation sets or appends a value to a named attribute.
+  #
+  class AttributeOperation < PopsObject
+    has_attr 'attribute_name', String, :lowerBound => 1
+    has_attr 'operator', RGen::MetamodelBuilder::DataTypes::Enum.new([:'=>', :'+>', ]), :lowerBound => 1
+    contains_one_uni 'value_expr', Expression, :lowerBound => 1
+  end
+
+  # An optional attribute operation sets or appends a value to a named attribute unless
+  # the value is undef/nil in which case the opereration is a Nop.
+  #
+  # This is a new feature proposed to solve the undef as antimatter problem
+  # @note Currently Unused
+  #
+  class OptionalAttributeOperation < AttributeOperation
+  end
+
+  # An object that collects stored objects from the central cache and returns
+  # them to the current host. Operations may optionally be applied.
+  #
+  class CollectExpression < Expression
+    contains_one_uni 'type_expr', Expression, :lowerBound => 1
+    contains_one_uni 'query', QueryExpression, :lowerBound => 1
+    contains_many_uni 'operations', AttributeOperation
+  end
+
+  class Parameter < PopsObject
+    has_attr 'name', String, :lowerBound => 1
+    contains_one_uni 'value', Expression
+  end
+
+  # Abstract base class for definitions.
+  #
+  class Definition < Expression
+    abstract
+    contains_many_uni 'parameters', Parameter
+    contains_one_uni 'body', Expression
+  end
+
+  # Abstract base class for named definitions.
+  class NamedDefinition < Definition
+    abstract
+    has_attr 'name', String, :lowerBound => 1
+  end
+
+  # A resource type definition (a 'define' in the DSL).
+  #
+  class ResourceTypeDefinition < NamedDefinition
+    # FUTURE
+    # contains_one_uni 'producer', Producer
+  end
+
+  # A node definition matches hosts using Strings, or Regular expressions. It may inherit from
+  # a parent node (also using a String or Regular expression).
+  #
+  class NodeDefinition < Expression
+    contains_one_uni 'parent', Expression
+    contains_many_uni 'host_matches', Expression, :lowerBound => 1
+    contains_one_uni 'body', Expression
+  end
+
+  # A class definition
+  #
+  class HostClassDefinition < NamedDefinition
+    has_attr 'parent_class', String
+  end
+
+  # i.e {|parameters| body }
+  class LambdaExpression < Definition; end
+
+  # If expression. If test is true, the then_expr part should be evaluated, else the (optional)
+  # else_expr. An 'elsif' is simply an else_expr = IfExpression, and 'else' is simply else == Block.
+  # a 'then' is typically a Block.
+  #
+  class IfExpression < Expression
+    contains_one_uni 'test', Expression, :lowerBound => 1
+    contains_one_uni 'then_expr', Expression, :lowerBound => 1
+    contains_one_uni 'else_expr', Expression
+  end
+
+  # An if expression with boolean reversed test.
+  #
+  class UnlessExpression < IfExpression
+  end
+
+  # An abstract call.
+  #
+  class CallExpression < Expression
+    abstract
+    # A bit of a crutch; functions are either procedures (void return) or has an rvalue
+    # this flag tells the evaluator that it is a failure to call a function that is void/procedure
+    # where a value is expected.
+    #
+    has_attr 'rval_required', Boolean, :defaultValueLiteral => "false"
+    contains_one_uni 'functor_expr', Expression, :lowerBound => 1
+    contains_many_uni 'arguments', Expression
+    contains_one_uni 'lambda', Expression
+  end
+
+  # A function call where the functor_expr should evaluate to something callable.
+  #
+  class CallFunctionExpression < CallExpression; end
+
+  # A function call where the given functor_expr should evaluate to the name
+  # of a function.
+  #
+  class CallNamedFunctionExpression < CallExpression; end
+
+  # A method/function call where the function expr is a NamedAccess and with support for
+  # an optional lambda block
+  #
+  class CallMethodExpression < CallExpression
+  end
+
+  # Abstract base class for literals.
+  #
+  class Literal < Expression
+    abstract
+  end
+
+  # A literal value is an abstract value holder. The type of the contained value is
+  # determined by the concrete subclass.
+  #
+  class LiteralValue < Literal
+    abstract
+    has_attr 'value', Object, :lowerBound => 1
+  end
+
+  # A Regular Expression Literal.
+  #
+  class LiteralRegularExpression < LiteralValue; end
+
+  # A Literal String
+  #
+  class LiteralString < LiteralValue; end
+
+  # A literal text is like a literal string, but has other rules for escaped characters. It
+  # is used as part of a ConcatenatedString
+  #
+  class LiteralText < LiteralValue; end
+
+  # A literal number has a radix of decimal (10), octal (8), or hex (16) to enable string conversion with the input radix.
+  # By default, a radix of 10 is used.
+  #
+  class LiteralNumber < LiteralValue
+    has_attr 'radix', Integer, :lowerBound => 1, :defaultValueLiteral => "10"
+  end
+
+  # The DSL `undef`.
+  #
+  class LiteralUndef < Literal; end
+
+  # The DSL `default`
+  class LiteralDefault < Literal; end
+
+  # DSL `true` or `false`
+  class LiteralBoolean < LiteralValue; end
+
+  # A text expression is an interpolation of an expression. If the embedded expression is
+  # a QualifiedName, it it taken as a variable name and resolved. All other expressions are evaluated.
+  # The result is transformed to a string.
+  #
+  class TextExpression < UnaryExpression; end
+
+  # An interpolated/concatenated string. The contained segments are expressions. Verbatim sections
+  # should be LiteralString instances, and interpolated expressions should either be
+  # TextExpression instances (if QualifiedNames should be turned into variables), or any other expression
+  # if such treatment is not needed.
+  #
+  class ConcatenatedString < Expression
+    contains_many_uni 'segments', Expression
+  end
+
+  # A DSL NAME (one or multiple parts separated by '::').
+  #
+  class QualifiedName < LiteralValue; end
+
+  # A DSL CLASSREF (one or multiple parts separated by '::' where (at least) the first part starts with an upper case letter).
+  #
+  class QualifiedReference < LiteralValue; end
+
+  # A Variable expression looks up value of expr (some kind of name) in scope.
+  # The expression is typically a QualifiedName, or QualifiedReference.
+  #
+  class VariableExpression < UnaryExpression; end
+
+  # A type reference is a reference to a type.
+  #
+  class TypeReference < Expression
+    contains_one_uni 'type_name', QualifiedReference, :lowerBound => 1
+  end
+
+  # An instance reference is a reference to one or many named instances of a particular type
+  #
+  class InstanceReferences < TypeReference
+    contains_many_uni 'names', Expression, :lowerBound => 1
+  end
+
+  # A resource body describes one resource instance
+  #
+  class ResourceBody < PopsObject
+    contains_one_uni 'title', Expression
+    contains_many_uni 'operations', AttributeOperation
+  end
+
+  # An abstract resource describes the form of the resource (regular, virtual or exported)
+  # and adds convenience methods to ask if it is virtual or exported.
+  # All derived classes may not support all forms, and these needs to be validated
+  #
+  class AbstractResource < Expression
+    has_attr 'form', RGen::MetamodelBuilder::DataTypes::Enum.new([:regular, :virtual, :exported ]), :lowerBound => 1, :defaultValueLiteral => "regular"
+    has_attr 'virtual', Boolean, :derived => true
+    has_attr 'exported', Boolean, :derived => true
+
+    module ClassModule
+      def virtual_derived
+        form == :virtual || form == :exported
+      end
+
+      def exported_derived
+        form == :exported
+      end
+    end
+
+  end
+
+  # A resource expression is used to instantiate one or many resource. Resources may optionally
+  # be virtual or exported, an exported resource is always virtual.
+  #
+  class ResourceExpression < AbstractResource
+    contains_one_uni 'type_name', Expression, :lowerBound => 1
+    contains_many_uni 'bodies', ResourceBody
+  end
+
+  # A resource defaults sets defaults for a resource type. This class inherits from AbstractResource
+  # but does only support the :regular form (this is intentional to be able to produce better error messages
+  # when illegal forms are applied to a model.
+  #
+  class ResourceDefaultsExpression < AbstractResource
+    contains_one_uni 'type_ref', QualifiedReference
+    contains_many_uni 'operations', AttributeOperation
+  end
+
+  # A resource override overrides already set values.
+  #
+  class ResourceOverrideExpression < Expression
+    contains_one_uni 'resources', Expression, :lowerBound => 1
+    contains_many_uni 'operations', AttributeOperation
+  end
+
+  # A selector entry describes a map from matching_expr to value_expr.
+  #
+  class SelectorEntry < PopsObject
+    contains_one_uni 'matching_expr', Expression, :lowerBound => 1
+    contains_one_uni 'value_expr', Expression, :lowerBound => 1
+  end
+
+  # A selector expression represents a mapping from a left_expr to a matching SelectorEntry.
+  #
+  class SelectorExpression < Expression
+    contains_one_uni 'left_expr', Expression, :lowerBound => 1
+    contains_many_uni 'selectors', SelectorEntry
+  end
+
+  # Create Invariant. Future suggested enhancement Puppet Types.
+  #
+  class CreateInvariantExpression < Expression
+    has_attr 'name', String
+    contains_one_uni 'message_expr', Expression, :lowerBound => 1
+    contains_one_uni 'constraint_expr', Expression, :lowerBound => 1
+  end
+
+  # Create Attribute. Future suggested enhancement Puppet Types.
+  #
+  class CreateAttributeExpression < Expression
+    has_attr 'name', String, :lowerBound => 1
+
+    # Should evaluate to name of datatype (String, Integer, Float, Boolean) or an EEnum metadata
+    # (created by CreateEnumExpression). If omitted, the type is a String.
+    #
+    contains_one_uni 'type', Expression
+    contains_one_uni 'min_expr', Expression
+    contains_one_uni 'max_expr', Expression
+    contains_one_uni 'default_value', Expression
+    contains_one_uni 'input_transformer', Expression
+    contains_one_uni 'derived_expr', Expression
+  end
+
+  # Create Attribute. Future suggested enhancement Puppet Types.
+  #
+  class CreateEnumExpression < Expression
+    has_attr 'name', String
+    contains_one_uni 'values', Expression
+  end
+
+  # Create Type. Future suggested enhancement Puppet Types.
+  #
+  class CreateTypeExpression < Expression
+    has_attr 'name', String, :lowerBound => 1
+    has_attr 'super_name', String
+    contains_many_uni 'attributes', CreateAttributeExpression
+    contains_many_uni 'invariants', CreateInvariantExpression
+  end
+
+  # Create ResourceType. Future suggested enhancement Puppet Types.
+  # @todo UNFINISHED
+  #
+  class CreateResourceType < CreateTypeExpression
+    # TODO CreateResourceType
+    # - has features required by the provider - provider invariant?
+    # - super type must be a ResourceType
+  end
+
+  # A named access expression looks up a named part. (e.g. $a.b)
+  #
+  class NamedAccessExpression < BinaryExpression; end
+
+  # A named function definition declares and defines a new function
+  # Future enhancement.
+  #
+  class NamedFunctionDefinition < NamedDefinition; end
+
+  # Future enhancements - Injection - Unfinished
+  #
+  module Injection
+    # A producer expression produces an instance of a type. The instance is initialized
+    # from an expression (or from the current scope if this expression is missing).
+    #--
+    # new. to handle production of injections
+    #
+    class Producer < Expression
+      contains_one_uni 'type_name', TypeReference, :lowerBound => 1
+      contains_one_uni 'instantiation_expr', Expression
+    end
+
+    # A binding entry binds one capability generically or named, specifies default bindings or
+    # composition of other bindings.
+    #
+    class BindingEntry < PopsObject
+      contains_one_uni 'key', Expression
+      contains_one_uni 'value', Expression
+    end
+
+    # Defines an optionally named binding.
+    #
+    class Binding < Expression
+      contains_one_uni 'title_expr', Expression
+      contains_many_uni 'bindings', BindingEntry
+    end
+
+    # An injection provides a value bound in the effective binding scope. The injection
+    # is based on a type (a capability) and an optional list of instance names (i.e. an InstanceReference).
+    # Invariants: optional and instantiation are mutually exclusive
+    #
+    class InjectExpression < Expression
+      has_attr 'optional', Boolean
+      contains_one_uni 'binding', Expression, :lowerBound => 1
+      contains_one_uni 'instantiation', Expression
+    end
+  end
+end
diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb
new file mode 100644
index 0000000..b48e7b4
--- /dev/null
+++ b/lib/puppet/pops/model/model_label_provider.rb
@@ -0,0 +1,75 @@
+# A provider of labels for model object, producing a human name for the model object.
+# As an example, if object is an ArithmeticExpression with operator +, `#a_an(o)` produces "a '+' Expression",
+# #the(o) produces "the + Expression", and #label produces "+ Expression".
+#
+class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider
+  def initialize
+    @@label_visitor ||= Puppet::Pops::Visitor.new(self,"label",0,0)
+  end
+
+  # Produces a label for the given objects type/operator without article.
+  def label o
+   @@label_visitor.visit(o)
+  end
+
+  def label_Factory o                     ; label(o.current)                    end
+  def label_Array o                       ; "Array Object"                      end
+  def label_LiteralNumber o               ; "Literal Number"                    end
+  def label_ArithmeticExpression o        ; "'#{o_operator}' expression"        end
+  def label_AccessExpression o            ; "'[]' expression"                   end
+  def label_MatchExpression o             ; "'#{o.operator}' expression"        end
+  def label_CollectExpression o           ; label(o.query)                      end
+  def label_ExportedQuery o               ; "Exported Query"                    end
+  def label_VirtualQuery o                ; "Virtual Query"                     end
+  def label_QueryExpression o             ; "Collect Query"                     end
+  def label_ComparisonExpression o        ; "'#{o.operator}' expression"        end
+  def label_AndExpression o               ; "'and' expression"                  end
+  def label_OrExpression o                ; "'or' expression"                   end
+  def label_InExpression o                ; "'in' expression"                   end
+  def label_ImportExpression o            ; "'import' expression"               end
+  def label_InstanceReferences o          ; "Resource Reference"                end
+  def label_AssignmentExpression o        ; "'#{o.operator}' expression"        end
+  def label_AttributeOperation o          ; "'#{o.operator}' expression"        end
+  def label_LiteralList o                 ; "Array Expression"                  end
+  def label_LiteralHash o                 ; "Hash Expression"                   end
+  def label_KeyedEntry o                  ; "Hash Entry"                        end
+  def label_LiteralBoolean o              ; "Boolean"                           end
+  def label_LiteralString o               ; "String"                            end
+  def label_LiteralText o                 ; "Text in Interpolated String"       end
+  def label_LambdaExpression o            ; "Lambda"                            end
+  def label_LiteralDefault o              ; "'default' expression"              end
+  def label_LiteralUndef o                ; "'undef' expression"                end
+  def label_LiteralRegularExpression o    ; "Regular Expression"                end
+  def label_Nop o                         ; "Nop Expression"                    end
+  def label_NamedAccessExpression o       ; "'.' expression"                    end
+  def label_NilClass o                    ; "Nil Object"                        end
+  def label_NotExpression o               ; "'not' expression"                  end
+  def label_VariableExpression o          ; "Variable"                          end
+  def label_TextExpression o              ; "Expression in Interpolated String" end
+  def label_UnaryMinusExpression o        ; "Unary Minus"                       end
+  def label_BlockExpression o             ; "Block Expression"                  end
+  def label_ConcatenatedString o          ; "Double Quoted String"              end
+  def label_HostClassDefinition o         ; "Host Class Definition"             end
+  def label_NodeDefinition o              ; "Node Definition"                   end
+  def label_ResourceTypeDefinition o      ; "'define' expression"               end
+  def label_ResourceOverrideExpression o  ; "Resource Override"                 end
+  def label_Parameter o                   ; "Parameter Definition"              end
+  def label_ParenthesizedExpression o     ; "Parenthesized Expression"          end
+  def label_IfExpression o                ; "'if' statement"                    end
+  def label_UnlessExpression o            ; "'unless' Statement"                end
+  def label_CallNamedFunctionExpression o ; "Function Call"                     end
+  def label_CallMethodExpression o        ; "Method call"                       end
+  def label_CaseExpression o              ; "'case' statement"                  end
+  def label_CaseOption o                  ; "Case Option"                       end
+  def label_RelationshipExpression o      ; "'#{o.operator}' expression"        end
+  def label_ResourceBody o                ; "Resource Instance Definition"      end
+  def label_ResourceDefaultsExpression o  ; "Resource Defaults Expression"      end
+  def label_ResourceExpression o          ; "Resource Statement"                end
+  def label_SelectorExpression o          ; "Selector Expression"               end
+  def label_SelectorEntry o               ; "Selector Option"                   end
+  def label_String o                      ; "Ruby String"                       end
+  def label_Object o                      ; "Ruby Object"                       end
+  def label_QualifiedName o               ; "Name"                              end
+  def label_QualifiedReference o          ; "Type Name"                         end
+
+end
diff --git a/lib/puppet/pops/model/model_tree_dumper.rb b/lib/puppet/pops/model/model_tree_dumper.rb
new file mode 100644
index 0000000..24d341c
--- /dev/null
+++ b/lib/puppet/pops/model/model_tree_dumper.rb
@@ -0,0 +1,352 @@
+# Dumps a Pops::Model in reverse polish notation; i.e. LISP style
+# The intention is to use this for debugging output
+# TODO: BAD NAME - A DUMP is a Ruby Serialization
+#
+class Puppet::Pops::Model::ModelTreeDumper < Puppet::Pops::Model::TreeDumper
+  def dump_Array o
+    o.collect {|e| do_dump(e) }
+  end
+
+  def dump_LiteralNumber o
+    case o.radix
+    when 10
+      o.value.to_s
+    when 8
+      "0%o" % o.value
+    when 16
+      "0x%X" % o.value
+    else
+      "bad radix:" + o.value.to_s
+    end
+  end
+
+  def dump_LiteralValue o
+    o.value.to_s
+  end
+
+  def dump_Factory o
+    do_dump(o.current)
+  end
+
+  def dump_ArithmeticExpression o
+    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  # x[y] prints as (slice x y)
+  def dump_AccessExpression o
+    if o.keys.size <= 1
+      ["slice", do_dump(o.left_expr), do_dump(o.keys[0])]
+    else
+      ["slice", do_dump(o.left_expr), do_dump(o.keys)]
+    end
+  end
+
+  def dump_MatchesExpression o
+    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_CollectExpression o
+    result = ["collect", do_dump(o.type_expr), :indent, :break, do_dump(o.query), :indent]
+    o.operations do |ao|
+      result << :break << do_dump(ao)
+    end
+    result += [:dedent, :dedent ]
+    result
+  end
+
+  def dump_ExportedQuery o
+    result = ["<<| |>>"]
+    result += dump_QueryExpression(o) unless is_nop?(o.expr)
+    result
+  end
+
+  def dump_VirtualQuery o
+    result = ["<| |>"]
+    result += dump_QueryExpression(o) unless is_nop?(o.expr)
+    result
+  end
+
+  def dump_QueryExpression o
+    [do_dump(o.expr)]
+  end
+
+  def dump_ComparisonExpression o
+    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_AndExpression o
+    ["&&", do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_OrExpression o
+    ["||", do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_InExpression o
+    ["in", do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_ImportExpression o
+    ["import"] + o.files.collect {|f| do_dump(f) }
+  end
+
+  def dump_InstanceReferences o
+    ["instances", do_dump(o.type_name)] + o.names.collect {|n| do_dump(n) }
+  end
+
+  def dump_AssignmentExpression o
+    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  # Produces (name => expr) or (name +> expr)
+  def dump_AttributeOperation o
+    [o.attribute_name, o.operator, do_dump(o.value_expr)]
+  end
+
+  def dump_LiteralList o
+    ["[]"] + o.values.collect {|x| do_dump(x)}
+  end
+
+  def dump_LiteralHash o
+    ["{}"] + o.entries.collect {|x| do_dump(x)}
+  end
+
+  def dump_KeyedEntry o
+    [do_dump(o.key), do_dump(o.value)]
+  end
+
+  def dump_MatchExpression o
+    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_LiteralString o
+    "'#{o.value}'"
+  end
+
+  def dump_LiteralText o
+    o.value
+  end
+
+  def dump_LambdaExpression o
+    result = ["lambda"]
+    result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0
+    if o.body
+      result << do_dump(o.body)
+    else
+      result << []
+    end
+    result
+  end
+
+  def dump_LiteralDefault o
+    ":default"
+  end
+
+  def dump_LiteralUndef o
+    ":undef"
+  end
+
+  def dump_LiteralRegularExpression o
+    "/#{o.value.source}/"
+  end
+
+  def dump_Nop o
+    ":nop"
+  end
+
+  def dump_NamedAccessExpression o
+    [".", do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_NilClass o
+    "()"
+  end
+
+  def dump_NotExpression o
+    ['!', dump(o.expr)]
+  end
+
+  def dump_VariableExpression o
+    "$#{dump(o.expr)}"
+  end
+
+  # Interpolation (to string) shown as (str expr)
+  def dump_TextExpression o
+    ["str", do_dump(o.expr)]
+  end
+
+  def dump_UnaryMinusExpression o
+    ['-', do_dump(o.expr)]
+  end
+
+  def dump_BlockExpression o
+    ["block"] + o.statements.collect {|x| do_dump(x) }
+  end
+
+  # Interpolated strings are shown as (cat seg0 seg1 ... segN)
+  def dump_ConcatenatedString o
+    ["cat"] + o.segments.collect {|x| do_dump(x)}
+  end
+
+  def dump_HostClassDefinition o
+    result = ["class", o.name]
+    result << ["inherits", o.parent_class] if o.parent_class
+    result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0
+    if o.body
+      result << do_dump(o.body)
+    else
+      result << []
+    end
+    result
+  end
+
+  def dump_NodeDefinition o
+    result = ["node"]
+    result << ["matches"] + o.host_matches.collect {|m| do_dump(m) }
+    result << ["parent", do_dump(o.parent)] if o.parent
+    if o.body
+      result << do_dump(o.body)
+    else
+      result << []
+    end
+    result
+  end
+
+  def dump_ResourceTypeDefinition o
+    result = ["define", o.name]
+    result << ["parameters"] + o.parameters.collect {|p| do_dump(p) } if o.parameters.size() > 0
+    if o.body
+      result << do_dump(o.body)
+    else
+      result << []
+    end
+    result
+  end
+
+  def dump_ResourceOverrideExpression o
+    result = ["override", do_dump(o.resources), :indent]
+    o.operations.each do |p|
+      result << :break << do_dump(p)
+    end
+    result << :dedent
+    result
+  end
+
+  # Produces parameters as name, or (= name value)
+  def dump_Parameter o
+    if o.value
+      ["=", o.name, do_dump(o.value)]
+    else
+      o.name
+    end
+  end
+
+  def dump_ParenthesizedExpression o
+    do_dump(o.expr)
+  end
+
+  def dump_IfExpression o
+    result = ["if", do_dump(o.test), :indent, :break,
+      ["then", :indent, do_dump(o.then_expr), :dedent]]
+    result +=
+    [:break,
+      ["else", :indent, do_dump(o.else_expr), :dedent],
+      :dedent] unless is_nop? o.else_expr
+    result
+  end
+
+  def dump_UnlessExpression o
+    result = ["unless", do_dump(o.test), :indent, :break,
+      ["then", :indent, do_dump(o.then_expr), :dedent]]
+    result +=
+    [:break,
+      ["else", :indent, do_dump(o.else_expr), :dedent],
+      :dedent] unless is_nop? o.else_expr
+    result
+  end
+
+  # Produces (invoke name args...) when not required to produce an rvalue, and
+  # (call name args ... ) otherwise.
+  #
+  def dump_CallNamedFunctionExpression o
+    result = [o.rval_required ? "call" : "invoke", do_dump(o.functor_expr)]
+    o.arguments.collect {|a| result << do_dump(a) }
+    result
+  end
+
+  #    def dump_CallNamedFunctionExpression o
+  #      result = [o.rval_required ? "call" : "invoke", do_dump(o.functor_expr)]
+  #      o.arguments.collect {|a| result << do_dump(a) }
+  #      result
+  #    end
+
+  def dump_CallMethodExpression o
+    result = [o.rval_required ? "call-method" : "invoke-method", do_dump(o.functor_expr)]
+    o.arguments.collect {|a| result << do_dump(a) }
+    result << do_dump(o.lambda) if o.lambda
+    result
+  end
+
+  def dump_CaseExpression o
+    result = ["case", do_dump(o.test), :indent]
+    o.options.each do |s|
+      result << :break << do_dump(s)
+    end
+    result << :dedent
+  end
+
+  def dump_CaseOption o
+    result = ["when"]
+    result << o.values.collect {|x| do_dump(x) }
+    result << ["then", do_dump(o.then_expr) ]
+    result
+  end
+
+  def dump_RelationshipExpression o
+    [o.operator.to_s, do_dump(o.left_expr), do_dump(o.right_expr)]
+  end
+
+  def dump_ResourceBody o
+    result = [do_dump(o.title), :indent]
+    o.operations.each do |p|
+      result << :break << do_dump(p)
+    end
+    result << :dedent
+    result
+  end
+
+  def dump_ResourceDefaultsExpression o
+    result = ["resource-defaults", do_dump(o.type_ref), :indent]
+    o.operations.each do |p|
+      result << :break << do_dump(p)
+    end
+    result << :dedent
+    result
+  end
+
+  def dump_ResourceExpression o
+    form = o.form == :regular ? '' : o.form.to_s + "-"
+    result = [form+"resource", do_dump(o.type_name), :indent]
+    o.bodies.each do |b|
+      result << :break << do_dump(b)
+    end
+    result << :dedent
+    result
+  end
+
+  def dump_SelectorExpression o
+    ["?", do_dump(o.left_expr)] + o.selectors.collect {|x| do_dump(x) }
+  end
+
+  def dump_SelectorEntry o
+    [do_dump(o.matching_expr), "=>", do_dump(o.value_expr)]
+  end
+
+  def dump_Object o
+    [o.class.to_s, o.to_s]
+  end
+
+  def is_nop? o
+    o.nil? || o.is_a?(Puppet::Pops::Model::Nop)
+  end
+end
diff --git a/lib/puppet/pops/model/tree_dumper.rb b/lib/puppet/pops/model/tree_dumper.rb
new file mode 100644
index 0000000..f0a7aba
--- /dev/null
+++ b/lib/puppet/pops/model/tree_dumper.rb
@@ -0,0 +1,59 @@
+# Base class for formatted textual dump of a "model"
+#
+class Puppet::Pops::Model::TreeDumper
+  attr_accessor :indent_count
+  def initialize initial_indentation = 0
+    @@dump_visitor ||= Puppet::Pops::Visitor.new(nil,"dump",0,0)
+    @indent_count = initial_indentation
+  end
+
+  def dump(o)
+    format(do_dump(o))
+  end
+
+  def do_dump(o)
+    @@dump_visitor.visit_this(self, o)
+  end
+
+  def indent
+    "  " * indent_count
+  end
+
+  def format(x)
+    result = ""
+    parts = format_r(x)
+    parts.each_index do |i|
+      if i > 0
+        # separate with space unless previous ends with whitepsace or (
+        result << ' ' if parts[i] != ")" && parts[i-1] !~ /.*(?:\s+|\()$/ && parts[i] !~ /\s+/
+      end
+      result << parts[i]
+    end
+    result
+  end
+
+  def format_r(x)
+    result = []
+    case x
+    when :break
+      result << "\n" + indent
+    when :indent
+      @indent_count += 1
+    when :dedent
+      @indent_count -= 1
+    when Array
+      result << '('
+      result += x.collect {|a| format_r(a) }.flatten
+      result << ')'
+    when Symbol
+      result << x.to_s # Allows Symbols in arrays e.g. ["text", =>, "text"]
+    else
+      result << x
+    end
+    result
+  end
+
+  def is_nop? o
+    o.nil? || o.is_a?(Puppet::Pops::Model::Nop)
+  end
+end
diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra
new file mode 100644
index 0000000..bb6a5db
--- /dev/null
+++ b/lib/puppet/pops/parser/egrammar.ra
@@ -0,0 +1,723 @@
+# vim: syntax=ruby
+
+# Parser using the Pops model, expression based
+
+class Puppet::Pops::Parser::Parser
+
+token STRING DQPRE DQMID DQPOST
+token LBRACK  RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE
+token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT
+token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN
+token IF ELSE
+token DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN
+token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSREF
+token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS
+token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
+token IN UNLESS PIPE
+token LAMBDA SELBRACE
+token LOW
+
+prechigh
+  left  HIGH
+  left  SEMIC
+  left  PIPE
+  left  LPAREN
+  left  RPAREN
+  left  AT
+  left  DOT
+  left  CALL
+  left  LBRACK
+  left  QMARK
+  left  LCOLLECT LLCOLLECT
+  right NOT
+  nonassoc UMINUS
+  left  IN
+  left  MATCH NOMATCH
+  left  TIMES DIV MODULO
+  left  MINUS PLUS
+  left  LSHIFT RSHIFT
+  left  NOTEQUAL ISEQUAL
+  left  GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL
+  left  AND
+  left  OR
+  right APPENDS EQUALS
+  left  LBRACE
+  left  SELBRACE
+  left  RBRACE
+  left  IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
+  left  TITLE_COLON
+  left  CASE_COLON
+  left  FARROW
+  left  COMMA
+  left  LOW
+preclow
+
+rule
+# Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty
+program
+  : statements { result = Factory.block_or_expression(*val[0]) }
+  | nil
+
+# Produces a semantic model (non validated, but semantically adjusted).
+statements
+  : syntactic_statements { result = transform_calls(val[0]) }
+
+# Change may have issues with nil; i.e. program is a sequence of nils/nops
+# Simplified from original which had validation for top level constructs - see statement rule
+# Produces Array<Model::Expression>
+syntactic_statements
+  : syntactic_statement                            { result = [val[0]]}
+  | syntactic_statements SEMIC syntactic_statement { result = val[0].push val[2] }
+  | syntactic_statements syntactic_statement       { result = val[0].push val[1] }
+
+# Produce a single expression or Array of expression
+syntactic_statement
+  : any_expression                            { result = val[0] }
+  | syntactic_statement COMMA any_expression  { result = aryfy(val[0]).push val[2] }
+
+any_expression
+  : relationship_expression
+
+relationship_expression
+  : resource_expression                                     =LOW  { result = val[0] }
+  | relationship_expression IN_EDGE      relationship_expression  { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
+  | relationship_expression IN_EDGE_SUB  relationship_expression  { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
+  | relationship_expression OUT_EDGE     relationship_expression  { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
+  | relationship_expression OUT_EDGE_SUB relationship_expression  { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
+
+#---EXPRESSION
+#
+# Produces Model::Expression
+expression
+  : higher_precedence
+  | expression LBRACK       expressions RBRACK =LBRACK { result = val[0][*val[2]]         ; loc result, val[0], val[3] }
+  | expression IN           expression                 { result = val[0].in val[2]        ; loc result, val[1] }
+  | expression MATCH        match_rvalue               { result = val[0] =~  val[2]       ; loc result, val[1] }
+  | expression NOMATCH      match_rvalue               { result = val[0].mne val[2]       ; loc result, val[1] }
+  | expression PLUS         expression                 { result = val[0] +   val[2]       ; loc result, val[1] }
+  | expression MINUS        expression                 { result = val[0] -   val[2]       ; loc result, val[1] }
+  | expression DIV          expression                 { result = val[0] /   val[2]       ; loc result, val[1] }
+  | expression TIMES        expression                 { result = val[0] *   val[2]       ; loc result, val[1] }
+  | expression MODULO       expression                 { result = val[0] %   val[2]       ; loc result, val[1] }
+  | expression LSHIFT       expression                 { result = val[0] <<  val[2]       ; loc result, val[1] }
+  | expression RSHIFT       expression                 { result = val[0] >>  val[2]       ; loc result, val[1] }
+  |            MINUS        expression         =UMINUS { result = val[1].minus()          ; loc result, val[0] }
+  | expression NOTEQUAL     expression                 { result = val[0].ne  val[2]       ; loc result, val[1] }
+  | expression ISEQUAL      expression                 { result = val[0] ==  val[2]       ; loc result, val[1] }
+  | expression GREATERTHAN  expression                 { result = val[0] >   val[2]       ; loc result, val[1] }
+  | expression GREATEREQUAL expression                 { result = val[0] >=  val[2]       ; loc result, val[1] }
+  | expression LESSTHAN     expression                 { result = val[0] <   val[2]       ; loc result, val[1] }
+  | expression LESSEQUAL    expression                 { result = val[0] <=  val[2]       ; loc result, val[1] }
+  |            NOT          expression                 { result = val[1].not              ; loc result, val[0] }
+  | expression AND          expression                 { result = val[0].and val[2]       ; loc result, val[1] }
+  | expression OR           expression                 { result = val[0].or  val[2]       ; loc result, val[1] }
+  | expression EQUALS       expression                 { result = val[0].set(val[2])      ; loc result, val[1] }
+  | expression APPENDS      expression                 { result = val[0].plus_set(val[2]) ; loc result, val[1] }
+  | expression QMARK selector_entries                  { result = val[0].select(*val[2])  ; loc result, val[0] }
+  |            LPAREN       expression RPAREN          { result = val[1].paren()          ; loc result, val[0] }
+
+#---EXPRESSIONS
+#   (e.g. argument list)
+#
+# This expression list can not contain function calls without parentheses around arguments
+# Produces Array<Model::Expression>
+expressions
+  : expression                   { result = [val[0]] }
+  | expressions COMMA expression { result = val[0].push(val[2]) }
+
+# These go through a chain of left recursion, ending with primary_expression
+higher_precedence
+  : call_function_expression
+
+primary_expression
+  : literal_expression
+  | variable
+  | call_method_with_lambda_expression
+  | collection_expression
+  | case_expression
+  | if_expression
+  | unless_expression
+  | definition_expression
+  | hostclass_expression
+  | node_definition_expression
+
+# Aleways have the same value
+literal_expression
+  : array
+  | boolean
+  | default
+  | hash
+  | regex
+  | text_or_name =LOW # resolves hash key ambiguity (racc W U require this?)
+  | type
+  | undef
+
+text_or_name
+  : name       { result = val[0] }
+  | quotedtext { result = val[0] }
+
+#---CALL FUNCTION
+#
+# Produces Model::CallNamedFunction
+
+call_function_expression
+  : primary_expression LPAREN expressions endcomma RPAREN {
+      result = Factory.CALL_NAMED(val[0], true, val[2])
+      loc result, val[0], val[4]
+    }
+  | primary_expression LPAREN RPAREN {
+      result = Factory.CALL_NAMED(val[0], true, [])
+      loc result, val[0], val[2]
+    }
+  | primary_expression LPAREN expressions endcomma RPAREN lambda {
+      result = Factory.CALL_NAMED(val[0], true, val[2])
+      loc result, val[0], val[4]
+      result.lambda = val[5]
+    }
+  | primary_expression LPAREN RPAREN  lambda {
+      result = Factory.CALL_NAMED(val[0], true, [])
+      loc result, val[0], val[2]
+      result.lambda = val[3]
+    }
+  | primary_expression = LOW { result = val[0] }
+
+#---CALL METHOD
+#
+call_method_with_lambda_expression
+  : call_method_expression         =LOW { result = val[0] }
+  | call_method_expression lambda  { result = val[0]; val[0].lambda = val[1] }
+
+  call_method_expression
+    : named_access LPAREN expressions RPAREN { result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] }
+    | named_access LPAREN RPAREN             { result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] }
+    | named_access =LOW                      { result = Factory.CALL_METHOD(val[0], []); loc result, val[0] }
+
+  # TODO: It may be of value to access named elements of types too
+  named_access
+    : expression DOT NAME {
+        result = val[0].dot(Factory.fqn(val[2][:value]))
+        loc result, val[1], val[2]
+      }
+
+#---LAMBDA
+#
+# This is a temporary switch while experimenting with concrete syntax
+# One should be picked for inclusion in puppet.
+lambda
+  : lambda_j8
+  | lambda_ruby
+
+# Java8-like lambda with parameters to the left of the body
+lambda_j8
+  : lambda_parameter_list optional_farrow lambda_rest {
+      result = Factory.LAMBDA(val[0], val[2])
+#      loc result, val[1] # TODO
+    }
+
+lambda_rest
+  : LBRACE statements RBRACE { result = val[1] }
+  | LBRACE RBRACE { result = nil }
+
+optional_farrow
+  : nil
+  | FARROW
+
+# Ruby-like lambda with parameters inside the body
+#
+lambda_ruby
+  : LAMBDA lambda_parameter_list statements RBRACE {
+      result = Factory.LAMBDA(val[1], val[2])
+      loc result, val[0], val[3]
+    }
+  | LAMBDA lambda_parameter_list RBRACE {
+      result = Factory.LAMBDA(val[1], nil)
+      loc result, val[0], val[2]
+    }
+
+# Produces Array<Model::Parameter>
+lambda_parameter_list
+: PIPE PIPE                     { result = [] }
+| PIPE parameters endcomma PIPE { result = val[1] }
+
+#---CONDITIONALS
+#
+
+#--IF
+#
+# Produces Model::IfExpression
+if_expression
+  : IF if_part {
+      result = val[1]
+      loc(result, val[0], val[1])
+    }
+
+  # Produces Model::IfExpression
+  if_part
+    : expression LBRACE statements RBRACE else {
+        result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4])
+        loc(result, val[0], (val[4] ? val[4] : val[3]))
+      }
+    | expression LBRACE RBRACE else {
+        result = Factory.IF(val[0], nil, val[3])
+        loc(result, val[0], (val[3] ? val[3] : val[2]))
+      }
+
+  # Produces [Model::Expression, nil] - nil if there is no else or elsif part
+  else
+    : # nothing
+    | ELSIF if_part {
+        result = val[1]
+        loc(result, val[0], val[1])
+      }
+    | ELSE LBRACE statements RBRACE {
+        result = Factory.block_or_expression(*val[2])
+        loc result, val[0], val[3]
+      }
+    | ELSE LBRACE RBRACE {
+        result = nil # don't think a nop is needed here either
+      }
+
+#--UNLESS
+#
+# Changed from Puppet 3x where there is no else part on unless
+#
+unless_expression
+  : UNLESS expression LBRACE statements RBRACE unless_else {
+      result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5])
+      loc result, val[0], val[4]
+    }
+  | UNLESS expression LBRACE RBRACE unless_else {
+      result = Factory.UNLESS(val[1], nil, nil)
+      loc result, val[0], val[4]
+    }
+
+  # Different from else part of if, since "elsif" is not supported, but 'else' is
+  #
+  # Produces [Model::Expression, nil] - nil if there is no else or elsif part
+  unless_else
+    : # nothing
+    | ELSE LBRACE statements RBRACE {
+        result = Factory.block_or_expression(*val[2])
+        loc result, val[0], val[3]
+      }
+    | ELSE LBRACE RBRACE {
+        result = nil # don't think a nop is needed here either
+      }
+
+#--- CASE EXPRESSION
+#
+# Produces Model::CaseExpression
+case_expression
+  : CASE expression LBRACE case_options RBRACE {
+      result = Factory.CASE(val[1], *val[3])
+      loc result, val[0], val[4]
+    }
+
+  # Produces Array<Model::CaseOption>
+  case_options
+    : case_option               { result = [val[0]] }
+    | case_options case_option { result = val[0].push val[1] }
+
+  # Produced Model::CaseOption (aka When)
+  case_option
+    : expressions case_colon LBRACE statements RBRACE {
+        result = Factory.WHEN(val[0], val[3])
+        loc result, val[1], val[4]
+      }
+    | expressions case_colon LBRACE RBRACE = LOW {
+        result = Factory.WHEN(val[0], nil)
+        loc result, val[1], val[3]
+      }
+
+  case_colon: COLON =CASE_COLON { result = val[0] }
+
+  # This special construct is required or racc will produce the wrong result when the selector entry
+  # LHS is generalized to any expression (LBRACE looks like a hash). Thus it is not possible to write
+  # a selector with a single entry where the entry LHS is a hash.
+  # The SELBRACE token is a LBRACE that follows a QMARK, and this is produced by the lexer with a lookback
+  # Produces Array<Model::SelectorEntry>
+  #
+  selector_entries
+    : selector_entry
+    | SELBRACE selector_entry_list endcomma RBRACE {
+        result = val[1]
+      }
+
+  # Produces Array<Model::SelectorEntry>
+  selector_entry_list
+    : selector_entry                           { result = [val[0]] }
+    | selector_entry_list COMMA selector_entry { result = val[0].push val[2] }
+
+  # Produces a Model::SelectorEntry
+  # This FARROW wins over FARROW in Hash
+  selector_entry
+    : expression FARROW expression { result = Factory.MAP(val[0], val[2]) ; loc result, val[1] }
+
+# --- IMPORT
+#     IMPORT is handled as a non parenthesized call and is transformed to an ImportExpression.
+#     i.e. there is no special grammar for it - it is just a "call statement".
+
+#---RESOURCE
+#
+# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression]
+
+# The resource expression parses a generalized syntax and then selects the correct
+# resulting model based on the combinatoin of the LHS and what follows.
+# It also handled exported and virtual resources, and the class case
+#
+resource_expression
+  : expression =LOW {
+      result = val[0]
+    }
+  | at expression LBRACE resourceinstances endsemi RBRACE {
+      result = case Factory.resource_shape(val[1])
+      when :resource, :class
+        tmp = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3])
+        tmp.form = val[0]
+        tmp
+      when :defaults
+        error "A resource default can not be virtual or exported"
+      when :override
+        error "A resource override can not be virtual or exported"
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+      loc result, val[1], val[4]
+    }
+  | at expression LBRACE attribute_operations endcomma RBRACE {
+      result = case Factory.resource_shape(val[1])
+      when :resource, :class
+        error "Defaults are not virtualizable"
+      when :defaults
+        error "Defaults are not virtualizable"
+      when :override
+        error "Defaults are not virtualizable"
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+    }
+  | expression LBRACE resourceinstances endsemi RBRACE {
+      result = case Factory.resource_shape(val[0])
+      when :resource, :class
+        Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
+      when :defaults
+        error "A resource default can not specify a resource name"
+      when :override
+        error "A resource override does not allow override of name of resource"
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+      loc result, val[0], val[4]
+    }
+  | expression LBRACE attribute_operations endcomma RBRACE {
+      result = case Factory.resource_shape(val[0])
+      when :resource, :class
+        # This catches deprecated syntax.
+        error "All resource specifications require names"
+      when :defaults
+        Factory.RESOURCE_DEFAULTS(val[0], val[2])
+      when :override
+        # This was only done for override in original - TODO shuld it be here at all
+        Factory.RESOURCE_OVERRIDE(val[0], val[2])
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+      loc result, val[0], val[4]
+    }
+  | CLASS LBRACE resourceinstances endsemi RBRACE {
+        result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
+        loc result, val[0], val[4]
+    }
+
+  resourceinst
+    : expression title_colon attribute_operations endcomma { result = Factory.RESOURCE_BODY(val[0], val[2]) }
+
+  title_colon : COLON =TITLE_COLON { result = val[0] }
+
+  resourceinstances
+    : resourceinst                         { result = [val[0]] }
+    | resourceinstances SEMIC resourceinst { result = val[0].push val[2] }
+
+  # Produces Symbol corresponding to resource form
+  #
+  at
+    : AT    { result = :virtual  }
+    | AT AT { result = :exported }
+
+#---COLLECTION
+#
+# A Collection is a predicate applied to a set of objects with an implied context (used variables are
+# attributes of the object.
+# i.e. this is equivalent for source.select(QUERY).apply(ATTRIBUTE_OPERATIONS)
+#
+# Produces Model::CollectExpression
+#
+collection_expression
+  : expression collect_query LBRACE attribute_operations endcomma RBRACE {
+      result = Factory.COLLECT(val[0], val[1], val[3])
+      loc result, val[0], val[5]
+    }
+  | expression collect_query =LOW {
+      result = Factory.COLLECT(val[0], val[1], [])
+      loc result, val[0], val[1]
+    }
+
+  collect_query
+    : LCOLLECT  optional_query RCOLLECT  { result = Factory.VIRTUAL_QUERY(val[1])   ; loc result, val[0], val[2] }
+    | LLCOLLECT optional_query RRCOLLECT { result = Factory.EXPORTED_QUERY(val[1])  ; loc result, val[0], val[2] }
+
+  optional_query
+    : nil
+    | expression
+
+#---ATTRIBUTE OPERATIONS
+#
+# (Not an expression)
+#
+# Produces Array<Model::AttributeOperation>
+#
+attribute_operations
+  :                                                { result = [] }
+  | attribute_operation                            { result = [val[0]] }
+  | attribute_operations COMMA attribute_operation { result = val[0].push(val[2]) }
+
+  # Produces String
+  # QUESTION: Why is BOOLEAN valid as an attribute name?
+  #
+  attribute_name
+    : NAME
+    | keyword
+    | BOOLEAN
+
+  # In this version, illegal combinations are validated instead of producing syntax errors
+  # (Can give nicer error message "+> is not applicable to...")
+  # Produces Model::AttributeOperation
+  #
+  attribute_operation
+    : attribute_name FARROW expression {
+        result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2])
+        loc result, val[0], val[2]
+      }
+    | attribute_name PARROW expression {
+        result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2])
+        loc result, val[0], val[2]
+      }
+
+#---DEFINE
+#
+# Produces Model::Definition
+#
+definition_expression
+  : DEFINE classname parameter_list LBRACE statements RBRACE {
+      result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])
+      loc result, val[0], val[5]
+      @lexer.indefine = false
+    }
+  | DEFINE classname parameter_list LBRACE RBRACE {
+      result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil)
+      loc result, val[0], val[4]
+      @lexer.indefine = false
+    }
+
+#---HOSTCLASS
+# ORIGINAL COMMENT: Our class gets defined in the parent namespace, not our own.
+#    WAT ??! This is way odd; should get its complete name, classnames do not nest
+#    Seems like the call to classname makes use of the name scope
+#    (This is uneccesary, since the parent name is known when evaluating)
+#
+# Produces Model::HostClassDefinition
+#
+hostclass_expression
+  : CLASS classname parameter_list classparent LBRACE statements RBRACE {
+      @lexer.namepop
+      result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])
+      loc result, val[0], val[6]
+    }
+  | CLASS classname parameter_list classparent LBRACE RBRACE {
+      @lexer.namepop
+      result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil)
+      loc result, val[0], val[5]
+      }
+
+  # Produces String, name or nil result
+  classparent
+    : nil
+    | INHERITS classnameordefault { result = val[1] }
+
+  # Produces String (this construct allows a class to be named "default" and to be referenced as
+  # the parent class.
+  # TODO: Investigate the validity
+  # Produces a String (classname), or a token (DEFAULT).
+  #
+  classnameordefault
+    : classname
+    | DEFAULT
+
+#---NODE
+#
+# Produces Model::NodeDefinition
+#
+node_definition_expression
+  : NODE hostnames nodeparent LBRACE statements RBRACE {
+      result = Factory.NODE(val[1], val[2], val[4])
+      loc result, val[0], val[5]
+    }
+  | NODE hostnames nodeparent LBRACE RBRACE {
+      result = Factory.NODE(val[1], val[2], nil)
+      loc result, val[0], val[4]
+    }
+
+  # Hostnames is not a list of names, it is a list of name matchers (including a Regexp).
+  # (The old implementation had a special "Hostname" object with some minimal validation)
+  #
+  # Produces Array<Model::LiteralExpression>
+  #
+  hostnames
+    : hostname                 { result = [result] }
+    | hostnames COMMA hostname { result = val[0].push(val[2]) }
+
+  # Produces a LiteralExpression (string, :default, or regexp)
+  # String with interpolation is validated for better error message
+  hostname
+    : NAME        { result = Factory.fqn(val[0][:value]); loc result, val[0] }
+    | quotedtext  { result = val[0] } 
+    | DEFAULT     { result = Factory.literal(:default); loc result, val[0] }
+    | regex
+
+  # Produces Expression, since hostname is an Expression
+  nodeparent
+    : nil
+    | INHERITS hostname { result = val[1] }
+
+#---NAMES AND PARAMTERS COMMON TO SEVERAL RULES
+# String result
+classname
+  : NAME  { result = val[0] }
+  | CLASS { result = val[0] }
+
+# Produces Array<Model::Parameter>
+parameter_list
+  : nil                               { result = [] }
+  | LPAREN  RPAREN                    { result = [] }
+  | LPAREN parameters endcomma RPAREN { result = val[1] }
+
+# Produces Array<Model::Parameter>
+parameters
+  : parameter                  { result = [val[0]] }
+  | parameters COMMA parameter { result = val[0].push(val[2]) }
+
+# Produces Model::Parameter
+parameter
+  : VARIABLE EQUALS expression { result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] }
+  | VARIABLE                   { result = Factory.PARAM(val[0][:value]); loc result, val[0] }
+
+#--RESTRICTED EXPRESSIONS
+#  i.e. where one could have expected an expression, but the set is limited
+
+# What is allowed RHS of match operators (see expression)
+match_rvalue
+  : regex
+  | text_or_name
+
+#--VARIABLE
+#
+variable
+  : VARIABLE { result = Factory.fqn(val[0][:value]).var ; loc result, val[0] }
+
+#---LITERALS (dynamic and static)
+#
+
+array
+  : LBRACK expressions       RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[2] }
+  | LBRACK expressions COMMA RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] }
+  | LBRACK                   RBRACK { result = Factory.literal([]) ; loc result, val[0] }
+
+hash
+  : LBRACE hashpairs RBRACE       { result = Factory.HASH(val[1]); loc result, val[0], val[2] }
+  | LBRACE hashpairs COMMA RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[3] }
+  | LBRACE RBRACE                 { result = Factory.literal({}) ; loc result, val[0], val[3] }
+
+  hashpairs
+    : hashpair                 { result = [val[0]] }
+    | hashpairs COMMA hashpair { result = val[0].push val[2] }
+
+  hashpair
+    : text_or_name FARROW expression { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] }
+
+quotedtext
+  : string
+  | dq_string
+
+string          : STRING                 { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
+dq_string       : dqpre dqrval           { result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] }
+dqpre           : DQPRE                  { result = Factory.literal(val[0][:value]); loc result, val[0] }
+dqpost          : DQPOST                 { result = Factory.literal(val[0][:value]); loc result, val[0] }
+dqmid           : DQMID                  { result = Factory.literal(val[0][:value]); loc result, val[0] }
+dqrval          : text_expression dqtail { result = [val[0]] + val[1] }
+text_expression : expression             { result = Factory.TEXT(val[0]) }
+
+dqtail
+  : dqpost        { result = [val[0]] }
+  | dqmid dqrval  { result = [val[0]] + val[1] }
+
+name     : NAME     { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] }
+type     : CLASSREF { result = Factory.QREF(val[0][:value])  ; loc result, val[0] }
+undef    : UNDEF    { result = Factory.literal(:undef); loc result, val[0] }
+default  : DEFAULT  { result = Factory.literal(:default); loc result, val[0] }
+
+          # Assumes lexer produces a Boolean value for booleans, or this will go wrong and produce a literal string
+          # with the text 'true'.
+          #TODO: could be changed to a specific boolean literal factory method to prevent this possible glitch.
+boolean  : BOOLEAN { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
+
+regex
+  : REGEX { result = Factory.literal(val[0][:value]); loc result, val[0] }
+
+#---MARKERS, SPECIAL TOKENS, SYNTACTIC SUGAR, etc.
+
+endcomma
+  : #
+  | COMMA { result = nil }
+
+endsemi
+  : #
+  | SEMIC
+
+keyword
+ : AND
+ | CASE
+ | CLASS
+ | DEFAULT
+ | DEFINE
+ | ELSE
+ | ELSIF
+ | IF
+ | IN
+ | INHERITS
+ | NODE
+ | OR
+ | UNDEF
+ | UNLESS
+
+nil
+  : { result = nil}
+
+end
+
+---- header ----
+require 'puppet'
+require 'puppet/util/loadedfile'
+require 'puppet/pops'
+
+module Puppet
+  class ParseError < Puppet::Error; end
+  class ImportError < Racc::ParseError; end
+  class AlreadyImportedError < ImportError; end
+end
+
+---- inner ----
+
+# Make emacs happy
+# Local Variables:
+# mode: ruby
+# End:
diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb
new file mode 100644
index 0000000..26fea92
--- /dev/null
+++ b/lib/puppet/pops/parser/eparser.rb
@@ -0,0 +1,2300 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by Racc 1.4.9
+# from Racc grammer file "".
+#
+
+require 'racc/parser.rb'
+
+require 'puppet'
+require 'puppet/util/loadedfile'
+require 'puppet/pops'
+
+module Puppet
+  class ParseError < Puppet::Error; end
+  class ImportError < Racc::ParseError; end
+  class AlreadyImportedError < ImportError; end
+end
+
+module Puppet
+  module Pops
+    module Parser
+      class Parser < Racc::Parser
+
+module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 719)
+
+# Make emacs happy
+# Local Variables:
+# mode: ruby
+# End:
+...end egrammar.ra/module_eval...
+##### State transition tables begin ###
+
+clist = [
+'68,-132,112,207,220,235,344,51,53,87,88,84,79,90,234,94,-130,89,51,53',
+'80,82,81,83,234,283,224,269,222,115,233,223,321,114,207,234,244,204',
+'93,-198,-207,-132,86,85,54,299,72,73,75,74,77,78,68,70,71,54,-130,316',
+'202,315,69,87,88,84,79,90,59,94,76,89,51,53,80,82,81,83,245,59,115,-198',
+'-207,301,114,115,115,51,53,114,114,115,93,330,291,114,86,85,105,104',
+'72,73,75,74,77,78,68,70,71,120,225,227,122,226,69,87,88,84,79,90,68',
+'94,76,89,54,297,80,82,81,83,68,59,115,90,268,94,114,89,243,51,53,105',
+'104,90,93,94,128,89,86,85,68,267,72,73,75,74,77,78,93,70,71,84,79,90',
+'68,94,69,89,93,306,80,82,81,83,76,192,54,90,316,94,315,89,309,70,71',
+'105,104,310,93,207,69,51,53,85,68,168,72,73,75,74,77,78,93,70,71,84',
+'79,90,313,94,69,89,51,53,80,82,81,83,76,105,68,68,317,51,53,229,228',
+'319,120,63,263,122,93,90,90,94,94,89,89,241,72,73,75,74,77,78,243,70',
+'71,120,59,326,122,327,69,68,241,91,93,93,120,267,76,122,87,88,84,79',
+'90,259,94,59,89,70,71,80,82,81,83,68,69,63,59,64,66,65,67,134,258,257',
+'336,79,90,93,94,243,89,86,85,80,243,72,73,75,74,77,78,116,70,71,241',
+'339,217,341,106,69,217,93,282,286,319,346,347,76,348,72,73,75,74,77',
+'78,68,70,71,349,99,352,353,354,69,285,63,60,79,90,361,94,76,89,362,363',
+'80,364,,,68,,,,,,,,,,,,79,90,93,94,,89,,,80,,72,73,75,74,77,78,68,70',
+'71,,,,,,69,,93,,,90,,94,76,89,72,73,75,74,77,78,68,70,71,,,,,,69,,,',
+'79,90,93,94,76,89,,,80,,72,73,75,74,77,78,68,70,71,,,,,,69,,93,,,90',
+',94,76,89,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,93,94,76',
+'89,,,80,82,81,83,68,,,,,70,71,,,,,,69,90,93,94,,89,86,85,,,72,73,75',
+'74,77,78,68,70,71,,,,,,69,,93,,,90,,94,76,89,72,73,75,74,77,78,68,70',
+'71,,,,,,69,,,,,90,93,94,76,89,,,,,72,73,75,74,,,,70,71,,,,,,69,,93,',
+',,,,76,,72,73,75,74,,,68,70,71,,,,,,69,87,88,84,79,90,239,94,76,89,',
+',80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,68,70,71',
+',,,,,69,87,88,84,79,90,,94,76,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86',
+'85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,,94,76,89,68',
+',80,82,81,83,,,,,,,,90,,94,,89,,,93,,,,86,85,,,72,73,75,74,77,78,,70',
+'71,,93,,,,69,,,,,,75,74,76,,68,70,71,,,,,,69,87,88,84,79,90,,94,76,89',
+'68,,80,82,81,83,,,,,,,,90,,94,,89,,,93,,,,86,85,,,72,73,75,74,77,78',
+',70,71,,93,,,,69,,,,,,75,74,76,,68,70,71,,,,,,69,87,88,84,79,90,,94',
+'76,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,68',
+'70,71,,,,,,69,87,88,84,79,90,,94,76,89,,,80,82,81,83,,,,,,,,,,,,,,,93',
+',,,86,85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,,94,76',
+'89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,68,70',
+'71,,,,,,69,87,88,84,79,90,,94,76,89,,,80,82,81,83,,,,,,,,,,,,,,,93,',
+',,86,85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,,94,76,89',
+',,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,',
+',,,,69,68,,213,,,,,76,,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,',
+',,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,212,,,,,76',
+',87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72',
+'73,75,74,77,78,,70,71,,,,,,69,68,,211,,,,,76,,87,88,84,79,90,,94,,89',
+',,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,',
+',,,,69,68,,210,,,,,76,,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,',
+',,,,,,93,,,,86,85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90',
+',94,76,89,,197,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77',
+'78,,70,71,51,53,,,47,69,48,,,,,,,76,,,,,,,,,13,,,,,,38,,44,,46,96,,45',
+'58,54,,40,57,,,,55,12,,,56,51,53,11,,47,,48,,,,59,,,,,,39,,,167,,,13',
+',,,,,170,187,181,188,46,182,190,183,179,177,,172,185,,,,55,12,191,186',
+'184,51,53,11,,47,,48,333,,,59,,,,,189,171,,,,,,13,,,,,,38,,44,,46,42',
+',45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,322,,,,,,59,,,,,,39,',
+',13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,170,187,181,188,46,182,190,183,179,177,,172,185,,,,55,12,191',
+'186,184,51,53,11,,47,,48,308,,,59,,,,,189,171,,,,,,13,,,,,,38,,44,,46',
+'42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,',
+',13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12',
+'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,199,,,,,38,,44,,46,96,,45,58',
+'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38',
+',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,',
+',,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47',
+'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
+'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,209,,,,,38,,44,,46,96,,45',
+'58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,',
+'38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59',
+',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
+',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57',
+'43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,',
+',13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,,,56,51,53,11,,47,290,48,,,,59,,,,,,39,,,,,,13,,,,,,38',
+',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,335,,,,,,59',
+',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
+',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57',
+',,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96',
+',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,273,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,',
+',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51',
+'53,56,,47,11,48,271,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54',
+',40,57,43,,,55,12,51,53,56,,47,11,48,265,,,,,,59,,,,,,39,,,13,,,,,,38',
+',44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,',
+',,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47',
+'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
+'12,,,56,51,53,11,,47,126,48,,,,59,,,,,,39,,,,,,13,,,,,,38,,44,,46,96',
+',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,',
+',,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,',
+',59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
+'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
+'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,',
+',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
+'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
+'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,351,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11',
+'48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12',
+'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54',
+',40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,356,,,,,,59,,,,',
+',39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47',
+'11,48,358,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43',
+',,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42',
+',45,58,54,61,40,57,43,,,55,12,51,53,56,,47,11,48,360,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11',
+'48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55',
+'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58',
+'54,,40,57,,,,55,12,,,56,,,11,,,,253,187,252,188,59,250,190,254,248,247',
+'39,249,251,,,,,,191,186,255,253,187,252,188,,250,190,254,248,247,,249',
+'251,,,189,256,,191,186,255,253,187,252,188,,250,190,254,248,247,,249',
+'251,,,189,256,,191,186,255,,,,,,,,,,,,,,,,189,256' ]
+        racc_action_table = arr = ::Array.new(4804, nil)
+        idx = 0
+        clist.each do |str|
+          str.split(',', -1).each do |i|
+            arr[idx] = i.to_i unless i.empty?
+            idx += 1
+          end
+        end
+
+clist = [
+'164,179,42,105,118,164,316,70,70,164,164,164,164,164,208,164,177,164',
+'71,71,164,164,164,164,274,217,125,208,118,42,141,125,274,42,217,141',
+'180,105,164,185,184,179,164,164,70,240,164,164,164,164,164,164,163,164',
+'164,71,177,271,103,271,164,163,163,163,163,163,70,163,164,163,220,220',
+'163,163,163,163,180,71,96,185,184,242,96,182,282,226,226,182,282,181',
+'163,282,226,181,163,163,306,306,163,163,163,163,163,163,162,163,163',
+'220,127,130,220,127,163,162,162,162,162,162,97,162,163,162,226,236,162',
+'162,162,162,151,220,44,97,207,97,44,97,246,48,48,199,199,151,162,151',
+'48,151,162,162,161,205,162,162,162,162,162,162,97,162,162,161,161,161',
+'142,161,162,161,151,260,161,161,161,161,162,92,48,142,313,142,313,142',
+'264,151,151,36,36,266,161,267,151,183,183,161,160,90,161,161,161,161',
+'161,161,142,161,161,160,160,160,270,160,161,160,45,45,160,160,160,160',
+'161,104,150,95,272,222,222,133,133,273,183,135,200,183,160,150,95,150',
+'95,150,95,277,160,160,160,160,160,160,278,160,160,45,183,279,45,280',
+'160,10,214,10,150,95,222,284,160,222,10,10,10,10,10,198,10,45,10,150',
+'150,10,10,10,10,159,150,62,222,7,7,7,7,60,196,194,296,159,159,10,159',
+'174,159,10,10,159,298,10,10,10,10,10,10,43,10,10,173,305,113,307,37',
+'10,117,159,215,219,317,319,320,10,324,159,159,159,159,159,159,158,159',
+'159,325,35,331,332,334,159,218,5,1,158,158,350,158,159,158,355,357,158',
+'359,,,157,,,,,,,,,,,,157,157,158,157,,157,,,157,,158,158,158,158,158',
+'158,155,158,158,,,,,,158,,157,,,155,,155,158,155,157,157,157,157,157',
+'157,156,157,157,,,,,,157,,,,156,156,155,156,157,156,,,156,,155,155,155',
+'155,155,155,149,155,155,,,,,,155,,156,,,149,,149,155,149,156,156,156',
+'156,156,156,312,156,156,,,,,,156,312,312,312,312,312,149,312,156,312',
+',,312,312,312,312,154,,,,,149,149,,,,,,149,154,312,154,,154,312,312',
+',,312,312,312,312,312,312,153,312,312,,,,,,312,,154,,,153,,153,312,153',
+'154,154,154,154,154,154,152,154,154,,,,,,154,,,,,152,153,152,154,152',
+',,,,153,153,153,153,,,,153,153,,,,,,153,,152,,,,,,153,,152,152,152,152',
+',,169,152,152,,,,,,152,169,169,169,169,169,169,169,152,169,,,169,169',
+'169,169,,,,,,,,,,,,,,,169,,,,169,169,,,169,169,169,169,169,169,304,169',
+'169,,,,,,169,304,304,304,304,304,,304,169,304,,,304,304,304,304,,,,',
+',,,,,,,,,,304,,,,304,304,,,304,304,304,304,304,304,303,304,304,,,,,',
+'304,303,303,303,303,303,,303,304,303,148,,303,303,303,303,,,,,,,,148',
+',148,,148,,,303,,,,303,303,,,303,303,303,303,303,303,,303,303,,148,',
+',,303,,,,,,148,148,303,,295,148,148,,,,,,148,295,295,295,295,295,,295',
+'148,295,147,,295,295,295,295,,,,,,,,147,,147,,147,,,295,,,,295,295,',
+',295,295,295,295,295,295,,295,295,,147,,,,295,,,,,,147,147,295,,293',
+'147,147,,,,,,147,293,293,293,293,293,,293,147,293,,,293,293,293,293',
+',,,,,,,,,,,,,,293,,,,293,293,,,293,293,293,293,293,293,193,293,293,',
+',,,,293,193,193,193,193,193,,193,293,193,,,193,193,193,193,,,,,,,,,',
+',,,,,193,,,,193,193,,,193,193,193,193,193,193,289,193,193,,,,,,193,289',
+'289,289,289,289,,289,193,289,,,289,289,289,289,,,,,,,,,,,,,,,289,,,',
+'289,289,,,289,289,289,289,289,289,131,289,289,,,,,,289,131,131,131,131',
+'131,,131,289,131,,,131,131,131,131,,,,,,,,,,,,,,,131,,,,131,131,,,131',
+'131,131,131,131,131,124,131,131,,,,,,131,124,124,124,124,124,,124,131',
+'124,,,124,124,124,124,,,,,,,,,,,,,,,124,,,,124,124,,,124,124,124,124',
+'124,124,,124,124,,,,,,124,111,,111,,,,,124,,111,111,111,111,111,,111',
+',111,,,111,111,111,111,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111',
+'111,111,,111,111,,,,,,111,110,,110,,,,,111,,110,110,110,110,110,,110',
+',110,,,110,110,110,110,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110',
+'110,110,,110,110,,,,,,110,109,,109,,,,,110,,109,109,109,109,109,,109',
+',109,,,109,109,109,109,,,,,,,,,,,,,,,109,,,,109,109,,,109,109,109,109',
+'109,109,,109,109,,,,,,109,107,,107,,,,,109,,107,107,107,107,107,,107',
+',107,,,107,107,107,107,,,,,,,,,,,,,,,107,,,,107,107,,,107,107,107,107',
+'107,107,98,107,107,,,,,,107,98,98,98,98,98,,98,107,98,,98,98,98,98,98',
+',,,,,,,,,,,,,,98,,,,98,98,,,98,98,98,98,98,98,,98,98,89,89,,,89,98,89',
+',,,,,,98,,,,,,,,,89,,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,89,89,,,89',
+'213,213,89,,213,,213,,,,89,,,,,,89,,,89,,,213,,,,,,213,213,213,213,213',
+'213,213,213,213,213,,213,213,,,,213,213,213,213,213,285,285,213,,285',
+',285,285,,,213,,,,,213,213,,,,,,285,,,,,,285,,285,,285,285,,285,285',
+'285,,285,285,285,,,285,285,275,275,285,,275,285,275,275,,,,,,285,,,',
+',,285,,,275,,,,,,275,,275,,275,275,,275,275,275,,275,275,,,,275,275',
+'72,72,275,,72,275,72,,,,,,,275,,,,,,275,,,72,,,,,,72,,72,,72,72,,72',
+'72,72,,72,72,,,,72,72,73,73,72,,73,72,73,,,,,,,72,,,,,,72,,,73,,,,,',
+'73,,73,,73,73,,73,73,73,,73,73,,,,73,73,74,74,73,,74,73,74,,,,,,,73',
+',,,,,73,,,74,,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,74,74,75,75,74,',
+'75,74,75,,,,,,,74,,,,,,74,,,75,,,,,,75,,75,,75,75,,75,75,75,,75,75,',
+',,75,75,76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,,,,,,76,,76,,76,76',
+',76,76,76,,76,76,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,',
+',,,,77,,77,,77,77,,77,77,77,,77,77,,,,77,77,78,78,77,,78,77,78,,,,,',
+',77,,,,,,77,,,78,,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,78,78,79,79',
+'78,,79,78,79,,,,,,,78,,,,,,78,,,79,,,,,,79,,79,,79,79,,79,79,79,,79',
+'79,,,,79,79,80,80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,,,,,,80,,80,,80',
+'80,,80,80,80,,80,80,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,,81',
+',,,,,81,,81,,81,81,,81,81,81,,81,81,,,,81,81,82,82,81,,82,81,82,,,,',
+',,81,,,,,,81,,,82,,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,82,82,83,83',
+'82,,83,82,83,,,,,,,82,,,,,,82,,,83,,,,,,83,,83,,83,83,,83,83,83,,83',
+'83,,,,83,83,84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,,,,,,84,,84,,84',
+'84,,84,84,84,,84,84,,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85',
+',,,,,85,,85,,85,85,,85,85,85,,85,85,,,,85,85,86,86,85,,86,85,86,,,,',
+',,85,,,,,,85,,,86,,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,86,86,167,167',
+'86,,167,86,167,,,,,,,86,,,,,,86,,,167,,,,,,167,,167,,167,167,,167,167',
+'167,,167,167,,,,167,167,88,88,167,,88,167,88,,,,,,,167,,,,,,167,,,88',
+',,,,,88,,88,,88,88,,88,88,88,,88,88,,,,88,88,68,68,88,,68,88,68,,,,',
+',,88,,,,,,88,,,68,,,,,,68,,68,,68,68,,68,68,68,,68,68,,,,68,68,268,268',
+'68,,268,68,268,,,,,,,68,,,,,,68,,,268,,,,,,268,,268,,268,268,,268,268',
+'268,,268,268,,,,268,268,91,91,268,,91,268,91,,,,,,,268,,,,,,268,,,91',
+',,,,,91,91,91,91,91,91,91,91,91,91,,91,91,,,,91,91,91,91,91,263,263',
+'91,,263,,263,263,,,91,,,,,91,91,,,,,,263,,,,,,263,,263,,263,263,,263',
+'263,263,,263,263,263,,,263,263,93,93,263,,93,263,93,,,,,,,263,,,,,,263',
+',,93,,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,93,93,94,94,93,,94,93,94',
+',,,,,,93,,,,,,93,,,94,,,,,,94,,94,,94,94,,94,94,94,,94,94,,,,94,94,259',
+'259,94,,259,94,259,,,,,,,94,,,,,,94,,,259,,,,,,259,,259,,259,259,,259',
+'259,259,,259,259,,,,259,259,245,245,259,,245,259,245,,,,,,,259,,,,,',
+'259,,,245,,,,,,245,,245,,245,245,,245,245,245,,245,245,,,,245,245,244',
+'244,245,,244,245,244,,,,,,,245,,,,,,245,,,244,,,,,,244,,244,,244,244',
+',244,244,244,,244,244,,,,244,244,67,67,244,,67,244,67,,,,,,,244,,,,',
+',244,,,67,,,,,,67,,67,,67,67,,67,67,67,,67,67,67,,,67,67,99,99,67,,99',
+'67,99,,,,,,,67,,,,,,67,,,99,99,,,,,99,,99,,99,99,,99,99,99,,99,99,,',
+',99,99,241,241,99,,241,99,241,,,,,,,99,,,,,,99,,,241,,,,,,241,,241,',
+'241,241,,241,241,241,,241,241,,,,241,241,235,235,241,,235,241,235,,',
+',,,,241,,,,,,241,,,235,,,,,,235,,235,,235,235,,235,235,235,,235,235',
+',,,235,235,234,234,235,,234,235,234,,,,,,,235,,,,,,235,,,234,,,,,,234',
+',234,,234,234,,234,234,234,,234,234,,,,234,234,106,106,234,,106,234',
+'106,,,,,,,234,,,,,,234,,,106,106,,,,,106,,106,,106,106,,106,106,106',
+',106,106,,,,106,106,66,66,106,,66,106,66,,,,,,,106,,,,,,106,,,66,,,',
+',,66,,66,,66,66,,66,66,66,,66,66,66,,,66,66,65,65,66,,65,66,65,,,,,',
+',66,,,,,,66,,,65,,,,,,65,,65,,65,65,,65,65,65,,65,65,65,,,65,65,64,64',
+'65,,64,65,64,,,,,,,65,,,,,,65,,,64,,,,,,64,,64,,64,64,,64,64,64,,64',
+'64,64,,,64,64,63,63,64,,63,64,63,,,,,,,64,,,,,,64,,,63,,,,,,63,,63,',
+'63,63,,63,63,63,,63,63,63,,,63,63,112,112,63,,112,63,112,,,,,,,63,,',
+',,,63,,,112,,,,,,112,,112,,112,112,,112,112,112,,112,112,,,,112,112',
+'232,232,112,,232,112,232,,,,,,,112,,,,,,112,,,232,,,,,,232,,232,,232',
+'232,,232,232,232,,232,232,,,,232,232,227,227,232,,227,232,227,,,,,,',
+'232,,,,,,232,,,227,,,,,,227,,227,,227,227,,227,227,227,,227,227,,,,227',
+'227,,,227,223,223,227,,223,223,223,,,,227,,,,,,227,,,,,,223,,,,,,223',
+',223,,223,223,,223,223,223,,223,223,,,,223,223,286,286,223,,286,223',
+'286,286,,,,,,223,,,,,,223,,,286,,,,,,286,,286,,286,286,,286,286,286',
+',286,286,286,,,286,286,69,69,286,,69,286,69,,,,,,,286,,,,,,286,,,69',
+',,,,,69,,69,,69,69,,69,69,69,,69,69,,,,69,69,212,212,69,,212,69,212',
+',,,,,,69,,,,,,69,,,212,,,,,,212,,212,,212,212,,212,212,212,,212,212',
+',,,212,212,211,211,212,,211,212,211,211,,,,,,212,,,,,,212,,,211,,,,',
+',211,,211,,211,211,,211,211,211,,211,211,211,,,211,211,61,61,211,,61',
+'211,61,,,,,,,211,,,,,,211,,,61,,,,,,61,,61,,61,61,,61,61,61,,61,61,61',
+',,61,61,210,210,61,,210,61,210,210,,,,,,61,,,,,,61,,,210,,,,,,210,,210',
+',210,210,,210,210,210,,210,210,210,,,210,210,203,203,210,,203,210,203',
+'203,,,,,,210,,,,,,210,,,203,,,,,,203,,203,,203,203,,203,203,203,,203',
+'203,203,,,203,203,52,52,203,,52,203,52,,,,,,,203,,,,,,203,,,52,,,,,',
+'52,,52,,52,52,,52,52,52,,52,52,,,,52,52,172,172,52,,172,52,172,,,,,',
+',52,,,,,,52,,,172,,,,,,172,,172,,172,172,,172,172,172,,172,172,,,,172',
+'172,,,172,47,47,172,,47,47,47,,,,172,,,,,,172,,,,,,47,,,,,,47,,47,,47',
+'47,,47,47,47,,47,47,,,,47,47,297,297,47,,297,47,297,,,,,,,47,,,,,,47',
+',,297,,,,,,297,,297,,297,297,,297,297,297,,297,297,,,,297,297,171,171',
+'297,,171,297,171,,,,,,,297,,,,,,297,,,171,,,,,,171,,171,,171,171,,171',
+'171,171,,171,171,,,,171,171,170,170,171,,170,171,170,,,,,,,171,,,,,',
+'171,,,170,,,,,,170,,170,,170,170,,170,170,170,,170,170,,,,170,170,41',
+'41,170,,41,170,41,,,,,,,170,,,,,,170,,,41,,,,,,41,,41,,41,41,,41,41',
+'41,,41,41,,,,41,41,40,40,41,,40,41,40,,,,,,,41,,,,,,41,,,40,,,,,,40',
+',40,,40,40,,40,40,40,,40,40,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,',
+',,40,,,39,,,,,,39,,39,,39,39,,39,39,39,,39,39,,,,39,39,38,38,39,,38',
+'39,38,,,,,,,39,,,,,,39,,,38,,,,,,38,,38,,38,38,,38,38,38,,38,38,,,,38',
+'38,315,315,38,,315,38,315,,,,,,,38,,,,,,38,,,315,,,,,,315,,315,,315',
+'315,,315,315,315,,315,315,,,,315,315,327,327,315,,327,315,327,327,,',
+',,,315,,,,,,315,,,327,,,,,,327,,327,,327,327,,327,327,327,,327,327,327',
+',,327,327,13,13,327,,13,327,13,,,,,,,327,,,,,,327,,,13,,,,,,13,,13,',
+'13,13,,13,13,13,,13,13,,,,13,13,12,12,13,,12,13,12,,,,,,,13,,,,,,13',
+',,12,,,,,,12,,12,,12,12,,12,12,12,,12,12,,,,12,12,11,11,12,,11,12,11',
+',,,,,,12,,,,,,12,,,11,,,,,,11,,11,,11,11,,11,11,11,,11,11,,,,11,11,344',
+'344,11,,344,11,344,344,,,,,,11,,,,,,11,,,344,,,,,,344,,344,,344,344',
+',344,344,344,,344,344,344,,,344,344,346,346,344,,346,344,346,346,,,',
+',,344,,,,,,344,,,346,,,,,,346,,346,,346,346,,346,346,346,,346,346,346',
+',,346,346,4,4,346,,4,346,4,,,,,,,346,,,,,,346,,,4,,,,,,4,,4,,4,4,,4',
+'4,4,4,4,4,4,,,4,4,347,347,4,,347,4,347,347,,,,,,4,,,,,,4,,,347,,,,,',
+'347,,347,,347,347,,347,347,347,,347,347,347,,,347,347,0,0,347,,0,347',
+'0,,,,,,,347,,,,,,347,,,0,,,,,,0,,0,,0,0,,0,0,0,,0,0,0,,,0,0,87,87,0',
+',87,0,87,,,,,,,0,,,,,,0,,,87,,,,,,87,,87,,87,87,,87,87,87,,87,87,,,',
+'87,87,,,87,,,87,,,,192,192,192,192,87,192,192,192,192,192,87,192,192',
+',,,,,192,192,192,238,238,238,238,,238,238,238,238,238,,238,238,,,192',
+'192,,238,238,238,243,243,243,243,,243,243,243,243,243,,243,243,,,238',
+'238,,243,243,243,,,,,,,,,,,,,,,,243,243' ]
+        racc_action_check = arr = ::Array.new(4804, nil)
+        idx = 0
+        clist.each do |str|
+          str.split(',', -1).each do |i|
+            arr[idx] = i.to_i unless i.empty?
+            idx += 1
+          end
+        end
+
+racc_action_pointer = [
+  4621,   340,   nil,   nil,  4529,   327,   nil,   219,   nil,   nil,
+   247,  4391,  4345,  4299,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   309,   115,   288,  4161,  4115,
+  4069,  4023,    -6,   262,    95,   208,   nil,  3839,   135,   nil,
+   nil,   nil,  3744,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   285,  3606,   267,  3189,  3143,  3097,  3051,  2775,  2358,  3468,
+     5,    16,  1576,  1622,  1668,  1714,  1760,  1806,  1852,  1898,
+  1944,  1990,  2036,  2082,  2128,  2174,  2220,  4667,  2312,  1386,
+   154,  2450,   165,  2545,  2591,   213,    43,   112,  1327,  2821,
+   nil,   nil,   nil,    47,   149,   -31,  3005,  1275,   nil,  1214,
+  1153,  1092,  3235,   286,   nil,   nil,   nil,   290,    -8,   nil,
+   nil,   nil,   nil,   nil,  1031,    19,   nil,    99,   nil,   nil,
+    98,   979,   nil,   219,   nil,   215,   nil,   nil,   nil,   nil,
+   nil,    23,   156,   nil,   nil,   nil,   nil,   771,   701,   427,
+   212,   122,   527,   503,   475,   375,   399,   347,   323,   271,
+   186,   142,    98,    46,    -6,   nil,   nil,  2266,   nil,   579,
+  3977,  3931,  3790,   268,   281,   nil,   nil,     5,   nil,   -10,
+    25,    54,    48,   187,    29,    28,   nil,   nil,   nil,   nil,
+   nil,   nil,  4694,   875,   242,   nil,   264,   nil,   255,    71,
+   220,   nil,   nil,  3698,   nil,   137,   nil,   117,     2,   nil,
+  3652,  3560,  3514,  1435,   214,   280,   nil,     0,   330,   309,
+    68,   nil,   219,  3376,   nil,   nil,    83,  3327,   nil,   nil,
+   nil,   nil,  3281,   nil,  2959,  2913,   111,   nil,  4715,   nil,
+    36,  2867,    72,  4736,  2729,  2683,   124,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,  2637,
+   142,   nil,   nil,  2499,   171,   nil,   117,   153,  2404,   nil,
+   197,    26,   211,   194,    12,  1530,   nil,   197,   232,   240,
+   243,   nil,    49,   nil,   247,  1484,  3422,   nil,   nil,   927,
+   nil,   nil,   nil,   823,   nil,   753,   279,  3885,   286,   nil,
+   nil,   nil,   nil,   683,   631,   300,    28,   302,   nil,   nil,
+   nil,   nil,   451,   145,   nil,  4207,    -2,   287,   nil,   311,
+   312,   nil,   nil,   nil,   313,   323,   nil,  4253,   nil,   nil,
+   nil,   309,   326,   nil,   327,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,  4437,   nil,  4483,  4575,   nil,   nil,
+   334,   nil,   nil,   nil,   nil,   338,   nil,   339,   nil,   341,
+   nil,   nil,   nil,   nil,   nil ]
+
+racc_action_default = [
+  -209,  -210,    -1,    -2,    -3,    -4,    -7,    -9,   -10,   -15,
+  -109,  -210,  -210,  -210,   -43,   -44,   -45,   -46,   -47,   -48,
+   -49,   -50,   -51,   -52,   -53,   -54,   -55,   -56,   -57,   -58,
+   -59,   -60,   -61,   -62,   -63,   -68,   -69,   -73,  -210,  -210,
+  -210,  -210,  -210,  -119,  -210,  -210,  -164,  -210,  -210,  -174,
+  -175,  -176,  -210,  -178,  -185,  -186,  -187,  -188,  -189,  -190,
+  -210,  -210,    -6,  -210,  -210,  -210,  -210,  -210,  -210,  -210,
+  -210,  -210,  -210,  -210,  -210,  -210,  -210,  -210,  -210,  -210,
+  -210,  -210,  -210,  -210,  -210,  -210,  -210,  -210,  -210,  -210,
+  -210,  -127,  -122,  -209,  -209,   -27,  -210,   -34,  -210,  -210,
+   -70,   -75,   -76,  -209,  -210,  -210,  -210,  -210,   -86,  -210,
+  -210,  -210,  -210,  -209,  -153,  -154,  -120,  -209,  -209,  -145,
+  -147,  -148,  -149,  -150,   -41,  -210,  -167,  -210,  -170,  -171,
+  -210,  -182,  -177,  -210,   365,    -5,    -8,   -11,   -12,   -13,
+   -14,  -210,   -17,   -18,  -162,  -163,   -19,   -20,   -21,   -22,
+   -23,   -24,   -25,   -26,   -28,   -29,   -30,   -31,   -32,   -33,
+   -35,   -36,   -37,   -38,  -210,   -39,  -104,  -210,   -74,  -210,
+  -202,  -208,  -196,  -193,  -191,  -117,  -128,  -185,  -131,  -189,
+  -210,  -199,  -197,  -205,  -187,  -188,  -195,  -200,  -201,  -203,
+  -204,  -206,  -127,  -126,  -210,  -125,  -210,   -40,  -191,   -65,
+  -210,   -80,   -81,  -210,   -84,  -191,  -158,  -161,  -210,   -72,
+  -210,  -210,  -210,  -127,  -193,  -209,  -155,  -210,  -210,  -210,
+  -210,  -151,  -210,  -210,  -165,  -168,  -210,  -210,  -179,  -180,
+  -181,  -183,  -210,   -16,  -210,  -210,  -191,  -106,  -127,  -116,
+  -210,  -194,  -210,  -192,  -210,  -210,  -191,  -130,  -132,  -196,
+  -197,  -198,  -199,  -202,  -205,  -207,  -208,  -123,  -124,  -192,
+  -210,   -67,   -77,  -210,  -210,   -83,  -210,  -192,  -210,   -71,
+  -210,   -89,  -210,   -95,  -210,  -210,   -99,  -193,  -191,  -210,
+  -210,  -139,  -210,  -156,  -191,  -210,  -210,  -146,  -152,   -42,
+  -166,  -169,  -172,  -173,  -184,  -108,  -210,  -192,  -191,  -112,
+  -118,  -113,  -129,  -133,  -134,  -210,   -64,  -210,   -79,   -82,
+   -85,  -159,  -160,   -89,   -88,  -210,  -210,   -95,   -94,  -210,
+  -210,  -103,   -98,  -100,  -210,  -210,  -114,  -210,  -140,  -141,
+  -142,  -210,  -210,  -136,  -210,  -144,  -105,  -107,  -115,  -121,
+   -66,   -78,   -87,   -90,  -210,   -93,  -210,  -210,  -110,  -111,
+  -210,  -138,  -157,  -135,  -143,  -210,   -92,  -210,   -97,  -210,
+  -102,  -137,   -91,   -96,  -101 ]
+
+racc_goto_table = [
+     2,   117,   100,    95,    97,    98,     3,   132,   129,   166,
+   174,   240,   130,   205,   314,   318,   123,   121,   215,   173,
+     1,   276,   218,   320,   287,    62,   288,   242,   194,   196,
+   107,   109,   110,   111,   145,   145,   125,   143,   146,   124,
+   214,   144,   144,   236,   131,   137,   138,   139,   140,   275,
+   300,   260,   279,   238,   343,   302,   342,   141,   266,   345,
+   124,   142,   262,   200,   147,   148,   149,   150,   151,   152,
+   153,   154,   155,   156,   157,   158,   159,   160,   161,   162,
+   163,   164,   135,   169,   323,   193,   193,   237,   198,   296,
+   280,   124,   328,   219,   203,   208,   311,   127,   124,   305,
+   165,   136,   231,   232,   169,   230,   nil,   nil,   nil,   201,
+   nil,   246,   nil,   nil,   nil,   324,   nil,   nil,   nil,   216,
+   nil,   nil,   nil,   216,   221,   284,   nil,   nil,   nil,   nil,
+   nil,   325,   278,   nil,   nil,   nil,   nil,   331,   117,   nil,
+   nil,   277,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   338,   nil,   nil,   123,   121,   nil,   298,   nil,   164,
+   nil,   nil,   107,   109,   110,   261,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   292,   294,   nil,   nil,
+   130,   123,   121,   123,   121,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   264,   124,   169,   nil,   nil,   nil,   nil,
+   270,   272,   nil,   nil,   nil,   289,   nil,   337,   nil,   293,
+   nil,   281,   nil,   nil,   131,   nil,   289,   295,   nil,   nil,
+   nil,   nil,   nil,   169,   nil,   nil,   303,   304,   nil,   329,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   289,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   312,   nil,   nil,   307,   nil,   nil,   nil,   124,   nil,   nil,
+   nil,   nil,   340,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   332,   334,   nil,   nil,   164,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   107,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   350,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   355,   nil,   357,   359 ]
+
+racc_goto_check = [
+     2,    65,    37,     9,     9,     9,     3,    78,    74,    52,
+    57,    56,    31,    45,    47,    48,    30,    35,    66,    55,
+     1,    50,    66,    51,    71,     5,    71,    36,    61,    61,
+     9,     9,     9,     9,    31,    31,    11,    12,    12,     9,
+    55,    30,    30,    53,     9,     7,     7,     7,     7,    49,
+    58,    36,    56,    59,    46,    62,    47,    11,    36,    48,
+     9,     9,    44,    43,     9,     9,     9,     9,     9,     9,
+     9,     9,     9,     9,     9,     9,     9,     9,     9,     9,
+     9,     9,     5,     9,    50,     9,     9,    52,    11,    36,
+    67,     9,    68,    70,    42,    11,    72,    73,     9,    36,
+    13,     6,    79,    80,     9,    82,   nil,   nil,   nil,     3,
+   nil,    57,   nil,   nil,   nil,    56,   nil,   nil,   nil,     3,
+   nil,   nil,   nil,     3,     3,    45,   nil,   nil,   nil,   nil,
+   nil,    36,    57,   nil,   nil,   nil,   nil,    36,    65,   nil,
+   nil,    55,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,    36,   nil,   nil,    30,    35,   nil,    57,   nil,     9,
+   nil,   nil,     9,     9,     9,    37,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,    74,    78,   nil,   nil,
+    31,    30,    35,    30,    35,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,     2,     9,     9,   nil,   nil,   nil,   nil,
+     2,     2,   nil,   nil,   nil,     9,   nil,    52,   nil,     9,
+   nil,     3,   nil,   nil,     9,   nil,     9,     9,   nil,   nil,
+   nil,   nil,   nil,     9,   nil,   nil,     9,     9,   nil,    65,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,     9,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+     9,   nil,   nil,     2,   nil,   nil,   nil,     9,   nil,   nil,
+   nil,   nil,    37,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,     2,     2,   nil,   nil,     9,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,     9,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,     2,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,     2,   nil,     2,     2 ]
+
+racc_goto_pointer = [
+   nil,    20,     0,     6,   nil,    21,    38,   -19,   nil,    -8,
+   nil,   -11,   -33,    11,   nil,   nil,   nil,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,   nil,
+   -29,   -36,   nil,   nil,   nil,   -28,  -147,   -34,   nil,   nil,
+   nil,   nil,   -10,   -40,  -138,   -92,  -261,  -257,  -258,  -163,
+  -191,  -251,   -80,  -124,   nil,   -72,  -162,   -81,  -191,  -116,
+   nil,   -65,  -188,   nil,   nil,   -43,   -95,  -125,  -190,   nil,
+   -25,  -196,  -171,    49,   -40,   nil,   nil,   nil,   -45,   -31,
+   -30,   nil,   -28 ]
+
+racc_goto_default = [
+   nil,   nil,   nil,   195,     4,     5,     6,     7,     8,    10,
+     9,   274,   nil,   nil,    14,    35,    15,    16,    17,    18,
+    19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
+    29,    30,    31,    32,    33,    34,   nil,   nil,    36,    37,
+   101,   102,   103,   nil,   nil,   nil,   108,   nil,   nil,   nil,
+   nil,   nil,   nil,   nil,    41,   nil,   nil,   nil,   175,   nil,
+    92,   nil,   176,   180,   178,   113,   nil,   nil,   nil,   118,
+   nil,   119,   206,   nil,   nil,    49,    50,    52,   nil,   nil,
+   nil,   133,   nil ]
+
+racc_reduce_table = [
+  0, 0, :racc_error,
+  1, 78, :_reduce_1,
+  1, 78, :_reduce_none,
+  1, 79, :_reduce_3,
+  1, 81, :_reduce_4,
+  3, 81, :_reduce_5,
+  2, 81, :_reduce_6,
+  1, 82, :_reduce_7,
+  3, 82, :_reduce_8,
+  1, 83, :_reduce_none,
+  1, 84, :_reduce_10,
+  3, 84, :_reduce_11,
+  3, 84, :_reduce_12,
+  3, 84, :_reduce_13,
+  3, 84, :_reduce_14,
+  1, 86, :_reduce_none,
+  4, 86, :_reduce_16,
+  3, 86, :_reduce_17,
+  3, 86, :_reduce_18,
+  3, 86, :_reduce_19,
+  3, 86, :_reduce_20,
+  3, 86, :_reduce_21,
+  3, 86, :_reduce_22,
+  3, 86, :_reduce_23,
+  3, 86, :_reduce_24,
+  3, 86, :_reduce_25,
+  3, 86, :_reduce_26,
+  2, 86, :_reduce_27,
+  3, 86, :_reduce_28,
+  3, 86, :_reduce_29,
+  3, 86, :_reduce_30,
+  3, 86, :_reduce_31,
+  3, 86, :_reduce_32,
+  3, 86, :_reduce_33,
+  2, 86, :_reduce_34,
+  3, 86, :_reduce_35,
+  3, 86, :_reduce_36,
+  3, 86, :_reduce_37,
+  3, 86, :_reduce_38,
+  3, 86, :_reduce_39,
+  3, 86, :_reduce_40,
+  1, 88, :_reduce_41,
+  3, 88, :_reduce_42,
+  1, 87, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 92, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 93, :_reduce_none,
+  1, 108, :_reduce_62,
+  1, 108, :_reduce_63,
+  5, 91, :_reduce_64,
+  3, 91, :_reduce_65,
+  6, 91, :_reduce_66,
+  4, 91, :_reduce_67,
+  1, 91, :_reduce_68,
+  1, 95, :_reduce_69,
+  2, 95, :_reduce_70,
+  4, 115, :_reduce_71,
+  3, 115, :_reduce_72,
+  1, 115, :_reduce_73,
+  3, 116, :_reduce_74,
+  1, 114, :_reduce_none,
+  1, 114, :_reduce_none,
+  3, 117, :_reduce_77,
+  3, 121, :_reduce_78,
+  2, 121, :_reduce_79,
+  1, 120, :_reduce_none,
+  1, 120, :_reduce_none,
+  4, 118, :_reduce_82,
+  3, 118, :_reduce_83,
+  2, 119, :_reduce_84,
+  4, 119, :_reduce_85,
+  2, 98, :_reduce_86,
+  5, 123, :_reduce_87,
+  4, 123, :_reduce_88,
+  0, 124, :_reduce_none,
+  2, 124, :_reduce_90,
+  4, 124, :_reduce_91,
+  3, 124, :_reduce_92,
+  6, 99, :_reduce_93,
+  5, 99, :_reduce_94,
+  0, 125, :_reduce_none,
+  4, 125, :_reduce_96,
+  3, 125, :_reduce_97,
+  5, 97, :_reduce_98,
+  1, 126, :_reduce_99,
+  2, 126, :_reduce_100,
+  5, 127, :_reduce_101,
+  4, 127, :_reduce_102,
+  1, 128, :_reduce_103,
+  1, 90, :_reduce_none,
+  4, 90, :_reduce_105,
+  1, 130, :_reduce_106,
+  3, 130, :_reduce_107,
+  3, 129, :_reduce_108,
+  1, 85, :_reduce_109,
+  6, 85, :_reduce_110,
+  6, 85, :_reduce_111,
+  5, 85, :_reduce_112,
+  5, 85, :_reduce_113,
+  5, 85, :_reduce_114,
+  4, 135, :_reduce_115,
+  1, 136, :_reduce_116,
+  1, 132, :_reduce_117,
+  3, 132, :_reduce_118,
+  1, 131, :_reduce_119,
+  2, 131, :_reduce_120,
+  6, 96, :_reduce_121,
+  2, 96, :_reduce_122,
+  3, 137, :_reduce_123,
+  3, 137, :_reduce_124,
+  1, 138, :_reduce_none,
+  1, 138, :_reduce_none,
+  0, 134, :_reduce_127,
+  1, 134, :_reduce_128,
+  3, 134, :_reduce_129,
+  1, 140, :_reduce_none,
+  1, 140, :_reduce_none,
+  1, 140, :_reduce_none,
+  3, 139, :_reduce_133,
+  3, 139, :_reduce_134,
+  6, 100, :_reduce_135,
+  5, 100, :_reduce_136,
+  7, 101, :_reduce_137,
+  6, 101, :_reduce_138,
+  1, 144, :_reduce_none,
+  2, 144, :_reduce_140,
+  1, 145, :_reduce_none,
+  1, 145, :_reduce_none,
+  6, 102, :_reduce_143,
+  5, 102, :_reduce_144,
+  1, 146, :_reduce_145,
+  3, 146, :_reduce_146,
+  1, 148, :_reduce_147,
+  1, 148, :_reduce_148,
+  1, 148, :_reduce_149,
+  1, 148, :_reduce_none,
+  1, 147, :_reduce_none,
+  2, 147, :_reduce_152,
+  1, 142, :_reduce_153,
+  1, 142, :_reduce_154,
+  1, 143, :_reduce_155,
+  2, 143, :_reduce_156,
+  4, 143, :_reduce_157,
+  1, 122, :_reduce_158,
+  3, 122, :_reduce_159,
+  3, 149, :_reduce_160,
+  1, 149, :_reduce_161,
+  1, 89, :_reduce_none,
+  1, 89, :_reduce_none,
+  1, 94, :_reduce_164,
+  3, 103, :_reduce_165,
+  4, 103, :_reduce_166,
+  2, 103, :_reduce_167,
+  3, 106, :_reduce_168,
+  4, 106, :_reduce_169,
+  2, 106, :_reduce_170,
+  1, 150, :_reduce_171,
+  3, 150, :_reduce_172,
+  3, 151, :_reduce_173,
+  1, 112, :_reduce_none,
+  1, 112, :_reduce_none,
+  1, 152, :_reduce_176,
+  2, 153, :_reduce_177,
+  1, 154, :_reduce_178,
+  1, 156, :_reduce_179,
+  1, 157, :_reduce_180,
+  2, 155, :_reduce_181,
+  1, 158, :_reduce_182,
+  1, 159, :_reduce_183,
+  2, 159, :_reduce_184,
+  1, 111, :_reduce_185,
+  1, 109, :_reduce_186,
+  1, 110, :_reduce_187,
+  1, 105, :_reduce_188,
+  1, 104, :_reduce_189,
+  1, 107, :_reduce_190,
+  0, 113, :_reduce_none,
+  1, 113, :_reduce_192,
+  0, 133, :_reduce_none,
+  1, 133, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  1, 141, :_reduce_none,
+  0, 80, :_reduce_209 ]
+
+racc_reduce_n = 210
+
+racc_shift_n = 365
+
+racc_token_table = {
+  false => 0,
+  :error => 1,
+  :STRING => 2,
+  :DQPRE => 3,
+  :DQMID => 4,
+  :DQPOST => 5,
+  :LBRACK => 6,
+  :RBRACK => 7,
+  :LBRACE => 8,
+  :RBRACE => 9,
+  :SYMBOL => 10,
+  :FARROW => 11,
+  :COMMA => 12,
+  :TRUE => 13,
+  :FALSE => 14,
+  :EQUALS => 15,
+  :APPENDS => 16,
+  :LESSEQUAL => 17,
+  :NOTEQUAL => 18,
+  :DOT => 19,
+  :COLON => 20,
+  :LLCOLLECT => 21,
+  :RRCOLLECT => 22,
+  :QMARK => 23,
+  :LPAREN => 24,
+  :RPAREN => 25,
+  :ISEQUAL => 26,
+  :GREATEREQUAL => 27,
+  :GREATERTHAN => 28,
+  :LESSTHAN => 29,
+  :IF => 30,
+  :ELSE => 31,
+  :DEFINE => 32,
+  :ELSIF => 33,
+  :VARIABLE => 34,
+  :CLASS => 35,
+  :INHERITS => 36,
+  :NODE => 37,
+  :BOOLEAN => 38,
+  :NAME => 39,
+  :SEMIC => 40,
+  :CASE => 41,
+  :DEFAULT => 42,
+  :AT => 43,
+  :LCOLLECT => 44,
+  :RCOLLECT => 45,
+  :CLASSREF => 46,
+  :NOT => 47,
+  :OR => 48,
+  :AND => 49,
+  :UNDEF => 50,
+  :PARROW => 51,
+  :PLUS => 52,
+  :MINUS => 53,
+  :TIMES => 54,
+  :DIV => 55,
+  :LSHIFT => 56,
+  :RSHIFT => 57,
+  :UMINUS => 58,
+  :MATCH => 59,
+  :NOMATCH => 60,
+  :REGEX => 61,
+  :IN_EDGE => 62,
+  :OUT_EDGE => 63,
+  :IN_EDGE_SUB => 64,
+  :OUT_EDGE_SUB => 65,
+  :IN => 66,
+  :UNLESS => 67,
+  :PIPE => 68,
+  :LAMBDA => 69,
+  :SELBRACE => 70,
+  :LOW => 71,
+  :HIGH => 72,
+  :CALL => 73,
+  :MODULO => 74,
+  :TITLE_COLON => 75,
+  :CASE_COLON => 76 }
+
+racc_nt_base = 77
+
+racc_use_result_var = true
+
+Racc_arg = [
+  racc_action_table,
+  racc_action_check,
+  racc_action_default,
+  racc_action_pointer,
+  racc_goto_table,
+  racc_goto_check,
+  racc_goto_default,
+  racc_goto_pointer,
+  racc_nt_base,
+  racc_reduce_table,
+  racc_token_table,
+  racc_shift_n,
+  racc_reduce_n,
+  racc_use_result_var ]
+
+Racc_token_to_s_table = [
+  "$end",
+  "error",
+  "STRING",
+  "DQPRE",
+  "DQMID",
+  "DQPOST",
+  "LBRACK",
+  "RBRACK",
+  "LBRACE",
+  "RBRACE",
+  "SYMBOL",
+  "FARROW",
+  "COMMA",
+  "TRUE",
+  "FALSE",
+  "EQUALS",
+  "APPENDS",
+  "LESSEQUAL",
+  "NOTEQUAL",
+  "DOT",
+  "COLON",
+  "LLCOLLECT",
+  "RRCOLLECT",
+  "QMARK",
+  "LPAREN",
+  "RPAREN",
+  "ISEQUAL",
+  "GREATEREQUAL",
+  "GREATERTHAN",
+  "LESSTHAN",
+  "IF",
+  "ELSE",
+  "DEFINE",
+  "ELSIF",
+  "VARIABLE",
+  "CLASS",
+  "INHERITS",
+  "NODE",
+  "BOOLEAN",
+  "NAME",
+  "SEMIC",
+  "CASE",
+  "DEFAULT",
+  "AT",
+  "LCOLLECT",
+  "RCOLLECT",
+  "CLASSREF",
+  "NOT",
+  "OR",
+  "AND",
+  "UNDEF",
+  "PARROW",
+  "PLUS",
+  "MINUS",
+  "TIMES",
+  "DIV",
+  "LSHIFT",
+  "RSHIFT",
+  "UMINUS",
+  "MATCH",
+  "NOMATCH",
+  "REGEX",
+  "IN_EDGE",
+  "OUT_EDGE",
+  "IN_EDGE_SUB",
+  "OUT_EDGE_SUB",
+  "IN",
+  "UNLESS",
+  "PIPE",
+  "LAMBDA",
+  "SELBRACE",
+  "LOW",
+  "HIGH",
+  "CALL",
+  "MODULO",
+  "TITLE_COLON",
+  "CASE_COLON",
+  "$start",
+  "program",
+  "statements",
+  "nil",
+  "syntactic_statements",
+  "syntactic_statement",
+  "any_expression",
+  "relationship_expression",
+  "resource_expression",
+  "expression",
+  "higher_precedence",
+  "expressions",
+  "match_rvalue",
+  "selector_entries",
+  "call_function_expression",
+  "primary_expression",
+  "literal_expression",
+  "variable",
+  "call_method_with_lambda_expression",
+  "collection_expression",
+  "case_expression",
+  "if_expression",
+  "unless_expression",
+  "definition_expression",
+  "hostclass_expression",
+  "node_definition_expression",
+  "array",
+  "boolean",
+  "default",
+  "hash",
+  "regex",
+  "text_or_name",
+  "type",
+  "undef",
+  "name",
+  "quotedtext",
+  "endcomma",
+  "lambda",
+  "call_method_expression",
+  "named_access",
+  "lambda_j8",
+  "lambda_ruby",
+  "lambda_parameter_list",
+  "optional_farrow",
+  "lambda_rest",
+  "parameters",
+  "if_part",
+  "else",
+  "unless_else",
+  "case_options",
+  "case_option",
+  "case_colon",
+  "selector_entry",
+  "selector_entry_list",
+  "at",
+  "resourceinstances",
+  "endsemi",
+  "attribute_operations",
+  "resourceinst",
+  "title_colon",
+  "collect_query",
+  "optional_query",
+  "attribute_operation",
+  "attribute_name",
+  "keyword",
+  "classname",
+  "parameter_list",
+  "classparent",
+  "classnameordefault",
+  "hostnames",
+  "nodeparent",
+  "hostname",
+  "parameter",
+  "hashpairs",
+  "hashpair",
+  "string",
+  "dq_string",
+  "dqpre",
+  "dqrval",
+  "dqpost",
+  "dqmid",
+  "text_expression",
+  "dqtail" ]
+
+Racc_debug_parser = false
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 57)
+  def _reduce_1(val, _values, result)
+     result = Factory.block_or_expression(*val[0]) 
+    result
+  end
+.,.,
+
+# reduce 2 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 62)
+  def _reduce_3(val, _values, result)
+     result = transform_calls(val[0]) 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 68)
+  def _reduce_4(val, _values, result)
+     result = [val[0]]
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 69)
+  def _reduce_5(val, _values, result)
+     result = val[0].push val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 70)
+  def _reduce_6(val, _values, result)
+     result = val[0].push val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 74)
+  def _reduce_7(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 75)
+  def _reduce_8(val, _values, result)
+     result = aryfy(val[0]).push val[2] 
+    result
+  end
+.,.,
+
+# reduce 9 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 81)
+  def _reduce_10(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 82)
+  def _reduce_11(val, _values, result)
+     result = val[0].relop(val[1][:value], val[2]); loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 83)
+  def _reduce_12(val, _values, result)
+     result = val[0].relop(val[1][:value], val[2]); loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 84)
+  def _reduce_13(val, _values, result)
+     result = val[0].relop(val[1][:value], val[2]); loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 85)
+  def _reduce_14(val, _values, result)
+     result = val[0].relop(val[1][:value], val[2]); loc result, val[1] 
+    result
+  end
+.,.,
+
+# reduce 15 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 92)
+  def _reduce_16(val, _values, result)
+     result = val[0][*val[2]]         ; loc result, val[0], val[3] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 93)
+  def _reduce_17(val, _values, result)
+     result = val[0].in val[2]        ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 94)
+  def _reduce_18(val, _values, result)
+     result = val[0] =~  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 95)
+  def _reduce_19(val, _values, result)
+     result = val[0].mne val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 96)
+  def _reduce_20(val, _values, result)
+     result = val[0] +   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 97)
+  def _reduce_21(val, _values, result)
+     result = val[0] -   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 98)
+  def _reduce_22(val, _values, result)
+     result = val[0] /   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 99)
+  def _reduce_23(val, _values, result)
+     result = val[0] *   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 100)
+  def _reduce_24(val, _values, result)
+     result = val[0] %   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 101)
+  def _reduce_25(val, _values, result)
+     result = val[0] <<  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 102)
+  def _reduce_26(val, _values, result)
+     result = val[0] >>  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 103)
+  def _reduce_27(val, _values, result)
+     result = val[1].minus()          ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 104)
+  def _reduce_28(val, _values, result)
+     result = val[0].ne  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 105)
+  def _reduce_29(val, _values, result)
+     result = val[0] ==  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 106)
+  def _reduce_30(val, _values, result)
+     result = val[0] >   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 107)
+  def _reduce_31(val, _values, result)
+     result = val[0] >=  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 108)
+  def _reduce_32(val, _values, result)
+     result = val[0] <   val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 109)
+  def _reduce_33(val, _values, result)
+     result = val[0] <=  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 110)
+  def _reduce_34(val, _values, result)
+     result = val[1].not              ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 111)
+  def _reduce_35(val, _values, result)
+     result = val[0].and val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 112)
+  def _reduce_36(val, _values, result)
+     result = val[0].or  val[2]       ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 113)
+  def _reduce_37(val, _values, result)
+     result = val[0].set(val[2])      ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 114)
+  def _reduce_38(val, _values, result)
+     result = val[0].plus_set(val[2]) ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 115)
+  def _reduce_39(val, _values, result)
+     result = val[0].select(*val[2])  ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 116)
+  def _reduce_40(val, _values, result)
+     result = val[1].paren()          ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 124)
+  def _reduce_41(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 125)
+  def _reduce_42(val, _values, result)
+     result = val[0].push(val[2]) 
+    result
+  end
+.,.,
+
+# reduce 43 omitted
+
+# reduce 44 omitted
+
+# reduce 45 omitted
+
+# reduce 46 omitted
+
+# reduce 47 omitted
+
+# reduce 48 omitted
+
+# reduce 49 omitted
+
+# reduce 50 omitted
+
+# reduce 51 omitted
+
+# reduce 52 omitted
+
+# reduce 53 omitted
+
+# reduce 54 omitted
+
+# reduce 55 omitted
+
+# reduce 56 omitted
+
+# reduce 57 omitted
+
+# reduce 58 omitted
+
+# reduce 59 omitted
+
+# reduce 60 omitted
+
+# reduce 61 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 155)
+  def _reduce_62(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 156)
+  def _reduce_63(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 164)
+  def _reduce_64(val, _values, result)
+          result = Factory.CALL_NAMED(val[0], true, val[2])
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 168)
+  def _reduce_65(val, _values, result)
+          result = Factory.CALL_NAMED(val[0], true, [])
+      loc result, val[0], val[2]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 172)
+  def _reduce_66(val, _values, result)
+          result = Factory.CALL_NAMED(val[0], true, val[2])
+      loc result, val[0], val[4]
+      result.lambda = val[5]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 177)
+  def _reduce_67(val, _values, result)
+          result = Factory.CALL_NAMED(val[0], true, [])
+      loc result, val[0], val[2]
+      result.lambda = val[3]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 181)
+  def _reduce_68(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 186)
+  def _reduce_69(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 187)
+  def _reduce_70(val, _values, result)
+     result = val[0]; val[0].lambda = val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 190)
+  def _reduce_71(val, _values, result)
+     result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 191)
+  def _reduce_72(val, _values, result)
+     result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 192)
+  def _reduce_73(val, _values, result)
+     result = Factory.CALL_METHOD(val[0], []); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 197)
+  def _reduce_74(val, _values, result)
+            result = val[0].dot(Factory.fqn(val[2][:value]))
+        loc result, val[1], val[2]
+      
+    result
+  end
+.,.,
+
+# reduce 75 omitted
+
+# reduce 76 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 212)
+  def _reduce_77(val, _values, result)
+          result = Factory.LAMBDA(val[0], val[2])
+#      loc result, val[1] # TODO
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 217)
+  def _reduce_78(val, _values, result)
+     result = val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 218)
+  def _reduce_79(val, _values, result)
+     result = nil 
+    result
+  end
+.,.,
+
+# reduce 80 omitted
+
+# reduce 81 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 228)
+  def _reduce_82(val, _values, result)
+          result = Factory.LAMBDA(val[1], val[2])
+      loc result, val[0], val[3]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 232)
+  def _reduce_83(val, _values, result)
+          result = Factory.LAMBDA(val[1], nil)
+      loc result, val[0], val[2]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 238)
+  def _reduce_84(val, _values, result)
+     result = [] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 239)
+  def _reduce_85(val, _values, result)
+     result = val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 249)
+  def _reduce_86(val, _values, result)
+          result = val[1]
+      loc(result, val[0], val[1])
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 256)
+  def _reduce_87(val, _values, result)
+            result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4])
+        loc(result, val[0], (val[4] ? val[4] : val[3]))
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 260)
+  def _reduce_88(val, _values, result)
+            result = Factory.IF(val[0], nil, val[3])
+        loc(result, val[0], (val[3] ? val[3] : val[2]))
+      
+    result
+  end
+.,.,
+
+# reduce 89 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 268)
+  def _reduce_90(val, _values, result)
+            result = val[1]
+        loc(result, val[0], val[1])
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 272)
+  def _reduce_91(val, _values, result)
+            result = Factory.block_or_expression(*val[2])
+        loc result, val[0], val[3]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 276)
+  def _reduce_92(val, _values, result)
+            result = nil # don't think a nop is needed here either
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 285)
+  def _reduce_93(val, _values, result)
+          result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5])
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 289)
+  def _reduce_94(val, _values, result)
+          result = Factory.UNLESS(val[1], nil, nil)
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+# reduce 95 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 299)
+  def _reduce_96(val, _values, result)
+            result = Factory.block_or_expression(*val[2])
+        loc result, val[0], val[3]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 303)
+  def _reduce_97(val, _values, result)
+            result = nil # don't think a nop is needed here either
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 311)
+  def _reduce_98(val, _values, result)
+          result = Factory.CASE(val[1], *val[3])
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 317)
+  def _reduce_99(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 318)
+  def _reduce_100(val, _values, result)
+     result = val[0].push val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 323)
+  def _reduce_101(val, _values, result)
+            result = Factory.WHEN(val[0], val[3])
+        loc result, val[1], val[4]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 327)
+  def _reduce_102(val, _values, result)
+            result = Factory.WHEN(val[0], nil)
+        loc result, val[1], val[3]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 331)
+  def _reduce_103(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+# reduce 104 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 342)
+  def _reduce_105(val, _values, result)
+            result = val[1]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 347)
+  def _reduce_106(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 348)
+  def _reduce_107(val, _values, result)
+     result = val[0].push val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 353)
+  def _reduce_108(val, _values, result)
+     result = Factory.MAP(val[0], val[2]) ; loc result, val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 369)
+  def _reduce_109(val, _values, result)
+          result = val[0]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 372)
+  def _reduce_110(val, _values, result)
+          result = case Factory.resource_shape(val[1])
+      when :resource, :class
+        tmp = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3])
+        tmp.form = val[0]
+        tmp
+      when :defaults
+        error "A resource default can not be virtual or exported"
+      when :override
+        error "A resource override can not be virtual or exported"
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+      loc result, val[1], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 387)
+  def _reduce_111(val, _values, result)
+          result = case Factory.resource_shape(val[1])
+      when :resource, :class
+        error "Defaults are not virtualizable"
+      when :defaults
+        error "Defaults are not virtualizable"
+      when :override
+        error "Defaults are not virtualizable"
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 399)
+  def _reduce_112(val, _values, result)
+          result = case Factory.resource_shape(val[0])
+      when :resource, :class
+        Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
+      when :defaults
+        error "A resource default can not specify a resource name"
+      when :override
+        error "A resource override does not allow override of name of resource"
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 412)
+  def _reduce_113(val, _values, result)
+          result = case Factory.resource_shape(val[0])
+      when :resource, :class
+        # This catches deprecated syntax.
+        error "All resource specifications require names"
+      when :defaults
+        Factory.RESOURCE_DEFAULTS(val[0], val[2])
+      when :override
+        # This was only done for override in original - TODO shuld it be here at all
+        Factory.RESOURCE_OVERRIDE(val[0], val[2])
+      else
+        error "Expression is not valid as a resource, resource-default, or resource-override"
+      end
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 427)
+  def _reduce_114(val, _values, result)
+            result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
+        loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 432)
+  def _reduce_115(val, _values, result)
+     result = Factory.RESOURCE_BODY(val[0], val[2]) 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 434)
+  def _reduce_116(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 437)
+  def _reduce_117(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 438)
+  def _reduce_118(val, _values, result)
+     result = val[0].push val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 443)
+  def _reduce_119(val, _values, result)
+     result = :virtual  
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 444)
+  def _reduce_120(val, _values, result)
+     result = :exported 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 456)
+  def _reduce_121(val, _values, result)
+          result = Factory.COLLECT(val[0], val[1], val[3])
+      loc result, val[0], val[5]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 460)
+  def _reduce_122(val, _values, result)
+          result = Factory.COLLECT(val[0], val[1], [])
+      loc result, val[0], val[1]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 465)
+  def _reduce_123(val, _values, result)
+     result = Factory.VIRTUAL_QUERY(val[1])   ; loc result, val[0], val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 466)
+  def _reduce_124(val, _values, result)
+     result = Factory.EXPORTED_QUERY(val[1])  ; loc result, val[0], val[2] 
+    result
+  end
+.,.,
+
+# reduce 125 omitted
+
+# reduce 126 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 479)
+  def _reduce_127(val, _values, result)
+     result = [] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 480)
+  def _reduce_128(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 481)
+  def _reduce_129(val, _values, result)
+     result = val[0].push(val[2]) 
+    result
+  end
+.,.,
+
+# reduce 130 omitted
+
+# reduce 131 omitted
+
+# reduce 132 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 497)
+  def _reduce_133(val, _values, result)
+            result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2])
+        loc result, val[0], val[2]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 501)
+  def _reduce_134(val, _values, result)
+            result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2])
+        loc result, val[0], val[2]
+      
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 511)
+  def _reduce_135(val, _values, result)
+          result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])
+      loc result, val[0], val[5]
+      @lexer.indefine = false
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 516)
+  def _reduce_136(val, _values, result)
+          result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil)
+      loc result, val[0], val[4]
+      @lexer.indefine = false
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 531)
+  def _reduce_137(val, _values, result)
+          @lexer.namepop
+      result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])
+      loc result, val[0], val[6]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 536)
+  def _reduce_138(val, _values, result)
+          @lexer.namepop
+      result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil)
+      loc result, val[0], val[5]
+      
+    result
+  end
+.,.,
+
+# reduce 139 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 544)
+  def _reduce_140(val, _values, result)
+     result = val[1] 
+    result
+  end
+.,.,
+
+# reduce 141 omitted
+
+# reduce 142 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 561)
+  def _reduce_143(val, _values, result)
+          result = Factory.NODE(val[1], val[2], val[4])
+      loc result, val[0], val[5]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 565)
+  def _reduce_144(val, _values, result)
+          result = Factory.NODE(val[1], val[2], nil)
+      loc result, val[0], val[4]
+    
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 575)
+  def _reduce_145(val, _values, result)
+     result = [result] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 576)
+  def _reduce_146(val, _values, result)
+     result = val[0].push(val[2]) 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 581)
+  def _reduce_147(val, _values, result)
+     result = Factory.fqn(val[0][:value]); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 582)
+  def _reduce_148(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 583)
+  def _reduce_149(val, _values, result)
+     result = Factory.literal(:default); loc result, val[0] 
+    result
+  end
+.,.,
+
+# reduce 150 omitted
+
+# reduce 151 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 589)
+  def _reduce_152(val, _values, result)
+     result = val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 594)
+  def _reduce_153(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 595)
+  def _reduce_154(val, _values, result)
+     result = val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 599)
+  def _reduce_155(val, _values, result)
+     result = [] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 600)
+  def _reduce_156(val, _values, result)
+     result = [] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 601)
+  def _reduce_157(val, _values, result)
+     result = val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 605)
+  def _reduce_158(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 606)
+  def _reduce_159(val, _values, result)
+     result = val[0].push(val[2]) 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 610)
+  def _reduce_160(val, _values, result)
+     result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 611)
+  def _reduce_161(val, _values, result)
+     result = Factory.PARAM(val[0][:value]); loc result, val[0] 
+    result
+  end
+.,.,
+
+# reduce 162 omitted
+
+# reduce 163 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 624)
+  def _reduce_164(val, _values, result)
+     result = Factory.fqn(val[0][:value]).var ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 630)
+  def _reduce_165(val, _values, result)
+     result = Factory.LIST(val[1]); loc result, val[0], val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 631)
+  def _reduce_166(val, _values, result)
+     result = Factory.LIST(val[1]); loc result, val[0], val[3] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 632)
+  def _reduce_167(val, _values, result)
+     result = Factory.literal([]) ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 635)
+  def _reduce_168(val, _values, result)
+     result = Factory.HASH(val[1]); loc result, val[0], val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 636)
+  def _reduce_169(val, _values, result)
+     result = Factory.HASH(val[1]); loc result, val[0], val[3] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 637)
+  def _reduce_170(val, _values, result)
+     result = Factory.literal({}) ; loc result, val[0], val[3] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 640)
+  def _reduce_171(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 641)
+  def _reduce_172(val, _values, result)
+     result = val[0].push val[2] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 644)
+  def _reduce_173(val, _values, result)
+     result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] 
+    result
+  end
+.,.,
+
+# reduce 174 omitted
+
+# reduce 175 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 650)
+  def _reduce_176(val, _values, result)
+     result = Factory.literal(val[0][:value]) ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 651)
+  def _reduce_177(val, _values, result)
+     result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 652)
+  def _reduce_178(val, _values, result)
+     result = Factory.literal(val[0][:value]); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 653)
+  def _reduce_179(val, _values, result)
+     result = Factory.literal(val[0][:value]); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 654)
+  def _reduce_180(val, _values, result)
+     result = Factory.literal(val[0][:value]); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 655)
+  def _reduce_181(val, _values, result)
+     result = [val[0]] + val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 656)
+  def _reduce_182(val, _values, result)
+     result = Factory.TEXT(val[0]) 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 659)
+  def _reduce_183(val, _values, result)
+     result = [val[0]] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 660)
+  def _reduce_184(val, _values, result)
+     result = [val[0]] + val[1] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 662)
+  def _reduce_185(val, _values, result)
+     result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 663)
+  def _reduce_186(val, _values, result)
+     result = Factory.QREF(val[0][:value])  ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 664)
+  def _reduce_187(val, _values, result)
+     result = Factory.literal(:undef); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 665)
+  def _reduce_188(val, _values, result)
+     result = Factory.literal(:default); loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 670)
+  def _reduce_189(val, _values, result)
+     result = Factory.literal(val[0][:value]) ; loc result, val[0] 
+    result
+  end
+.,.,
+
+module_eval(<<'.,.,', 'egrammar.ra', 673)
+  def _reduce_190(val, _values, result)
+     result = Factory.literal(val[0][:value]); loc result, val[0] 
+    result
+  end
+.,.,
+
+# reduce 191 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 679)
+  def _reduce_192(val, _values, result)
+     result = nil 
+    result
+  end
+.,.,
+
+# reduce 193 omitted
+
+# reduce 194 omitted
+
+# reduce 195 omitted
+
+# reduce 196 omitted
+
+# reduce 197 omitted
+
+# reduce 198 omitted
+
+# reduce 199 omitted
+
+# reduce 200 omitted
+
+# reduce 201 omitted
+
+# reduce 202 omitted
+
+# reduce 203 omitted
+
+# reduce 204 omitted
+
+# reduce 205 omitted
+
+# reduce 206 omitted
+
+# reduce 207 omitted
+
+# reduce 208 omitted
+
+module_eval(<<'.,.,', 'egrammar.ra', 702)
+  def _reduce_209(val, _values, result)
+     result = nil
+    result
+  end
+.,.,
+
+def _reduce_none(val, _values, result)
+  val[0]
+end
+
+      end   # class Parser
+      end   # module Parser
+    end   # module Pops
+  end   # module Puppet
diff --git a/lib/puppet/pops/parser/grammar.ra b/lib/puppet/pops/parser/grammar.ra
new file mode 100644
index 0000000..7352bdb
--- /dev/null
+++ b/lib/puppet/pops/parser/grammar.ra
@@ -0,0 +1,746 @@
+# vim: syntax=ruby
+
+# Parser using the Pops model
+# This grammar is a half step between the current 3.1. grammar and egrammar.
+# FIXME! Keep as reference until egrammar is proven to work.
+
+class Puppet::Pops::Impl::Parser::Parser
+
+token STRING DQPRE DQMID DQPOST
+token LBRACK  RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE
+token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT
+token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN
+token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN
+token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSREF
+token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS
+token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
+token IN UNLESS PIPE
+token LAMBDA
+
+prechigh
+  left DOT
+#  left LBRACE
+#  left LCOLLECT LLCOLLECT
+  right NOT
+  nonassoc UMINUS
+  left  IN MATCH NOMATCH
+  left  TIMES DIV
+  left  MINUS PLUS
+  left  LSHIFT RSHIFT
+  left  NOTEQUAL ISEQUAL
+  left  GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL
+  left  AND
+  left  OR
+#  left  IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
+preclow
+
+rule
+# Produces [Model::BlockExpression, Model::Expression, nil] depending on multiple statements, single statement or empty
+program
+  : statements { result = Factory.block_or_expression(*val[0]) }
+  | nil
+
+# Change may have issues with nil; i.e. program is a sequence of nils/nops
+# Simplified from original which had validation for top level constructs - see statement rule
+# Produces Array<Model::Expression>
+statements
+  : statement            { result = [val[0]]}
+  | statements statement { result = val[0].push val[1] }
+
+# Removed validation construct regarding "top level statements" as it did not seem to catch all problems
+# and relied on a "top-level-ness" encoded in the abstract syntax tree objects
+#
+# The main list of valid statements
+# Produces Model::Expression
+#
+statement
+  : resource
+  | virtual_resource
+  | collection
+  | assignment
+  | casestatement
+  | if_expression
+  | unless_expression
+  | import
+  | call_named_function
+  | definition
+  | hostclass
+  | nodedef
+  | resource_override
+  | append
+  | relationship
+  | call_method_with_lambda
+
+keyword
+ : AND
+ | CASE
+ | CLASS
+ | DEFAULT
+ | DEFINE
+ | ELSE
+ | ELSIF
+ | IF
+ | IN
+ | IMPORT
+ | INHERITS
+ | NODE
+ | OR
+ | UNDEF
+ | UNLESS
+
+# Produces Model::RelationshipExpression
+relationship
+  : relationship_side edge relationship_side { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
+  | relationship edge relationship_side      { result = val[0].relop(val[1][:value], val[2]); loc result, val[1] }
+
+# Produces Model::Expression
+relationship_side
+  : resource
+  | resourceref
+  | collection
+  | variable
+  | quotedtext
+  | selector
+  | casestatement
+  | hasharrayaccesses
+
+# Produces String
+edge
+  : IN_EDGE
+  | OUT_EDGE
+  | IN_EDGE_SUB
+  | OUT_EDGE_SUB
+
+# Produces Model::CallNamedFunctionExpression
+call_named_function
+  : NAME LPAREN expressions RPAREN       { result = Factory.CALL_NAMED(val[0][:value], false, val[2]) ; loc result, val[0], val[3] }
+  | NAME LPAREN expressions COMMA RPAREN { result = Factory.CALL_NAMED(val[0][:value], false, val[2]) ; loc result, val[0], val[4] }            
+  | NAME LPAREN RPAREN                   { result = Factory.CALL_NAMED(val[0][:value], false, [])     ; loc result, val[0], val[2] }
+  | NAME func_call_args                  { result = Factory.CALL_NAMED(val[0][:value], false, val[1]) ; loc result, val[0] }
+
+call_method_with_lambda
+  : call_method        { result = val[0] }
+  | call_method lambda { result = val[0]; val[0].lambda = val[1] }
+  
+call_method
+  : named_access LPAREN expressions RPAREN { result = Factory.CALL_METHOD(val[0], val[2]); loc result, val[1], val[3] }
+  | named_access LPAREN RPAREN             { result = Factory.CALL_METHOD(val[0], []); loc result, val[1], val[3] }
+  | named_access                           { result = Factory.CALL_METHOD(val[0], []); loc result, val[0] }
+
+named_access
+  : named_access_lval DOT NAME {
+      result = val[0].dot(Factory.fqn(val[2][:value]))
+      loc result, val[1], val[2]
+    }
+  
+# Obviously not ideal, it is not possible to use literal array or hash as lhs
+# These must be assigned to a variable - this is also an issue in other places
+#
+named_access_lval
+  : variable 
+  | hasharrayaccesses
+  | selector
+  | quotedtext
+  | call_named_rval_function
+    
+lambda
+  : LAMBDA lambda_parameter_list statements RBRACE {
+      result = Factory.LAMBDA(val[1], val[2])
+      loc result, val[0], val[3]
+    }
+  | LAMBDA lambda_parameter_list RBRACE {
+      result = Factory.LAMBDA(val[1], nil)
+      loc result, val[0], val[2]
+    }
+# Produces Array<Model::Parameter>
+lambda_parameter_list
+  : PIPE PIPE             { result = [] }
+  | PIPE parameters endcomma PIPE { result = val[1] }
+  
+# Produces Array<Model::Expression>
+func_call_args
+  : rvalue                      { result = [val[0]] }
+  | func_call_args COMMA rvalue { result = val[0].push(val[2]) }
+
+# Produces Array<Model::Expression>
+expressions
+  : expression                   { result = [val[0]] }
+  | expressions comma expression { result = val[0].push(val[2]) }
+
+
+# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression]
+resource
+  : classname LBRACE resourceinstances endsemi RBRACE {
+      result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
+      loc result, val[0], val[4]
+    }
+  | classname LBRACE attribute_operations endcomma RBRACE {
+      # This is a deprecated syntax.
+      # It also fails hard - TODO: create model and validate this case
+      error "All resource specifications require names"
+    }
+  | type LBRACE attribute_operations endcomma RBRACE {
+      # a defaults setting for a type
+      result = Factory.RESOURCE_DEFAULTS(val[0], val[2])
+      loc result, val[0], val[4]
+    }
+
+# Override a value set elsewhere in the configuration.
+# Produces Model::ResourceOverrideExpression
+resource_override
+  : resourceref LBRACE attribute_operations endcomma RBRACE {
+      @lexer.commentpop
+      result = Factory.RESOURCE_OVERRIDE(val[0], val[2])
+      loc result, val[0], val[4]
+    }
+
+# Exported and virtual resources; these don't get sent to the client
+# unless they get collected elsewhere in the db.
+# The original had validation here; checking if storeconfigs is on; this is moved to a validation step
+# Also, validation was performed if an attempt was made to virtualize or export a resource defaults
+# this is also now deferred to validation 
+# Produces [Model::ResourceExpression, Model::ResourceDefaultsExpression]
+virtual_resource
+  :  at resource { 
+       val[1].form = val[0] # :virtual, :exported, (or :regular)
+       result = val[1] 
+     }
+
+# Produces Symbol corresponding to resource form
+at
+  : AT    { result = :virtual  }
+  | AT AT { result = :exported }
+
+# A collection statement.  Currently supports no arguments at all, but eventually
+# will, I assume.
+#
+# Produces Model::CollectExpression
+#
+collection
+  : type collect_query LBRACE attribute_operations endcomma RBRACE {
+      @lexer.commentpop
+      result = Factory.COLLECT(val[0].value.downcase, val[1], val[3])
+      loc result, val[0], val[5]
+    }
+  | type collect_query {
+      result = Factory.COLLECT(val[0].value.downcase, val[1], [])
+      loc result, val[0], val[1]
+    }
+
+collect_query
+  : LCOLLECT  optional_query RCOLLECT  { result = Factory.VIRTUAL_QUERY(val[1])   ; loc result, val[0], val[2] }
+  | LLCOLLECT optional_query RRCOLLECT { result = Factory.EXPORTED_QUERY(val[1])  ; loc result, val[0], val[2] }
+
+# ORIGINAL COMMENT: A mini-language for handling collection comparisons.  This is organized
+#                   to avoid the need for precedence indications.
+# (New implementation is slightly different; and when finished, it may be possible to streamline the
+#  grammar - the difference is mostly in evaluation, not in grammar)
+#
+optional_query
+  : nil
+  | query
+
+# ORIGINAL: Had a weird list structure where AND and OR where at the same level, and hence, there was the
+#           need to keep track of where parenthesis were (to get order correct).
+# 
+# This is now not needed as AND has higher precedence than OR, and parenthesis are low in precedence
+
+query
+  : predicate_lval ISEQUAL  expression { result = (val[0] == val[2])  ; loc result, val[1] } 
+  | predicate_lval NOTEQUAL expression { result = (val[0].ne(val[2])) ; loc result, val[1] }
+  | LPAREN query RPAREN                { result = val[1] }
+  | query AND query                    { result = val[0].and(val[2]) ; loc result, val[1] }
+  | query OR  query                    { result = val[0].or(val[2])  ; loc result, val[1] }
+
+
+# Produces Model::VariableExpression, or Model::QualifiedName
+predicate_lval
+  : variable
+  | name
+
+resourceinst
+  : resourcename COLON attribute_operations endcomma { result = Factory.RESOURCE_BODY(val[0], val[2]) }
+
+resourceinstances
+  : resourceinst                         { result = [val[0]] }
+  | resourceinstances SEMIC resourceinst { result = val[0].push val[2] }
+
+
+resourcename
+  : quotedtext
+  | name
+  | type
+  | selector
+  | variable
+  | array
+  | hasharrayaccesses
+
+# Assignment, only assignment to variable is legal, but parser builds expression for [] = anyway to 
+# enable a better error message
+assignment
+  : VARIABLE EQUALS expression { result = Factory.var(Factory.fqn(val[0][:value])).set(val[2]) ; loc result, val[1] }
+  | hasharrayaccess EQUALS expression { result val[0].set(val[2]); loc result, val[1] }
+
+append
+  : VARIABLE APPENDS expression { result = Factory.var(val[0][:value]).plus_set(val[1]) ; loc result, val[1] }
+
+# Produces Array<Model::AttributeOperation>
+attribute_operations
+  :                                                { result = [] }
+  | attribute_operation                            { result = [val[0]] }
+  | attribute_operations COMMA attribute_operation { result = val[0].push(val[2]) }
+
+# Produces String
+attribute_name
+  : NAME 
+  | keyword 
+  | BOOLEAN
+
+# Several grammar issues here: the addparam did not allow keyword and booleans as names.
+# In this version, the wrong combinations are validated instead of producing syntax errors
+# (Can give nicer error message +> is not applicable to...)
+# WAT - Boolean as attribute name? 
+# Produces Model::AttributeOperation
+#
+attribute_operation
+  : attribute_name FARROW expression {
+      result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2])
+      loc result, val[0], val[2]
+    }
+  | attribute_name PARROW expression {
+      result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2])
+      loc result, val[0], val[2]
+    }
+
+# Produces Model::CallNamedFunction
+call_named_rval_function
+  : NAME LPAREN expressions RPAREN { result = Factory.CALL_NAMED(val[0][:value], true, val[2]) ; loc result, val[0], val[3] }
+  | NAME LPAREN RPAREN             { result = Factory.CALL_NAMED(val[0][:value], true, []) ; loc result, val[0], val[2] }
+
+quotedtext
+  : STRING       { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
+  | dqpre dqrval { result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1] } 
+
+dqpre           : DQPRE      { result = Factory.literal(val[0][:value]); loc result, val[0] }
+dqpost          : DQPOST     { result = Factory.literal(val[0][:value]); loc result, val[0] }
+dqmid           : DQMID      { result = Factory.literal(val[0][:value]); loc result, val[0] }
+text_expression : expression { result = Factory.TEXT(val[0]) }
+
+dqrval
+  : text_expression dqtail { result = [val[0]] + val[1] }
+  
+dqtail
+  : dqpost       { result = [val[0]] }
+  | dqmid dqrval  { result = [val[0]] + val[1] }
+
+
+# Reference to Resource (future also reference to other instances of other types than Resources).
+# First form (lower case name) is deprecated (deprecation message handled in validation). Note that
+# this requires use of token NAME since a rule call to name causes shift reduce conflict with
+# a function call NAME NAME (calling function with NAME as argument e.g. foo bar).
+#
+# Produces InstanceReference
+resourceref
+  : NAME LBRACK expressions RBRACK {
+  	  # Would want to use rule name here, but can't (need a NAME with higher precedence), so must
+  	  # create a QualifiedName instance here for NAME 
+      result = Factory.INSTANCE(Factory.QNAME_OR_NUMBER(val[0][:value]), val[2]);
+      loc result, val[0], val[2][-1]
+    }
+  | type LBRACK expressions RBRACK {
+      result = Factory.INSTANCE(val[0], val[2]); 
+      loc result, val[0], val[2][-1]
+    }
+
+# Changed from Puppet 3x where there is no else part on unless
+#
+unless_expression
+  : UNLESS expression LBRACE statements RBRACE unless_else {
+      @lexer.commentpop
+      result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5])
+      loc result, val[0], val[4]
+    }
+  | UNLESS expression LBRACE RBRACE unless_else {
+      @lexer.commentpop
+      result = Factory.UNLESS(val[1], nil, nil)
+      loc result, val[0], val[4]
+    }
+
+# Different from else part of if, since "elsif" is not supported, but else is
+#
+# Produces [Model::Expression, nil] - nil if there is no else or elsif part
+unless_else
+  : # nothing
+  | ELSE LBRACE statements RBRACE {
+      @lexer.commentpop
+      result = Factory.block_or_expression(*val[2])
+      loc result, val[0], val[3]
+    }
+  | ELSE LBRACE RBRACE {
+      @lexer.commentpop
+  	  result = nil # don't think a nop is needed here either
+    }
+
+# Produces Model::IfExpression
+if_expression
+  : IF if_expression_part {
+      result = val[1]
+    }
+
+# Produces Model::IfExpression
+if_expression_part
+  : expression LBRACE statements RBRACE else {
+      @lexer.commentpop
+      result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4])
+      loc(result, val[0], (val[4] ? val[4] : val[3]))
+    }
+  | expression LBRACE RBRACE else {
+      result = Factory.IF(val[0], nil, val[3])
+      loc(result, val[0], (val[3] ? val[3] : val[2]))
+    }
+
+# Produces [Model::Expression, nil] - nil if there is no else or elsif part
+else
+  : # nothing
+  | ELSIF if_expression_part { result = val[1] }
+  | ELSE LBRACE statements RBRACE {
+      @lexer.commentpop
+      result = Factory.block_or_expression(*val[2])
+      loc result, val[0], val[3]
+    }
+  | ELSE LBRACE RBRACE {
+      @lexer.commentpop
+  	  result = nil # don't think a nop is needed here either
+    }
+
+# Produces Model::Expression
+expression
+  : rvalue
+  | hash
+  | expression IN expression           { result = val[0].in val[2]  ; loc result, val[1] }
+  | expression MATCH match_rvalue      { result = val[0] =~  val[2] ; loc result, val[1] }
+  | expression NOMATCH match_rvalue    { result = val[0].mne val[2] ; loc result, val[1] }
+  | expression PLUS expression         { result = val[0] +   val[2] ; loc result, val[1] }
+  | expression MINUS expression        { result = val[0] -   val[2] ; loc result, val[1] }
+  | expression DIV expression          { result = val[0] /   val[2] ; loc result, val[1] }
+  | expression TIMES expression        { result = val[0] *   val[2] ; loc result, val[1] }
+  | expression LSHIFT expression       { result = val[0] <<  val[2] ; loc result, val[1] }
+  | expression RSHIFT expression       { result = val[0] >>  val[2] ; loc result, val[1] }
+  | MINUS expression =UMINUS           { result = val[1].minus()    ; loc result, val[0] }
+  | expression NOTEQUAL expression     { result = val[0].ne  val[2] ; loc result, val[1] }
+  | expression ISEQUAL expression      { result = val[0] ==  val[2] ; loc result, val[1] }
+  | expression GREATERTHAN expression  { result = val[0] >   val[2] ; loc result, val[1] }
+  | expression GREATEREQUAL expression { result = val[0] >=  val[2] ; loc result, val[1] }
+  | expression LESSTHAN expression     { result = val[0] <   val[2] ; loc result, val[1] }
+  | expression LESSEQUAL expression    { result = val[0] <=  val[2] ; loc result, val[1] }
+  | NOT expression                     { result = val[1].not        ; loc result, val[0] }
+  | expression AND expression          { result = val[0].and val[2] ; loc result, val[1] }
+  | expression OR expression           { result = val[0].or  val[2] ; loc result, val[1] }
+  | LPAREN expression RPAREN           { result = val[1]            ;                    }
+  | call_method_with_lambda 
+
+match_rvalue
+  : regex
+  | quotedtext
+  
+# Produces Model::CaseExpression
+casestatement
+  : CASE expression LBRACE case_options RBRACE {
+      @lexer.commentpop
+      result = Factory.CASE(val[1], *val[3])
+      loc result, val[0], val[4]
+    }
+
+# Produces Array<Model::CaseOption>
+case_options
+  : case_option               { result = [val[0]] }
+  | case_options case_option { result = val[0].push val[1] }
+
+# Produced Model::CaseOption (aka When)
+case_option
+  : case_values COLON LBRACE statements RBRACE {
+      @lexer.commentpop
+      result = Factory.WHEN(val[0], val[3])
+      loc result, val[1], val[4]
+    }
+  | case_values COLON LBRACE RBRACE {
+      @lexer.commentpop
+      result = Factory.WHEN(val[0], nil)
+      loc result, val[1], val[3]
+    }
+
+# Produces Array<Expression> mostly literals
+case_values
+  : selectable                   { result = [val[0]] }
+  | case_values COMMA selectable { result = val[0].push val[2] }
+
+# Produces Model::SelectorExpression
+selector
+  : selectable QMARK selector_entries { result = val[0].select(*val[2]) ; loc result, val[1] }
+
+# Produces Array<Model::SelectorEntry>
+selector_entries
+  : selector_entry { result = [val[0]] }
+  | LBRACE selector_entry_list endcomma RBRACE {
+      @lexer.commentpop
+      result = val[1]
+    }
+
+# Produces Array<Model::SelectorEntry>
+selector_entry_list
+  : selector_entry                           { result = [val[0]] }
+  | selector_entry_list COMMA selector_entry { result = val[0].push val[2] }
+
+# Produces a Model::SelectorEntry
+selector_entry
+  : selectable FARROW rvalue { result = Factory.MAP(val[0], val[2]) ; loc result, val[1] }
+
+# Produces Model::Expression (most of the literals)
+selectable
+  : name
+  | type
+  | quotedtext
+  | variable
+  | call_named_rval_function
+  | boolean
+  | undef
+  | hasharrayaccess
+  | default
+  | regex
+
+  
+
+# Produces nil (noop)
+import
+  : IMPORT strings {
+      error "Import not supported in this version of the parser", \
+          :line => stmt.context[:line], :file => stmt.context[:file]
+      result = nil
+  }
+
+# IMPORT (T.B DEPRECATED IN PUPPET WHEN IT HAS BEEN FIGURED OUT HOW TO SUPPORT
+# THE THINGS IMPORTS ARE USED FOR.
+# BOLDLY DECIDED TO SKIP THIS COMPLETELY IN THIS IMPLEMENTATION - will trigger an error
+#
+# These are only used for importing, no interpolation
+string
+  : STRING       { result = [val[0][:value]] }
+
+strings
+  :  string
+  |  strings COMMA string { result = val[0].push val[2] }
+
+# Produces Model::Definition
+definition
+  : DEFINE classname parameter_list LBRACE statements RBRACE {
+      @lexer.commentpop
+      result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])
+      loc result, val[0], val[5]
+      @lexer.indefine = false
+    }
+  | DEFINE classname parameter_list LBRACE RBRACE {
+      @lexer.commentpop
+      result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil)
+      loc result, val[0], val[4]
+      @lexer.indefine = false
+    }
+
+# ORIGINAL COMMENT: Our class gets defined in the parent namespace, not our own. 
+#    WAT ??! This is way odd; should get its complete name, classnames do not nest
+#    Seems like the call to classname makes use of the name scope
+#    (This is uneccesary, since the parent name is known when evaluating)
+#
+# Produces Model::HostClassDefinition
+#
+hostclass
+  : CLASS classname parameter_list classparent LBRACE statements RBRACE {
+      @lexer.commentpop
+      @lexer.namepop
+      result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])
+      loc result, val[0], val[6]
+    }
+  | CLASS classname parameter_list classparent LBRACE RBRACE {
+      @lexer.commentpop
+      @lexer.namepop
+      result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil)
+      loc result, val[0], val[5]
+      }
+
+# Produces Model::NodeDefinition
+nodedef
+  : NODE hostnames nodeparent LBRACE statements RBRACE {
+      @lexer.commentpop
+      result = Factory.NODE(val[1], val[2], val[4])
+      loc result, val[0], val[5]
+    }
+  | NODE hostnames nodeparent LBRACE RBRACE {
+      @lexer.commentpop
+      result = Factory.NODE(val[1], val[2], nil)
+      loc result, val[0], val[4]
+    }
+
+# String result
+classname
+  : NAME  { result = val[0] }
+  | CLASS { result = val[0] }
+
+# Hostnames is not a list of names, it is a list of name matchers (including a Regexp).
+# (The old implementation had a special "Hostname" object with some minimal validation)
+#
+# Produces Array<Model::LiteralExpression> 
+#
+hostnames
+  : nodename                 { result = [result] }
+  | hostnames COMMA nodename { result = val[0].push(val[2]) }
+
+# Produces Model::LiteralExpression
+#
+nodename
+  : hostname
+
+# Produces a LiteralExpression (string, :default, or regexp)
+hostname
+  : NAME    { result = Factory.fqn(val[0][:value]); loc result, val[0] }
+  | STRING  { result = Factory.literal(val[0][:value]); loc result, val[0] }
+  | DEFAULT { result = Factory.literal(:default); loc result, val[0] }
+  | regex
+
+
+# Produces Array<Model::Parameter>
+parameter_list
+  : nil                               { result = [] }
+  | LPAREN  RPAREN                    { result = [] }
+  | LPAREN parameters endcomma RPAREN { result = val[1] }
+
+# Produces Array<Model::Parameter>
+parameters
+  : parameter                 { result = [val[0]] }
+  | parameters COMMA parameter { result = val[0].push(val[2]) }
+
+# Produces Model::Parameter
+parameter
+  : VARIABLE EQUALS expression { result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0] }
+  | VARIABLE                   { result = Factory.PARAM(val[0][:value]); loc result, val[0] }
+
+# Produces Expression, since hostname is an Expression
+nodeparent
+  : nil
+  | INHERITS hostname { result = val[1] }
+
+# Produces String, name or nil result
+classparent
+  : nil
+  | INHERITS classnameordefault { result = val[1] }
+
+# Produces String (this construct allows a class to be named "default" and to be referenced as
+# the parent class.
+# TODO: Investigate the validity 
+# Produces a String (classname), or a token (DEFAULT).
+#
+classnameordefault
+  : classname
+  | DEFAULT
+
+rvalue
+  : quotedtext
+  | name
+  | type
+  | boolean
+  | selector
+  | variable
+  | array
+  | hasharrayaccesses
+  | resourceref
+  | call_named_rval_function
+  | undef
+
+array
+  : LBRACK expressions       RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[2] }
+  | LBRACK expressions COMMA RBRACK { result = Factory.LIST(val[1]); loc result, val[0], val[3] }
+  | LBRACK                   RBRACK { result = Factory.literal([]) ; loc result, val[0] }
+
+
+hash
+  : LBRACE hashpairs RBRACE       { result = Factory.HASH(val[1]); loc result, val[0], val[2] }
+  | LBRACE hashpairs COMMA RBRACE { result = Factory.HASH(val[1]); loc result, val[0], val[3] }
+  | LBRACE RBRACE                 { result = Factory.literal({}) ; loc result, val[0], val[3] }
+
+hashpairs
+  : hashpair                 { result = [val[0]] }
+  | hashpairs COMMA hashpair { result = val[0].push val[2] } 
+
+hashpair
+  : key FARROW expression { result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1] }
+
+key
+  : NAME       { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
+  | quotedtext { result = val[0] }
+
+# NOTE: Limitation that LHS is a variable, means that it is not possible to do foo(10)[2] without
+# using an intermediate variable
+#
+hasharrayaccess
+  : variable LBRACK expression RBRACK { result = val[0][val[2]]; loc result, val[0], val[3] }
+
+hasharrayaccesses
+  : hasharrayaccess
+  | hasharrayaccesses LBRACK expression RBRACK { result = val[0][val[2]] ; loc result, val[1], val[3] }
+
+# Produces Model::VariableExpression
+variable : VARIABLE { result = Factory.fqn(val[0][:value]).var ; loc result, val[0] }
+undef    : UNDEF    { result = Factory.literal(:undef); loc result, val[0] }
+name     : NAME     { result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0] }
+type     : CLASSREF { result = Factory.QREF(val[0][:value])  ; loc result, val[0] }
+
+default
+  : DEFAULT { result = Factory.literal(:default); loc result, val[0] }
+
+boolean
+  # Assumes lexer produces a Boolean value for booleans, or this will go wrong (e.g. produce. LiteralString)
+  : BOOLEAN { result = Factory.literal(val[0][:value]) ; loc result, val[0] }
+
+regex
+  : REGEX { result = Factory.literal(val[0][:value]); loc result, val[0] }
+
+# ---Special markers & syntactic sugar
+
+# WAT !!!! this means array can be [1=>2=>3], func (1=>2=>3), and other retarded constructs
+# TODO: Remove the FARROW (investigate if there is any validity)
+comma
+  : FARROW
+  | COMMA
+
+endcomma
+  : #
+  | COMMA { result = nil }
+
+endsemi
+  : #
+  | SEMIC
+
+nil
+  : { result = nil}
+
+## Empty list - not really needed? TODO: Check if this can be removed
+#empty_list
+#  : { result = [] }
+
+end
+
+---- header ----
+require 'puppet'
+require 'puppet/util/loadedfile'
+require 'puppet/pops'
+
+module Puppet
+  class ParseError < Puppet::Error; end
+  class ImportError < Racc::ParseError; end
+  class AlreadyImportedError < ImportError; end
+end
+
+---- inner ----
+
+# Make emacs happy
+# Local Variables:
+# mode: ruby
+# End:
diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb
new file mode 100644
index 0000000..106f407
--- /dev/null
+++ b/lib/puppet/pops/parser/lexer.rb
@@ -0,0 +1,842 @@
+# the scanner/lexer
+
+require 'forwardable'
+require 'strscan'
+require 'puppet'
+require 'puppet/util/methodhelper'
+
+module Puppet
+  class LexError < RuntimeError; end
+end
+
+class Puppet::Pops::Parser::Lexer
+  extend Forwardable
+
+  attr_reader :file, :lexing_context, :token_queue
+
+  attr_reader :locator
+
+  attr_accessor :indefine
+  alias :indefine? :indefine
+
+  def lex_error msg
+    raise Puppet::LexError.new(msg)
+  end
+
+  class Token
+    ALWAYS_ACCEPTABLE = Proc.new { |context| true }
+
+    include Puppet::Util::MethodHelper
+
+    attr_accessor :regex, :name, :string, :skip, :skip_text
+    alias skip? skip
+
+    # @param string_or_regex[String] a literal string token matcher
+    # @param string_or_regex[Regexp] a regular expression token text matcher
+    # @param name [String] the token name (what it is known as in the grammar)
+    # @param options [Hash] see {#set_options}
+    #
+    def initialize(string_or_regex, name, options = {})
+      if string_or_regex.is_a?(String)
+        @name, @string = name, string_or_regex
+        @regex = Regexp.new(Regexp.escape(string_or_regex))
+      else
+        @name, @regex = name, string_or_regex
+      end
+
+      set_options(options)
+      @acceptable_when = ALWAYS_ACCEPTABLE
+    end
+
+    # @return [String] human readable token reference; the String if literal, else the token name
+    def to_s
+      string or @name.to_s
+    end
+
+    # @return [Boolean] if the token is acceptable in the given context or not.
+    #   this implementation always returns true.
+    # @param context [Hash] ? ? ?
+    #
+    def acceptable?(context={})
+      @acceptable_when.call(context)
+    end
+
+
+    # Defines when the token is able to match.
+    # This provides context that cannot be expressed otherwise, such as feature flags.
+    #
+    # @param block [Proc] a proc that given a context returns a boolean
+    def acceptable_when(block)
+      @acceptable_when = block
+    end
+  end
+
+  # Maintains a list of tokens.
+  class TokenList
+    extend Forwardable
+
+    attr_reader :regex_tokens, :string_tokens
+    def_delegator :@tokens, :[]
+    # Adds a new token to the set of recognized tokens
+    # @param name [String] the token name
+    # @param regex [Regexp, String] source text token matcher, a litral string or regular expression
+    # @param options [Hash] see {Token::set_options}
+    # @param block [Proc] optional block set as the created tokens `convert` method
+    # @raise [ArgumentError] if the token with the given name is already defined
+    #
+    def add_token(name, regex, options = {}, &block)
+      raise(ArgumentError, "Token #{name} already exists") if @tokens.include?(name)
+      token = Token.new(regex, name, options)
+      @tokens[token.name] = token
+      if token.string
+        @string_tokens << token
+        @tokens_by_string[token.string] = token
+      else
+        @regex_tokens << token
+      end
+
+      token.meta_def(:convert, &block) if block_given?
+
+      token
+    end
+
+    # Creates an empty token list
+    #
+    def initialize
+      @tokens = {}
+      @regex_tokens = []
+      @string_tokens = []
+      @tokens_by_string = {}
+    end
+
+    # Look up a token by its literal (match) value, rather than name.
+    # @param string [String, nil] the literal match string to obtain a {Token} for, or nil if it does not exist.
+    def lookup(string)
+      @tokens_by_string[string]
+    end
+
+    # Adds tokens from a hash where key is a matcher (literal string or regexp) and the
+    # value is the token's name
+    # @param hash [Hash<{String => Symbol}, Hash<{Regexp => Symbol}] map token text matcher to token name
+    # @return [void]
+    #
+    def add_tokens(hash)
+      hash.each do |regex, name|
+        add_token(name, regex)
+      end
+    end
+
+    # Sort literal (string-) tokens by length, so we know once we match, we're done.
+    # This helps avoid the O(n^2) nature of token matching.
+    # The tokens are sorted in place.
+    # @return [void]
+    def sort_tokens
+      @string_tokens.sort! { |a, b| b.string.length <=> a.string.length }
+    end
+
+    # Yield each token name and value in turn.
+    def each
+      @tokens.each {|name, value| yield name, value }
+    end
+  end
+
+  TOKENS = TokenList.new
+  TOKENS.add_tokens(
+  '['   => :LBRACK,
+  ']'   => :RBRACK,
+  #    '{'   => :LBRACE, # Specialized to handle lambda
+  '}'   => :RBRACE,
+  '('   => :LPAREN,
+  ')'   => :RPAREN,
+  '='   => :EQUALS,
+  '+='  => :APPENDS,
+  '=='  => :ISEQUAL,
+  '>='  => :GREATEREQUAL,
+  '>'   => :GREATERTHAN,
+  '<'   => :LESSTHAN,
+  '<='  => :LESSEQUAL,
+  '!='  => :NOTEQUAL,
+  '!'   => :NOT,
+  ','   => :COMMA,
+  '.'   => :DOT,
+  ':'   => :COLON,
+  '@'   => :AT,
+  '|'   => :PIPE,
+  '<<|' => :LLCOLLECT,
+  '|>>' => :RRCOLLECT,
+  '->'  => :IN_EDGE,
+  '<-'  => :OUT_EDGE,
+  '~>'  => :IN_EDGE_SUB,
+  '<~'  => :OUT_EDGE_SUB,
+  '<|'  => :LCOLLECT,
+  '|>'  => :RCOLLECT,
+  ';'   => :SEMIC,
+  '?'   => :QMARK,
+  '\\'  => :BACKSLASH,
+  '=>'  => :FARROW,
+  '+>'  => :PARROW,
+  '+'   => :PLUS,
+  '-'   => :MINUS,
+  '/'   => :DIV,
+  '*'   => :TIMES,
+  '%'   => :MODULO,
+  '<<'  => :LSHIFT,
+  '>>'  => :RSHIFT,
+  '=~'  => :MATCH,
+  '!~'  => :NOMATCH,
+  %r{((::){0,1}[A-Z][-\w]*)+} => :CLASSREF,
+  "<string>" => :STRING,
+  "<dqstring up to first interpolation>" => :DQPRE,
+  "<dqstring between two interpolations>" => :DQMID,
+  "<dqstring after final interpolation>" => :DQPOST,
+  "<boolean>" => :BOOLEAN,
+  "<lambda start>" => :LAMBDA, # A LBRACE followed by '|'
+  "<select start>" => :SELBRACE # A QMARK followed by '{'
+  )
+
+  module Contextual
+    QUOTE_TOKENS = [:DQPRE,:DQMID]
+    REGEX_INTRODUCING_TOKENS = [:NODE,:LBRACE, :SELBRACE, :RBRACE,:MATCH,:NOMATCH,:COMMA]
+
+    NOT_INSIDE_QUOTES = Proc.new do |context|
+      !QUOTE_TOKENS.include? context[:after]
+    end
+
+    INSIDE_QUOTES = Proc.new do |context|
+      QUOTE_TOKENS.include? context[:after]
+    end
+
+    IN_REGEX_POSITION = Proc.new do |context|
+      REGEX_INTRODUCING_TOKENS.include? context[:after]
+    end
+
+    IN_STRING_INTERPOLATION = Proc.new do |context|
+      context[:string_interpolation_depth] > 0
+    end
+
+    DASHED_VARIABLES_ALLOWED = Proc.new do |context|
+      Puppet[:allow_variables_with_dashes]
+    end
+
+    VARIABLE_AND_DASHES_ALLOWED = Proc.new do |context|
+      Contextual::DASHED_VARIABLES_ALLOWED.call(context) and TOKENS[:VARIABLE].acceptable?(context)
+    end
+  end
+
+  # LBRACE needs look ahead to differentiate between '{' and a '{'
+  # followed by a '|' (start of lambda) The racc grammar can only do one
+  # token lookahead.
+  #
+  TOKENS.add_token :LBRACE, /\{/ do | lexer, value |
+    if lexer.match?(/[ \t\r]*\|/)
+      [TOKENS[:LAMBDA], value]
+    elsif lexer.lexing_context[:after] == :QMARK
+      [TOKENS[:SELBRACE], value]
+    else
+      [TOKENS[:LBRACE], value]
+    end
+  end
+
+  # Numbers are treated separately from names, so that they may contain dots.
+  TOKENS.add_token :NUMBER, %r{\b(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b} do |lexer, value|
+    lexer.assert_numeric(value)
+    [TOKENS[:NAME], value]
+  end
+  TOKENS[:NUMBER].acceptable_when Contextual::NOT_INSIDE_QUOTES
+
+  TOKENS.add_token :NAME, %r{((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*} do |lexer, value|
+    # A name starting with a number must be a valid numeric string (not that
+    # NUMBER token captures those names that do not comply with the name rule.
+    if value =~ /^[0-9].*$/
+      lexer.assert_numeric(value)
+    end
+
+    string_token = self
+    # we're looking for keywords here
+    if tmp = KEYWORDS.lookup(value)
+      string_token = tmp
+      if [:TRUE, :FALSE].include?(string_token.name)
+        value = eval(value)
+        string_token = TOKENS[:BOOLEAN]
+      end
+    end
+    [string_token, value]
+  end
+  [:NAME, :CLASSREF].each do |name_token|
+    TOKENS[name_token].acceptable_when Contextual::NOT_INSIDE_QUOTES
+  end
+
+  TOKENS.add_token :COMMENT, %r{#.*}, :skip => true do |lexer,value|
+    value.sub!(/# ?/,'')
+    [self, value]
+  end
+
+  TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m, :skip => true do |lexer, value|
+    value.sub!(/^\/\* ?/,'')
+    value.sub!(/ ?\*\/$/,'')
+    [self,value]
+  end
+
+  TOKENS.add_token :REGEX, %r{/[^/\n]*/} do |lexer, value|
+    # Make sure we haven't matched an escaped /
+    while value[-2..-2] == '\\'
+      other = lexer.scan_until(%r{/})
+      value += other
+    end
+    regex = value.sub(%r{\A/}, "").sub(%r{/\Z}, '').gsub("\\/", "/")
+    [self, Regexp.new(regex)]
+  end
+  TOKENS[:REGEX].acceptable_when Contextual::IN_REGEX_POSITION
+
+  TOKENS.add_token :RETURN, "\n", :skip => true, :skip_text => true
+
+  TOKENS.add_token :SQUOTE, "'" do |lexer, value|
+    [TOKENS[:STRING], lexer.slurpstring(value,["'"],:ignore_invalid_escapes).first ]
+  end
+
+  DQ_initial_token_types      = {'$' => :DQPRE,'"' => :STRING}
+  DQ_continuation_token_types = {'$' => :DQMID,'"' => :DQPOST}
+
+  TOKENS.add_token :DQUOTE, /"/ do |lexer, value|
+    lexer.tokenize_interpolated_string(DQ_initial_token_types)
+  end
+
+  TOKENS.add_token :DQCONT, /\}/ do |lexer, value|
+    lexer.tokenize_interpolated_string(DQ_continuation_token_types)
+  end
+  TOKENS[:DQCONT].acceptable_when Contextual::IN_STRING_INTERPOLATION
+
+  TOKENS.add_token :DOLLAR_VAR_WITH_DASH, %r{\$(?:::)?(?:[-\w]+::)*[-\w]+} do |lexer, value|
+    lexer.warn_if_variable_has_hyphen(value)
+
+    [TOKENS[:VARIABLE], value[1..-1]]
+  end
+  TOKENS[:DOLLAR_VAR_WITH_DASH].acceptable_when Contextual::DASHED_VARIABLES_ALLOWED
+
+  TOKENS.add_token :DOLLAR_VAR, %r{\$(::)?(\w+::)*\w+} do |lexer, value|
+    [TOKENS[:VARIABLE],value[1..-1]]
+  end
+
+  TOKENS.add_token :VARIABLE_WITH_DASH, %r{(?:::)?(?:[-\w]+::)*[-\w]+} do |lexer, value|
+    lexer.warn_if_variable_has_hyphen(value)
+    # If the varname (following $, or ${ is followed by (, it is a function call, and not a variable
+    # reference.
+    #
+    if lexer.match?(%r{[ \t\r]*\(})
+      [TOKENS[:NAME],value]
+    else
+      [TOKENS[:VARIABLE], value]
+    end
+  end
+  TOKENS[:VARIABLE_WITH_DASH].acceptable_when Contextual::VARIABLE_AND_DASHES_ALLOWED
+
+  TOKENS.add_token :VARIABLE, %r{(::)?(\w+::)*\w+} do |lexer, value|
+    # If the varname (following $, or ${ is followed by (, it is a function call, and not a variable
+    # reference.
+    #
+    if lexer.match?(%r{[ \t\r]*\(})
+      [TOKENS[:NAME],value]
+    else
+      [TOKENS[:VARIABLE],value]
+    end
+
+  end
+  TOKENS[:VARIABLE].acceptable_when Contextual::INSIDE_QUOTES
+
+  TOKENS.sort_tokens
+
+  @@pairs = {
+    "{"   => "}",
+    "("   => ")",
+    "["   => "]",
+    "<|"  => "|>",
+    "<<|" => "|>>",
+    "|"   => "|"
+  }
+
+  KEYWORDS = TokenList.new
+  KEYWORDS.add_tokens(
+  "case"     => :CASE,
+  "class"    => :CLASS,
+  "default"  => :DEFAULT,
+  "define"   => :DEFINE,
+  #    "import"   => :IMPORT,
+  "if"       => :IF,
+  "elsif"    => :ELSIF,
+  "else"     => :ELSE,
+  "inherits" => :INHERITS,
+  "node"     => :NODE,
+  "and"      => :AND,
+  "or"       => :OR,
+  "undef"    => :UNDEF,
+  "false"    => :FALSE,
+  "true"     => :TRUE,
+  "in"       => :IN,
+  "unless"   => :UNLESS
+  )
+
+  def clear
+    initvars
+  end
+
+  def expected
+    return nil if @expected.empty?
+    name = @expected[-1]
+    TOKENS.lookup(name) or lex_error "Internal Lexer Error: Could not find expected token #{name}"
+  end
+
+  # scan the whole file
+  # basically just used for testing
+  def fullscan
+    array = []
+
+    self.scan { |token, str|
+      # Ignore any definition nesting problems
+      @indefine = false
+      array.push([token,str])
+    }
+    array
+  end
+
+  def file=(file)
+    @file = file
+    contents = File.exists?(file) ? File.read(file) : ""
+    @scanner = StringScanner.new(contents)
+    @locator = Locator.new(contents, multibyte?)
+  end
+
+  def_delegator :@token_queue, :shift, :shift_token
+
+  def find_string_token
+    # We know our longest string token is three chars, so try each size in turn
+    # until we either match or run out of chars.  This way our worst-case is three
+    # tries, where it is otherwise the number of string token we have.  Also,
+    # the lookups are optimized hash lookups, instead of regex scans.
+    #
+    s = @scanner.peek(3)
+    token = TOKENS.lookup(s[0,3]) || TOKENS.lookup(s[0,2]) || TOKENS.lookup(s[0,1])
+    [ token, token && @scanner.scan(token.regex) ]
+  end
+
+  # Find the next token that matches a regex.  We look for these first.
+  def find_regex_token
+    best_token = nil
+    best_length = 0
+
+    # I tried optimizing based on the first char, but it had
+    # a slightly negative affect and was a good bit more complicated.
+    TOKENS.regex_tokens.each do |token|
+      if length = @scanner.match?(token.regex) and token.acceptable?(lexing_context)
+        # We've found a longer match
+        if length > best_length
+          best_length = length
+          best_token = token
+        end
+      end
+    end
+
+    return best_token, @scanner.scan(best_token.regex) if best_token
+  end
+
+  # Find the next token, returning the string and the token.
+  def find_token
+    shift_token || find_regex_token || find_string_token
+  end
+
+  def initialize
+    @multibyte = init_multibyte
+    initvars
+  end
+
+  def assert_numeric(value)
+    if value =~ /^0[xX].*$/
+      lex_error (positioned_message("Not a valid hex number #{value}")) unless value =~ /^0[xX][0-9A-Fa-f]+$/
+    elsif value =~ /^0[^.].*$/
+      lex_error(positioned_message("Not a valid octal number #{value}")) unless value =~ /^0[0-7]+$/
+    else
+      lex_error(positioned_message("Not a valid decimal number #{value}")) unless value =~ /0?\d+(?:\.\d+)?(?:[eE]-?\d+)?/
+    end
+  end
+
+  # Returns true if ruby version >= 1.9.3 since regexp supports multi-byte matches and expanded
+  # character categories like [[:blank:]].
+  #
+  # This implementation will fail if there are more than 255 minor or micro versions of ruby
+  #
+  def init_multibyte
+    numver = RUBY_VERSION.split(".").collect {|s| s.to_i }
+    return true if (numver[0] << 16 | numver[1] << 8 | numver[2]) >= (1 << 16 | 9 << 8 | 3)
+    false
+  end
+
+  def multibyte?
+    @multibyte
+  end
+
+  def initvars
+    @previous_token = nil
+    @scanner = nil
+    @file = nil
+
+    # AAARRGGGG! okay, regexes in ruby are bloody annoying
+    # no one else has "\n" =~ /\s/
+
+    if multibyte?
+      # Skip all kinds of space, and CR, but not newlines
+      @skip = %r{[[:blank:]\r]+}
+    else
+      @skip = %r{[ \t\r]+}
+    end
+
+    @namestack = []
+    @token_queue = []
+    @indefine = false
+    @expected = []
+    @lexing_context = {
+      :after => nil,
+      :start_of_line => true,
+      :offset => 0,      # byte offset before where token starts
+      :end_offset => 0,  # byte offset after scanned token
+      :string_interpolation_depth => 0
+    }
+  end
+
+  # Make any necessary changes to the token and/or value.
+  def munge_token(token, value)
+    # A token may already have been munged (converted and positioned)
+    #
+    return token, value if value.is_a? Hash
+
+    skip if token.skip_text
+
+    return if token.skip
+
+    token, value = token.convert(self, value) if token.respond_to?(:convert)
+
+    return unless token
+
+    return if token.skip
+
+    # If the conversion performed the munging/positioning
+    return token, value if value.is_a? Hash
+
+    pos_hash = position_in_source
+    pos_hash[:value] = value
+
+    # Add one to pos, first char on line is 1
+    return token, pos_hash
+  end
+
+  # Returns a hash with the current position in source based on the current lexing context
+  #
+  def position_in_source
+    pos        = @locator.pos_on_line(lexing_context[:offset])
+    offset     = @locator.char_offset(lexing_context[:offset])
+    length     = @locator.char_length(lexing_context[:offset], lexing_context[:end_offset])
+    start_line = @locator.line_for_offset(lexing_context[:offset])
+
+    return { :line => start_line, :pos => pos, :offset => offset, :length => length}
+  end
+
+  def pos
+    @locator.pos_on_line(lexing_context[:offset])
+  end
+
+  # Handling the namespace stack
+  def_delegator :@namestack, :pop, :namepop
+
+  # This value might have :: in it, but we don't care -- it'll be handled
+  # normally when joining, and when popping we want to pop this full value,
+  # however long the namespace is.
+  def_delegator :@namestack, :<<, :namestack
+
+  # Collect the current namespace.
+  def namespace
+    @namestack.join("::")
+  end
+
+  def_delegator :@scanner, :rest
+  # this is the heart of the lexer
+  def scan
+    #Puppet.debug("entering scan")
+    lex_error "Internal Error: No string or file given to lexer to process." unless @scanner
+
+    # Skip any initial whitespace.
+    skip
+
+    until token_queue.empty? and @scanner.eos? do
+      yielded = false
+      offset = @scanner.pos
+      matched_token, value = find_token
+      end_offset = @scanner.pos
+
+      # error out if we didn't match anything at all
+      lex_error "Could not match #{@scanner.rest[/^(\S+|\s+|.*)/]}" unless matched_token
+
+      newline = matched_token.name == :RETURN
+
+      lexing_context[:start_of_line] = newline
+      lexing_context[:offset] = offset
+      lexing_context[:end_offset] = end_offset
+
+      final_token, token_value = munge_token(matched_token, value)
+      # update end position since munging may have moved the end offset
+      lexing_context[:end_offset] = @scanner.pos
+
+      unless final_token
+        skip
+        next
+      end
+
+      lexing_context[:after] = final_token.name unless newline
+      lexing_context[:string_interpolation_depth] += 1 if final_token.name == :DQPRE
+      lexing_context[:string_interpolation_depth] -= 1 if final_token.name == :DQPOST
+
+      value = token_value[:value]
+
+      if match = @@pairs[value] and final_token.name != :DQUOTE and final_token.name != :SQUOTE
+        @expected << match
+      elsif exp = @expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE
+        @expected.pop
+      end
+
+      yield [final_token.name, token_value]
+
+      if @previous_token
+        namestack(value) if @previous_token.name == :CLASS and value != '{'
+
+        if @previous_token.name == :DEFINE
+          if indefine?
+            msg = "Cannot nest definition #{value} inside #{@indefine}"
+            self.indefine = false
+            raise Puppet::ParseError, msg
+          end
+
+          @indefine = value
+        end
+      end
+      @previous_token = final_token
+      skip
+    end
+    # Cannot reset @scanner to nil here - it is needed to answer questions about context after
+    # completed parsing.
+    # Seems meaningless to do this. Everything will be gc anyway.
+    #@scanner = nil
+
+    # This indicates that we're done parsing.
+    yield [false,false]
+  end
+
+  # Skip any skipchars in our remaining string.
+  def skip
+    @scanner.skip(@skip)
+  end
+
+  def match? r
+    @scanner.match?(r)
+  end
+
+  # Provide some limited access to the scanner, for those
+  # tokens that need it.
+  def_delegator :@scanner, :scan_until
+
+  # we've encountered the start of a string...
+  # slurp in the rest of the string and return it
+  def slurpstring(terminators,escapes=%w{ \\  $ ' " r n t s }+["\n"],ignore_invalid_escapes=false)
+    # we search for the next quote that isn't preceded by a
+    # backslash; the caret is there to match empty strings
+    last = @scanner.matched
+    tmp_offset = @scanner.pos
+    str = @scanner.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]/) || lex_error(positioned_message("Unclosed quote after #{format_quote(last)} followed by '#{followed_by}'"))
+    str.gsub!(/\\(.)/m) {
+      ch = $1
+      if escapes.include? ch
+        case ch
+        when 'r'; "\r"
+        when 'n'; "\n"
+        when 't'; "\t"
+        when 's'; " "
+        when "\n"; ''
+        else      ch
+        end
+      else
+        Puppet.warning(positioned_message("Unrecognized escape sequence '\\#{ch}'")) unless ignore_invalid_escapes
+        "\\#{ch}"
+      end
+    }
+    [ str[0..-2],str[-1,1] ]
+  end
+
+  # Formats given message by appending file, line and position if available.
+  def positioned_message msg
+    result = [msg]
+    result << "in file #{file}" if file
+    result << "at line #{line}:#{pos}" if line
+    result.join(" ")
+  end
+
+  # Returns "<eof>" if at end of input, else the following 5 characters with \n \r \t escaped
+  def followed_by
+    return "<eof>" if @scanner.eos?
+    result = @scanner.rest[0,5] + "..."
+    result.gsub!("\t", '\t')
+    result.gsub!("\n", '\n')
+    result.gsub!("\r", '\r')
+    result
+  end
+
+  def format_quote q
+    if q == "'"
+      '"\'"'
+    else
+      "'#{q}'"
+    end
+  end
+
+  def tokenize_interpolated_string(token_type,preamble='')
+    # Expecting a (possibly empty) stretch of text terminated by end of string ", a variable $, or expression ${
+    # The length of this part includes the start and terminating characters.
+    value,terminator = slurpstring('"$')
+
+    # Advanced after '{' if this is in expression ${} interpolation
+    braced = terminator == '$' && @scanner.scan(/\{/)
+    # make offset to end_ofset be the length of the pre expression string including its start and terminating chars
+    lexing_context[:end_offset] = @scanner.pos
+
+    token_queue << [TOKENS[token_type[terminator]],position_in_source().merge!({:value => preamble+value})]
+    variable_regex = if Puppet[:allow_variables_with_dashes]
+      TOKENS[:VARIABLE_WITH_DASH].regex
+    else
+      TOKENS[:VARIABLE].regex
+    end
+    if terminator != '$' or braced
+      return token_queue.shift
+    end
+
+    tmp_offset = @scanner.pos
+    if var_name = @scanner.scan(variable_regex)
+      lexing_context[:offset] = tmp_offset
+      lexing_context[:end_offset] = @scanner.pos
+      warn_if_variable_has_hyphen(var_name)
+      # If the varname after ${ is followed by (, it is a function call, and not a variable
+      # reference.
+      #
+      if braced && @scanner.match?(%r{[ \t\r]*\(})
+        token_queue << [TOKENS[:NAME], position_in_source().merge!({:value=>var_name})]
+      else
+        token_queue << [TOKENS[:VARIABLE],position_in_source().merge!({:value=>var_name})]
+      end
+      lexing_context[:offset] = @scanner.pos
+      tokenize_interpolated_string(DQ_continuation_token_types)
+    else
+      tokenize_interpolated_string(token_type, replace_false_start_with_text(terminator))
+    end
+  end
+
+  def replace_false_start_with_text(appendix)
+    last_token = token_queue.pop
+    value = last_token.last
+    if value.is_a? Hash
+      value[:value] + appendix
+    else
+      value + appendix
+    end
+  end
+
+  # just parse a string, not a whole file
+  def string=(string)
+    @scanner = StringScanner.new(string)
+    @locator = Locator.new(string, multibyte?)
+  end
+
+  def warn_if_variable_has_hyphen(var_name)
+    if var_name.include?('-')
+      Puppet.deprecation_warning("Using `-` in variable names is deprecated at #{file || '<string>'}:#{line}. See http://links.puppetlabs.com/puppet-hyphenated-variable-deprecation")
+    end
+  end
+
+  # Returns the line number (starting from 1) for the current position
+  # in the scanned text (at the end of the last produced, but not necessarily
+  # consumed.
+  #
+  def line
+    return 1 unless lexing_context && locator
+    locator.line_for_offset(lexing_context[:end_offset])
+  end
+
+  # Helper class that keeps track of where line breaks are located and can answer questions about positions.
+  #
+  class Locator
+    attr_reader :line_index
+    attr_reader :string
+
+    # Create a locator based on a content string, and a boolean indicating if ruby version support multi-byte strings
+    # or not.
+    #
+    def initialize(string, multibyte)
+      @string = string
+      @multibyte = multibyte
+      compute_line_index
+    end
+
+    # Returns whether this a ruby version that supports multi-byte strings or not
+    #
+    def multibyte?
+      @multibyte
+    end
+
+    # Computes the start offset for each line.
+    #
+    def compute_line_index
+      scanner = StringScanner.new(@string)
+      result = [0] # first line starts at 0
+      while scanner.scan_until(/\n/)
+        result << scanner.pos
+      end
+      @line_index = result
+    end
+
+    # Returns the line number (first line is 1) for the given offset
+    def line_for_offset(offset)
+      if line_nbr = line_index.index {|x| x > offset}
+        return line_nbr
+      end
+      # If not found it is after last
+      return line_index.size
+    end
+
+    # Returns the offset on line (first offset on a line is 0).
+    #
+    def offset_on_line(offset)
+      line_offset = line_index[line_for_offset(offset)-1]
+      if multibyte?
+        @string.byteslice(line_offset, offset-line_offset).length
+      else
+        offset - line_offset
+      end
+    end
+
+    # Returns the position on line (first position on a line is 1)
+    def pos_on_line(offset)
+      offset_on_line(offset) +1
+    end
+
+    # Returns the character offset for a given byte offset
+    def char_offset(byte_offset)
+      if multibyte?
+        @string.byteslice(0, byte_offset).length
+      else
+        byte_offset
+      end
+    end
+
+    # Returns the length measured in number of characters from the given start and end byte offseta
+    def char_length(offset, end_offset)
+      if multibyte?
+        @string.byteslice(offset, end_offset - offset).length
+      else
+        end_offset - offset
+      end
+    end
+  end
+end
diff --git a/lib/puppet/pops/parser/makefile b/lib/puppet/pops/parser/makefile
new file mode 100644
index 0000000..f521747
--- /dev/null
+++ b/lib/puppet/pops/parser/makefile
@@ -0,0 +1,13 @@
+
+#parser.rb: grammar.ra
+#	racc -o$@ grammar.ra
+#
+#grammar.output: grammar.ra
+#	racc -v -o$@ grammar.ra
+#
+
+eparser.rb: egrammar.ra
+	racc -o$@ egrammar.ra
+
+egrammar.output: egrammar.ra
+	racc -v -o$@ egrammar.ra
\ No newline at end of file
diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb
new file mode 100644
index 0000000..db35aad
--- /dev/null
+++ b/lib/puppet/pops/parser/parser_support.rb
@@ -0,0 +1,203 @@
+require 'puppet/parser/functions'
+require 'puppet/parser/files'
+require 'puppet/resource/type_collection'
+require 'puppet/resource/type_collection_helper'
+require 'puppet/resource/type'
+require 'monitor'
+
+# Supporting logic for the parser.
+# This supporting logic has slightly different responsibilities compared to the original Puppet::Parser::Parser.
+# It is only concerned with parsing.
+#
+class Puppet::Pops::Parser::Parser
+  # Note that the name of the contained class and the file name (currently parser_support.rb)
+  # needs to be different as the class is generated by Racc, and this file (parser_support.rb) is included as a mix in
+  #
+
+  # Simplify access to the Model factory
+  # Note that the parser/parser support does not have direct knowledge about the Model.
+  # All model construction/manipulation is made by the Factory.
+  #
+  Factory = Puppet::Pops::Model::Factory
+  Model = Puppet::Pops::Model
+
+  include Puppet::Resource::TypeCollectionHelper
+
+  attr_accessor :lexer
+
+  # Returns the token text of the given lexer token, or nil, if token is nil
+  def token_text t
+    return t if t.nil?
+    t = t.current if t.respond_to?(:current)
+    return t.value if t.is_a? Model::QualifiedName
+
+    # else it is a lexer token
+    t[:value]
+  end
+
+  # Produces the fully qualified name, with the full (current) namespace for a given name.
+  #
+  # This is needed because class bodies are lazily evaluated and an inner class' container(s) may not
+  # have been evaluated before some external reference is made to the inner class; its must therefore know its complete name
+  # before evaluation-time.
+  #
+  def classname(name)
+    [@lexer.namespace, name].join("::").sub(/^::/, '')
+  end
+
+  # Reinitializes variables (i.e. creates a new lexer instance
+  #
+  def clear
+    initvars
+  end
+
+  # Raises a Parse error.
+  def error(message, options = {})
+    except = Puppet::ParseError.new(message)
+    except.line = options[:line] || @lexer.line
+    except.file = options[:file] || @lexer.file
+    except.pos = options[:pos]   || @lexer.pos
+
+    raise except
+  end
+
+  # Parses a file expected to contain pp DSL logic.
+  def parse_file(file)
+    unless FileTest.exist?(file)
+      unless file =~ /\.pp$/
+        file = file + ".pp"
+      end
+    end
+    @lexer.file = file
+    _parse()
+  end
+
+  def initialize()
+    # Since the parser is not responsible for importing (removed), and does not perform linking,
+    # and there is no syntax that requires knowing if something referenced exists, it is safe
+    # to assume that no environment is needed when parsing. (All that comes later).
+    #
+    initvars
+  end
+
+  # Initializes the parser support by creating a new instance of {Puppet::Pops::Parser::Lexer}
+  # @return [void]
+  #
+  def initvars
+    @lexer = Puppet::Pops::Parser::Lexer.new
+  end
+
+  # This is a callback from the generated grammar (when an error occurs while parsing)
+  # TODO Picks up origin information from the lexer, probably needs this from the caller instead
+  #   (for code strings, and when start line is not line 1 in a code string (or file), etc.)
+  #
+  def on_error(token,value,stack)
+    if token == 0 # denotes end of file
+      value = 'end of file'
+    else
+      value = "'#{value[:value]}'"
+    end
+    error = "Syntax error at #{value}"
+
+    # The 'expected' is only of value at end of input, otherwise any parse error involving a
+    # start of a pair will be reported as expecting the close of the pair - e.g. "$x.each |$x {", would
+    # report that "seeing the '{', the '}' is expected. That would be wrong.
+    # Real "expected" tokens are very difficult to compute (would require parsing of racc output data). Output of the stack
+    # could help, but can require extensive backtracking and produce many options.
+    #
+    if token == 0 && brace = @lexer.expected
+      error += "; expected '#{brace}'"
+    end
+
+    except = Puppet::ParseError.new(error)
+    except.line = @lexer.line
+    except.file = @lexer.file if @lexer.file
+    except.pos  = @lexer.pos
+
+    raise except
+  end
+
+  # Parses a String of pp DSL code.
+  # @todo make it possible to pass a given origin
+  #
+  def parse_string(code)
+    @lexer.string = code
+    _parse()
+  end
+
+  # Mark the factory wrapped model object with location information
+  # @todo the lexer produces :line for token, but no offset or length
+  # @return [Puppet::Pops::Model::Factory] the given factory
+  # @api private
+  #
+  def loc(factory, start_token, end_token = nil)
+    factory.record_position(sourcepos(start_token), sourcepos(end_token))
+  end
+
+  # Associate documentation with the factory wrapped model object.
+  # @return [Puppet::Pops::Model::Factory] the given factory
+  # @api private
+  def doc factory, doc_string
+    factory.doc = doc_string
+  end
+
+  def sourcepos(o)
+    if !o
+      Puppet::Pops::Adapters::SourcePosAdapter.new
+    elsif o.is_a? Puppet::Pops::Model::Factory
+      # It is a built model element with loc set returns start at pos 0
+      o.loc
+    else
+      loc = Puppet::Pops::Adapters::SourcePosAdapter.new
+      # It must be a token
+      loc.line = o[:line]
+      loc.pos = o[:pos]
+      loc.offset = o[:offset]
+      loc.length = o[:length]
+      loc
+    end
+  end
+
+  def aryfy(o)
+    o = [o] unless o.is_a?(Array)
+    o
+  end
+
+  # Transforms an array of expressions containing literal name expressions to calls if followed by an
+  # expression, or expression list
+  #
+  def transform_calls(expressions)
+    Factory.transform_calls(expressions)
+  end
+
+  # Performs the parsing and returns the resulting model.
+  # The lexer holds state, and this is setup with {#parse_string}, or {#parse_file}.
+  #
+  # TODO: Drop support for parsing a ruby file this way (should be done where it is decided
+  #   which file to load/run (i.e. loaders), and initial file to run
+  # TODO: deal with options containing origin (i.e. parsing a string from externally known location).
+  # TODO: should return the model, not a Hostclass
+  #
+  # @api private
+  #
+  def _parse()
+    begin
+      @yydebug = false
+      main = yyparse(@lexer,:scan)
+      # #Commented out now because this hides problems in the racc grammar while developing
+      # # TODO include this when test coverage is good enough.
+      #      rescue Puppet::ParseError => except
+      #        except.line ||= @lexer.line
+      #        except.file ||= @lexer.file
+      #        except.pos  ||= @lexer.pos
+      #        raise except
+      #      rescue => except
+      #        raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, @lexer.pos, except)
+    end
+    main.record_origin(@lexer.file) if main
+    return main
+  ensure
+    @lexer.clear
+  end
+
+end
diff --git a/lib/puppet/pops/patterns.rb b/lib/puppet/pops/patterns.rb
new file mode 100644
index 0000000..d18384f
--- /dev/null
+++ b/lib/puppet/pops/patterns.rb
@@ -0,0 +1,35 @@
+# The Patterns module contains common regular expression patters for the Puppet DSL language
+module Puppet::Pops::Patterns
+
+  # NUMERIC matches hex, octal, decimal, and floating point and captures three parts
+  # 0 = entire matched number, leading and trailing whitespace included
+  # 1 = hexadecimal number
+  # 2 = non hex integer portion, possibly with leading 0 (octal)
+  # 3 = floating point part, starts with ".", decimals and optional exponent
+  #
+  # Thus, a hex number has group 1 value, an octal value has group 2 (if it starts with 0), and no group 3
+  # and a floating point value has group 2 and group 3.
+  #
+  NUMERIC = %r{^\s*(?:(0[xX][0-9A-Fa-f]+)|(0?\d+)((?:\.\d+)?(?:[eE]-?\d+)?))\s*$}
+
+  # ILLEGAL_P3_1_HOSTNAME matches if a hostname contains illegal characters.
+  # This check does not prevent pathological names like 'a....b', '.....', "---". etc.
+  ILLEGAL_HOSTNAME_CHARS = %r{[^-\w.]}
+
+  # NAME matches a name the same way as the lexer.
+  # This name includes hyphen, which may be illegal in variables, and names in general.
+  NAME = %r{((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*}
+
+  # CLASSREF_EXT matches a class reference the same way as the lexer - i.e. the external source form
+  # where each part must start with a capital letter A-Z.
+  # This name includes hyphen, which may be illegal in some cases.
+  #
+  CLASSREF_EXT = %r{((::){0,1}[A-Z][-\w]*)+}
+
+  # CLASSREF matches a class reference the way it is represented internall in the
+  # model (i.e. in lower case).
+  # This name includes hyphen, which may be illegal in some cases.
+  #
+  CLASSREF = %r{((::){0,1}[a-z][-\w]*)+}
+
+end
diff --git a/lib/puppet/pops/utils.rb b/lib/puppet/pops/utils.rb
new file mode 100644
index 0000000..104a269
--- /dev/null
+++ b/lib/puppet/pops/utils.rb
@@ -0,0 +1,104 @@
+# Provides utility methods
+module Puppet::Pops::Utils
+  # Can the given o be converted to numeric? (or is numeric already)
+  # Accepts a leading '::'
+  # Returns a boolean if the value is numeric
+  # If testing if value can be converted it is more efficient to call {#to_n} or {#to_n_with_radix} directly
+  # and check if value is nil.
+  def self.is_numeric?(o)
+    case o
+    when Numeric, Integer, Fixnum, Float
+      !!o
+    else
+      !!Puppet::Pops::Patterns::NUMERIC.match(relativize_name(o.to_s))
+    end
+  end
+
+  # To LiteralNumber with radix, or nil if not a number.
+  # If the value is already a number it is returned verbatim with a radix of 10.
+  # @param o [String, Number] a string containing a number in octal, hex, integer (decimal) or floating point form
+  # @return [Array<Number, Integer>, nil] array with converted number and radix, or nil if not possible to convert
+  # @api public
+  #
+  def self.to_n_with_radix o
+    begin
+      case o
+      when String
+        match = Puppet::Pops::Patterns::NUMERIC.match(relativize_name(o))
+        if !match
+          nil
+        elsif match[3].to_s.length > 0
+          # Use default radix (default is decimal == 10) for floats
+          [Float(match[0]), 10]
+        else
+          # Set radix (default is decimal == 10)
+          radix = 10
+          if match[1].to_s.length > 0
+            radix = 16
+          elsif match[2].to_s.length > 0 && match[2][0] == '0'
+            radix = 8
+          end
+          [Integer(match[0], radix), radix]
+        end
+      when Numeric, Fixnum, Integer, Float
+        # Impossible to calculate radix, assume decimal
+        [o, 10]
+      else
+        nil
+      end
+    rescue ArgumentError
+      nil
+    end
+  end
+
+  # To Numeric (or already numeric)
+  # Returns nil if value is not numeric, else an Integer or Float
+  # A leading '::' is accepted (and ignored)
+  #
+  def self.to_n o
+    begin
+      case o
+      when String
+        match = Puppet::Pops::Patterns::NUMERIC.match(relativize_name(o))
+        if !match
+          nil
+        elsif match[3].to_s.length > 0
+          Float(match[0])
+        else
+          Integer(match[0])
+        end
+      when Numeric, Fixnum, Integer, Float
+        o
+      else
+        nil
+      end
+    rescue ArgumentError
+      nil
+    end
+  end
+
+  # is the name absolute (i.e. starts with ::)
+  def self.is_absolute? name
+    name.start_with? "::"
+  end
+
+  def self.name_to_segments name
+    name.split("::")
+  end
+
+  def self.relativize_name name
+    is_absolute?(name) ? name[2..-1] : name
+  end
+
+  # Finds an adapter for o or for one of its containers, or nil, if none of the containers
+  # was adapted with the given adapter.
+  # This method can only be used with objects that respond to `:eContainer`.
+  # with true, and Adaptable#adapters.
+  #
+  def self.find_adapter(o, adapter)
+    return nil unless o
+    a = adapter.get(o)
+    return a if a
+    return find_adapter(o.eContainer, adapter)
+  end
+end
diff --git a/lib/puppet/pops/validation.rb b/lib/puppet/pops/validation.rb
new file mode 100644
index 0000000..5e9aa2b
--- /dev/null
+++ b/lib/puppet/pops/validation.rb
@@ -0,0 +1,297 @@
+# A module with base functionality for validation of a model.
+#
+# * SeverityProducer - produces a severity (:error, :warning, :ignore) for a given Issue
+# * DiagnosticProducer - produces a Diagnostic which binds an Issue to an occurrence of that issue
+# * Acceptor - the receiver/sink/collector of computed diagnostics
+# * DiagnosticFormatter - produces human readable output for a Diagnostic
+#
+module Puppet::Pops::Validation
+  # Decides on the severity of a given issue.
+  # The produced severity is one of `:error`, `:warning`, or `:ignore`.
+  # By default, a severity of `:error` is produced for all issues. To configure the severity
+  # of an issue call `#severity=(issue, level)`.
+  #
+  class SeverityProducer
+    # Creates a new instance where all issues are diagnosed as :error unless overridden.
+    #
+    def initialize
+      # If diagnose is not set, the default is returned by the block
+      @severities = Hash.new :error
+    end
+
+    # Returns the severity of the given issue.
+    # @returns [Symbol] severity level :error, :warning, or :ignore
+    #
+    def severity issue
+      assert_issue(issue)
+      @severities[issue]
+    end
+
+    def [] issue
+      severity issue
+    end
+
+    # Override a default severity with the given severity level.
+    #
+    # @param issue [Puppet::Pops::Issues::Issue] the issue for which to set severity
+    # @param level [Symbol] the severity level (:error, :warning, or :ignore).
+    #
+    def []= issue, level
+      assert_issue(issue)
+      assert_severity(level)
+      raise Puppet::DevError.new("Attempt to demote the hard issue '#{issue.issue_code}' to #{level}") unless issue.demotable? || level == :error
+      @severities[issue] = level
+    end
+
+    # Returns true if the issue should be reported or not.
+    # @returns [Boolean] this implementation returns true for errors and warnings
+    #
+    def should_report? issue
+      diagnose = self[issue]
+      diagnose == :error || diagnose == :warning || diagnose == :deprecation
+    end
+
+    def assert_issue issue
+      raise Puppet::DevError.new("Attempt to get validation severity for something that is not an Issue. (Got #{issue.class})") unless issue.is_a? Puppet::Pops::Issues::Issue
+    end
+
+    def assert_severity level
+      raise Puppet::DevError.new("Illegal severity level: #{option}") unless [:ignore, :warning, :error, :deprecation].include? level
+    end
+  end
+
+  # A producer of diagnostics.
+  # An producer of diagnostics is given each issue occurrence as they are found by a diagnostician/validator. It then produces
+  # a Diagnostic, which it passes on to a configured Acceptor.
+  #
+  # This class exists to aid a diagnostician/validator which will typically first check if a particular issue
+  # will be accepted at all (before checking for an occurrence of the issue; i.e. to perform check avoidance for expensive checks).
+  # A validator passes an instance of Issue, the semantic object (the "culprit"), a hash with arguments, and an optional
+  # exception. The semantic object is used to determine the location of the occurrence of the issue (file/line), and it
+  # sets keys in the given argument hash that may be used in the formatting of the issue message.
+  #
+  class DiagnosticProducer
+
+    # A producer of severity for a given issue
+    # @return [SeverityProducer]
+    #
+    attr_reader :severity_producer
+
+    # A producer of labels for objects involved in the issue
+    # @return [LabelProvider]
+    #
+    attr_reader :label_provider
+    # Initializes this producer.
+    #
+    # @param acceptor [Acceptor] a sink/collector of diagnostic results
+    # @param severity_producer [SeverityProducer] the severity producer to use to determine severity of a given issue
+    # @param label_provider [LabelProvider] a provider of model element type to human readable label
+    #
+    def initialize(acceptor, severity_producer, label_provider)
+      @acceptor           = acceptor
+      @severity_producer  = severity_producer
+      @label_provider     = label_provider
+    end
+
+    def accept(issue, semantic, arguments={}, except=nil)
+      return unless will_accept? issue
+
+      # Set label provider unless caller provided a special label provider
+      arguments[:label]    ||= @label_provider
+      arguments[:semantic] ||= semantic
+
+      # A detail message is always provided, but is blank by default.
+      arguments[:detail] ||= ''
+
+      origin_adapter = Puppet::Pops::Utils.find_adapter(semantic, Puppet::Pops::Adapters::OriginAdapter)
+      file = origin_adapter ? origin_adapter.origin : nil
+      source_pos = Puppet::Pops::Utils.find_adapter(semantic, Puppet::Pops::Adapters::SourcePosAdapter)
+      severity = @severity_producer.severity(issue)
+      @acceptor.accept(Diagnostic.new(severity, issue, file, source_pos, arguments))
+    end
+
+    def will_accept? issue
+      @severity_producer.should_report? issue
+    end
+  end
+
+  class Diagnostic
+    attr_reader :severity
+    attr_reader :issue
+    attr_reader :arguments
+    attr_reader :exception
+    attr_reader :file
+    attr_reader :source_pos
+    def initialize severity, issue, file, source_pos, arguments={}, exception=nil
+      @severity = severity
+      @issue = issue
+      @file = file
+      @source_pos = source_pos
+      @arguments = arguments
+      @exception = exception
+    end
+  end
+
+  # Formats a diagnostic for output.
+  # Produces a diagnostic output typical for a compiler (suitable for interpretation by tools)
+  # The format is:
+  # `file:line:pos: Message`, where pos, line and file are included if available.
+  #
+  class DiagnosticFormatter
+    def format diagnostic
+      "#{loc(diagnostic)} #{format_severity(diagnostic)}#{format_message(diagnostic)}"
+    end
+
+    def format_message diagnostic
+      diagnostic.issue.format(diagnostic.arguments)
+    end
+
+    # This produces "Deprecation notice: " prefix if the diagnostic has :deprecation severity, otherwise "".
+    # The idea is that all other diagnostics are emitted with the methods Puppet.err (or an exception), and
+    # Puppet.warning.
+    # @note Note that it is not a good idea to use Puppet.deprecation_warning as it is for internal deprecation.
+    #
+    def format_severity diagnostic
+      diagnostic.severity == :deprecation ? "Deprecation notice: " : ""
+    end
+
+    def format_location diagnostic
+      file = diagnostic.file
+      line = diagnostic.source_pos.line
+      pos = diagnostic.source_pos.pos
+      if file && line && pos
+        "#{file}:#{line}:#{pos}:"
+      elsif file && line
+        "#{file}:#{line}:"
+      elsif file
+        "#{file}:"
+      else
+        ""
+      end
+    end
+  end
+
+  # Produces a diagnostic output in the "puppet style", where the location is appended with an "at ..." if the
+  # location is known.
+  #
+  class DiagnosticFormatterPuppetStyle < DiagnosticFormatter
+    def format diagnostic
+      if (location = format_location diagnostic) != ""
+        "#{format_severity(diagnostic)}#{format_message(diagnostic)}#{location}"
+      else
+        format_message(diagnostic)
+      end
+    end
+
+    # The somewhat (machine) unusable format in current use by puppet.
+    # have to be used here for backwards compatibility.
+    def format_location diagnostic
+      file = diagnostic.file
+      line = diagnostic.source_pos.line
+      pos = diagnostic.source_pos.pos
+      if file && line && pos
+        " at #{file}:#{line}:#{pos}"
+      elsif file and line
+        " at #{file}:#{line}"
+      elsif line && pos
+        " at line #{line}:#{pos}"
+      elsif line
+        " at line #{line}"
+      elsif file
+        " in #{file}"
+      else
+        ""
+      end
+    end
+  end
+
+  # An acceptor of diagnostics.
+  # An acceptor of diagnostics is given each issue as they are found by a diagnostician/validator. An
+  # acceptor can collect all found issues, or decide to collect a few and then report, or give up as the first issue
+  # if found.
+  # This default implementation collects all diagnostics in the order they are produced, and can then
+  # answer questions about what was diagnosed.
+  #
+  class Acceptor
+
+    # All diagnstic in the order they were issued
+    attr_reader :diagnostics
+
+    # The number of :warning severity issues + number of :deprecation severity issues
+    attr_reader :warning_count
+
+    # The number of :error severity issues
+    attr_reader :error_count
+    # Initializes this diagnostics acceptor.
+    # By default, the acceptor is configured with a default severity producer.
+    # @param severity_producer [SeverityProducer] the severity producer to use to determine severity of an issue
+    #
+    # TODO add semantic_label_provider
+    #
+    def initialize()
+      @diagnostics = []
+      @error_count = 0
+      @warning_count = 0
+    end
+
+    # Returns true when errors have been diagnosed.
+    def errors?
+      @error_count > 0
+    end
+
+    # Returns true when warnings have been diagnosed.
+    def warnings?
+      @warning_count > 0
+    end
+
+    # Returns true when errors and/or warnings have been diagnosed.
+    def errors_or_warnings?
+      errors? || warnings?
+    end
+
+    # Returns the diagnosed errors in the order thwy were reported.
+    def errors
+      @diagnostics.select {|d| d.severity == :error }
+    end
+
+    # Returns the diagnosed warnings in the order thwy were reported.
+    # (This includes :warning and :deprecation severity)
+    def warnings
+      @diagnostics.select {|d| d.severity == :warning || d.severity == :deprecation }
+    end
+
+    def errors_and_warnings
+      @diagnostics.select {|d| d.severity != :ignored}
+    end
+
+    # Returns the ignored diagnostics in the order thwy were reported (if reported at all)
+    def ignored
+      @diagnostics.select {|d| d.severity == :ignore }
+    end
+
+    # Add a diagnostic to the set of diagnostics
+    def accept(diagnostic)
+      self.send(diagnostic.severity, diagnostic)
+    end
+
+    private
+
+    def ignore diagnostic
+      @diagnostics << diagnostic
+    end
+
+    def error diagnostic
+      @diagnostics << diagnostic
+      @error_count += 1
+    end
+
+    def warning diagnostic
+      @diagnostics << diagnostic
+      @warning_count += 1
+    end
+
+    def deprecation diagnostic
+      warning diagnostic
+    end
+  end
+end
diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb
new file mode 100644
index 0000000..69df726
--- /dev/null
+++ b/lib/puppet/pops/validation/checker3_1.rb
@@ -0,0 +1,551 @@
+# A Validator validates a model.
+#
+# Validation is performed on each model element in isolation. Each method should validate the model element's state
+# but not validate its referenced/contained elements except to check their validity in their respective role.
+# The intent is to drive the validation with a tree iterator that visits all elements in a model.
+#
+#
+# TODO: Add validation of multiplicities - this is a general validation that can be checked for all
+#       Model objects via their metamodel. (I.e an extra call to multiplicity check in polymorph check).
+#       This is however mostly valuable when validating model to model transformations, and is therefore T.B.D
+#
+class Puppet::Pops::Validation::Checker3_1
+  Issues = Puppet::Pops::Issues
+  Model = Puppet::Pops::Model
+
+  attr_reader :acceptor
+  # Initializes the validator with a diagnostics producer. This object must respond to
+  # `:will_accept?` and `:accept`.
+  #
+  def initialize(diagnostics_producer)
+    @@check_visitor       ||= Puppet::Pops::Visitor.new(nil, "check", 0, 0)
+    @@rvalue_visitor      ||= Puppet::Pops::Visitor.new(nil, "rvalue", 0, 0)
+    @@hostname_visitor    ||= Puppet::Pops::Visitor.new(nil, "hostname", 1, 1)
+    @@assignment_visitor  ||= Puppet::Pops::Visitor.new(nil, "assign", 0, 1)
+    @@query_visitor       ||= Puppet::Pops::Visitor.new(nil, "query", 0, 0)
+    @@top_visitor         ||= Puppet::Pops::Visitor.new(nil, "top", 1, 1)
+    @@relation_visitor    ||= Puppet::Pops::Visitor.new(nil, "relation", 1, 1)
+
+    @acceptor = diagnostics_producer
+  end
+
+  # Validates the entire model by visiting each model element and calling `check`.
+  # The result is collected (or acted on immediately) by the configured diagnostic provider/acceptor
+  # given when creating this Checker.
+  #
+  def validate(model)
+    # tree iterate the model, and call check for each element
+    check(model)
+    model.eAllContents.each {|m| check(m) }
+  end
+
+  # Performs regular validity check
+  def check(o)
+    @@check_visitor.visit_this(self, o)
+  end
+
+  # Performs check if this is a vaid hostname expression
+  def hostname(o, semantic)
+    @@hostname_visitor.visit_this(self, o, semantic)
+  end
+
+  # Performs check if this is valid as a query
+  def query(o)
+    @@query_visitor.visit_this(self, o)
+  end
+
+  # Performs check if this is valid as a relationship side
+  def relation(o, container)
+    @@relation_visitor.visit_this(self, o, container)
+  end
+
+  # Performs check if this is valid as a rvalue
+  def rvalue(o)
+    @@rvalue_visitor.visit_this(self, o)
+  end
+
+  # Performs check if this is valid as a container of a definition (class, define, node)
+  def top(o, definition)
+    @@top_visitor.visit_this(self, o, definition)
+  end
+
+  # Checks the LHS of an assignment (is it assignable?).
+  # If args[0] is true, assignment via index is checked.
+  #
+  def assign(o, *args)
+    @@assignment_visitor.visit_this(self, o, *args)
+  end
+
+  #---ASSIGNMENT CHECKS
+
+  def assign_VariableExpression(o, *args)
+    varname_string = varname_to_s(o.expr)
+    if varname_string =~ /^[0-9]+$/
+      acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string)
+    end
+    # Can not assign to something in another namespace (i.e. a '::' in the name is not legal)
+    if acceptor.will_accept? Issues::CROSS_SCOPE_ASSIGNMENT
+      if varname_string =~ /::/
+        acceptor.accept(Issues::CROSS_SCOPE_ASSIGNMENT, o, :name => varname_string)
+      end
+    end
+    # TODO: Could scan for reassignment of the same variable if done earlier in the same container
+    #       Or if assigning to a parameter (more work).
+    # TODO: Investigate if there are invalid cases for += assignment
+  end
+
+  def assign_AccessExpression(o, *args)
+    # Are indexed assignments allowed at all ? $x[x] = '...'
+    if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT
+      acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o)
+    else
+      # Then the left expression must be assignable-via-index
+      assign(o.left_expr, true)
+    end
+  end
+
+  def assign_Object(o, *args)
+    # Can not assign to anything else (differentiate if this is via index or not)
+    # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases)
+    #
+    acceptor.accept(args[0] ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o)
+  end
+
+  #---CHECKS
+
+  def check_Object(o)
+  end
+
+  def check_Factory(o)
+    check(o.current)
+  end
+
+  def check_AccessExpression(o)
+    # Check multiplicity of keys
+    case o.left_expr
+    when Model::QualifiedName
+      # allows many keys, but the name should really be a QualifiedReference
+      acceptor.accept(Issues::DEPRECATED_NAME_AS_TYPE, o, :name => o.value)
+    when Model::QualifiedReference
+      # ok, allows many - this is a resource reference
+
+    else
+      # i.e. for any other expression that may produce an array or hash
+      if o.keys.size > 1
+        acceptor.accept(Issues::UNSUPPORTED_RANGE, o, :count => o.keys.size)
+      end
+      if o.keys.size < 1
+        acceptor.accept(Issues::MISSING_INDEX, o)
+      end
+    end
+  end
+
+  def check_AssignmentExpression(o)
+    assign(o.left_expr)
+    rvalue(o.right_expr)
+  end
+
+  # Checks that operation with :+> is contained in a ResourceOverride or Collector.
+  #
+  # Parent of an AttributeOperation can be one of:
+  # * CollectExpression
+  # * ResourceOverride
+  # * ResourceBody (ILLEGAL this is a regular resource expression)
+  # * ResourceDefaults (ILLEGAL)
+  #
+  def check_AttributeOperation(o)
+    if o.operator == :'+>'
+      # Append operator use is constrained
+      parent = o.eContainer
+      unless parent.is_a?(Model::CollectExpression) || parent.is_a?(Model::ResourceOverrideExpression)
+        acceptor.accept(Issues::ILLEGAL_ATTRIBUTE_APPEND, o, {:name=>o.attribute_name, :parent=>parent})
+      end
+    end
+    rvalue(o.value_expr)
+  end
+
+  def check_BinaryExpression(o)
+    rvalue(o.left_expr)
+    rvalue(o.right_expr)
+  end
+
+  def check_CallNamedFunctionExpression(o)
+    unless o.functor_expr.is_a? Model::QualifiedName
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o)
+    end
+  end
+
+  def check_MethodCallExpression(o)
+    unless o.functor_expr.is_a? Model::QualifiedName
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o)
+    end
+  end
+
+  def check_CaseExpression(o)
+    # There should only be one LiteralDefault case option value
+    # TODO: Implement this check
+  end
+
+  def check_CollectExpression(o)
+    unless o.type_expr.is_a? Model::QualifiedReference
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature=> 'type name', :container => o)
+    end
+
+    # If a collect expression tries to collect exported resources and storeconfigs is not on
+    # then it will not work... This was checked in the parser previously. This is a runtime checking
+    # thing as opposed to a language thing.
+    if acceptor.will_accept?(Issues::RT_NO_STORECONFIGS) && o.query.is_a?(Model::ExportedQuery)
+      acceptor.accept(Issues::RT_NO_STORECONFIGS, o)
+    end
+  end
+
+  # Only used for function names, grammar should not be able to produce something faulty, but
+  # check anyway if model is created programatically (it will fail in transformation to AST for sure).
+  def check_NamedAccessExpression(o)
+    name = o.right_expr
+    unless name.is_a? Model::QualifiedName
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, name, :feature=> 'function name', :container => o.eContainer)
+    end
+  end
+
+  # for 'class' and 'define'
+  def check_NamedDefinition(o)
+    top(o.eContainer, o)
+    if (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.name.include?('-')
+      acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.name})
+    end
+  end
+
+  def check_ImportExpression(o)
+    o.files.each do |f|
+      unless f.is_a? Model::LiteralString
+        acceptor.accept(Issues::ILLEGAL_EXPRESSION, f, :feature => 'file name', :container => o)
+      end
+    end
+  end
+
+  def check_InstanceReference(o)
+    # TODO: Original warning is :
+    #       Puppet.warning addcontext("Deprecation notice:  Resource references should now be capitalized")
+    #       This model element is not used in the egrammar.
+    #       Either implement checks or deprecate the use of InstanceReference (the same is acheived by
+    #       transformation of AccessExpression when used where an Instance/Resource reference is allowed.
+    #
+  end
+
+  # Restrictions on hash key are because of the strange key comparisons/and merge rules in the AST evaluation
+  # (Even the allowed ones are handled in a strange way).
+  #
+  def transform_KeyedEntry(o)
+    case o.key
+    when Model::QualifiedName
+    when Model::LiteralString
+    when Model::LiteralNumber
+    when Model::ConcatenatedString
+    else
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => o.eContainer)
+    end
+  end
+
+  # A Lambda is a Definition, but it may appear in other scopes that top scope (Which check_Definition asserts).
+  #
+  def check_LambdaExpression(o)
+  end
+
+  def check_NodeDefinition(o)
+    # Check that hostnames are valid hostnames (or regular expressons)
+    hostname(o.host_matches, o)
+    top(o.eContainer, o)
+  end
+
+  # Asserts that value is a valid QualifiedName. No additional checking is made, objects that use
+  # a QualifiedName as a name should check the validity - this since a QualifiedName is used as a BARE WORD
+  # and then additional chars may be valid (like a hyphen).
+  #
+  def check_QualifiedName(o)
+    # Is this a valid qualified name?
+    if o.value !~ Puppet::Pops::Patterns::NAME
+      acceptor.accept(Issues::ILLEGAL_NAME, o, {:name=>o.value})
+    end
+  end
+
+  # Checks that the value is a valid UpperCaseWord (a CLASSREF), and optionally if it contains a hypen.
+  # DOH: QualifiedReferences are created with LOWER CASE NAMES at parse time
+  def check_QualifiedReference(o)
+    # Is this a valid qualified name?
+    if o.value !~ Puppet::Pops::Patterns::CLASSREF
+      acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.value})
+    elsif (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.value.include?('-')
+      acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.value})
+    end
+  end
+
+  def check_QueryExpression(o)
+    rvalue(o.expr) if o.expr  # is optional
+  end
+
+  def relation_Object(o, rel_expr)
+    acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature => o.eContainingFeature, :container => rel_expr})
+  end
+
+  def relation_AccessExpression(o, rel_expr); end
+
+  def relation_CollectExpression(o, rel_expr); end
+
+  def relation_VariableExpression(o, rel_expr); end
+
+  def relation_LiteralString(o, rel_expr); end
+
+  def relation_ConcatenatedStringExpression(o, rel_expr); end
+
+  def relation_SelectorExpression(o, rel_expr); end
+
+  def relation_CaseExpression(o, rel_expr); end
+
+  def relation_ResourceExpression(o, rel_expr); end
+
+  def relation_RelationshipExpression(o, rel_expr); end
+
+  def check_Parameter(o)
+    if o.name =~ /^[0-9]+$/
+      acceptor.accept(Issues::ILLEGAL_NUMERIC_PARAMETER, o, :name => o.name)
+    end
+  end
+
+  #relationship_side: resource
+  #  | resourceref
+  #  | collection
+  #  | variable
+  #  | quotedtext
+  #  | selector
+  #  | casestatement
+  #  | hasharrayaccesses
+
+  def check_RelationshipExpression(o)
+    relation(o.left_expr, o)
+    relation(o.right_expr, o)
+  end
+
+  def check_ResourceExpression(o)
+    # A resource expression must have a lower case NAME as its type e.g. 'file { ... }'
+    unless o.type_name.is_a? Model::QualifiedName
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_name, :feature => 'resource type', :container => o)
+    end
+
+    # This is a runtime check - the model is valid, but will have runtime issues when evaluated
+    # and storeconfigs is not set.
+    if acceptor.will_accept?(Issues::RT_NO_STORECONFIGS) && o.exported
+      acceptor.accept(Issues::RT_NO_STORECONFIGS_EXPORT, o)
+    end
+  end
+
+  def check_ResourceDefaultsExpression(o)
+    if o.form && o.form != :regular
+      acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o)
+    end
+  end
+
+  # Transformation of SelectorExpression is limited to certain types of expressions.
+  # This is probably due to constraints in the old grammar rather than any real concerns.
+  def select_SelectorExpression(o)
+    case o.left_expr
+    when Model::CallNamedFunctionExpression
+    when Model::AccessExpression
+    when Model::VariableExpression
+    when Model::ConcatenatedString
+    else
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.left_expr, :feature => 'left operand', :container => o)
+    end
+  end
+
+  def check_UnaryExpression(o)
+    rvalue(o.expr)
+  end
+
+  def check_UnlessExpression(o)
+    # TODO: Unless may not have an elsif
+    # TODO: 3.x unless may not have an else
+  end
+
+  def check_VariableExpression(o)
+    # The expression must be a qualified name
+    if !o.expr.is_a? Model::QualifiedName
+      acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o)
+    else
+      # Note, that if it later becomes illegal with hyphen in any name, this special check
+      # can be skipped in favor of the check in QualifiedName, which is now not done if contained in
+      # a VariableExpression
+      name = o.expr.value
+      if (acceptor.will_accept? Issues::VAR_WITH_HYPHEN) && name.include?('-')
+        acceptor.accept(Issues::VAR_WITH_HYPHEN, o, {:name => name})
+      end
+    end
+  end
+
+  #--- HOSTNAME CHECKS
+
+  # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
+  def hostname_Array(o, semantic)
+    o.each {|x| hostname x, semantic }
+  end
+
+  def hostname_String(o, semantic)
+    # The 3.x checker only checks for illegal characters - if matching /[^-\w.]/ the name is invalid,
+    # but this allows pathological names like "a..b......c", "----"
+    # TODO: Investigate if more illegal hostnames should be flagged.
+    #
+    if o =~ Puppet::Pops::Patterns::ILLEGAL_HOSTNAME_CHARS
+      acceptor.accept(Issues::ILLEGAL_HOSTNAME_CHARS, semantic, :hostname => o)
+    end
+  end
+
+  def hostname_LiteralValue(o, semantic)
+    hostname_String(o.value.to_s, o)
+  end
+
+  def hostname_ConcatenatedString(o, semantic)
+    # Puppet 3.1. only accepts a concatenated string without interpolated expressions
+    if the_expr = o.segments.index {|s| s.is_a?(Model::TextExpression) }
+      acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr)
+    elsif o.segments.size() != 1
+      # corner case, bad model, concatenation of several plain strings
+      acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o)
+    else
+      # corner case, may be ok, but lexer may have replaced with plain string, this is
+      # here if it does not
+      hostname_String(o.segments[0], o.segments[0])
+    end
+  end
+
+  def hostname_QualifiedName(o, semantic)
+    hostname_String(o.value.to_s, o)
+  end
+
+  def hostname_QualifiedReference(o, semantic)
+    hostname_String(o.value.to_s, o)
+  end
+
+  def hostname_LiteralNumber(o, semantic)
+    # always ok
+  end
+
+  def hostname_LiteralDefault(o, semantic)
+    # always ok
+  end
+
+  def hostname_LiteralRegularExpression(o, semantic)
+    # always ok
+  end
+
+  def hostname_Object(o, semantic)
+    acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=>'hostname', :container=>semantic})
+  end
+
+  #---QUERY CHECKS
+
+  # Anything not explicitly allowed is flagged as error.
+  def query_Object(o)
+    acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o)
+  end
+
+  # Puppet AST only allows == and !=
+  #
+  def query_ComparisonExpression(o)
+    acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) unless [:'==', :'!='].include? o.operator
+  end
+
+  # Allows AND, OR, and checks if left/right are allowed in query.
+  def query_BooleanExpression(o)
+    query o.left_expr
+    query o.right_expr
+  end
+
+  def query_ParenthesizedExpression(o)
+    query(o.expr)
+  end
+
+  def query_VariableExpression(o); end
+
+  def query_QualifiedName(o); end
+
+  def query_LiteralNumber(o); end
+
+  def query_LiteralString(o); end
+
+  def query_LiteralBoolean(o); end
+
+  #---RVALUE CHECKS
+
+  # By default, all expressions are reported as being rvalues
+  # Implement specific rvalue checks for those that are not.
+  #
+  def rvalue_Expression(o); end
+
+  def rvalue_ImportExpression(o)          ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_BlockExpression(o)           ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_CaseExpression(o)            ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_IfExpression(o)              ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_UnlessExpression(o)          ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_ResourceExpression(o)        ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_ResourceOverrideExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_CollectExpression(o)         ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_Definition(o)                ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_NodeDefinition(o)            ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
+
+  def rvalue_UnaryExpression(o)           ; rvalue o.expr                 ; end
+
+  #---TOP CHECK
+
+  def top_NilClass(o, definition)
+    # ok, reached the top, no more parents
+  end
+
+  def top_Object(o, definition)
+    # fail, reached a container that is not top level
+    acceptor.accept(Issues::NOT_TOP_LEVEL, definition)
+  end
+
+  def top_BlockExpression(o, definition)
+    # ok, if this is a block representing the body of a class, or is top level
+    top o.eContainer, definition
+  end
+
+  def top_HostClassDefinition(o, definition)
+    # ok, stop scanning parents
+  end
+
+  # A LambdaExpression is a BlockExpression, and this method is needed to prevent the polymorph method for BlockExpression
+  # to accept a lambda.
+  # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure.
+  #
+  def top_LambdaExpression(o, definition)
+    # fail, stop scanning parents
+    acceptor.accept(Issues::NOT_TOP_LEVEL, definition)
+  end
+
+  #--- NON POLYMORPH, NON CHECKING CODE
+
+  # Produces string part of something named, or nil if not a QualifiedName or QualifiedReference
+  #
+  def varname_to_s(o)
+    case o
+    when Model::QualifiedName
+      o.value
+    when Model::QualifiedReference
+      o.value
+    else
+      nil
+    end
+  end
+end
diff --git a/lib/puppet/pops/validation/validator_factory_3_1.rb b/lib/puppet/pops/validation/validator_factory_3_1.rb
new file mode 100644
index 0000000..ee5ca3e
--- /dev/null
+++ b/lib/puppet/pops/validation/validator_factory_3_1.rb
@@ -0,0 +1,41 @@
+# Configures validation suitable for 3.1 + iteration
+#
+class Puppet::Pops::Validation::ValidatorFactory_3_1
+  Issues = Puppet::Pops::Issues
+
+  # Produces a validator with the given acceptor as the recipient of produced diagnostics.
+  #
+  def validator acceptor
+    checker(diagnostic_producer(acceptor))
+  end
+
+  # Produces the diagnostics producer to use given an acceptor as the recipient of produced diagnostics
+  #
+  def diagnostic_producer acceptor
+    Puppet::Pops::Validation::DiagnosticProducer.new(acceptor, severity_producer(), label_provider())
+  end
+
+  # Produces the checker to use
+  def checker diagnostic_producer
+    Puppet::Pops::Validation::Checker3_1.new(diagnostic_producer)
+  end
+
+  # Produces the label provider to use
+  def label_provider
+    Puppet::Pops::Model::ModelLabelProvider.new()
+  end
+
+  # Produces the severity producer to use
+  def severity_producer
+    p = Puppet::Pops::Validation::SeverityProducer.new
+
+    # Configure each issue that should **not** be an error
+    #
+    p[Issues::RT_NO_STORECONFIGS_EXPORT]    = :warning
+    p[Issues::RT_NO_STORECONFIGS]           = :warning
+    p[Issues::NAME_WITH_HYPHEN]             = :deprecation
+    p[Issues::DEPRECATED_NAME_AS_TYPE]      = :deprecation
+
+    p
+  end
+end
diff --git a/lib/puppet/pops/visitable.rb b/lib/puppet/pops/visitable.rb
new file mode 100644
index 0000000..2f72e37
--- /dev/null
+++ b/lib/puppet/pops/visitable.rb
@@ -0,0 +1,6 @@
+# Visitable is a mix-in module that makes a class visitable by a Visitor
+module Puppet::Pops::Visitable
+  def accept(visitor, *arguments)
+    visitor.visit(self, *arguments)
+  end
+end
diff --git a/lib/puppet/pops/visitor.rb b/lib/puppet/pops/visitor.rb
new file mode 100644
index 0000000..d5526ac
--- /dev/null
+++ b/lib/puppet/pops/visitor.rb
@@ -0,0 +1,50 @@
+# A Visitor performs delegation to a given receiver based on the configuration of the Visitor.
+# A new visitor is created with a given receiver, a method prefix, min, and max argument counts.
+# e.g.
+#   vistor = Visitor.new(self, "visit_from", 1, 1)
+# will make the visitor call "self.visit_from_CLASS(x)" where CLASS is resolved to the given
+# objects class, or one of is ancestors, the first class for which there is an implementation of
+# a method will be selected.
+#
+# Raises RuntimeError if there are too few or too many arguments, or if the receiver is not
+# configured to handle a given visiting object.
+#
+class Puppet::Pops::Visitor
+  attr_reader :receiver, :message, :min_args, :max_args, :cache
+  def initialize(receiver, message, min_args=0, max_args=nil)
+    raise ArgumentError.new("min_args must be >= 0") if min_args < 0
+    raise ArgumentError.new("max_args must be >= min_args or nil") if max_args && max_args < min_args
+
+    @receiver = receiver
+    @message = message
+    @min_args = min_args
+    @max_args = max_args
+    @cache = Hash.new
+  end
+
+  # Visit the configured receiver
+  def visit(thing, *args)
+    visit_this(@receiver, thing, *args)
+  end
+
+  # Visit an explicit receiver
+  def visit_this(receiver, thing, *args)
+    raise "Visitor Error: Too few arguments passed. min = #{@min_args}" unless args.length >= @min_args
+    if @max_args
+      raise "Visitor Error: Too many arguments passed. max = #{@max_args}" unless args.length <= @max_args
+    end
+    if method_name = @cache[thing.class]
+      return receiver.send(method_name, thing, *args)
+    else
+      thing.class.ancestors().each do |ancestor|
+        method_name = :"#{@message}_#{ancestor.name.split("::").last}"
+        # DEBUG OUTPUT
+        # puts "Visitor checking: #{receiver.class}.#{method_name}, responds to: #{@receiver.respond_to? method_name}"
+        next unless receiver.respond_to? method_name
+        @cache[thing.class] = method_name
+        return receiver.send(method_name, thing, *args)
+      end
+    end
+    raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}"
+  end
+end
diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb
index 3f08a15..e20c9c0 100644
--- a/lib/puppet/resource/type.rb
+++ b/lib/puppet/resource/type.rb
@@ -1,8 +1,9 @@
-require 'puppet/parser/parser'
+require 'puppet/parser'
 require 'puppet/util/warnings'
 require 'puppet/util/errors'
 require 'puppet/util/inline_docs'
 require 'puppet/parser/ast/leaf'
+require 'puppet/parser/ast/block_expression'
 require 'puppet/dsl'
 
 class Puppet::Resource::Type
@@ -190,14 +191,15 @@ def merge(other)
       return
     end
 
-    array_class = Puppet::Parser::AST::ASTArray
-    self.code = array_class.new(:children => [self.code]) unless self.code.is_a?(array_class)
-
-    if other.code.is_a?(array_class)
-      code.children += other.code.children
-    else
-      code.children << other.code
-    end
+    self.code = Puppet::Parser::AST::BlockExpression.new(:children => [self.code, other.code])
+#    array_class = Puppet::Parser::AST::ASTArray
+#    self.code = array_class.new(:children => [self.code]) unless self.code.is_a?(array_class)
+#
+#    if other.code.is_a?(array_class)
+#      code.children += other.code.children
+#    else
+#      code.children << other.code
+#    end
   end
 
   # Make an instance of the resource type, and place it in the catalog
diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb
index e9943ee..cbcbc65 100644
--- a/lib/puppet/util/monkey_patches.rb
+++ b/lib/puppet/util/monkey_patches.rb
@@ -126,6 +126,25 @@ def drop(n)
 
     slice(n, length - n) or []
   end unless method_defined? :drop
+  
+  # Array does not have a to_hash method and Hash uses this instead of checking with "respond_to?" if an
+  # array can convert itself or not. (This is a bad thing in Hash). When Array is extended with a method_missing,
+  # (like when using the RGen package 0.6.1 where meta methods are available on arrays), this trips up the
+  # Hash implementation.
+  # This patch simply does what the regular to_hash does, and it is accompanied by a respond_to? method that
+  # returns false for :to_hash.
+  # This is really ugly, and should be removed when the implementation in lib/rgen/array_extension.rb is
+  # fixed to handle to_hash correctly.
+  def to_hash
+    raise NoMethodError.new
+  end
+  
+  # @see #to_hash   
+  def respond_to? m
+    return false if m == :to_hash
+    super
+  end
+
 end
 
 
diff --git a/spec/fixtures/unit/pops/parser/lexer/aliastest.pp b/spec/fixtures/unit/pops/parser/lexer/aliastest.pp
new file mode 100644
index 0000000..f2b6159
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/aliastest.pp
@@ -0,0 +1,16 @@
+file { "a file":
+    path => "/tmp/aliastest",
+    ensure => file
+}
+
+file { "another":
+    path => "/tmp/aliastest2",
+    ensure => file,
+    require => File["a file"]
+}
+
+file { "a third":
+    path => "/tmp/aliastest3",
+    ensure => file,
+    require => File["/tmp/aliastest"]
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/append.pp b/spec/fixtures/unit/pops/parser/lexer/append.pp
new file mode 100644
index 0000000..20cbda6
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/append.pp
@@ -0,0 +1,11 @@
+$var=['/tmp/file1','/tmp/file2']
+
+class arraytest {
+    $var += ['/tmp/file3', '/tmp/file4']
+    file {
+        $var:
+            content => "test"
+    }
+}
+
+include arraytest
diff --git a/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp b/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp
new file mode 100644
index 0000000..eac9dd7
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp
@@ -0,0 +1,14 @@
+# $Id$
+
+define testargs($file, $mode = 755) {
+    file { $file: ensure => file, mode => $mode }
+}
+
+testargs { "testingname":
+    file => "/tmp/argumenttest1"
+}
+
+testargs { "testingother":
+    file => "/tmp/argumenttest2",
+    mode => 644
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/arithmetic_expression.pp b/spec/fixtures/unit/pops/parser/lexer/arithmetic_expression.pp
new file mode 100644
index 0000000..2d27d7d
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/arithmetic_expression.pp
@@ -0,0 +1,8 @@
+
+$one = 1.30
+$two = 2.034e-2
+
+$result = ((( $two + 2) / $one) + 4 * 5.45) - (6 << 7) + (0x800 + -9)
+
+
+notice("result is $result == 1295.87692307692")
diff --git a/spec/fixtures/unit/pops/parser/lexer/arraytrailingcomma.pp b/spec/fixtures/unit/pops/parser/lexer/arraytrailingcomma.pp
new file mode 100644
index 0000000..a410f95
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/arraytrailingcomma.pp
@@ -0,0 +1,3 @@
+file {
+    ["/tmp/arraytrailingcomma1","/tmp/arraytrailingcomma2", ]: content => "tmp"
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/casestatement.pp b/spec/fixtures/unit/pops/parser/lexer/casestatement.pp
new file mode 100644
index 0000000..66ecd72
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/casestatement.pp
@@ -0,0 +1,65 @@
+# $Id$
+
+$var = "value"
+
+case $var {
+  "nope": {
+     file { "/tmp/fakefile": mode => 644, ensure => file }
+  }
+  "value": {
+     file { "/tmp/existsfile": mode => 755, ensure => file }
+  }
+}
+
+$ovar = "yayness"
+
+case $ovar {
+    "fooness": {
+         file { "/tmp/nostillexistsfile": mode => 644, ensure => file }
+    }
+    "booness", "yayness": {
+        case $var {
+            "nep": {
+                 file { "/tmp/noexistsfile": mode => 644, ensure => file }
+            }
+            "value": {
+                 file { "/tmp/existsfile2": mode => 755, ensure => file }
+            }
+        }
+    }
+}
+
+case $ovar {
+    "fooness": {
+         file { "/tmp/nostillexistsfile": mode => 644, ensure => file }
+    }
+    default: {
+        file { "/tmp/existsfile3": mode => 755, ensure => file }
+    }
+}
+
+$bool = true
+
+case $bool {
+    true: {
+        file { "/tmp/existsfile4": mode => 755, ensure => file }
+    }
+}
+
+$yay = yay
+$a = yay
+$b = boo
+
+case $yay {
+    $a: { file { "/tmp/existsfile5": mode => 755, ensure => file } }
+    $b: { file { "/tmp/existsfile5": mode => 644, ensure => file } }
+    default: { file { "/tmp/existsfile5": mode => 711, ensure => file } }
+
+}
+
+$regexvar = "exists regex"
+case $regexvar {
+    "no match": { file { "/tmp/existsfile6": mode => 644, ensure => file } }
+    /(.*) regex$/: { file { "/tmp/${1}file6": mode => 755, ensure => file } }
+    default: { file { "/tmp/existsfile6": mode => 711, ensure => file } }
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp b/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp
new file mode 100644
index 0000000..36619d8
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp
@@ -0,0 +1,15 @@
+# $Id$
+
+class base {
+    file { "/tmp/classheir1": ensure => file, mode => 755 }
+}
+
+class sub1 inherits base {
+    file { "/tmp/classheir2": ensure => file, mode => 755 }
+}
+
+class sub2 inherits base {
+    file { "/tmp/classheir3": ensure => file, mode => 755 }
+}
+
+include sub1, sub2
diff --git a/spec/fixtures/unit/pops/parser/lexer/classincludes.pp b/spec/fixtures/unit/pops/parser/lexer/classincludes.pp
new file mode 100644
index 0000000..bd5b44e
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/classincludes.pp
@@ -0,0 +1,17 @@
+# $Id$
+
+class base {
+    file { "/tmp/classincludes1": ensure => file, mode => 755 }
+}
+
+class sub1 inherits base {
+    file { "/tmp/classincludes2": ensure => file, mode => 755 }
+}
+
+class sub2 inherits base {
+    file { "/tmp/classincludes3": ensure => file, mode => 755 }
+}
+
+$sub = "sub2"
+
+include sub1, $sub
diff --git a/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp b/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp
new file mode 100644
index 0000000..5803333
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp
@@ -0,0 +1,11 @@
+# $Id$
+
+define mytype {
+    file { "/tmp/classtest": ensure => file, mode => 755 }
+}
+
+class testing {
+    mytype { "componentname": }
+}
+
+include testing
diff --git a/spec/fixtures/unit/pops/parser/lexer/collection.pp b/spec/fixtures/unit/pops/parser/lexer/collection.pp
new file mode 100644
index 0000000..bc29510
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/collection.pp
@@ -0,0 +1,10 @@
+class one {
+    @file { "/tmp/colltest1": content => "one" }
+    @file { "/tmp/colltest2": content => "two" }
+}
+
+class two {
+    File <| content == "one" |>
+}
+
+include one, two
diff --git a/spec/fixtures/unit/pops/parser/lexer/collection_override.pp b/spec/fixtures/unit/pops/parser/lexer/collection_override.pp
new file mode 100644
index 0000000..b1b39ab
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/collection_override.pp
@@ -0,0 +1,8 @@
+@file {
+    "/tmp/collection":
+        content => "whatever"
+}
+
+File<| |> {
+    mode => 0600
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/collection_within_virtual_definitions.pp b/spec/fixtures/unit/pops/parser/lexer/collection_within_virtual_definitions.pp
new file mode 100644
index 0000000..3c21468
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/collection_within_virtual_definitions.pp
@@ -0,0 +1,20 @@
+define test($name) {
+    file {"/tmp/collection_within_virtual_definitions1_$name.txt":
+        content => "File name $name\n"
+    }
+    Test2 <||>
+}
+
+define test2() {
+    file {"/tmp/collection_within_virtual_definitions2_$name.txt":
+        content => "This is a test\n"
+    }
+}
+
+node default {
+    @test {"foo":
+        name => "foo"
+    }
+    @test2 {"foo2": }
+    Test <||>
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/componentmetaparams.pp b/spec/fixtures/unit/pops/parser/lexer/componentmetaparams.pp
new file mode 100644
index 0000000..7d9f0c2
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/componentmetaparams.pp
@@ -0,0 +1,11 @@
+file { "/tmp/component1":
+    ensure => file
+}
+
+define thing {
+    file { $name: ensure => file }
+}
+
+thing { "/tmp/component2":
+    require => File["/tmp/component1"]
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp b/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp
new file mode 100644
index 0000000..a61d205
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp
@@ -0,0 +1,8 @@
+define testfile($mode) {
+    file { $name: mode => $mode, ensure => present }
+}
+
+testfile { "/tmp/testing_component_requires2": mode => 755 }
+
+file { "/tmp/testing_component_requires1": mode => 755, ensure => present,
+    require => Testfile["/tmp/testing_component_requires2"] }
diff --git a/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp b/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp
new file mode 100644
index 0000000..249e633
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp
@@ -0,0 +1,23 @@
+# $Id$
+
+class base {
+    file { "/tmp/deepclassheir1": ensure => file, mode => 755 }
+}
+
+class sub1 inherits base {
+    file { "/tmp/deepclassheir2": ensure => file, mode => 755 }
+}
+
+class sub2 inherits sub1 {
+    file { "/tmp/deepclassheir3": ensure => file, mode => 755 }
+}
+
+class sub3 inherits sub2 {
+    file { "/tmp/deepclassheir4": ensure => file, mode => 755 }
+}
+
+class sub4 inherits sub3 {
+    file { "/tmp/deepclassheir5": ensure => file, mode => 755 }
+}
+
+include sub4
diff --git a/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp b/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp
new file mode 100644
index 0000000..c68b139
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp
@@ -0,0 +1,17 @@
+# $Id$
+
+$file = "/tmp/defineoverrides1"
+
+define myfile($mode) {
+    file { $name: ensure => file, mode => $mode }
+}
+
+class base {
+    myfile { $file: mode => 644 }
+}
+
+class sub inherits base {
+    Myfile[$file] { mode => 755, } # test the end-comma
+}
+
+include sub
diff --git a/spec/fixtures/unit/pops/parser/lexer/emptyclass.pp b/spec/fixtures/unit/pops/parser/lexer/emptyclass.pp
new file mode 100644
index 0000000..48047e7
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/emptyclass.pp
@@ -0,0 +1,9 @@
+# $Id$
+
+define component {
+}
+
+class testing {
+}
+
+include testing
diff --git a/spec/fixtures/unit/pops/parser/lexer/emptyexec.pp b/spec/fixtures/unit/pops/parser/lexer/emptyexec.pp
new file mode 100644
index 0000000..847a30d
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/emptyexec.pp
@@ -0,0 +1,3 @@
+exec { "touch /tmp/emptyexectest":
+    path => "/usr/bin:/bin"
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/emptyifelse.pp b/spec/fixtures/unit/pops/parser/lexer/emptyifelse.pp
new file mode 100644
index 0000000..598b486
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/emptyifelse.pp
@@ -0,0 +1,9 @@
+
+if false {
+} else {
+  # nothing here
+}
+
+if true {
+  # still nothing
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/falsevalues.pp b/spec/fixtures/unit/pops/parser/lexer/falsevalues.pp
new file mode 100644
index 0000000..2143b79
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/falsevalues.pp
@@ -0,0 +1,3 @@
+$value = false
+
+file { "/tmp/falsevalues$value": ensure => file }
diff --git a/spec/fixtures/unit/pops/parser/lexer/filecreate.pp b/spec/fixtures/unit/pops/parser/lexer/filecreate.pp
new file mode 100644
index 0000000..d7972c2
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/filecreate.pp
@@ -0,0 +1,11 @@
+# $Id$
+
+file {
+    "/tmp/createatest": ensure => file, mode => 755;
+    "/tmp/createbtest": ensure => file, mode => 755
+}
+
+file {
+    "/tmp/createctest": ensure => file;
+    "/tmp/createdtest": ensure => file;
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/fqdefinition.pp b/spec/fixtures/unit/pops/parser/lexer/fqdefinition.pp
new file mode 100644
index 0000000..ddb0675
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/fqdefinition.pp
@@ -0,0 +1,5 @@
+define one::two($ensure) {
+    file { "/tmp/fqdefinition": ensure => $ensure }
+}
+
+one::two { "/tmp/fqdefinition": ensure => file }
diff --git a/spec/fixtures/unit/pops/parser/lexer/fqparents.pp b/spec/fixtures/unit/pops/parser/lexer/fqparents.pp
new file mode 100644
index 0000000..ee2f654
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/fqparents.pp
@@ -0,0 +1,11 @@
+class base {
+    class one {
+        file { "/tmp/fqparent1": ensure => file }
+    }
+}
+
+class two::three inherits base::one {
+    file { "/tmp/fqparent2": ensure => file }
+}
+
+include two::three
diff --git a/spec/fixtures/unit/pops/parser/lexer/funccomma.pp b/spec/fixtures/unit/pops/parser/lexer/funccomma.pp
new file mode 100644
index 0000000..32e34f9
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/funccomma.pp
@@ -0,0 +1,5 @@
+@file {
+    ["/tmp/funccomma1","/tmp/funccomma2"]: content => "1"
+}
+
+realize( File["/tmp/funccomma1"], File["/tmp/funccomma2"] , )
diff --git a/spec/fixtures/unit/pops/parser/lexer/hash.pp b/spec/fixtures/unit/pops/parser/lexer/hash.pp
new file mode 100644
index 0000000..d332498
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/hash.pp
@@ -0,0 +1,33 @@
+
+$hash = { "file" => "/tmp/myhashfile1" }
+
+file {
+    $hash["file"]:
+        ensure => file, content => "content";
+}
+
+$hash2 = { "a" => { key => "/tmp/myhashfile2" }}
+
+file {
+    $hash2["a"][key]:
+        ensure => file, content => "content";
+}
+
+define test($a = { "b" => "c" }) {
+    file {
+        $a["b"]:
+            ensure => file, content => "content"
+    }
+}
+
+test {
+    "test":
+        a => { "b" => "/tmp/myhashfile3" }
+}
+
+$hash3 = { mykey => "/tmp/myhashfile4" }
+$key = "mykey"
+
+file {
+    $hash3[$key]: ensure => file, content => "content"
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp b/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp
new file mode 100644
index 0000000..29a6372
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp
@@ -0,0 +1,12 @@
+$one = 1
+$two = 2
+
+if ($one < $two) and (($two < 3) or ($two == 2)) {
+    notice("True!")
+}
+
+if "test regex" =~ /(.*) regex/ {
+    file {
+        "/tmp/${1}iftest": ensure => file, mode => 0755
+    }
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp b/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp
new file mode 100644
index 0000000..6f34cb2
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp
@@ -0,0 +1,15 @@
+# $Id$
+
+$files = ["/tmp/iterationatest", "/tmp/iterationbtest"]
+
+file { $files: ensure => file, mode => 755 }
+
+file { ["/tmp/iterationctest", "/tmp/iterationdtest"]:
+    ensure => file,
+    mode => 755
+}
+
+file {
+    ["/tmp/iterationetest", "/tmp/iterationftest"]: ensure => file, mode => 755;
+    ["/tmp/iterationgtest", "/tmp/iterationhtest"]: ensure => file, mode => 755;
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/multilinecomments.pp b/spec/fixtures/unit/pops/parser/lexer/multilinecomments.pp
new file mode 100644
index 0000000..f9819c0
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/multilinecomments.pp
@@ -0,0 +1,10 @@
+
+/*
+file {
+    "/tmp/multilinecomments": content => "pouet"
+}
+*/
+
+/* and another one for #2333, the whitespace after the 
+end comment is here on purpose */  
+
diff --git a/spec/fixtures/unit/pops/parser/lexer/multipleclass.pp b/spec/fixtures/unit/pops/parser/lexer/multipleclass.pp
new file mode 100644
index 0000000..ae02edc
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/multipleclass.pp
@@ -0,0 +1,9 @@
+class one {
+    file { "/tmp/multipleclassone": content => "one" }
+}
+
+class one {
+    file { "/tmp/multipleclasstwo": content => "two" }
+}
+
+include one
diff --git a/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp b/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp
new file mode 100644
index 0000000..2f9b3c2
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp
@@ -0,0 +1,7 @@
+# $Id$
+
+file {
+    "/tmp/multipleinstancesa": ensure => file, mode => 755;
+    "/tmp/multipleinstancesb": ensure => file, mode => 755;
+    "/tmp/multipleinstancesc": ensure => file, mode => 755;
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/multisubs.pp b/spec/fixtures/unit/pops/parser/lexer/multisubs.pp
new file mode 100644
index 0000000..bcec69e
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/multisubs.pp
@@ -0,0 +1,13 @@
+class base {
+    file { "/tmp/multisubtest": content => "base", mode => 644 }
+}
+
+class sub1 inherits base {
+    File["/tmp/multisubtest"] { mode => 755 }
+}
+
+class sub2 inherits base {
+    File["/tmp/multisubtest"] { content => sub2 }
+}
+
+include sub1, sub2
diff --git a/spec/fixtures/unit/pops/parser/lexer/namevartest.pp b/spec/fixtures/unit/pops/parser/lexer/namevartest.pp
new file mode 100644
index 0000000..dbee1c3
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/namevartest.pp
@@ -0,0 +1,9 @@
+define filetest($mode, $ensure = file) {
+    file { $name:
+        mode => $mode,
+        ensure => $ensure
+    }
+}
+
+filetest { "/tmp/testfiletest": mode => 644}
+filetest { "/tmp/testdirtest": mode => 755, ensure => directory}
diff --git a/spec/fixtures/unit/pops/parser/lexer/scopetest.pp b/spec/fixtures/unit/pops/parser/lexer/scopetest.pp
new file mode 100644
index 0000000..3314917
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/scopetest.pp
@@ -0,0 +1,13 @@
+
+$mode = 640
+
+define thing {
+    file { "/tmp/$name": ensure => file, mode => $mode }
+}
+
+class testing {
+    $mode = 755
+    thing {scopetest: }
+}
+
+include testing
diff --git a/spec/fixtures/unit/pops/parser/lexer/selectorvalues.pp b/spec/fixtures/unit/pops/parser/lexer/selectorvalues.pp
new file mode 100644
index 0000000..d80d26c
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/selectorvalues.pp
@@ -0,0 +1,49 @@
+$value1 = ""
+$value2 = true
+$value3 = false
+$value4 = yay
+
+$test = "yay"
+
+$mode1 = $value1 ? {
+    "" => 755,
+    default => 644
+}
+
+$mode2 = $value2 ? {
+    true => 755,
+    default => 644
+}
+
+$mode3 = $value3 ? {
+    false => 755,
+    default => 644
+}
+
+$mode4 = $value4 ? {
+    $test => 755,
+    default => 644
+}
+
+$mode5 = yay ? {
+    $test => 755,
+    default => 644
+}
+
+$mode6 = $mode5 ? {
+    755 => 755
+}
+
+$mode7 = "test regex" ? {
+    /regex$/ => 755,
+    default => 644
+}
+
+
+file { "/tmp/selectorvalues1": ensure => file, mode => $mode1 }
+file { "/tmp/selectorvalues2": ensure => file, mode => $mode2 }
+file { "/tmp/selectorvalues3": ensure => file, mode => $mode3 }
+file { "/tmp/selectorvalues4": ensure => file, mode => $mode4 }
+file { "/tmp/selectorvalues5": ensure => file, mode => $mode5 }
+file { "/tmp/selectorvalues6": ensure => file, mode => $mode6 }
+file { "/tmp/selectorvalues7": ensure => file, mode => $mode7 }
diff --git a/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp b/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp
new file mode 100644
index 0000000..63d199a
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp
@@ -0,0 +1,5 @@
+# $Id$
+
+File { mode => 755 }
+
+file { "/tmp/defaulttest": ensure => file }
diff --git a/spec/fixtures/unit/pops/parser/lexer/simpleselector.pp b/spec/fixtures/unit/pops/parser/lexer/simpleselector.pp
new file mode 100644
index 0000000..8b9bc72
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/simpleselector.pp
@@ -0,0 +1,38 @@
+# $Id$
+
+$var = "value"
+
+file { "/tmp/snippetselectatest":
+    ensure => file,
+    mode => $var ? {
+        nottrue => 641,
+        value => 755
+    }
+}
+
+file { "/tmp/snippetselectbtest":
+    ensure => file,
+    mode => $var ? {
+        nottrue => 644,
+        default => 755
+    }
+}
+
+$othervar = "complex value"
+
+file { "/tmp/snippetselectctest":
+    ensure => file,
+    mode => $othervar ? {
+        "complex value" => 755,
+        default => 644
+    }
+}
+$anothervar = Yayness
+
+file { "/tmp/snippetselectdtest":
+    ensure => file,
+    mode => $anothervar ? {
+        Yayness => 755,
+        default => 644
+    }
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/singleary.pp b/spec/fixtures/unit/pops/parser/lexer/singleary.pp
new file mode 100644
index 0000000..9ce56dd
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/singleary.pp
@@ -0,0 +1,19 @@
+# $Id$
+
+file { "/tmp/singleary1":
+    ensure => file
+}
+
+file { "/tmp/singleary2":
+    ensure => file
+}
+
+file { "/tmp/singleary3":
+    ensure => file,
+    require => [File["/tmp/singleary1"], File["/tmp/singleary2"]]
+}
+
+file { "/tmp/singleary4":
+    ensure => file,
+    require => [File["/tmp/singleary1"]]
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/singlequote.pp b/spec/fixtures/unit/pops/parser/lexer/singlequote.pp
new file mode 100644
index 0000000..dc876a2
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/singlequote.pp
@@ -0,0 +1,11 @@
+# $Id$
+
+file { "/tmp/singlequote1":
+    ensure => file,
+    content => 'a $quote'
+}
+
+file { "/tmp/singlequote2":
+    ensure => file,
+    content => 'some "\yayness\"'
+}
diff --git a/spec/fixtures/unit/pops/parser/lexer/singleselector.pp b/spec/fixtures/unit/pops/parser/lexer/singleselector.pp
new file mode 100644
index 0000000..520a140
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/singleselector.pp
@@ -0,0 +1,22 @@
+$value1 = ""
+$value2 = true
+$value3 = false
+$value4 = yay
+
+$test = "yay"
+
+$mode1 = $value1 ? {
+    "" => 755
+}
+
+$mode2 = $value2 ? {
+    true => 755
+}
+
+$mode3 = $value3 ? {
+    default => 755
+}
+
+file { "/tmp/singleselector1": ensure => file, mode => $mode1 }
+file { "/tmp/singleselector2": ensure => file, mode => $mode2 }
+file { "/tmp/singleselector3": ensure => file, mode => $mode3 }
diff --git a/spec/fixtures/unit/pops/parser/lexer/subclass_name_duplication.pp b/spec/fixtures/unit/pops/parser/lexer/subclass_name_duplication.pp
new file mode 100755
index 0000000..10f1d75
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/subclass_name_duplication.pp
@@ -0,0 +1,11 @@
+#!/usr/bin/env puppet
+
+class one::fake {
+    file { "/tmp/subclass_name_duplication1": ensure => present }
+}
+
+class two::fake {
+    file { "/tmp/subclass_name_duplication2": ensure => present }
+}
+
+include one::fake, two::fake
diff --git a/spec/fixtures/unit/pops/parser/lexer/tag.pp b/spec/fixtures/unit/pops/parser/lexer/tag.pp
new file mode 100644
index 0000000..e6e770d
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/tag.pp
@@ -0,0 +1,9 @@
+# $Id$
+
+$variable = value
+
+tag yayness, rahness
+
+tag booness, $variable
+
+file { "/tmp/settestingness": ensure => file }
diff --git a/spec/fixtures/unit/pops/parser/lexer/tagged.pp b/spec/fixtures/unit/pops/parser/lexer/tagged.pp
new file mode 100644
index 0000000..7bf90a6
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/tagged.pp
@@ -0,0 +1,35 @@
+# $Id$
+
+tag testing
+tag(funtest)
+
+class tagdefine {
+    $path = tagged(tagdefine) ? {
+        true => "true", false => "false"
+    }
+
+    file { "/tmp/taggeddefine$path": ensure => file }
+}
+
+include tagdefine
+
+$yayness = tagged(yayness) ? {
+    true => "true", false => "false"
+}
+
+$funtest = tagged(testing) ? {
+    true => "true", false => "false"
+}
+
+$both = tagged(testing, yayness) ? {
+    true => "true", false => "false"
+}
+
+$bothtrue = tagged(testing, testing) ? {
+    true => "true", false => "false"
+}
+
+file { "/tmp/taggedyayness$yayness": ensure => file }
+file { "/tmp/taggedtesting$funtest": ensure => file }
+file { "/tmp/taggedboth$both": ensure => file }
+file { "/tmp/taggedbothtrue$bothtrue": ensure => file }
diff --git a/spec/fixtures/unit/pops/parser/lexer/virtualresources.pp b/spec/fixtures/unit/pops/parser/lexer/virtualresources.pp
new file mode 100644
index 0000000..a29406b
--- /dev/null
+++ b/spec/fixtures/unit/pops/parser/lexer/virtualresources.pp
@@ -0,0 +1,14 @@
+class one {
+    @file { "/tmp/virtualtest1": content => "one" }
+    @file { "/tmp/virtualtest2": content => "two" }
+    @file { "/tmp/virtualtest3": content => "three" }
+    @file { "/tmp/virtualtest4": content => "four" }
+}
+
+class two {
+    File <| content == "one" |>
+    realize File["/tmp/virtualtest2"]
+    realize(File["/tmp/virtualtest3"], File["/tmp/virtualtest4"])
+}
+
+include one, two
diff --git a/spec/integration/parser/collector_spec.rb b/spec/integration/parser/collector_spec.rb
index cb210b3..49ce745 100755
--- a/spec/integration/parser/collector_spec.rb
+++ b/spec/integration/parser/collector_spec.rb
@@ -64,7 +64,7 @@ def expect_the_message_to_be(expected_messages, code, node = Puppet::Node.new('t
     expect_the_message_to_be(["the message", "different message"], <<-MANIFEST)
       @notify { "testing":     message => "different message", withpath => true }
       @notify { "other":       message => "the message" }
-      @notify { "yet another": message => "the message",       withpath => true } 
+      @notify { "yet another": message => "the message",       withpath => true }
 
       Notify <| (title == "testing" or message == "the message") and withpath == true |>
     MANIFEST
diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb
index 9a4deb2..6126421 100755
--- a/spec/integration/parser/compiler_spec.rb
+++ b/spec/integration/parser/compiler_spec.rb
@@ -1,7 +1,8 @@
 #! /usr/bin/env ruby
 require 'spec_helper'
+require 'puppet/parser/parser_factory'
 
-describe Puppet::Parser::Compiler do
+describe "Puppet::Parser::Compiler" do
   before :each do
     @node = Puppet::Node.new "testnode"
 
@@ -13,297 +14,322 @@
     Puppet.settings.clear
   end
 
-  it "should be able to determine the configuration version from a local version control repository" do
-    pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do
-      # This should always work, because we should always be
-      # in the puppet repo when we run this.
-      version = %x{git rev-parse HEAD}.chomp
+  # shared because tests are invoked both for classic and future parser
+  #
+  shared_examples_for "the compiler" do
+    it "should be able to determine the configuration version from a local version control repository" do
+      pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do
+        # This should always work, because we should always be
+        # in the puppet repo when we run this.
+        version = %x{git rev-parse HEAD}.chomp
 
-      Puppet.settings[:config_version] = 'git rev-parse HEAD'
+        Puppet.settings[:config_version] = 'git rev-parse HEAD'
 
-      @parser = Puppet::Parser::Parser.new "development"
-      @compiler = Puppet::Parser::Compiler.new(@node)
+  #      @parser = Puppet::Parser::Parser.new "development"
+        @parser = Puppet::Parser::ParserFactory.parser "development"
+        @compiler = Puppet::Parser::Compiler.new(@node)
 
-      @compiler.catalog.version.should == version
+        @compiler.catalog.version.should == version
+      end
     end
-  end
-
-  it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do
-    Puppet[:code] = <<-PP
-      class foo
-      {
-        notify { foo_notify: }
-        include bar
-      }
-      class bar
-      {
-        notify { bar_notify: }
-      }
-    PP
-
-    @node.stubs(:classes).returns(['foo', 'bar'])
-
-    catalog = Puppet::Parser::Compiler.compile(@node)
-
-    catalog.resource("Notify[foo_notify]").should_not be_nil
-    catalog.resource("Notify[bar_notify]").should_not be_nil
-  end
 
-  describe "when resolving class references" do
-    it "should favor local scope, even if there's an included class in topscope" do
+    it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do
       Puppet[:code] = <<-PP
-        class experiment {
-          class baz {
-          }
-          notify {"x" : require => Class[Baz] }
+        class foo
+        {
+          notify { foo_notify: }
+          include bar
         }
-        class baz {
+        class bar
+        {
+          notify { bar_notify: }
         }
-        include baz
-        include experiment
-        include experiment::baz
       PP
 
-      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+      @node.stubs(:classes).returns(['foo', 'bar'])
 
-      notify_resource = catalog.resource( "Notify[x]" )
+      catalog = Puppet::Parser::Compiler.compile(@node)
 
-      notify_resource[:require].title.should == "Experiment::Baz"
+      catalog.resource("Notify[foo_notify]").should_not be_nil
+      catalog.resource("Notify[bar_notify]").should_not be_nil
     end
 
-    it "should favor local scope, even if there's an unincluded class in topscope" do
-      Puppet[:code] = <<-PP
-        class experiment {
+    describe "when resolving class references" do
+      it "should favor local scope, even if there's an included class in topscope" do
+        Puppet[:code] = <<-PP
+          class experiment {
+            class baz {
+            }
+            notify {"x" : require => Class[Baz] }
+          }
           class baz {
           }
-          notify {"x" : require => Class[Baz] }
-        }
-        class baz {
-        }
-        include experiment
-        include experiment::baz
-      PP
+          include baz
+          include experiment
+          include experiment::baz
+        PP
 
-      catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+        catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
 
-      notify_resource = catalog.resource( "Notify[x]" )
+        notify_resource = catalog.resource( "Notify[x]" )
 
-      notify_resource[:require].title.should == "Experiment::Baz"
-    end
-  end
-  describe "(ticket #13349) when explicitly specifying top scope" do
-    ["class {'::bar::baz':}", "include ::bar::baz"].each do |include|
-      describe "with #{include}" do
-        it "should find the top level class" do
-          Puppet[:code] = <<-MANIFEST
-            class { 'foo::test': }
-            class foo::test {
-            	#{include}
-            }
-            class bar::baz {
-            	notify { 'good!': }
-            }
-            class foo::bar::baz {
-            	notify { 'bad!': }
+        notify_resource[:require].title.should == "Experiment::Baz"
+      end
+
+      it "should favor local scope, even if there's an unincluded class in topscope" do
+        Puppet[:code] = <<-PP
+          class experiment {
+            class baz {
             }
-          MANIFEST
+            notify {"x" : require => Class[Baz] }
+          }
+          class baz {
+          }
+          include experiment
+          include experiment::baz
+        PP
 
-          catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+        catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
 
-          catalog.resource("Class[Bar::Baz]").should_not be_nil
-          catalog.resource("Notify[good!]").should_not be_nil
-          catalog.resource("Class[Foo::Bar::Baz]").should be_nil
-          catalog.resource("Notify[bad!]").should be_nil
+        notify_resource = catalog.resource( "Notify[x]" )
+
+        notify_resource[:require].title.should == "Experiment::Baz"
+      end
+    end
+    describe "(ticket #13349) when explicitly specifying top scope" do
+      ["class {'::bar::baz':}", "include ::bar::baz"].each do |include|
+        describe "with #{include}" do
+          it "should find the top level class" do
+            Puppet[:code] = <<-MANIFEST
+              class { 'foo::test': }
+              class foo::test {
+              	#{include}
+              }
+              class bar::baz {
+              	notify { 'good!': }
+              }
+              class foo::bar::baz {
+              	notify { 'bad!': }
+              }
+            MANIFEST
+
+            catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+
+            catalog.resource("Class[Bar::Baz]").should_not be_nil
+            catalog.resource("Notify[good!]").should_not be_nil
+            catalog.resource("Class[Foo::Bar::Baz]").should be_nil
+            catalog.resource("Notify[bad!]").should be_nil
+          end
         end
       end
     end
-  end
 
-  it "should recompute the version after input files are re-parsed" do
-    Puppet[:code] = 'class foo { }'
-    Time.stubs(:now).returns(1)
-    node = Puppet::Node.new('mynode')
-    Puppet::Parser::Compiler.compile(node).version.should == 1
-    Time.stubs(:now).returns(2)
-    Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change
-    Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change
-    Puppet::Parser::Compiler.compile(node).version.should == 2
-  end
+    it "should recompute the version after input files are re-parsed" do
+      Puppet[:code] = 'class foo { }'
+      Time.stubs(:now).returns(1)
+      node = Puppet::Node.new('mynode')
+      Puppet::Parser::Compiler.compile(node).version.should == 1
+      Time.stubs(:now).returns(2)
+      Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change
+      Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change
+      Puppet::Parser::Compiler.compile(node).version.should == 2
+    end
 
-  ['class', 'define', 'node'].each do |thing|
-    it "should not allow #{thing} inside evaluated conditional constructs" do
-      Puppet[:code] = <<-PP
-        if true {
-          #{thing} foo {
+    ['class', 'define', 'node'].each do |thing|
+      it "should not allow #{thing} inside evaluated conditional constructs" do
+        Puppet[:code] = <<-PP
+          if true {
+            #{thing} foo {
+            }
+            notify { decoy: }
           }
-          notify { decoy: }
-        }
-      PP
+        PP
 
-      begin
-        Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
-        raise "compilation should have raised Puppet::Error"
-      rescue Puppet::Error => e
-        e.message.should =~ /at line 2/
+        begin
+          Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode"))
+          raise "compilation should have raised Puppet::Error"
+        rescue Puppet::Error => e
+          e.message.should =~ /at line 2/
+        end
       end
     end
-  end
 
-  it "should not allow classes inside unevaluated conditional constructs" do
-    Puppet[:code] = <<-PP
-      if false {
-        class foo {
+    it "should not allow classes inside unevaluated conditional constructs" do
+      Puppet[:code] = <<-PP
+        if false {
+          class foo {
+          }
         }
-      }
-    PP
-
-    lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error)
-  end
+      PP
 
-  describe "when defining relationships" do
-    def extract_name(ref)
-      ref.sub(/File\[(\w+)\]/, '\1')
+      lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error)
     end
 
-    let(:node) { Puppet::Node.new('mynode') }
-    let(:code) do
-      <<-MANIFEST
-        file { [a,b,c]:
-          mode => 0644,
-        }
-        file { [d,e]:
-          mode => 0755,
-        }
-      MANIFEST
-    end
-    let(:expected_relationships) { [] }
-    let(:expected_subscriptions) { [] }
+    describe "when defining relationships" do
+      def extract_name(ref)
+        ref.sub(/File\[(\w+)\]/, '\1')
+      end
 
-    before :each do
-      Puppet[:code] = code
-    end
+      let(:node) { Puppet::Node.new('mynode') }
+      let(:code) do
+        <<-MANIFEST
+          file { [a,b,c]:
+            mode => 0644,
+          }
+          file { [d,e]:
+            mode => 0755,
+          }
+        MANIFEST
+      end
+      let(:expected_relationships) { [] }
+      let(:expected_subscriptions) { [] }
 
-    after :each do
-      catalog = described_class.compile(node)
+      before :each do
+        Puppet[:code] = code
+      end
 
-      resources = catalog.resources.select { |res| res.type == 'File' }
+      after :each do
+        catalog = Puppet::Parser::Compiler.compile(node)
 
-      actual_relationships, actual_subscriptions = [:before, :notify].map do |relation|
-        resources.map do |res|
-          dependents = Array(res[relation])
-          dependents.map { |ref| [res.title, extract_name(ref)] }
-        end.inject(&:concat)
-      end
+        resources = catalog.resources.select { |res| res.type == 'File' }
 
-      actual_relationships.should =~ expected_relationships
-      actual_subscriptions.should =~ expected_subscriptions
-    end
+        actual_relationships, actual_subscriptions = [:before, :notify].map do |relation|
+          resources.map do |res|
+            dependents = Array(res[relation])
+            dependents.map { |ref| [res.title, extract_name(ref)] }
+          end.inject(&:concat)
+        end
 
-    it "should create a relationship" do
-      code << "File[a] -> File[b]"
+        actual_relationships.should =~ expected_relationships
+        actual_subscriptions.should =~ expected_subscriptions
+      end
 
-      expected_relationships << ['a','b']
-    end
+      it "should create a relationship" do
+        code << "File[a] -> File[b]"
 
-    it "should create a subscription" do
-      code << "File[a] ~> File[b]"
+        expected_relationships << ['a','b']
+      end
 
-      expected_subscriptions << ['a', 'b']
-    end
+      it "should create a subscription" do
+        code << "File[a] ~> File[b]"
 
-    it "should create relationships using title arrays" do
-      code << "File[a,b] -> File[c,d]"
+        expected_subscriptions << ['a', 'b']
+      end
 
-      expected_relationships.concat [
-        ['a', 'c'],
-        ['b', 'c'],
-        ['a', 'd'],
-        ['b', 'd'],
-      ]
-    end
+      it "should create relationships using title arrays" do
+        code << "File[a,b] -> File[c,d]"
 
-    it "should create relationships using collection expressions" do
-      code << "File <| mode == 0644 |> -> File <| mode == 0755 |>"
-
-      expected_relationships.concat [
-        ['a', 'd'],
-        ['b', 'd'],
-        ['c', 'd'],
-        ['a', 'e'],
-        ['b', 'e'],
-        ['c', 'e'],
-      ]
-    end
+        expected_relationships.concat [
+          ['a', 'c'],
+          ['b', 'c'],
+          ['a', 'd'],
+          ['b', 'd'],
+        ]
+      end
 
-    it "should create relationships using resource names" do
-      code << "'File[a]' -> 'File[b]'"
+      it "should create relationships using collection expressions" do
+        code << "File <| mode == 0644 |> -> File <| mode == 0755 |>"
+
+        expected_relationships.concat [
+          ['a', 'd'],
+          ['b', 'd'],
+          ['c', 'd'],
+          ['a', 'e'],
+          ['b', 'e'],
+          ['c', 'e'],
+        ]
+      end
 
-      expected_relationships << ['a', 'b']
-    end
+      it "should create relationships using resource names" do
+        code << "'File[a]' -> 'File[b]'"
 
-    it "should create relationships using variables" do
-      code << <<-MANIFEST
-        $var = File[a]
-        $var -> File[b]
-      MANIFEST
+        expected_relationships << ['a', 'b']
+      end
 
-      expected_relationships << ['a', 'b']
-    end
+      it "should create relationships using variables" do
+        code << <<-MANIFEST
+          $var = File[a]
+          $var -> File[b]
+        MANIFEST
 
-    it "should create relationships using case statements" do
-      code << <<-MANIFEST
-        $var = 10
-        case $var {
-          10: {
-            file { s1: }
-          }
-          12: {
-            file { s2: }
-          }
-        }
-        ->
-        case $var + 2 {
-          10: {
-            file { t1: }
+        expected_relationships << ['a', 'b']
+      end
+
+      it "should create relationships using case statements" do
+        code << <<-MANIFEST
+          $var = 10
+          case $var {
+            10: {
+              file { s1: }
+            }
+            12: {
+              file { s2: }
+            }
           }
-          12: {
-            file { t2: }
+          ->
+          case $var + 2 {
+            10: {
+              file { t1: }
+            }
+            12: {
+              file { t2: }
+            }
           }
-        }
-      MANIFEST
+        MANIFEST
 
-      expected_relationships << ['s1', 't2']
-    end
+        expected_relationships << ['s1', 't2']
+      end
 
-    it "should create relationships using array members" do
-      code << <<-MANIFEST
-        $var = [ [ [ File[a], File[b] ] ] ]
-        $var[0][0][0] -> $var[0][0][1]
-      MANIFEST
+      it "should create relationships using array members" do
+        code << <<-MANIFEST
+          $var = [ [ [ File[a], File[b] ] ] ]
+          $var[0][0][0] -> $var[0][0][1]
+        MANIFEST
 
-      expected_relationships << ['a', 'b']
-    end
+        expected_relationships << ['a', 'b']
+      end
 
-    it "should create relationships using hash members" do
-      code << <<-MANIFEST
-        $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}}
-        $var[foo][bar][source] -> $var[foo][bar][target]
-      MANIFEST
+      it "should create relationships using hash members" do
+        code << <<-MANIFEST
+          $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}}
+          $var[foo][bar][source] -> $var[foo][bar][target]
+        MANIFEST
 
-      expected_relationships << ['a', 'b']
-    end
+        expected_relationships << ['a', 'b']
+      end
 
-    it "should create relationships using resource declarations" do
-      code << "file { l: } -> file { r: }"
+      it "should create relationships using resource declarations" do
+        code << "file { l: } -> file { r: }"
 
-      expected_relationships << ['l', 'r']
+        expected_relationships << ['l', 'r']
+      end
+
+      it "should chain relationships" do
+        code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]"
+
+        expected_relationships << ['a', 'b'] << ['d', 'c']
+        expected_subscriptions << ['b', 'c'] << ['e', 'd']
+      end
     end
+  end
 
-    it "should chain relationships" do
-      code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]"
+  describe 'using classic parser' do
+    before :each do
+      Puppet[:parser] = 'current'
+    end
+    it_behaves_like 'the compiler' do
+    end
+  end
 
-      expected_relationships << ['a', 'b'] << ['d', 'c']
-      expected_subscriptions << ['b', 'c'] << ['e', 'd']
+  describe 'using future parser' do
+    # have absolutely no clue to why this is needed - if not required here (even if required by used classes)
+    # the tests will fail with error that rgen/ecore/ruby_to_ecore cannot be found...
+    # TODO: Solve this mystery !
+    require 'rgen/metamodel_builder'
+
+    before :each do
+      Puppet[:parser] = 'future'
     end
+    it_behaves_like 'the compiler'
   end
 end
diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb
index 4274303..759643a 100755
--- a/spec/integration/parser/parser_spec.rb
+++ b/spec/integration/parser/parser_spec.rb
@@ -1,11 +1,12 @@
 #! /usr/bin/env ruby
 require 'spec_helper'
+require 'puppet/parser/parser_factory'
 
-describe Puppet::Parser::Parser do
+describe "Puppet::Parser::Parser" do
   module ParseMatcher
     class ParseAs
       def initialize(klass)
-        @parser = Puppet::Parser::Parser.new "development"
+        @parser = Puppet::Parser::ParserFactory.parser("development")
         @class = klass
       end
 
@@ -38,7 +39,7 @@ def parse_as(klass)
 
     class ParseWith
       def initialize(block)
-        @parser = Puppet::Parser::Parser.new "development"
+        @parser = Puppet::Parser::ParserFactory.parser("development")
         @block = block
       end
 
@@ -74,76 +75,193 @@ def parse_with(&block)
 
   before :each do
     @resource_type_collection = Puppet::Resource::TypeCollection.new("env")
-    @parser = Puppet::Parser::Parser.new "development"
-  end
-
-  describe "when parsing comments before statement" do
-    it "should associate the documentation to the statement AST node" do
-      ast = @parser.parse("""
-      # comment
-      class test {}
-      """)
+    @parser = Puppet::Parser::ParserFactory.parser("development")
 
-      ast.code[0].should be_a(Puppet::Parser::AST::Hostclass)
-      ast.code[0].name.should == 'test'
-      ast.code[0].instantiate('')[0].doc.should == "comment\n"
-    end
+#    @parser = Puppet::Parser::Parser.new "development"
   end
+  shared_examples_for 'a puppet parser' do
+    describe "when parsing comments before statement" do
+      it "should associate the documentation to the statement AST node" do
+        if Puppet[:parser] == 'future'
+          pending "egrammar does not yet process comments"
+        end
+        ast = @parser.parse("""
+        # comment
+        class test {}
+        """)
 
-  describe "when parsing" do
-    it "should be able to parse normal left to right relationships" do
-      "Notify[foo] -> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
+        ast.code[0].should be_a(Puppet::Parser::AST::Hostclass)
+        ast.code[0].name.should == 'test'
+        ast.code[0].instantiate('')[0].doc.should == "comment\n"
+      end
     end
 
-    it "should be able to parse right to left relationships" do
-      "Notify[foo] <- Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
-    end
+    describe "when parsing" do
+      it "should be able to parse normal left to right relationships" do
+        "Notify[foo] -> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
+      end
 
-    it "should be able to parse normal left to right subscriptions" do
-      "Notify[foo] ~> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
-    end
+      it "should be able to parse right to left relationships" do
+        "Notify[foo] <- Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
+      end
 
-    it "should be able to parse right to left subscriptions" do
-      "Notify[foo] <~ Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
-    end
+      it "should be able to parse normal left to right subscriptions" do
+        "Notify[foo] ~> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
+      end
+
+      it "should be able to parse right to left subscriptions" do
+        "Notify[foo] <~ Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship)
+      end
 
-    it "should correctly set the arrow type of a relationship" do
-      "Notify[foo] <~ Notify[bar]".should parse_with { |rel| rel.arrow == "<~" }
+      it "should correctly set the arrow type of a relationship" do
+        "Notify[foo] <~ Notify[bar]".should parse_with { |rel| rel.arrow == "<~" }
+      end
+
+      it "should be able to parse deep hash access" do
+        %q{
+          $hash = { 'a' => { 'b' => { 'c' => 'it works' } } }
+          $out = $hash['a']['b']['c']
+        }.should parse_with { |v| v.value.is_a?(Puppet::Parser::AST::ASTHash) }
+      end
+
+      it "should fail if asked to parse '$foo::::bar'" do
+        expect { @parser.parse("$foo::::bar") }.to raise_error(Puppet::ParseError, /Syntax error at ':'/)
+      end
+
+      describe "function calls" do
+        it "should be able to pass an array to a function" do
+          "my_function([1,2,3])".should parse_with { |fun|
+            fun.is_a?(Puppet::Parser::AST::Function) &&
+            fun.arguments[0].evaluate(stub 'scope') == ['1','2','3']
+          }
+        end
+
+        it "should be able to pass a hash to a function" do
+          "my_function({foo => bar})".should parse_with { |fun|
+            fun.is_a?(Puppet::Parser::AST::Function) &&
+            fun.arguments[0].evaluate(stub 'scope') == {'foo' => 'bar'}
+          }
+        end
+      end
+
+      describe "collections" do
+        it "should find resources according to an expression" do
+          %q{ File <| mode == 0700 + 0050 + 0050 |> }.should parse_with { |coll|
+            coll.is_a?(Puppet::Parser::AST::Collection) &&
+              coll.query.evaluate(stub 'scope').first == ["mode", "==", 0700 + 0050 + 0050]
+          }
+        end
+      end
     end
+  end
 
-    it "should be able to parse deep hash access" do
-      %q{
-        $hash = { 'a' => { 'b' => { 'c' => 'it works' } } }
-        $out = $hash['a']['b']['c']
-      }.should parse_with { |v| v.value.is_a?(Puppet::Parser::AST::ASTHash) }
+  describe 'using classic parser' do
+    before :each do
+      Puppet[:parser] = 'current'
     end
+    it_behaves_like 'a puppet parser'
+  end
 
-    it "should fail if asked to parse '$foo::::bar'" do
-      expect { @parser.parse("$foo::::bar") }.to raise_error(Puppet::ParseError, /Syntax error at ':'/)
+  describe 'using future parser' do
+    before :each do
+      Puppet[:parser] = 'future'
     end
+    it_behaves_like 'a puppet parser'
 
-    describe "function calls" do
-      it "should be able to pass an array to a function" do
-        "my_function([1,2,3])".should parse_with { |fun|
-          fun.is_a?(Puppet::Parser::AST::Function) &&
-          fun.arguments[0].evaluate(stub 'scope') == ['1','2','3']
-        }
+    context 'more detailed errors should be generated' do
+      before :each do
+        Puppet[:parser] = 'future'
+        @resource_type_collection = Puppet::Resource::TypeCollection.new("env")
+        @parser = Puppet::Parser::ParserFactory.parser("development")
+      end
+
+      it 'should flag illegal type references' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        1+1 { "title": }
+        SOURCE
+        # This error message is currently produced by the parser, and is not as detailed as desired
+        # It references position 16 at the closing '}'
+        expect { @parser.parse(source) }.to raise_error(/Expression is not valid as a resource.*line 1:16/)
+      end
+
+      it 'should flag illegal type references and get position correct' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        1+1 { "title":
+          }
+        SOURCE
+        # This error message is currently produced by the parser, and is not as detailed as desired
+        # It references position 16 at the closing '}'
+        expect { @parser.parse(source) }.to raise_error(/Expression is not valid as a resource.*line 2:3/)
       end
 
-      it "should be able to pass a hash to a function" do
-        "my_function({foo => bar})".should parse_with { |fun|
-          fun.is_a?(Puppet::Parser::AST::Function) &&
-          fun.arguments[0].evaluate(stub 'scope') == {'foo' => 'bar'}
+      it 'should flag illegal use of non r-value producing if' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        $a = if true {
+          false
         }
+        SOURCE
+        expect { @parser.parse(source) }.to raise_error(/An 'if' statement does not produce a value at line 1:6/)
       end
-    end
 
-    describe "collections" do
-      it "should find resources according to an expression" do
-        %q{ File <| mode == 0700 + 0050 + 0050 |> }.should parse_with { |coll|
-          coll.is_a?(Puppet::Parser::AST::Collection) &&
-            coll.query.evaluate(stub 'scope').first == ["mode", "==", 0700 + 0050 + 0050]
+      it 'should flag illegal use of non r-value producing case' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        $a = case true {
+          false :{ }
         }
+        SOURCE
+        expect { @parser.parse(source) }.to raise_error(/A 'case' statement does not produce a value at line 1:6/)
+      end
+
+      it 'should flag illegal use of non r-value producing <| |>' do
+        expect { @parser.parse("$a = file <| |>") }.to raise_error(/A Virtual Query does not produce a value at line 1:6/)
+      end
+
+      it 'should flag illegal use of non r-value producing <<| |>>' do
+        expect { @parser.parse("$a = file <<| |>>") }.to raise_error(/An Exported Query does not produce a value at line 1:6/)
+      end
+
+      it 'should flag illegal use of non r-value producing define' do
+        Puppet.expects(:err).with("Invalid use of expression. A 'define' expression does not produce a value at line 1:6")
+        Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6")
+        expect { @parser.parse("$a = define foo { }") }.to raise_error(/2 errors/)
+      end
+
+      it 'should flag illegal use of non r-value producing class' do
+        Puppet.expects(:err).with("Invalid use of expression. A Host Class Definition does not produce a value at line 1:6")
+        Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6")
+        expect { @parser.parse("$a = class foo { }") }.to raise_error(/2 errors/)
+      end
+
+      it 'unclosed quote should be flagged for start position of string' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        $a = "xx
+        yyy
+        SOURCE
+        expect { @parser.parse(source) }.to raise_error(/Unclosed quote after '"' followed by 'xx\\nyy\.\.\.' at line 1:6/)
+      end
+
+      it 'can produce multiple errors and raise a summary exception' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        $a = node x { }
+        SOURCE
+        Puppet.expects(:err).with("Invalid use of expression. A Node Definition does not produce a value at line 1:6")
+        Puppet.expects(:err).with("Classes, definitions, and nodes may only appear at toplevel or inside other classes at line 1:6")
+        expect { @parser.parse(source) }.to raise_error(/2 errors/)
+      end
+
+      it 'can produce detailed error for a bad hostname' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        node 'macbook+owned+by+name' { }
+        SOURCE
+        expect { @parser.parse(source) }.to raise_error(/The hostname 'macbook\+owned\+by\+name' contains illegal characters.*at line 1:6/)
+      end
+
+      it 'can produce detailed error for a hostname with interpolation' do
+        source = <<-SOURCE.gsub(/^ {8}/,'')
+        $name = 'fred'
+        node "macbook-owned-by$name" { }
+        SOURCE
+        expect { @parser.parse(source) }.to raise_error(/An interpolated expression is not allowed in a hostname of a node at line 2:24/)
       end
     end
   end
diff --git a/spec/integration/parser/scope_spec.rb b/spec/integration/parser/scope_spec.rb
index 66d29a4..8df5439 100644
--- a/spec/integration/parser/scope_spec.rb
+++ b/spec/integration/parser/scope_spec.rb
@@ -182,7 +182,7 @@ class bar inherits foo {
           class c {
             notify { 'something': message => "$a::b" }
           }
-          
+
           class a { }
 
           node default {
diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb
index f7aacc2..754be7b 100755
--- a/spec/unit/node/environment_spec.rb
+++ b/spec/unit/node/environment_spec.rb
@@ -6,6 +6,7 @@
 require 'puppet/node/environment'
 require 'puppet/util/execution'
 require 'puppet_spec/modules'
+require 'puppet/parser/parser_factory'
 
 describe Puppet::Node::Environment do
   let(:env) { Puppet::Node::Environment.new("testing") }
@@ -15,438 +16,454 @@
     Puppet::Node::Environment.clear
   end
 
-  it "should use the filetimeout for the ttl for the modulepath" do
-    Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout])
-  end
-
-  it "should use the filetimeout for the ttl for the module list" do
-    Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout])
-  end
-
-  it "should use the default environment if no name is provided while initializing an environment" do
-    Puppet[:environment] = "one"
-    Puppet::Node::Environment.new.name.should == :one
-  end
-
-  it "should treat environment instances as singletons" do
-    Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one"))
-  end
-
-  it "should treat an environment specified as names or strings as equivalent" do
-    Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one"))
-  end
-
-  it "should return its name when converted to a string" do
-    Puppet::Node::Environment.new(:one).to_s.should == "one"
-  end
-
-  it "should just return any provided environment if an environment is provided as the name" do
-    one = Puppet::Node::Environment.new(:one)
-    Puppet::Node::Environment.new(one).should equal(one)
-  end
-
-  describe "when managing known resource types" do
-    before do
-      @collection = Puppet::Resource::TypeCollection.new(env)
-      env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
-      Thread.current[:known_resource_types] = nil
-    end
-
-    it "should create a resource type collection if none exists" do
-      Puppet::Resource::TypeCollection.expects(:new).with(env).returns @collection
-      env.known_resource_types.should equal(@collection)
-    end
-
-    it "should reuse any existing resource type collection" do
-      env.known_resource_types.should equal(env.known_resource_types)
-    end
-
-    it "should perform the initial import when creating a new collection" do
-      env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
-      env.known_resource_types
-    end
-
-    it "should return the same collection even if stale if it's the same thread" do
-      Puppet::Resource::TypeCollection.stubs(:new).returns @collection
-      env.known_resource_types.stubs(:stale?).returns true
-
-      env.known_resource_types.should equal(@collection)
+  shared_examples_for 'the environment' do
+    it "should use the filetimeout for the ttl for the modulepath" do
+      Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout])
     end
-
-    it "should return the current thread associated collection if there is one" do
-      Thread.current[:known_resource_types] = @collection
-
-      env.known_resource_types.should equal(@collection)
+  
+    it "should use the filetimeout for the ttl for the module list" do
+      Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout])
     end
-
-    it "should give to all threads using the same environment the same collection if the collection isn't stale" do
-      @original_thread_type_collection = Puppet::Resource::TypeCollection.new(env)
-      Puppet::Resource::TypeCollection.expects(:new).with(env).returns @original_thread_type_collection
-      env.known_resource_types.should equal(@original_thread_type_collection)
-
-      @original_thread_type_collection.expects(:require_reparse?).returns(false)
-      Puppet::Resource::TypeCollection.stubs(:new).with(env).returns @collection
-
-      t = Thread.new {
-        env.known_resource_types.should equal(@original_thread_type_collection)
-      }
-      t.join
-    end
-
-    it "should generate a new TypeCollection if the current one requires reparsing" do
-      old_type_collection = env.known_resource_types
-      old_type_collection.stubs(:require_reparse?).returns true
-      Thread.current[:known_resource_types] = nil
-      new_type_collection = env.known_resource_types
-
-      new_type_collection.should be_a Puppet::Resource::TypeCollection
-      new_type_collection.should_not equal(old_type_collection)
-    end
-  end
-
-  it "should validate the modulepath directories" do
-    real_file = tmpdir('moduledir')
-    path = %W[/one /two #{real_file}].join(File::PATH_SEPARATOR)
-
-    Puppet[:modulepath] = path
-
-    env.modulepath.should == [real_file]
-  end
-
-  it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do
-    Puppet::Util.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do
-      module_path = %w{/one /two}.join(File::PATH_SEPARATOR)
-      env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two}
-      env.expects(:[]).with(:modulepath).returns module_path
-
-      env.modulepath.should == %w{/l1 /l2 /one /two}
-    end
-  end
-
-  describe "when validating modulepath or manifestdir directories" do
-    before :each do
-      @path_one = tmpdir("path_one")
-      @path_two = tmpdir("path_one")
-      sep = File::PATH_SEPARATOR
-      Puppet[:modulepath] = "#{@path_one}#{sep}#{@path_two}"
+  
+    it "should use the default environment if no name is provided while initializing an environment" do
+      Puppet[:environment] = "one"
+      Puppet::Node::Environment.new.name.should == :one
     end
-
-    it "should not return non-directories" do
-      FileTest.expects(:directory?).with(@path_one).returns true
-      FileTest.expects(:directory?).with(@path_two).returns false
-
-      env.validate_dirs([@path_one, @path_two]).should == [@path_one]
+  
+    it "should treat environment instances as singletons" do
+      Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one"))
     end
-
-    it "should use the current working directory to fully-qualify unqualified paths" do
-      FileTest.stubs(:directory?).returns true
-
-      two = File.expand_path(File.join(Dir.getwd, "two"))
-      env.validate_dirs([@path_one, 'two']).should == [@path_one, two]
+  
+    it "should treat an environment specified as names or strings as equivalent" do
+      Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one"))
     end
-  end
-
-  describe "when modeling a specific environment" do
-    it "should have a method for returning the environment name" do
-      Puppet::Node::Environment.new("testing").name.should == :testing
+  
+    it "should return its name when converted to a string" do
+      Puppet::Node::Environment.new(:one).to_s.should == "one"
     end
-
-    it "should provide an array-like accessor method for returning any environment-specific setting" do
-      env.should respond_to(:[])
+  
+    it "should just return any provided environment if an environment is provided as the name" do
+      one = Puppet::Node::Environment.new(:one)
+      Puppet::Node::Environment.new(one).should equal(one)
     end
-
-    it "should ask the Puppet settings instance for the setting qualified with the environment name" do
-      Puppet.settings.set_value(:server, "myval", :testing)
-      env[:server].should == "myval"
+  
+    describe "when managing known resource types" do
+      before do
+        @collection = Puppet::Resource::TypeCollection.new(env)
+        env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
+        Thread.current[:known_resource_types] = nil
+      end
+  
+      it "should create a resource type collection if none exists" do
+        Puppet::Resource::TypeCollection.expects(:new).with(env).returns @collection
+        env.known_resource_types.should equal(@collection)
+      end
+  
+      it "should reuse any existing resource type collection" do
+        env.known_resource_types.should equal(env.known_resource_types)
+      end
+  
+      it "should perform the initial import when creating a new collection" do
+        env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
+        env.known_resource_types
+      end
+  
+      it "should return the same collection even if stale if it's the same thread" do
+        Puppet::Resource::TypeCollection.stubs(:new).returns @collection
+        env.known_resource_types.stubs(:stale?).returns true
+  
+        env.known_resource_types.should equal(@collection)
+      end
+  
+      it "should return the current thread associated collection if there is one" do
+        Thread.current[:known_resource_types] = @collection
+  
+        env.known_resource_types.should equal(@collection)
+      end
+  
+      it "should give to all threads using the same environment the same collection if the collection isn't stale" do
+        @original_thread_type_collection = Puppet::Resource::TypeCollection.new(env)
+        Puppet::Resource::TypeCollection.expects(:new).with(env).returns @original_thread_type_collection
+        env.known_resource_types.should equal(@original_thread_type_collection)
+  
+        @original_thread_type_collection.expects(:require_reparse?).returns(false)
+        Puppet::Resource::TypeCollection.stubs(:new).with(env).returns @collection
+  
+        t = Thread.new {
+          env.known_resource_types.should equal(@original_thread_type_collection)
+        }
+        t.join
+      end
+  
+      it "should generate a new TypeCollection if the current one requires reparsing" do
+        old_type_collection = env.known_resource_types
+        old_type_collection.stubs(:require_reparse?).returns true
+        Thread.current[:known_resource_types] = nil
+        new_type_collection = env.known_resource_types
+  
+        new_type_collection.should be_a Puppet::Resource::TypeCollection
+        new_type_collection.should_not equal(old_type_collection)
+      end
     end
-
-    it "should be able to return an individual module that exists in its module path" do
-      env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))]
-
-      mod = env.module('one')
-      mod.should be_a(Puppet::Module)
-      mod.name.should == 'one'
+  
+    it "should validate the modulepath directories" do
+      real_file = tmpdir('moduledir')
+      path = %W[/one /two #{real_file}].join(File::PATH_SEPARATOR)
+  
+      Puppet[:modulepath] = path
+  
+      env.modulepath.should == [real_file]
     end
-
-    it "should not return a module if the module doesn't exist" do
-      env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))]
-
-      env.module('two').should be_nil
+  
+    it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do
+      Puppet::Util.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do
+        module_path = %w{/one /two}.join(File::PATH_SEPARATOR)
+        env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two}
+        env.expects(:[]).with(:modulepath).returns module_path
+  
+        env.modulepath.should == %w{/l1 /l2 /one /two}
+      end
     end
-
-    it "should return nil if asked for a module that does not exist in its path" do
-      modpath = tmpdir('modpath')
-      env.modulepath = [modpath]
-
-      env.module("one").should be_nil
+  
+    describe "when validating modulepath or manifestdir directories" do
+      before :each do
+        @path_one = tmpdir("path_one")
+        @path_two = tmpdir("path_one")
+        sep = File::PATH_SEPARATOR
+        Puppet[:modulepath] = "#{@path_one}#{sep}#{@path_two}"
+      end
+  
+      it "should not return non-directories" do
+        FileTest.expects(:directory?).with(@path_one).returns true
+        FileTest.expects(:directory?).with(@path_two).returns false
+  
+        env.validate_dirs([@path_one, @path_two]).should == [@path_one]
+      end
+  
+      it "should use the current working directory to fully-qualify unqualified paths" do
+        FileTest.stubs(:directory?).returns true
+  
+        two = File.expand_path(File.join(Dir.getwd, "two"))
+        env.validate_dirs([@path_one, 'two']).should == [@path_one, two]
+      end
     end
-
-    describe "module data" do
-      before do
-        dir = tmpdir("deep_path")
-
-        @first = File.join(dir, "first")
-        @second = File.join(dir, "second")
-        Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}"
-
-        FileUtils.mkdir_p(@first)
-        FileUtils.mkdir_p(@second)
+  
+    describe "when modeling a specific environment" do
+      it "should have a method for returning the environment name" do
+        Puppet::Node::Environment.new("testing").name.should == :testing
       end
-
-      describe "#modules_by_path" do
-        it "should return an empty list if there are no modules" do
-          env.modules_by_path.should == {
-            @first  => [],
-            @second => []
-          }
-        end
-
-        it "should include modules even if they exist in multiple dirs in the modulepath" do
-          modpath1 = File.join(@first, "foo")
-          FileUtils.mkdir_p(modpath1)
-          modpath2 = File.join(@second, "foo")
-          FileUtils.mkdir_p(modpath2)
-
-          env.modules_by_path.should == {
-            @first  => [Puppet::Module.new('foo', modpath1, env)],
-            @second => [Puppet::Module.new('foo', modpath2, env)]
-          }
-        end
-
-        it "should ignore modules with invalid names" do
-          FileUtils.mkdir_p(File.join(@first, 'foo'))
-          FileUtils.mkdir_p(File.join(@first, 'foo2'))
-          FileUtils.mkdir_p(File.join(@first, 'foo-bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo_bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo=bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo.bar'))
-          FileUtils.mkdir_p(File.join(@first, '-foo'))
-          FileUtils.mkdir_p(File.join(@first, 'foo-'))
-          FileUtils.mkdir_p(File.join(@first, 'foo--bar'))
-
-          env.modules_by_path[@first].collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
-        end
-
+  
+      it "should provide an array-like accessor method for returning any environment-specific setting" do
+        env.should respond_to(:[])
       end
-
-      describe "#module_requirements" do
-        it "should return a list of what modules depend on other modules" do
-          PuppetSpec::Modules.create(
-            'foo',
-            @first,
-            :metadata => {
-              :author       => 'puppetlabs',
-              :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => ">= 1.0.0" }]
-            }
-          )
-          PuppetSpec::Modules.create(
-            'bar',
-            @second,
-            :metadata => {
-              :author       => 'puppetlabs',
-              :dependencies => [{ 'name' => 'puppetlabs/foo', "version_requirement" => "<= 2.0.0" }]
-            }
-          )
-          PuppetSpec::Modules.create(
-            'baz',
-            @first,
-            :metadata => {
-              :author       => 'puppetlabs',
-              :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "3.0.0" }]
-            }
-          )
-          PuppetSpec::Modules.create(
-            'alpha',
-            @first,
-            :metadata => {
-              :author       => 'puppetlabs',
-              :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "~3.0.0" }]
-            }
-          )
-
-          env.module_requirements.should == {
-            'puppetlabs/alpha' => [],
-            'puppetlabs/foo' => [
-              {
-                "name"    => "puppetlabs/bar",
-                "version" => "9.9.9",
-                "version_requirement" => "<= 2.0.0"
-              }
-            ],
-            'puppetlabs/bar' => [
-              {
-                "name"    => "puppetlabs/alpha",
-                "version" => "9.9.9",
-                "version_requirement" => "~3.0.0"
-              },
-              {
-                "name"    => "puppetlabs/baz",
-                "version" => "9.9.9",
-                "version_requirement" => "3.0.0"
-              },
-              {
-                "name"    => "puppetlabs/foo",
-                "version" => "9.9.9",
-                "version_requirement" => ">= 1.0.0"
-              }
-            ],
-            'puppetlabs/baz' => []
-          }
-        end
+  
+      it "should ask the Puppet settings instance for the setting qualified with the environment name" do
+        Puppet.settings.set_value(:server, "myval", :testing)
+        env[:server].should == "myval"
       end
-
-      describe ".module_by_forge_name" do
-        it "should find modules by forge_name" do
-          mod = PuppetSpec::Modules.create(
-            'baz',
-            @first,
-            :metadata => {:author => 'puppetlabs'},
-            :environment => env
-          )
-          env.module_by_forge_name('puppetlabs/baz').should == mod
-        end
-
-        it "should not find modules with same name by the wrong author" do
-          mod = PuppetSpec::Modules.create(
-            'baz',
-            @first,
-            :metadata => {:author => 'sneakylabs'},
-            :environment => env
-          )
-          env.module_by_forge_name('puppetlabs/baz').should == nil
-        end
-
-        it "should return nil when the module can't be found" do
-          env.module_by_forge_name('ima/nothere').should be_nil
-        end
+  
+      it "should be able to return an individual module that exists in its module path" do
+        env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))]
+  
+        mod = env.module('one')
+        mod.should be_a(Puppet::Module)
+        mod.name.should == 'one'
       end
-
-      describe ".modules" do
-        it "should return an empty list if there are no modules" do
-          env.modules.should == []
+  
+      it "should not return a module if the module doesn't exist" do
+        env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))]
+  
+        env.module('two').should be_nil
+      end
+  
+      it "should return nil if asked for a module that does not exist in its path" do
+        modpath = tmpdir('modpath')
+        env.modulepath = [modpath]
+  
+        env.module("one").should be_nil
+      end
+  
+      describe "module data" do
+        before do
+          dir = tmpdir("deep_path")
+  
+          @first = File.join(dir, "first")
+          @second = File.join(dir, "second")
+          Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}"
+  
+          FileUtils.mkdir_p(@first)
+          FileUtils.mkdir_p(@second)
         end
-
-        it "should return a module named for every directory in each module path" do
-          %w{foo bar}.each do |mod_name|
-            FileUtils.mkdir_p(File.join(@first, mod_name))
+  
+        describe "#modules_by_path" do
+          it "should return an empty list if there are no modules" do
+            env.modules_by_path.should == {
+              @first  => [],
+              @second => []
+            }
           end
-          %w{bee baz}.each do |mod_name|
-            FileUtils.mkdir_p(File.join(@second, mod_name))
+  
+          it "should include modules even if they exist in multiple dirs in the modulepath" do
+            modpath1 = File.join(@first, "foo")
+            FileUtils.mkdir_p(modpath1)
+            modpath2 = File.join(@second, "foo")
+            FileUtils.mkdir_p(modpath2)
+  
+            env.modules_by_path.should == {
+              @first  => [Puppet::Module.new('foo', modpath1, env)],
+              @second => [Puppet::Module.new('foo', modpath2, env)]
+            }
+          end
+  
+          it "should ignore modules with invalid names" do
+            FileUtils.mkdir_p(File.join(@first, 'foo'))
+            FileUtils.mkdir_p(File.join(@first, 'foo2'))
+            FileUtils.mkdir_p(File.join(@first, 'foo-bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo_bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo=bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo.bar'))
+            FileUtils.mkdir_p(File.join(@first, '-foo'))
+            FileUtils.mkdir_p(File.join(@first, 'foo-'))
+            FileUtils.mkdir_p(File.join(@first, 'foo--bar'))
+  
+            env.modules_by_path[@first].collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
           end
-          env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort
+  
         end
-
-        it "should remove duplicates" do
-          FileUtils.mkdir_p(File.join(@first,  'foo'))
-          FileUtils.mkdir_p(File.join(@second, 'foo'))
-
-          env.modules.collect{|mod| mod.name}.sort.should == %w{foo}
+  
+        describe "#module_requirements" do
+          it "should return a list of what modules depend on other modules" do
+            PuppetSpec::Modules.create(
+              'foo',
+              @first,
+              :metadata => {
+                :author       => 'puppetlabs',
+                :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => ">= 1.0.0" }]
+              }
+            )
+            PuppetSpec::Modules.create(
+              'bar',
+              @second,
+              :metadata => {
+                :author       => 'puppetlabs',
+                :dependencies => [{ 'name' => 'puppetlabs/foo', "version_requirement" => "<= 2.0.0" }]
+              }
+            )
+            PuppetSpec::Modules.create(
+              'baz',
+              @first,
+              :metadata => {
+                :author       => 'puppetlabs',
+                :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "3.0.0" }]
+              }
+            )
+            PuppetSpec::Modules.create(
+              'alpha',
+              @first,
+              :metadata => {
+                :author       => 'puppetlabs',
+                :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "~3.0.0" }]
+              }
+            )
+  
+            env.module_requirements.should == {
+              'puppetlabs/alpha' => [],
+              'puppetlabs/foo' => [
+                {
+                  "name"    => "puppetlabs/bar",
+                  "version" => "9.9.9",
+                  "version_requirement" => "<= 2.0.0"
+                }
+              ],
+              'puppetlabs/bar' => [
+                {
+                  "name"    => "puppetlabs/alpha",
+                  "version" => "9.9.9",
+                  "version_requirement" => "~3.0.0"
+                },
+                {
+                  "name"    => "puppetlabs/baz",
+                  "version" => "9.9.9",
+                  "version_requirement" => "3.0.0"
+                },
+                {
+                  "name"    => "puppetlabs/foo",
+                  "version" => "9.9.9",
+                  "version_requirement" => ">= 1.0.0"
+                }
+              ],
+              'puppetlabs/baz' => []
+            }
+          end
         end
-
-        it "should ignore modules with invalid names" do
-          FileUtils.mkdir_p(File.join(@first, 'foo'))
-          FileUtils.mkdir_p(File.join(@first, 'foo2'))
-          FileUtils.mkdir_p(File.join(@first, 'foo-bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo_bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo=bar'))
-          FileUtils.mkdir_p(File.join(@first, 'foo bar'))
-
-          env.modules.collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
+  
+        describe ".module_by_forge_name" do
+          it "should find modules by forge_name" do
+            mod = PuppetSpec::Modules.create(
+              'baz',
+              @first,
+              :metadata => {:author => 'puppetlabs'},
+              :environment => env
+            )
+            env.module_by_forge_name('puppetlabs/baz').should == mod
+          end
+  
+          it "should not find modules with same name by the wrong author" do
+            mod = PuppetSpec::Modules.create(
+              'baz',
+              @first,
+              :metadata => {:author => 'sneakylabs'},
+              :environment => env
+            )
+            env.module_by_forge_name('puppetlabs/baz').should == nil
+          end
+  
+          it "should return nil when the module can't be found" do
+            env.module_by_forge_name('ima/nothere').should be_nil
+          end
         end
-
-        it "should create modules with the correct environment" do
-          FileUtils.mkdir_p(File.join(@first, 'foo'))
-          env.modules.each {|mod| mod.environment.should == env }
+  
+        describe ".modules" do
+          it "should return an empty list if there are no modules" do
+            env.modules.should == []
+          end
+  
+          it "should return a module named for every directory in each module path" do
+            %w{foo bar}.each do |mod_name|
+              FileUtils.mkdir_p(File.join(@first, mod_name))
+            end
+            %w{bee baz}.each do |mod_name|
+              FileUtils.mkdir_p(File.join(@second, mod_name))
+            end
+            env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort
+          end
+  
+          it "should remove duplicates" do
+            FileUtils.mkdir_p(File.join(@first,  'foo'))
+            FileUtils.mkdir_p(File.join(@second, 'foo'))
+  
+            env.modules.collect{|mod| mod.name}.sort.should == %w{foo}
+          end
+  
+          it "should ignore modules with invalid names" do
+            FileUtils.mkdir_p(File.join(@first, 'foo'))
+            FileUtils.mkdir_p(File.join(@first, 'foo2'))
+            FileUtils.mkdir_p(File.join(@first, 'foo-bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo_bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo=bar'))
+            FileUtils.mkdir_p(File.join(@first, 'foo bar'))
+  
+            env.modules.collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
+          end
+  
+          it "should create modules with the correct environment" do
+            FileUtils.mkdir_p(File.join(@first, 'foo'))
+            env.modules.each {|mod| mod.environment.should == env }
+          end
+  
         end
-
+      end
+  
+      it "should cache the module list" do
+        env.modulepath = %w{/a}
+        Dir.expects(:entries).once.with("/a").returns %w{foo}
+  
+        env.modules
+        env.modules
       end
     end
-
-    it "should cache the module list" do
-      env.modulepath = %w{/a}
-      Dir.expects(:entries).once.with("/a").returns %w{foo}
-
-      env.modules
-      env.modules
-    end
-  end
-
-  describe Puppet::Node::Environment::Helper do
-    before do
-      @helper = Object.new
-      @helper.extend(Puppet::Node::Environment::Helper)
-    end
-
-    it "should be able to set and retrieve the environment as a symbol" do
-      @helper.environment = :foo
-      @helper.environment.name.should == :foo
-    end
-
-    it "should accept an environment directly" do
-      @helper.environment = Puppet::Node::Environment.new(:foo)
-      @helper.environment.name.should == :foo
+  
+    describe Puppet::Node::Environment::Helper do
+      before do
+        @helper = Object.new
+        @helper.extend(Puppet::Node::Environment::Helper)
+      end
+  
+      it "should be able to set and retrieve the environment as a symbol" do
+        @helper.environment = :foo
+        @helper.environment.name.should == :foo
+      end
+  
+      it "should accept an environment directly" do
+        @helper.environment = Puppet::Node::Environment.new(:foo)
+        @helper.environment.name.should == :foo
+      end
+  
+      it "should accept an environment as a string" do
+        @helper.environment = 'foo'
+        @helper.environment.name.should == :foo
+      end
     end
-
-    it "should accept an environment as a string" do
-      @helper.environment = 'foo'
-      @helper.environment.name.should == :foo
+  
+    describe "when performing initial import" do
+      before do
+        @parser = Puppet::Parser::ParserFactory.parser("test")
+#        @parser = Puppet::Parser::EParserAdapter.new(Puppet::Parser::Parser.new("test")) # TODO: FIX PARSER FACTORY
+        Puppet::Parser::ParserFactory.stubs(:parser).returns @parser
+      end
+  
+      it "should set the parser's string to the 'code' setting and parse if code is available" do
+        Puppet.settings[:code] = "my code"
+        @parser.expects(:string=).with "my code"
+        @parser.expects(:parse)
+        env.instance_eval { perform_initial_import }
+      end
+  
+      it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do
+        filename = tmpfile('myfile')
+        File.open(filename, 'w'){|f| }
+        Puppet.settings[:manifest] = filename
+        @parser.expects(:file=).with filename
+        @parser.expects(:parse)
+        env.instance_eval { perform_initial_import }
+      end
+  
+      it "should pass the manifest file to the parser even if it does not exist on disk" do
+        filename = tmpfile('myfile')
+        Puppet.settings[:code] = ""
+        Puppet.settings[:manifest] = filename
+        @parser.expects(:file=).with(filename).once
+        @parser.expects(:parse).once
+        env.instance_eval { perform_initial_import }
+      end
+  
+      it "should fail helpfully if there is an error importing" do
+        File.stubs(:exist?).returns true
+        env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env)
+        @parser.expects(:file=).once
+        @parser.expects(:parse).raises ArgumentError
+        lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error)
+      end
+  
+      it "should not do anything if the ignore_import settings is set" do
+        Puppet.settings[:ignoreimport] = true
+        @parser.expects(:string=).never
+        @parser.expects(:file=).never
+        @parser.expects(:parse).never
+        env.instance_eval { perform_initial_import }
+      end
+  
+      it "should mark the type collection as needing a reparse when there is an error parsing" do
+        @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...")
+        env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env)
+  
+        lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../)
+        env.known_resource_types.require_reparse?.should be_true
+      end
     end
   end
-
-  describe "when performing initial import" do
-    before do
-      @parser = Puppet::Parser::Parser.new("test")
-      Puppet::Parser::Parser.stubs(:new).returns @parser
-    end
-
-    it "should set the parser's string to the 'code' setting and parse if code is available" do
-      Puppet.settings[:code] = "my code"
-      @parser.expects(:string=).with "my code"
-      @parser.expects(:parse)
-      env.instance_eval { perform_initial_import }
-    end
-
-    it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do
-      filename = tmpfile('myfile')
-      File.open(filename, 'w'){|f| }
-      Puppet.settings[:manifest] = filename
-      @parser.expects(:file=).with filename
-      @parser.expects(:parse)
-      env.instance_eval { perform_initial_import }
-    end
-
-    it "should pass the manifest file to the parser even if it does not exist on disk" do
-      filename = tmpfile('myfile')
-      Puppet.settings[:code] = ""
-      Puppet.settings[:manifest] = filename
-      @parser.expects(:file=).with(filename).once
-      @parser.expects(:parse).once
-      env.instance_eval { perform_initial_import }
-    end
-
-    it "should fail helpfully if there is an error importing" do
-      File.stubs(:exist?).returns true
-      env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env)
-      @parser.expects(:file=).once
-      @parser.expects(:parse).raises ArgumentError
-      lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error)
-    end
-
-    it "should not do anything if the ignore_import settings is set" do
-      Puppet.settings[:ignoreimport] = true
-      @parser.expects(:string=).never
-      @parser.expects(:file=).never
-      @parser.expects(:parse).never
-      env.instance_eval { perform_initial_import }
+  describe 'with classic parser' do
+    before :each do
+      Puppet[:parser] = 'current'
     end
-
-    it "should mark the type collection as needing a reparse when there is an error parsing" do
-      @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...")
-      env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env)
-
-      lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../)
-      env.known_resource_types.require_reparse?.should be_true
+    it_behaves_like 'the environment'
+  end
+  describe 'with future parser' do
+    before :each do
+      Puppet[:parser] = 'future'
     end
+    it_behaves_like 'the environment'
   end
+  
 end
diff --git a/spec/unit/parser/ast/arithmetic_operator_spec.rb b/spec/unit/parser/ast/arithmetic_operator_spec.rb
index ae42fc9..cdd01e3 100755
--- a/spec/unit/parser/ast/arithmetic_operator_spec.rb
+++ b/spec/unit/parser/ast/arithmetic_operator_spec.rb
@@ -61,4 +61,100 @@
     operator.evaluate(@scope).should == 4.33
   end
 
+  context "when applied to array" do
+    before :each do
+      Puppet[:parser] = 'future'
+    end
+
+    it "+ should concatenate an array" do
+      one = stub 'one', :safeevaluate => [1,2,3]
+      two = stub 'two', :safeevaluate => [4,5]
+      operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two
+      operator.evaluate(@scope).should == [1,2,3,4,5]
+    end
+
+    it "<< should append array to an array" do
+      one = stub 'one', :safeevaluate => [1,2,3]
+      two = stub 'two', :safeevaluate => [4,5]
+      operator = ast::ArithmeticOperator.new :lval => one, :operator => "<<", :rval => two
+      operator.evaluate(@scope).should == [1,2,3, [4,5]]
+    end
+
+    it "<< should append object to an array" do
+      one = stub 'one', :safeevaluate => [1,2,3]
+      two = stub 'two', :safeevaluate => 'a b c'
+      operator = ast::ArithmeticOperator.new :lval => one, :operator => "<<", :rval => two
+      operator.evaluate(@scope).should == [1,2,3, 'a b c']
+    end
+
+    context "and input is invalid" do
+      it "should raise error for + if left is not an array" do
+        one = stub 'one', :safeevaluate => 4
+        two = stub 'two', :safeevaluate => [4,5]
+        operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two
+        lambda { operator.evaluate(@scope).should == [1,2,3,4,5] }.should raise_error(/left/)
+      end
+
+      it "should raise error for << if left is not an array" do
+        one = stub 'one', :safeevaluate => 4
+        two = stub 'two', :safeevaluate => [4,5]
+        operator = ast::ArithmeticOperator.new :lval => one, :operator => "<<", :rval => two
+        lambda { operator.evaluate(@scope).should == [1,2,3,4,5] }.should raise_error(/left/)
+      end
+
+      it "should raise error for + if right is not an array" do
+        one = stub 'one', :safeevaluate => [1,2]
+        two = stub 'two', :safeevaluate => 45
+        operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two
+        lambda { operator.evaluate(@scope).should == [1,2,3,4,5] }.should raise_error(/right/)
+      end
+
+      %w{ - * / % >>}.each do |op|
+        it "should raise error for '#{op}'" do
+          one = stub 'one', :safeevaluate => [1,2,3]
+          two = stub 'two', :safeevaluate => [4,5]
+          operator = ast::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two
+          lambda { operator.evaluate(@scope).should == [1,2,3,4,5] }.should raise_error
+        end
+      end
+    end
+
+    context "when applied to hash" do
+      before :each do
+        Puppet[:parser] = 'future'
+      end
+
+      it "+ should merge two hashes" do
+        one = stub 'one', :safeevaluate => {'a' => 1, 'b' => 2}
+        two = stub 'two', :safeevaluate => {'c' => 3 }
+        operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two
+        operator.evaluate(@scope).should == {'a' => 1, 'b' => 2, 'c' => 3}
+      end
+
+      context "and input is invalid" do
+        it "should raise error for + if left is not a hash" do
+          one = stub 'one', :safeevaluate => 4
+          two = stub 'two', :safeevaluate => {'a' => 1}
+          operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two
+          lambda { operator.evaluate(@scope).should == [1,2,3,4,5] }.should raise_error(/left/)
+        end
+
+        it "should raise error for + if right is not a hash" do
+          one = stub 'one', :safeevaluate => {'a' => 1}
+          two = stub 'two', :safeevaluate => 1
+          operator = ast::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two
+          lambda { operator.evaluate(@scope).should == {'a'=>1, 1=>nil} }.should raise_error(/right/)
+        end
+
+        %w{ - * / % << >>}.each do |op|
+          it "should raise error for '#{op}'" do
+            one = stub 'one', :safeevaluate => {'a' => 1, 'b' => 2}
+            two = stub 'two', :safeevaluate => {'c' => 3 }
+            operator = ast::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two
+            lambda { operator.evaluate(@scope).should == [1,2,3,4,5] }.should raise_error
+          end
+        end
+      end
+    end
+  end
 end
diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb
index 2c4b522..d64e57e 100755
--- a/spec/unit/parser/compiler_spec.rb
+++ b/spec/unit/parser/compiler_spec.rb
@@ -152,7 +152,7 @@ def resource(type, title)
 
     it "should transform node class hashes into a class list" do
       node = Puppet::Node.new("mynode")
-      node.classes = {'foo'=>{'one'=>'1'}, 'bar'=>{'two'=>'2'}}
+      node.classes = {'foo'=>{'one'=>'p1'}, 'bar'=>{'two'=>'p2'}}
       compiler = Puppet::Parser::Compiler.new(node)
 
       compiler.classlist.should =~ ['foo', 'bar']
@@ -231,7 +231,7 @@ def compile_stub(*except)
     end
 
     it "should evaluate any parameterized classes named in the node" do
-      classes = {'foo'=>{'1'=>'one'}, 'bar'=>{'2'=>'two'}}
+      classes = {'foo'=>{'p1'=>'one'}, 'bar'=>{'p2'=>'two'}}
       @node.stubs(:classes).returns(classes)
       @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope)
       @compiler.compile
@@ -609,7 +609,7 @@ def add_resource(name, parent = nil)
       # Define the given class with default parameters
       def define_class(name, parameters)
         @node.classes[name] = parameters
-        klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => @ast_obj, '2' => @ast_obj})
+        klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'p1' => @ast_obj, 'p2' => @ast_obj})
         @compiler.topscope.known_resource_types.add klass
       end
 
@@ -627,20 +627,20 @@ def compile
       it "should provide default values for parameters that have no values specified" do
         define_class('foo', {})
         compile()
-        @catalog.resource(:class, 'foo')['1'].should == "foo"
+        @catalog.resource(:class, 'foo')['p1'].should == "foo"
       end
 
       it "should use any provided values" do
-        define_class('foo', {'1' => 'real_value'})
+        define_class('foo', {'p1' => 'real_value'})
         compile()
-        @catalog.resource(:class, 'foo')['1'].should == "real_value"
+        @catalog.resource(:class, 'foo')['p1'].should == "real_value"
       end
 
       it "should support providing some but not all values" do
-        define_class('foo', {'1' => 'real_value'})
+        define_class('foo', {'p1' => 'real_value'})
         compile()
-        @catalog.resource(:class, 'Foo')['1'].should == "real_value"
-        @catalog.resource(:class, 'Foo')['2'].should == "foo"
+        @catalog.resource(:class, 'Foo')['p1'].should == "real_value"
+        @catalog.resource(:class, 'Foo')['p2'].should == "foo"
       end
 
       it "should ensure each node class is in catalog and has appropriate tags" do
@@ -648,7 +648,7 @@ def compile
         @node.classes = klasses
         ast_obj = Puppet::Parser::AST::String.new(:value => 'foo')
         klasses.each do |name|
-          klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj})
+          klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'p1' => ast_obj, 'p2' => ast_obj})
           @compiler.topscope.known_resource_types.add klass
         end
         catalog = @compiler.compile
@@ -659,11 +659,11 @@ def compile
     end
 
     it "should fail if required parameters are missing" do
-      klass = {'foo'=>{'1'=>'one'}}
+      klass = {'foo'=>{'a'=>'one'}}
       @node.classes = klass
-      klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil})
+      klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'a' => nil, 'b' => nil})
       @compiler.topscope.known_resource_types.add klass
-      lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Must pass 2 to Class[Foo]")
+      lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Must pass b to Class[Foo]")
     end
 
     it "should fail if invalid parameters are passed" do
diff --git a/spec/unit/parser/eparser_adapter_spec.rb b/spec/unit/parser/eparser_adapter_spec.rb
new file mode 100644
index 0000000..0516334
--- /dev/null
+++ b/spec/unit/parser/eparser_adapter_spec.rb
@@ -0,0 +1,407 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/parser/e_parser_adapter'
+
+describe Puppet::Parser do
+
+  Puppet::Parser::AST
+
+  before :each do
+    @known_resource_types = Puppet::Resource::TypeCollection.new("development")
+    @classic_parser = Puppet::Parser::Parser.new "development"
+    @parser = Puppet::Parser::EParserAdapter.new(@classic_parser)
+    @classic_parser.stubs(:known_resource_types).returns @known_resource_types
+    @true_ast = Puppet::Parser::AST::Boolean.new :value => true
+  end
+
+  it "should require an environment at initialization" do
+    expect {
+      Puppet::Parser::EParserAdapter.new
+    }.to raise_error(ArgumentError, /wrong number of arguments/)
+  end
+
+  describe "when parsing append operator" do
+
+    it "should not raise syntax errors" do
+      expect { @parser.parse("$var += something") }.to_not raise_error
+    end
+
+    it "should raise syntax error on incomplete syntax " do
+      expect {
+        @parser.parse("$var += ")
+      }.to raise_error(Puppet::ParseError, /Syntax error at end of file/)
+    end
+
+    it "should create ast::VarDef with append=true" do
+      vardef = @parser.parse("$var += 2").code[0]
+      vardef.should be_a(Puppet::Parser::AST::VarDef)
+      vardef.append.should == true
+    end
+
+    it "should work with arrays too" do
+      vardef = @parser.parse("$var += ['test']").code[0]
+      vardef.should be_a(Puppet::Parser::AST::VarDef)
+      vardef.append.should == true
+    end
+
+  end
+
+  describe "when parsing selector" do
+    it "should support hash access on the left hand side" do
+      expect { @parser.parse("$h = { 'a' => 'b' } $a = $h['a'] ? { 'b' => 'd', default => undef }") }.to_not raise_error
+    end
+  end
+
+  describe "parsing 'unless'" do
+    it "should create the correct ast objects" do
+      Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) }
+      @parser.parse("unless false { $var = 1 }")
+    end
+
+    it "should not raise an error with empty statements" do
+      expect {  @parser.parse("unless false { }") }.to_not raise_error
+    end
+
+    #test for bug #13296
+    it "should not override 'unless' as a parameter inside resources" do
+      lambda { @parser.parse("exec {'/bin/echo foo': unless => '/usr/bin/false',}") }.should_not raise_error
+    end
+  end
+
+  describe "when parsing parameter names" do
+    Puppet::Parser::Lexer::KEYWORDS.sort_tokens.each do |keyword|
+      it "should allow #{keyword} as a keyword" do
+        lambda { @parser.parse("exec {'/bin/echo foo': #{keyword} => '/usr/bin/false',}") }.should_not raise_error
+      end
+    end
+  end
+
+  describe "when parsing 'if'" do
+    it "not, it should create the correct ast objects" do
+      Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) }
+      @parser.parse("if ! true { $var = 1 }")
+    end
+
+    it "boolean operation, it should create the correct ast objects" do
+      Puppet::Parser::AST::BooleanOperator.expects(:new).with {
+        |h| h[:rval].is_a?(Puppet::Parser::AST::Boolean) and h[:lval].is_a?(Puppet::Parser::AST::Boolean) and h[:operator]=="or"
+      }
+      @parser.parse("if true or true { $var = 1 }")
+
+    end
+
+    it "comparison operation, it should create the correct ast objects" do
+      Puppet::Parser::AST::ComparisonOperator.expects(:new).with {
+        |h| h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="<"
+      }
+      @parser.parse("if 1 < 2 { $var = 1 }")
+
+    end
+
+  end
+
+  describe "when parsing if complex expressions" do
+    it "should create a correct ast tree" do
+      aststub = stub_everything 'ast'
+      Puppet::Parser::AST::ComparisonOperator.expects(:new).with {
+        |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]==">"
+      }.returns(aststub)
+      Puppet::Parser::AST::ComparisonOperator.expects(:new).with {
+        |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="=="
+      }.returns(aststub)
+      Puppet::Parser::AST::BooleanOperator.expects(:new).with {
+        |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and"
+      }
+      @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }")
+    end
+
+    it "should raise an error on incorrect expression" do
+      expect {
+        @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }")
+      }.to raise_error(Puppet::ParseError, /Syntax error at '\)'/)
+    end
+
+  end
+
+  describe "when parsing resource references" do
+
+    it "should not raise syntax errors" do
+      expect { @parser.parse('exec { test: param => File["a"] }') }.to_not raise_error
+    end
+
+    it "should not raise syntax errors with multiple references" do
+      expect { @parser.parse('exec { test: param => File["a","b"] }') }.to_not raise_error
+    end
+
+    it "should create an ast::ResourceReference" do
+      # NOTE: In egrammar, type and name are unified immediately to lower case whereas the regular grammar
+      # keeps the UC name in some contexts - it gets downcased later as the name of the type is in lower case.
+      #
+      Puppet::Parser::AST::ResourceReference.expects(:new).with { |arg|
+        arg[:line]==1 and arg[:pos] ==25 and arg[:type]=="file" and arg[:title].is_a?(Puppet::Parser::AST::ASTArray)
+      }
+      @parser.parse('exec { test: command => File["a","b"] }')
+    end
+  end
+
+  describe "when parsing resource overrides" do
+
+    it "should not raise syntax errors" do
+      expect { @parser.parse('Resource["title"] { param => value }') }.to_not raise_error
+    end
+
+    it "should not raise syntax errors with multiple overrides" do
+      expect { @parser.parse('Resource["title1","title2"] { param => value }') }.to_not raise_error
+    end
+
+    it "should create an ast::ResourceOverride" do
+      ro = @parser.parse('Resource["title1","title2"] { param => value }').code[0]
+      ro.should be_a(Puppet::Parser::AST::ResourceOverride)
+      ro.line.should == 1
+      ro.object.should be_a(Puppet::Parser::AST::ResourceReference)
+      ro.parameters[0].should be_a(Puppet::Parser::AST::ResourceParam)
+    end
+
+  end
+
+  describe "when parsing if statements" do
+
+    it "should not raise errors with empty if" do
+      expect { @parser.parse("if true { }") }.to_not raise_error
+    end
+
+    it "should not raise errors with empty else" do
+      expect { @parser.parse("if false { notice('if') } else { }") }.to_not raise_error
+    end
+
+    it "should not raise errors with empty if and else" do
+      expect { @parser.parse("if false { } else { }") }.to_not raise_error
+    end
+
+    it "should create a nop node for empty branch" do
+      Puppet::Parser::AST::Nop.expects(:new).twice
+      @parser.parse("if true { }")
+    end
+
+    it "should create a nop node for empty else branch" do
+      Puppet::Parser::AST::Nop.expects(:new)
+      @parser.parse("if true { notice('test') } else { }")
+    end
+
+    it "should build a chain of 'ifs' if there's an 'elsif'" do
+      expect { @parser.parse(<<-PP) }.to_not raise_error
+        if true { notice('test') } elsif true {} else { }
+      PP
+    end
+
+  end
+
+  describe "when parsing function calls" do
+    it "should not raise errors with no arguments" do
+      expect { @parser.parse("tag()") }.to_not raise_error
+    end
+
+    it "should not raise errors with rvalue function with no args" do
+      expect { @parser.parse("$a = template()") }.to_not raise_error
+    end
+
+    it "should not raise errors with arguments" do
+      expect { @parser.parse("notice(1)") }.to_not raise_error
+    end
+
+    it "should not raise errors with multiple arguments" do
+      expect { @parser.parse("notice(1,2)") }.to_not raise_error
+    end
+
+    it "should not raise errors with multiple arguments and a trailing comma" do
+      expect { @parser.parse("notice(1,2,)") }.to_not raise_error
+    end
+
+  end
+
+  describe "when parsing arrays" do
+    it "should parse an array" do
+      expect { @parser.parse("$a = [1,2]") }.to_not raise_error
+    end
+
+    it "should not raise errors with a trailing comma" do
+      expect { @parser.parse("$a = [1,2,]") }.to_not raise_error
+    end
+
+    it "should accept an empty array" do
+      expect { @parser.parse("$var = []\n") }.to_not raise_error
+    end
+  end
+
+  describe "when parsing classes" do
+    before :each do
+      @krt = Puppet::Resource::TypeCollection.new("development")
+      @classic_parser = Puppet::Parser::Parser.new "development"
+      @parser = Puppet::Parser::EParserAdapter.new(@classic_parser)
+      @classic_parser.stubs(:known_resource_types).returns @krt
+    end
+
+    it "should not create new classes" do
+      @parser.parse("class foobar {}").code[0].should be_a(Puppet::Parser::AST::Hostclass)
+      @krt.hostclass("foobar").should be_nil
+    end
+
+    it "should correctly set the parent class when one is provided" do
+      @parser.parse("class foobar inherits yayness {}").code[0].instantiate('')[0].parent.should == "yayness"
+    end
+
+    it "should correctly set the parent class for multiple classes at a time" do
+      statements = @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}").code
+      statements[0].instantiate('')[0].parent.should == "yayness"
+      statements[1].instantiate('')[0].parent.should == "bar"
+    end
+
+    it "should define the code when some is provided" do
+      @parser.parse("class foobar { $var = val }").code[0].code.should_not be_nil
+    end
+
+    it "should accept parameters with trailing comma" do
+      @parser.parse("file { '/example': ensure => file, }").should be
+    end
+
+    it "should accept parametrized classes with trailing comma" do
+      @parser.parse("class foobar ($var1 = 0,) { $var = val }").code[0].code.should_not be_nil
+    end
+
+    it "should define parameters when provided" do
+      foobar = @parser.parse("class foobar($biz,$baz) {}").code[0].instantiate('')[0]
+      foobar.arguments.should == {"biz" => nil, "baz" => nil}
+    end
+  end
+
+  describe "when parsing resources" do
+    before :each do
+      @krt = Puppet::Resource::TypeCollection.new("development")
+      @classic_parser = Puppet::Parser::Parser.new "development"
+      @parser = Puppet::Parser::EParserAdapter.new(@classic_parser)
+      @classic_parser.stubs(:known_resource_types).returns @krt
+    end
+
+    it "should be able to parse class resources" do
+      @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil}))
+      expect { @parser.parse("class { foobar: biz => stuff }") }.to_not raise_error
+    end
+
+    it "should correctly mark exported resources as exported" do
+      @parser.parse("@@file { '/file': }").code[0].exported.should be_true
+    end
+
+    it "should correctly mark virtual resources as virtual" do
+      @parser.parse("@file { '/file': }").code[0].virtual.should be_true
+    end
+  end
+
+  describe "when parsing nodes" do
+    it "should be able to parse a node with a single name" do
+      node = @parser.parse("node foo { }").code[0]
+      node.should be_a Puppet::Parser::AST::Node
+      node.names.length.should == 1
+      node.names[0].value.should == "foo"
+    end
+
+    it "should be able to parse a node with two names" do
+      node = @parser.parse("node foo, bar { }").code[0]
+      node.should be_a Puppet::Parser::AST::Node
+      node.names.length.should == 2
+      node.names[0].value.should == "foo"
+      node.names[1].value.should == "bar"
+    end
+
+    it "should be able to parse a node with three names" do
+      node = @parser.parse("node foo, bar, baz { }").code[0]
+      node.should be_a Puppet::Parser::AST::Node
+      node.names.length.should == 3
+      node.names[0].value.should == "foo"
+      node.names[1].value.should == "bar"
+      node.names[2].value.should == "baz"
+    end
+  end
+
+  it "should fail if trying to collect defaults" do
+    expect {
+      @parser.parse("@Port { protocols => tcp }")
+    }.to raise_error(Puppet::ParseError, /Defaults are not virtualizable/)
+  end
+
+  context "when parsing collections" do
+    it "should parse basic collections" do
+      @parser.parse("Port <| |>").code.
+      should be_all {|x| x.is_a? Puppet::Parser::AST::Collection }
+    end
+
+    it "should parse fully qualified collections" do
+      @parser.parse("Port::Range <| |>").code.
+      should be_all {|x| x.is_a? Puppet::Parser::AST::Collection }
+    end
+  end
+
+  it "should not assign to a fully qualified variable" do
+    expect {
+      @parser.parse("$one::two = yay")
+    }.to raise_error(Puppet::ParseError, /Cannot assign to variables in other namespaces/)
+  end
+
+  it "should parse assignment of undef" do
+    tree = @parser.parse("$var = undef")
+    tree.code.children[0].should be_an_instance_of Puppet::Parser::AST::VarDef
+    tree.code.children[0].value.should be_an_instance_of Puppet::Parser::AST::Undef
+  end
+
+  it "should treat classes as case insensitive" do
+    @classic_parser.known_resource_types.import_ast(@parser.parse("class yayness {}"), '')
+    @classic_parser.known_resource_types.hostclass('yayness').
+    should == @classic_parser.find_hostclass("", "YayNess")
+  end
+
+  it "should treat defines as case insensitive" do
+    @classic_parser.known_resource_types.import_ast(@parser.parse("define funtest {}"), '')
+    @classic_parser.known_resource_types.hostclass('funtest').
+    should == @classic_parser.find_hostclass("", "fUntEst")
+  end
+  context "when parsing method calls" do
+    it "should parse method call with one param lambda" do
+      expect { @parser.parse("$a.foreach {|$a| debug $a }") }.to_not raise_error
+    end
+    it "should parse method call with two param lambda" do
+      expect { @parser.parse("$a.foreach {|$a,$b| debug $a }") }.to_not raise_error
+    end
+    it "should parse method call with two param lambda and default value" do
+      expect { @parser.parse("$a.foreach {|$a,$b=1| debug $a }") }.to_not raise_error
+    end
+    it "should parse method call without lambda (statement)" do
+      expect { @parser.parse("$a.foreach") }.to_not raise_error
+    end
+    it "should parse method call without lambda (expression)" do
+      expect { @parser.parse("$x = $a.foreach + 1") }.to_not raise_error
+    end
+    context "a receiver expression of type" do
+      it "variable should be allowed" do
+        expect { @parser.parse("$a.foreach") }.to_not raise_error
+      end
+      it "hasharrayaccess should be allowed" do
+        expect { @parser.parse("$a[0][1].foreach") }.to_not raise_error
+      end
+      it "quoted text should be allowed" do
+        expect { @parser.parse("\"monkey\".foreach") }.to_not raise_error
+        expect { @parser.parse("'monkey'.foreach") }.to_not raise_error
+      end
+      it "selector text should be allowed" do
+        expect { @parser.parse("$a ? { 'banana'=>[1,2,3]}.foreach") }.to_not raise_error
+      end
+      it "function call should be allowed" do
+        expect { @parser.parse("duh(1,2,3).foreach") }.to_not raise_error
+      end
+      it "method call should be allowed" do
+        expect { @parser.parse("$a.foo.bar") }.to_not raise_error
+      end
+      it "chained method calls with lambda should be allowed" do
+        expect { @parser.parse("$a.foo{||}.bar{||}") }.to_not raise_error
+      end
+    end
+  end
+end
diff --git a/spec/unit/parser/methods/collect_spec.rb b/spec/unit/parser/methods/collect_spec.rb
new file mode 100644
index 0000000..7d32596
--- /dev/null
+++ b/spec/unit/parser/methods/collect_spec.rb
@@ -0,0 +1,120 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+
+describe 'the collect method' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+  end
+
+  context "using future parser" do
+    before :each do
+      Puppet[:parser] = "future"
+    end
+    context "in Ruby style should be callable as" do
+      it 'collect on an array (multiplying each value by 2)' do
+        catalog = compile_to_catalog(<<-MANIFEST)
+          $a = [1,2,3]
+          $a.collect {|$x| $x*2}.foreach {|$v| 
+            file { "/file_$v": ensure => present }
+          }
+        MANIFEST
+  
+        catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+        catalog.resource(:file, "/file_4")['ensure'].should == 'present'
+        catalog.resource(:file, "/file_6")['ensure'].should == 'present'
+      end
+      
+      it 'collect on a hash selecting keys' do
+        catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {'a'=>1,'b'=>2,'c'=>3}
+        $a.collect {|$x| $x[0]}.foreach {|$k| 
+            file { "/file_$k": ensure => present }
+          }
+        MANIFEST
+    
+        catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+        catalog.resource(:file, "/file_b")['ensure'].should == 'present'
+        catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+      end
+      it 'foreach on a hash selecting value' do
+        catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {'a'=>1,'b'=>2,'c'=>3}
+        $a.collect {|$x| $x[1]}.foreach {|$k| 
+            file { "/file_$k": ensure => present }
+          }
+        MANIFEST
+    
+        catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+        catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+        catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+      end
+    end
+    context "in Java style should be callable as" do
+      shared_examples_for 'java style' do
+        it 'collect on an array (multiplying each value by 2)' do
+          catalog = compile_to_catalog(<<-MANIFEST)
+            $a = [1,2,3]
+            $a.collect |$x| #{farr}{ $x*2}.foreach |$v| #{farr}{ 
+              file { "/file_$v": ensure => present }
+            }
+          MANIFEST
+    
+          catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+          catalog.resource(:file, "/file_4")['ensure'].should == 'present'
+          catalog.resource(:file, "/file_6")['ensure'].should == 'present'
+        end
+        
+        it 'collect on a hash selecting keys' do
+          catalog = compile_to_catalog(<<-MANIFEST)
+          $a = {'a'=>1,'b'=>2,'c'=>3}
+          $a.collect |$x| #{farr}{ $x[0]}.foreach |$k| #{farr}{ 
+              file { "/file_$k": ensure => present }
+            }
+          MANIFEST
+      
+          catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+          catalog.resource(:file, "/file_b")['ensure'].should == 'present'
+          catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+        end
+        
+        it 'foreach on a hash selecting value' do
+          catalog = compile_to_catalog(<<-MANIFEST)
+          $a = {'a'=>1,'b'=>2,'c'=>3}
+          $a.collect |$x| #{farr} {$x[1]}.foreach |$k|#{farr}{ 
+              file { "/file_$k": ensure => present }
+            }
+          MANIFEST
+      
+          catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+          catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+          catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+        end
+      end
+      describe 'without fat arrow' do
+        it_should_behave_like 'java style' do
+          let(:farr) { '' }
+        end
+      end
+      describe 'with fat arrow' do
+        it_should_behave_like 'java style' do
+          let(:farr) { '=>' }
+        end
+      end
+    end
+  end
+end
diff --git a/spec/unit/parser/methods/each_spec.rb b/spec/unit/parser/methods/each_spec.rb
new file mode 100644
index 0000000..b8ab5f6
--- /dev/null
+++ b/spec/unit/parser/methods/each_spec.rb
@@ -0,0 +1,105 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'rubygems'
+
+describe 'methods' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+    Puppet[:parser] = 'future'
+  end
+
+  context "should be callable as" do
+    it 'each on an array selecting each value' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1,2,3]
+        $a.each |$v| { 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+    it 'each on an array selecting each value - function call style' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1,2,3]
+        foreach ($a) |$index, $v| => { 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+
+    it 'each on an array with index' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [present, absent, present]
+        $a.each |$k,$v| { 
+          file { "/file_${$k+1}": ensure => $v }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+
+    it 'each on a hash selecting entries' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {'a'=>'present','b'=>'absent','c'=>'present'}
+        $a.each |$e| { 
+        file { "/file_${e[0]}": ensure => $e[1] }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+    end
+    it 'each on a hash selecting key and value' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {'a'=>present,'b'=>absent,'c'=>present}
+        $a.each |$k, $v| { 
+          file { "/file_$k": ensure => $v }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+    end
+  end
+  context "should produce receiver" do
+    it 'each checking produced value using single expression' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1, 3, 2]
+        $b = $a.each |$x| { $x }
+        file { "/file_${b[1]}":
+          ensure => present
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+
+  end
+end
diff --git a/spec/unit/parser/methods/foreach_spec.rb b/spec/unit/parser/methods/foreach_spec.rb
new file mode 100755
index 0000000..b85296a
--- /dev/null
+++ b/spec/unit/parser/methods/foreach_spec.rb
@@ -0,0 +1,106 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'rubygems'
+
+describe 'methods' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+    # These tests only work with future parser
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+    Puppet[:parser] = 'future'
+  end
+
+  context "should be callable as" do
+    it 'foreach on an array selecting each value' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1,2,3]
+        $a.foreach {|$v| 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+    it 'foreach on an array selecting each value - function call style' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1,2,3]
+        foreach ($a) {|$v| 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+
+    it 'foreach on an array with index' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [present, absent, present]
+        $a.foreach {|$k,$v| 
+          file { "/file_${$k+1}": ensure => $v }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+
+    it 'foreach on a hash selecting entries' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {'a'=>'present','b'=>'absent','c'=>'present'}
+        $a.foreach {|$e| 
+        file { "/file_${e[0]}": ensure => $e[1] }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+    end
+    it 'foreach on a hash selecting key and value' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {'a'=>present,'b'=>absent,'c'=>present}
+        $a.foreach {|$k, $v| 
+          file { "/file_$k": ensure => $v }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+    end
+  end
+  context "should produce receiver" do
+    it 'each checking produced value using single expression' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1, 3, 2]
+        $b = $a.each |$x| { $x }
+        file { "/file_${b[1]}":
+          ensure => present
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+
+  end
+end
diff --git a/spec/unit/parser/methods/reduce_spec.rb b/spec/unit/parser/methods/reduce_spec.rb
new file mode 100644
index 0000000..99ecfd1
--- /dev/null
+++ b/spec/unit/parser/methods/reduce_spec.rb
@@ -0,0 +1,67 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+
+describe 'the reduce method' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+    # These tests only work with future parser
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+    Puppet[:parser] = 'future'
+  end
+
+  context "should be callable as" do
+    it 'reduce on an array' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1,2,3]
+        $b = $a.reduce {|$memo, $x| $memo + $x }
+        file { "/file_$b": ensure => present }
+      MANIFEST
+
+      catalog.resource(:file, "/file_6")['ensure'].should == 'present'
+    end    
+    it 'reduce on an array with start value' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1,2,3]
+        $b = $a.reduce(4) {|$memo, $x| $memo + $x }
+        file { "/file_$b": ensure => present }
+      MANIFEST
+  
+      catalog.resource(:file, "/file_10")['ensure'].should == 'present'
+    end    
+    it 'reduce on a hash' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {a=>1, b=>2, c=>3}
+        $start = [ignored, 4]
+        $b = $a.reduce {|$memo, $x| ['sum', $memo[1] + $x[1]] }
+        file { "/file_${$b[0]}_${$b[1]}": ensure => present }
+      MANIFEST
+    
+      catalog.resource(:file, "/file_sum_6")['ensure'].should == 'present'
+    end    
+    it 'reduce on a hash with start value' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {a=>1, b=>2, c=>3}
+        $start = ['ignored', 4]
+        $b = $a.reduce($start) {|$memo, $x| ['sum', $memo[1] + $x[1]] }
+        file { "/file_${$b[0]}_${$b[1]}": ensure => present }
+      MANIFEST
+  
+      catalog.resource(:file, "/file_sum_10")['ensure'].should == 'present'
+    end    
+  end
+end
diff --git a/spec/unit/parser/methods/reject_spec.rb b/spec/unit/parser/methods/reject_spec.rb
new file mode 100644
index 0000000..792e019
--- /dev/null
+++ b/spec/unit/parser/methods/reject_spec.rb
@@ -0,0 +1,52 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+
+describe 'the reject method' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+    # These tests only work with future parser
+    Puppet[:parser] = 'future'
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+    Puppet[:parser] = 'future'
+  end
+
+  context "should be callable as" do
+    it 'reject on an array (no berries)' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = ['strawberry','blueberry','orange']
+        $a.reject {|$x| $x  =~ /berry$/}.foreach {|$v| 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_strawberry").should == nil
+    end    
+    it 'reject on an array (no berries)' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = ['strawberry','blueberry','orange']
+        $a.reject {|$x| $foo = $x  =~ /berry$/}.foreach {|$v| 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_strawberry").should == nil
+    end    
+  end
+end
diff --git a/spec/unit/parser/methods/select_spec.rb b/spec/unit/parser/methods/select_spec.rb
new file mode 100644
index 0000000..88cef12
--- /dev/null
+++ b/spec/unit/parser/methods/select_spec.rb
@@ -0,0 +1,41 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+
+describe 'the select method' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+    # These tests only work with future parser
+    Puppet[:parser] = 'future'
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+    Puppet[:parser] = 'future'
+  end
+
+  context "should be callable as" do
+    it 'select on an array (all berries)' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = ['strawberry','blueberry','orange']
+        $a.select {|$x| $x  =~ /berry$/}.foreach {|$v| 
+          file { "/file_$v": ensure => present }
+        }
+      MANIFEST
+
+      catalog.resource(:file, "/file_strawberry")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present'
+    end    
+  end
+end
diff --git a/spec/unit/parser/methods/slice_spec.rb b/spec/unit/parser/methods/slice_spec.rb
new file mode 100644
index 0000000..b213415
--- /dev/null
+++ b/spec/unit/parser/methods/slice_spec.rb
@@ -0,0 +1,97 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'rubygems'
+
+describe 'methods' do
+  include PuppetSpec::Compiler
+
+  before :all do
+    # enable switching back 
+    @saved_parser = Puppet[:parser]
+    # These tests only work with future parser
+    Puppet[:parser] = 'future'
+  end
+  after :all do
+    # switch back to original 
+    Puppet[:parser] = @saved_parser
+  end
+
+  before :each do
+    node      = Puppet::Node.new("floppy", :environment => 'production')
+    @compiler = Puppet::Parser::Compiler.new(node)
+    @scope    = Puppet::Parser::Scope.new(@compiler)
+    @topscope = @scope.compiler.topscope
+    @scope.parent = @topscope
+    Puppet[:parser] = 'future'
+  end
+
+  context "should be callable on array as" do
+    
+    it 'slice with explicit parameters' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1, present, 2, absent, 3, present]
+        $a.slice(2) |$k,$v| { 
+          file { "/file_${$k}": ensure => $v }
+        }
+      MANIFEST
+  
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+    it 'slice with one parameter' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1, present, 2, absent, 3, present]
+        $a.slice(2) |$k| { 
+          file { "/file_${$k[0]}": ensure => $k[1] }
+        }
+      MANIFEST
+  
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+    end
+    it 'slice with shorter last slice' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1, present, 2, present, 3, absent]
+        $a.slice(4) |$a, $b, $c, $d| { 
+          file { "/file_$a.$c": ensure => $b }
+        }
+      MANIFEST
+    
+      catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_3.")['ensure'].should == 'absent'
+    end
+  end
+  context "should be callable on hash as" do
+    
+    it 'slice with explicit parameters, missing are empty' do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = {1=>present, 2=>present, 3=>absent}
+        $a.slice(2) |$a,$b| { 
+          file { "/file_${a[0]}.${b[0]}": ensure => $a[1] }
+        }
+      MANIFEST
+  
+      catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_3.")['ensure'].should == 'absent'
+    end
+  
+  end
+  context "when called without a block" do
+    it "should produce an array with the result" do
+      catalog = compile_to_catalog(<<-MANIFEST)
+        $a = [1, present, 2, absent, 3, present]
+        $a.slice(2).each |$k| { 
+          file { "/file_${$k[0]}": ensure => $k[1] }
+        }
+      MANIFEST
+  
+      catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+      catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
+      catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+
+    end
+  end
+end
diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb
index 7ff0c17..07ab678 100755
--- a/spec/unit/parser/parser_spec.rb
+++ b/spec/unit/parser/parser_spec.rb
@@ -100,11 +100,11 @@
       Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) }
       @parser.parse("unless false { $var = 1 }")
     end
-    
+
     it "should not raise an error with empty statements" do
       expect { @parser.parse("unless false { }") }.to_not raise_error
     end
-    
+
     #test for bug #13296
     it "should not override 'unless' as a parameter inside resources" do
       lambda { @parser.parse("exec {'/bin/echo foo': unless => '/usr/bin/false',}") }.should_not raise_error
diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb
index 646f4b7..8540ad2 100755
--- a/spec/unit/parser/scope_spec.rb
+++ b/spec/unit/parser/scope_spec.rb
@@ -495,6 +495,27 @@ def create_class_scope(name)
     end
   end
 
+  context "when using ephemeral as local scope" do
+    it "should store all variables in local scope" do
+      @scope.new_ephemeral true
+      @scope.setvar("apple", :fruit)
+      @scope["apple"].should == :fruit
+    end
+
+    it "should remove all local scope variables on unset" do
+      @scope.new_ephemeral true
+      @scope.setvar("apple", :fruit)
+      @scope["apple"].should == :fruit
+      @scope.unset_ephemeral_var
+      @scope["apple"].should == nil
+    end
+    it "should be created from a hash" do
+      @scope.ephemeral_from({ "apple" => :fruit, "strawberry" => :berry})
+      @scope["apple"].should == :fruit
+      @scope["strawberry"].should == :berry
+    end
+  end
+
   describe "when setting ephemeral vars from matches" do
     before :each do
       @match = stub 'match', :is_a? => true
diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb
index e15c05f..2938d45 100755
--- a/spec/unit/parser/type_loader_spec.rb
+++ b/spec/unit/parser/type_loader_spec.rb
@@ -2,6 +2,8 @@
 require 'spec_helper'
 
 require 'puppet/parser/type_loader'
+require 'puppet/parser/parser_factory'
+require 'puppet/parser/e_parser_adapter'
 require 'puppet_spec/modules'
 require 'puppet_spec/files'
 
@@ -9,222 +11,246 @@
   include PuppetSpec::Modules
   include PuppetSpec::Files
 
-  before do
-    @loader = Puppet::Parser::TypeLoader.new(:myenv)
-    Puppet.expects(:deprecation_warning).never
-  end
-
-  it "should support an environment" do
-    loader = Puppet::Parser::TypeLoader.new(:myenv)
-    loader.environment.name.should == :myenv
-  end
-
-  it "should include the Environment Helper" do
-    @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper)
-  end
-
-  it "should delegate its known resource types to its environment" do
-    @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection)
-  end
-
-  describe "when loading names from namespaces" do
-    it "should do nothing if the name to import is an empty string" do
-      @loader.expects(:name2files).never
-      @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil
+  shared_examples_for 'the typeloader' do
+    before do
+      @loader = Puppet::Parser::TypeLoader.new(:myenv)
+      Puppet.expects(:deprecation_warning).never
     end
 
-    it "should attempt to import each generated name" do
-      @loader.expects(:import).with("foo/bar",nil).returns([])
-      @loader.expects(:import).with("foo",nil).returns([])
-      @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false }
+    it "should support an environment" do
+      loader = Puppet::Parser::TypeLoader.new(:myenv)
+      loader.environment.name.should == :myenv
     end
-  end
 
-  describe "when importing" do
-    before do
-      Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}]
-      Puppet::Parser::Parser.any_instance.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
-      Puppet::Parser::Parser.any_instance.stubs(:file=)
+    it "should include the Environment Helper" do
+      @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper)
     end
 
-    it "should return immediately when imports are being ignored" do
-      Puppet::Parser::Files.expects(:find_manifests).never
-      Puppet[:ignoreimport] = true
-      @loader.import("foo").should be_nil
+    it "should delegate its known resource types to its environment" do
+      @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection)
     end
 
-    it "should find all manifests matching the file or pattern" do
-      Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}]
-      @loader.import("myfile")
-    end
+    describe "when loading names from namespaces" do
+      it "should do nothing if the name to import is an empty string" do
+        @loader.expects(:name2files).never
+        @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil
+      end
 
-    it "should use the directory of the current file if one is set" do
-      Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == make_absolute("/current") }.returns ["modname", %w{one}]
-      @loader.import("myfile", make_absolute("/current/file"))
+      it "should attempt to import each generated name" do
+        @loader.expects(:import).with("foo/bar",nil).returns([])
+        @loader.expects(:import).with("foo",nil).returns([])
+        @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false }
+      end
     end
 
-    it "should pass the environment when looking for files" do
-      Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}]
-      @loader.import("myfile")
-    end
+    describe "when importing" do
+      before do
+        Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}]
+        parser_class.any_instance.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
+        parser_class.any_instance.stubs(:file=)
+      end
 
-    it "should fail if no files are found" do
-      Puppet::Parser::Files.expects(:find_manifests).returns [nil, []]
-      lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError)
-    end
+      it "should return immediately when imports are being ignored" do
+        Puppet::Parser::Files.expects(:find_manifests).never
+        Puppet[:ignoreimport] = true
+        @loader.import("foo").should be_nil
+      end
 
-    it "should parse each found file" do
-      Puppet::Parser::Files.expects(:find_manifests).returns ["modname", [make_absolute("/one")]]
-      @loader.expects(:parse_file).with(make_absolute("/one")).returns(Puppet::Parser::AST::Hostclass.new(''))
-      @loader.import("myfile")
-    end
+      it "should find all manifests matching the file or pattern" do
+        Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}]
+        @loader.import("myfile")
+      end
 
-    it "should make each file qualified before attempting to parse it" do
-      Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}]
-      @loader.expects(:parse_file).with(make_absolute("/current/one")).returns(Puppet::Parser::AST::Hostclass.new(''))
-      @loader.import("myfile", make_absolute("/current/file"))
-    end
+      it "should use the directory of the current file if one is set" do
+        Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == make_absolute("/current") }.returns ["modname", %w{one}]
+        @loader.import("myfile", make_absolute("/current/file"))
+      end
 
-    it "should not attempt to import files that have already been imported" do
-      Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}]
-      Puppet::Parser::Parser.any_instance.expects(:parse).once.returns(Puppet::Parser::AST::Hostclass.new(''))
-      @loader.import("myfile")
+      it "should pass the environment when looking for files" do
+        Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}]
+        @loader.import("myfile")
+      end
 
-      # This will fail if it tries to reimport the file.
-      @loader.import("myfile")
-    end
-  end
+      it "should fail if no files are found" do
+        Puppet::Parser::Files.expects(:find_manifests).returns [nil, []]
+        lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError)
+      end
 
-  describe "when importing all" do
-    before do
-      @base = tmpdir("base")
+      it "should parse each found file" do
+        Puppet::Parser::Files.expects(:find_manifests).returns ["modname", [make_absolute("/one")]]
+        @loader.expects(:parse_file).with(make_absolute("/one")).returns(Puppet::Parser::AST::Hostclass.new(''))
+        @loader.import("myfile")
+      end
 
-      # Create two module path directories
-      @modulebase1 = File.join(@base, "first")
-      FileUtils.mkdir_p(@modulebase1)
-      @modulebase2 = File.join(@base, "second")
-      FileUtils.mkdir_p(@modulebase2)
+      it "should make each file qualified before attempting to parse it" do
+        Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}]
+        @loader.expects(:parse_file).with(make_absolute("/current/one")).returns(Puppet::Parser::AST::Hostclass.new(''))
+        @loader.import("myfile", make_absolute("/current/file"))
+      end
 
-      Puppet[:modulepath] = "#{@modulebase1}#{File::PATH_SEPARATOR}#{@modulebase2}"
-    end
+      it "should not attempt to import files that have already been imported" do
+        @loader = Puppet::Parser::TypeLoader.new(:myenv)
 
-    def mk_module(basedir, name)
-      PuppetSpec::Modules.create(name, basedir)
+        Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}]
+        parser_class.any_instance.expects(:parse).once.returns(Puppet::Parser::AST::Hostclass.new(''))
+        other_parser_class.any_instance.expects(:parse).never.returns(Puppet::Parser::AST::Hostclass.new(''))
+        @loader.import("myfile")
+
+        # This will fail if it tries to reimport the file.
+        @loader.import("myfile")
+      end
     end
 
-    # We have to pass the base path so that we can
-    # write to modules that are in the second search path
-    def mk_manifests(base, mod, type, files)
-      exts = {"ruby" => ".rb", "puppet" => ".pp"}
-      files.collect do |file|
-        name = mod.name + "::" + file.gsub("/", "::")
-        path = File.join(base, mod.name, "manifests", file + exts[type])
-        FileUtils.mkdir_p(File.split(path)[0])
-
-        # write out the class
-        if type == "ruby"
-          File.open(path, "w") { |f| f.print "hostclass '#{name}' do\nend" }
-        else
-          File.open(path, "w") { |f| f.print "class #{name} {}" }
+    describe "when importing all" do
+      before do
+        @base = tmpdir("base")
+
+        # Create two module path directories
+        @modulebase1 = File.join(@base, "first")
+        FileUtils.mkdir_p(@modulebase1)
+        @modulebase2 = File.join(@base, "second")
+        FileUtils.mkdir_p(@modulebase2)
+
+        Puppet[:modulepath] = "#{@modulebase1}#{File::PATH_SEPARATOR}#{@modulebase2}"
+      end
+
+      def mk_module(basedir, name)
+        PuppetSpec::Modules.create(name, basedir)
+      end
+
+      # We have to pass the base path so that we can
+      # write to modules that are in the second search path
+      def mk_manifests(base, mod, type, files)
+        exts = {"ruby" => ".rb", "puppet" => ".pp"}
+        files.collect do |file|
+          name = mod.name + "::" + file.gsub("/", "::")
+          path = File.join(base, mod.name, "manifests", file + exts[type])
+          FileUtils.mkdir_p(File.split(path)[0])
+
+          # write out the class
+          if type == "ruby"
+            File.open(path, "w") { |f| f.print "hostclass '#{name}' do\nend" }
+          else
+            File.open(path, "w") { |f| f.print "class #{name} {}" }
+          end
+          name
         end
-        name
       end
-    end
 
-    it "should load all puppet manifests from all modules in the specified environment" do
-      @module1 = mk_module(@modulebase1, "one")
-      @module2 = mk_module(@modulebase2, "two")
+      it "should load all puppet manifests from all modules in the specified environment" do
+        @module1 = mk_module(@modulebase1, "one")
+        @module2 = mk_module(@modulebase2, "two")
 
-      mk_manifests(@modulebase1, @module1, "puppet", %w{a b})
-      mk_manifests(@modulebase2, @module2, "puppet", %w{c d})
+        mk_manifests(@modulebase1, @module1, "puppet", %w{a b})
+        mk_manifests(@modulebase2, @module2, "puppet", %w{c d})
 
-      @loader.import_all
+        @loader.import_all
 
-      @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type)
-    end
+        @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type)
+      end
 
-    it "should load all ruby manifests from all modules in the specified environment" do
-      Puppet.expects(:deprecation_warning).at_least(1)
+      it "should load all ruby manifests from all modules in the specified environment" do
+        Puppet.expects(:deprecation_warning).at_least(1)
 
-      @module1 = mk_module(@modulebase1, "one")
-      @module2 = mk_module(@modulebase2, "two")
+        @module1 = mk_module(@modulebase1, "one")
+        @module2 = mk_module(@modulebase2, "two")
 
-      mk_manifests(@modulebase1, @module1, "ruby", %w{a b})
-      mk_manifests(@modulebase2, @module2, "ruby", %w{c d})
+        mk_manifests(@modulebase1, @module1, "ruby", %w{a b})
+        mk_manifests(@modulebase2, @module2, "ruby", %w{c d})
 
-      @loader.import_all
+        @loader.import_all
 
-      @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type)
-    end
+        @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type)
+      end
 
-    it "should not load manifests from duplicate modules later in the module path" do
-      @module1 = mk_module(@modulebase1, "one")
+      it "should not load manifests from duplicate modules later in the module path" do
+        @module1 = mk_module(@modulebase1, "one")
 
-      # duplicate
-      @module2 = mk_module(@modulebase2, "one")
+        # duplicate
+        @module2 = mk_module(@modulebase2, "one")
 
-      mk_manifests(@modulebase1, @module1, "puppet", %w{a})
-      mk_manifests(@modulebase2, @module2, "puppet", %w{c})
+        mk_manifests(@modulebase1, @module1, "puppet", %w{a})
+        mk_manifests(@modulebase2, @module2, "puppet", %w{c})
 
-      @loader.import_all
+        @loader.import_all
 
-      @loader.environment.known_resource_types.hostclass("one::c").should be_nil
-    end
+        @loader.environment.known_resource_types.hostclass("one::c").should be_nil
+      end
 
-    it "should load manifests from subdirectories" do
-      @module1 = mk_module(@modulebase1, "one")
+      it "should load manifests from subdirectories" do
+        @module1 = mk_module(@modulebase1, "one")
 
-      mk_manifests(@modulebase1, @module1, "puppet", %w{a a/b a/b/c})
+        mk_manifests(@modulebase1, @module1, "puppet", %w{a a/b a/b/c})
 
-      @loader.import_all
+        @loader.import_all
 
-      @loader.environment.known_resource_types.hostclass("one::a::b").should be_instance_of(Puppet::Resource::Type)
-      @loader.environment.known_resource_types.hostclass("one::a::b::c").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("one::a::b").should be_instance_of(Puppet::Resource::Type)
+        @loader.environment.known_resource_types.hostclass("one::a::b::c").should be_instance_of(Puppet::Resource::Type)
+      end
     end
-  end
 
-  describe "when parsing a file" do
-    before do
-      @parser = Puppet::Parser::Parser.new(@loader.environment)
-      @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
-      @parser.stubs(:file=)
-      Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser
-    end
+    describe "when parsing a file" do
+      before do
+        @parser = Puppet::Parser::ParserFactory.parser(@loader.environment)
+        @parser.class.should == parser_class
+        @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
+        @parser.stubs(:file=)
+        Puppet::Parser::ParserFactory.stubs(:parser).with(@loader.environment).returns @parser
+      end
 
-    it "should create a new parser instance for each file using the current environment" do
-      Puppet::Parser::Parser.expects(:new).with(@loader.environment).returns @parser
-      @loader.parse_file("/my/file")
-    end
+      it "should create a new parser instance for each file using the current environment" do
+        Puppet::Parser::ParserFactory.expects(:parser).with(@loader.environment).returns @parser
+        @loader.parse_file("/my/file")
+      end
 
-    it "should assign the parser its file and parse" do
-      @parser.expects(:file=).with("/my/file")
-      @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
-      @loader.parse_file("/my/file")
+      it "should assign the parser its file and parse" do
+        @parser.expects(:file=).with("/my/file")
+        @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new(''))
+        @loader.parse_file("/my/file")
+      end
     end
-  end
 
-  it "should be able to add classes to the current resource type collection" do
-    file = tmpfile("simple_file.pp")
-    File.open(file, "w") { |f| f.puts "class foo {}" }
-    @loader.import(file)
+    it "should be able to add classes to the current resource type collection" do
+      file = tmpfile("simple_file.pp")
+      File.open(file, "w") { |f| f.puts "class foo {}" }
+      @loader.import(file)
 
-    @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type)
-  end
+      @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type)
+    end
 
-  describe "when deciding where to look for files" do
-    { 'foo' => ['foo'],
-      'foo::bar' => ['foo/bar', 'foo'],
-      'foo::bar::baz' => ['foo/bar/baz', 'foo/bar', 'foo']
-    }.each do |fqname, expected_paths|
-      it "should look for #{fqname.inspect} in #{expected_paths.inspect}" do
-        @loader.instance_eval { name2files(fqname) }.should == expected_paths
+    describe "when deciding where to look for files" do
+      { 'foo' => ['foo'],
+        'foo::bar' => ['foo/bar', 'foo'],
+        'foo::bar::baz' => ['foo/bar/baz', 'foo/bar', 'foo']
+      }.each do |fqname, expected_paths|
+        it "should look for #{fqname.inspect} in #{expected_paths.inspect}" do
+          @loader.instance_eval { name2files(fqname) }.should == expected_paths
+        end
       end
     end
   end
+  describe 'when using the classic parser' do
+    before :each do
+      Puppet[:parser] = 'current'
+    end
+    it_should_behave_like 'the typeloader' do
+      let(:parser_class) { Puppet::Parser::Parser}
+      let(:other_parser_class) { Puppet::Parser::EParserAdapter}
+    end
+  end
+  describe 'when using the future parser' do
+    before :each do
+      Puppet[:parser] = 'future'
+    end
+    it_should_behave_like 'the typeloader' do
+      let(:parser_class) { Puppet::Parser::EParserAdapter}
+      let(:other_parser_class) { Puppet::Parser::Parser}
+    end
+  end
 end
diff --git a/spec/unit/pops/adaptable_spec.rb b/spec/unit/pops/adaptable_spec.rb
new file mode 100644
index 0000000..e9b5b80
--- /dev/null
+++ b/spec/unit/pops/adaptable_spec.rb
@@ -0,0 +1,143 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+describe Puppet::Pops::Adaptable::Adapter do
+  class ValueAdapter < Puppet::Pops::Adaptable::Adapter
+    attr_accessor :value
+  end
+
+  class OtherAdapter < Puppet::Pops::Adaptable::Adapter
+    attr_accessor :value
+    def OtherAdapter.create_adapter(o)
+      x = new
+      x.value="I am calling you Daffy."
+      x
+    end
+  end
+
+  module Farm
+    class FarmAdapter < Puppet::Pops::Adaptable::Adapter
+      attr_accessor :value
+    end
+  end
+
+  class Duck
+    include Puppet::Pops::Adaptable
+  end
+
+  it "should create specialized adapter instance on call to adapt" do
+    d = Duck.new
+    a = ValueAdapter.adapt(d)
+    a.class.should == ValueAdapter
+  end
+
+  it "should produce the same instance on multiple adaptations" do
+    d = Duck.new
+    a = ValueAdapter.adapt(d)
+    a.value = 10
+    b = ValueAdapter.adapt(d)
+    b.value.should == 10
+  end
+
+  it "should return the correct adapter if there are several" do
+    d = Duck.new
+    other = OtherAdapter.adapt(d)
+    a = ValueAdapter.adapt(d)
+    a.value = 10
+    b = ValueAdapter.adapt(d)
+    b.value.should == 10
+  end
+
+  it "should allow specialization to override creating" do
+    d = Duck.new
+    a = OtherAdapter.adapt(d)
+    a.value.should == "I am calling you Daffy."
+  end
+
+  it "should create a new adapter overriding existing" do
+    d = Duck.new
+    a = OtherAdapter.adapt(d)
+    a.value.should == "I am calling you Daffy."
+    a.value = "Something different"
+    a.value.should == "Something different"
+    b = OtherAdapter.adapt(d)
+    b.value.should == "Something different"
+    b = OtherAdapter.adapt_new(d)
+    b.value.should == "I am calling you Daffy."
+  end
+
+  it "should not create adapter on get" do
+    d = Duck.new
+    a = OtherAdapter.get(d)
+    a.should == nil
+  end
+
+  it "should return same adapter from get after adapt" do
+    d = Duck.new
+    a = OtherAdapter.get(d)
+    a.should == nil
+    a = OtherAdapter.adapt(d)
+    a.value.should == "I am calling you Daffy."
+    b = OtherAdapter.get(d)
+    b.value.should == "I am calling you Daffy."
+    a.should == b
+  end
+
+  it "should handle adapters in nested namespaces" do
+    d = Duck.new
+    a = Farm::FarmAdapter.get(d)
+    a.should == nil
+    a = Farm::FarmAdapter.adapt(d)
+    a.value = 10
+    b = Farm::FarmAdapter.get(d)
+    b.value.should == 10
+    # Test implementation detail
+    d.instance_variables.include?(:@Farm_FarmAdapter).should == true
+  end
+
+  it "should be able to clear the adapter" do
+    d = Duck.new
+    a = OtherAdapter.adapt(d)
+    a.value.should == "I am calling you Daffy."
+    # The adapter cleared should be returned
+    OtherAdapter.clear(d).value.should == "I am calling you Daffy."
+    OtherAdapter.get(d).should == nil
+  end
+
+  context "When adapting with #adapt it" do
+    it "should be possible to pass a block to configure the adapter" do
+      d = Duck.new
+      a = OtherAdapter.adapt(d) do |x|
+        x.value = "Donald"
+      end
+      a.value.should == "Donald"
+    end
+
+    it "should be possible to pass a block to configure the adapter and get the adapted" do
+      d = Duck.new
+      a = OtherAdapter.adapt(d) do |x, o|
+        x.value = "Donald, the #{o.class.name}"
+      end
+      a.value.should == "Donald, the Duck"
+    end
+  end
+
+  context "When adapting with #adapt_new it" do
+    it "should be possible to pass a block to configure the adapter" do
+      d = Duck.new
+      a = OtherAdapter.adapt_new(d) do |x|
+        x.value = "Donald"
+      end
+      a.value.should == "Donald"
+    end
+
+    it "should be possible to pass a block to configure the adapter and get the adapted" do
+      d = Duck.new
+      a = OtherAdapter.adapt_new(d) do |x, o|
+        x.value = "Donald, the #{o.class.name}"
+      end
+      a.value.should == "Donald, the Duck"
+    end
+  end
+end
diff --git a/spec/unit/pops/containment_spec.rb b/spec/unit/pops/containment_spec.rb
new file mode 100644
index 0000000..da5fae7
--- /dev/null
+++ b/spec/unit/pops/containment_spec.rb
@@ -0,0 +1,25 @@
+require 'puppet/pops'
+require File.join(File.dirname(__FILE__), 'factory_rspec_helper')
+
+describe Puppet::Pops::Containment do
+  include FactoryRspecHelper
+
+  it "Should return an Enumerable if eAllContents is called without arguments" do
+    (literal(1) + literal(2)).current.eAllContents.is_a?(Enumerable).should == true
+  end
+
+  it "Should return all content" do
+    # Note the top object is not included (an ArithmeticOperation with + operator)
+    (literal(1) + literal(2) + literal(3)).current.eAllContents.collect {|x| x}.size.should == 4
+  end
+
+  it "Should return containing feature" do
+    left = literal(1)
+    right = literal(2)
+    op = left + right
+
+    #pending "eContainingFeature does not work on _uni containments in RGen < 0.6.1"
+    left.current.eContainingFeature.should == :left_expr
+    right.current.eContainingFeature.should == :right_expr
+  end
+end
diff --git a/spec/unit/pops/factory_rspec_helper.rb b/spec/unit/pops/factory_rspec_helper.rb
new file mode 100644
index 0000000..b11f6ee
--- /dev/null
+++ b/spec/unit/pops/factory_rspec_helper.rb
@@ -0,0 +1,77 @@
+require 'puppet/pops'
+
+module FactoryRspecHelper
+  def literal(x)
+    Puppet::Pops::Model::Factory.literal(x)
+  end
+
+  def block(*args)
+    Puppet::Pops::Model::Factory.block(*args)
+  end
+
+  def var(x)
+    Puppet::Pops::Model::Factory.var(x)
+  end
+
+  def fqn(x)
+    Puppet::Pops::Model::Factory.fqn(x)
+  end
+
+  def string(*args)
+    Puppet::Pops::Model::Factory.string(*args)
+  end
+
+  def text(x)
+    Puppet::Pops::Model::Factory.text(x)
+  end
+
+  def minus(x)
+    Puppet::Pops::Model::Factory.minus(x)
+  end
+
+  def IF(test, then_expr, else_expr=nil)
+    Puppet::Pops::Model::Factory.IF(test, then_expr, else_expr)
+  end
+
+  def UNLESS(test, then_expr, else_expr=nil)
+    Puppet::Pops::Model::Factory.UNLESS(test, then_expr, else_expr)
+  end
+
+  def CASE(test, *options)
+    Puppet::Pops::Model::Factory.CASE(test, *options)
+  end
+
+  def WHEN(values, block)
+    Puppet::Pops::Model::Factory.WHEN(values, block)
+  end
+
+  def respond_to? method
+    if Puppet::Pops::Model::Factory.respond_to? method
+      true
+    else
+      super
+    end
+  end
+
+  def method_missing(method, *args, &block)
+    if Puppet::Pops::Model::Factory.respond_to? method
+      Puppet::Pops::Model::Factory.send(method, *args, &block)
+    else
+      super
+    end
+  end
+
+  # i.e. Selector Entry 1 => 'hello'
+  def MAP(match, value)
+    Puppet::Pops::Model::Factory.MAP(match, value)
+  end
+
+  def dump(x)
+    Puppet::Pops::Model::ModelTreeDumper.new.dump(x)
+  end
+
+  def unindent x
+    (x.gsub /^#{x[/\A\s*/]}/, '').chomp
+  end
+  factory ||= Puppet::Pops::Model::Factory
+end
diff --git a/spec/unit/pops/factory_spec.rb b/spec/unit/pops/factory_spec.rb
new file mode 100644
index 0000000..3cefbc7
--- /dev/null
+++ b/spec/unit/pops/factory_spec.rb
@@ -0,0 +1,329 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+require File.join(File.dirname(__FILE__), '/factory_rspec_helper')
+
+# This file contains testing of the pops model factory
+#
+
+describe Puppet::Pops::Model::Factory do
+  include FactoryRspecHelper
+
+  context "When factory methods are invoked they should produce expected results" do
+    it "tests #var should create a VariableExpression" do
+      var('a').current.class.should == Puppet::Pops::Model::VariableExpression
+    end
+
+    it "tests #fqn should create a QualifiedName" do
+      fqn('a').current.class.should == Puppet::Pops::Model::QualifiedName
+    end
+
+    it "tests #QNAME should create a QualifiedName" do
+      QNAME('a').current.class.should == Puppet::Pops::Model::QualifiedName
+    end
+
+    it "tests #QREF should create a QualifiedReference" do
+      QREF('a').current.class.should == Puppet::Pops::Model::QualifiedReference
+    end
+
+    it "tests #block should create a BlockExpression" do
+      block().current.is_a?(Puppet::Pops::Model::BlockExpression).should == true
+    end
+
+    it "should create a literal undef on :undef" do
+      literal(:undef).current.class.should == Puppet::Pops::Model::LiteralUndef
+    end
+
+    it "should create a literal default on :default" do
+      literal(:default).current.class.should == Puppet::Pops::Model::LiteralDefault
+    end
+  end
+
+  context "When calling block_or_expression" do
+    it "A single expression should produce identical output" do
+      block_or_expression(literal(1) + literal(2)).current.is_a?(Puppet::Pops::Model::ArithmeticExpression).should == true
+    end
+
+    it "Multiple expressions should produce a block expression" do
+      built = block_or_expression(literal(1) + literal(2), literal(2) + literal(3)).current
+      built.is_a?(Puppet::Pops::Model::BlockExpression).should == true
+      built.statements.size.should == 2
+    end
+  end
+
+  context "When processing calls with CALL_NAMED" do
+    it "Should be possible to state that r-value is required" do
+      built = CALL_NAMED("foo", true, []).current
+      built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression).should == true
+      built.rval_required.should == true
+    end
+
+    it "Should produce a call expression without arguments" do
+      built = CALL_NAMED("foo", false, []).current
+      built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression).should == true
+      built.functor_expr.is_a?(Puppet::Pops::Model::QualifiedName).should == true
+      built.functor_expr.value.should == "foo"
+      built.rval_required.should == false
+      built.arguments.size.should == 0
+    end
+
+    it "Should produce a call expression with one argument" do
+      built = CALL_NAMED("foo", false, [literal(1) + literal(2)]).current
+      built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression).should == true
+      built.functor_expr.is_a?(Puppet::Pops::Model::QualifiedName).should == true
+      built.functor_expr.value.should == "foo"
+      built.rval_required.should == false
+      built.arguments.size.should == 1
+      built.arguments[0].is_a?(Puppet::Pops::Model::ArithmeticExpression).should == true
+    end
+
+    it "Should produce a call expression with two arguments" do
+      built = CALL_NAMED("foo", false, [literal(1) + literal(2), literal(1) + literal(2)]).current
+      built.is_a?(Puppet::Pops::Model::CallNamedFunctionExpression).should == true
+      built.functor_expr.is_a?(Puppet::Pops::Model::QualifiedName).should == true
+      built.functor_expr.value.should == "foo"
+      built.rval_required.should == false
+      built.arguments.size.should == 2
+      built.arguments[0].is_a?(Puppet::Pops::Model::ArithmeticExpression).should == true
+      built.arguments[1].is_a?(Puppet::Pops::Model::ArithmeticExpression).should == true
+    end
+  end
+
+  context "When creating attribute operations" do
+    it "Should produce an attribute operation for =>" do
+      built = ATTRIBUTE_OP("aname", :'=>', 'x').current
+      built.is_a?(Puppet::Pops::Model::AttributeOperation)
+      built.operator.should == :'=>'
+      built.attribute_name.should == "aname"
+      built.value_expr.is_a?(Puppet::Pops::Model::LiteralString).should == true
+    end
+
+    it "Should produce an attribute operation for +>" do
+      built = ATTRIBUTE_OP("aname", :'+>', 'x').current
+      built.is_a?(Puppet::Pops::Model::AttributeOperation)
+      built.operator.should == :'+>'
+      built.attribute_name.should == "aname"
+      built.value_expr.is_a?(Puppet::Pops::Model::LiteralString).should == true
+    end
+  end
+
+  context "When processing RESOURCE" do
+    it "Should create a Resource body" do
+      built = RESOURCE_BODY("title", [ATTRIBUTE_OP('aname', :'=>', 'x')]).current
+      built.is_a?(Puppet::Pops::Model::ResourceBody).should == true
+      built.title.is_a?(Puppet::Pops::Model::LiteralString).should == true
+      built.operations.size.should == 1
+      built.operations[0].class.should == Puppet::Pops::Model::AttributeOperation
+      built.operations[0].attribute_name.should == 'aname'
+    end
+
+    it "Should create a RESOURCE without a resource body" do
+      bodies = []
+      built = RESOURCE("rtype", bodies).current
+      built.class.should == Puppet::Pops::Model::ResourceExpression
+      built.bodies.size.should == 0
+    end
+
+    it "Should create a RESOURCE with 1 resource body" do
+      bodies = [] << RESOURCE_BODY('title', [])
+      built = RESOURCE("rtype", bodies).current
+      built.class.should == Puppet::Pops::Model::ResourceExpression
+      built.bodies.size.should == 1
+      built.bodies[0].title.value.should == 'title'
+    end
+
+    it "Should create a RESOURCE with 2 resource bodies" do
+      bodies = [] << RESOURCE_BODY('title', []) << RESOURCE_BODY('title2', [])
+      built = RESOURCE("rtype", bodies).current
+      built.class.should == Puppet::Pops::Model::ResourceExpression
+      built.bodies.size.should == 2
+      built.bodies[0].title.value.should == 'title'
+      built.bodies[1].title.value.should == 'title2'
+    end
+  end
+
+  context "When processing simple literals" do
+    it "Should produce a literal boolean from a boolean" do
+      built = literal(true).current
+      built.class.should == Puppet::Pops::Model::LiteralBoolean
+      built.value.should == true
+      built = literal(false).current
+      built.class.should == Puppet::Pops::Model::LiteralBoolean
+      built.value.should == false
+    end
+  end
+
+  context "When processing COLLECT" do
+    it "should produce a virtual query" do
+      built = VIRTUAL_QUERY(fqn('a') == literal(1)).current
+      built.class.should == Puppet::Pops::Model::VirtualQuery
+      built.expr.class.should == Puppet::Pops::Model::ComparisonExpression
+      built.expr.operator.should ==  :'=='
+    end
+
+    it "should produce an export query" do
+      built = EXPORTED_QUERY(fqn('a') == literal(1)).current
+      built.class.should == Puppet::Pops::Model::ExportedQuery
+      built.expr.class.should == Puppet::Pops::Model::ComparisonExpression
+      built.expr.operator.should ==  :'=='
+    end
+
+    it "should produce a collect expression" do
+      q = VIRTUAL_QUERY(fqn('a') == literal(1))
+      built = COLLECT(literal('t'), q, [ATTRIBUTE_OP('name', :'=>', 3)]).current
+      built.class.should == Puppet::Pops::Model::CollectExpression
+      built.operations.size.should == 1
+    end
+
+    it "should produce a collect expression without attribute operations" do
+      q = VIRTUAL_QUERY(fqn('a') == literal(1))
+      built = COLLECT(literal('t'), q, []).current
+      built.class.should == Puppet::Pops::Model::CollectExpression
+      built.operations.size.should == 0
+    end
+  end
+
+  context "When processing concatenated string(iterpolation)" do
+    it "should handle 'just a string'" do
+      built = string('blah blah').current
+      built.class.should == Puppet::Pops::Model::ConcatenatedString
+      built.segments.size == 1
+      built.segments[0].class.should == Puppet::Pops::Model::LiteralString
+      built.segments[0].value.should == "blah blah"
+    end
+
+    it "should handle one expression in the middle" do
+      built = string('blah blah', TEXT(literal(1)+literal(2)), 'blah blah').current
+      built.class.should == Puppet::Pops::Model::ConcatenatedString
+      built.segments.size == 3
+      built.segments[0].class.should == Puppet::Pops::Model::LiteralString
+      built.segments[0].value.should == "blah blah"
+      built.segments[1].class.should == Puppet::Pops::Model::TextExpression
+      built.segments[1].expr.class.should == Puppet::Pops::Model::ArithmeticExpression
+      built.segments[2].class.should == Puppet::Pops::Model::LiteralString
+      built.segments[2].value.should == "blah blah"
+    end
+
+    it "should handle one expression at the end" do
+      built = string('blah blah', TEXT(literal(1)+literal(2))).current
+      built.class.should == Puppet::Pops::Model::ConcatenatedString
+      built.segments.size == 2
+      built.segments[0].class.should == Puppet::Pops::Model::LiteralString
+      built.segments[0].value.should == "blah blah"
+      built.segments[1].class.should == Puppet::Pops::Model::TextExpression
+      built.segments[1].expr.class.should == Puppet::Pops::Model::ArithmeticExpression
+    end
+
+    it "should handle only one expression" do
+      built = string(TEXT(literal(1)+literal(2))).current
+      built.class.should == Puppet::Pops::Model::ConcatenatedString
+      built.segments.size == 1
+      built.segments[0].class.should == Puppet::Pops::Model::TextExpression
+      built.segments[0].expr.class.should == Puppet::Pops::Model::ArithmeticExpression
+    end
+
+    it "should handle several expressions" do
+      built = string(TEXT(literal(1)+literal(2)), TEXT(literal(1)+literal(2))).current
+      built.class.should == Puppet::Pops::Model::ConcatenatedString
+      built.segments.size == 2
+      built.segments[0].class.should == Puppet::Pops::Model::TextExpression
+      built.segments[0].expr.class.should == Puppet::Pops::Model::ArithmeticExpression
+      built.segments[1].class.should == Puppet::Pops::Model::TextExpression
+      built.segments[1].expr.class.should == Puppet::Pops::Model::ArithmeticExpression
+    end
+
+    it "should handle no expression" do
+      built = string().current
+      built.class.should == Puppet::Pops::Model::ConcatenatedString
+      built.segments.size == 0
+    end
+  end
+
+  context "When processing instance / resource references" do
+    it "should produce an InstanceReference without a reference" do
+      built = INSTANCE(QREF('a'), []).current
+      built.class.should == Puppet::Pops::Model::InstanceReferences
+      built.names.size.should == 0
+    end
+
+    it "should produce an InstanceReference with one reference" do
+      built = INSTANCE(QREF('a'), [QNAME('b')]).current
+      built.class.should == Puppet::Pops::Model::InstanceReferences
+      built.names.size.should == 1
+      built.names[0].value.should == 'b'
+    end
+
+    it "should produce an InstanceReference with two references" do
+      built = INSTANCE(QREF('a'), [QNAME('b'), QNAME('c')]).current
+      built.class.should == Puppet::Pops::Model::InstanceReferences
+      built.names.size.should == 2
+      built.names[0].value.should == 'b'
+      built.names[1].value.should == 'c'
+    end
+  end
+
+  context "When processing UNLESS" do
+    it "should create an UNLESS expression with then part" do
+      built = UNLESS(true, literal(1), nil).current
+      built.class.should == Puppet::Pops::Model::UnlessExpression
+      built.test.class.should == Puppet::Pops::Model::LiteralBoolean
+      built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber
+      built.else_expr.class.should == Puppet::Pops::Model::Nop
+    end
+
+    it "should create an UNLESS expression with then and else parts" do
+      built = UNLESS(true, literal(1), literal(2)).current
+      built.class.should == Puppet::Pops::Model::UnlessExpression
+      built.test.class.should == Puppet::Pops::Model::LiteralBoolean
+      built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber
+      built.else_expr.class.should == Puppet::Pops::Model::LiteralNumber
+    end
+  end
+
+  context "When processing IF" do
+    it "should create an IF expression with then part" do
+      built = IF(true, literal(1), nil).current
+      built.class.should == Puppet::Pops::Model::IfExpression
+      built.test.class.should == Puppet::Pops::Model::LiteralBoolean
+      built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber
+      built.else_expr.class.should == Puppet::Pops::Model::Nop
+    end
+
+    it "should create an IF expression with then and else parts" do
+      built = IF(true, literal(1), literal(2)).current
+      built.class.should == Puppet::Pops::Model::IfExpression
+      built.test.class.should == Puppet::Pops::Model::LiteralBoolean
+      built.then_expr.class.should == Puppet::Pops::Model::LiteralNumber
+      built.else_expr.class.should == Puppet::Pops::Model::LiteralNumber
+    end
+  end
+
+  context "When processing a Parameter" do
+    it "should create a Parameter" do
+      # PARAM(name, expr)
+      # PARAM(name)
+      #
+    end
+  end
+
+  # LIST, HASH, KEY_ENTRY
+  context "When processing Definition" do
+    # DEFINITION(classname, arguments, statements)
+    # should accept empty arguments, and no statements
+  end
+
+  context "When processing Hostclass" do
+    # HOSTCLASS(classname, arguments, parent, statements)
+    # parent may be passed as a nop /nil - check this works, should accept empty statements (nil)
+    # should accept empty arguments
+
+  end
+
+  context "When processing Node" do
+  end
+
+  # Tested in the evaluator test already, but should be here to test factory assumptions
+  #
+  # TODO: CASE / WHEN
+  # TODO: MAP
+end
diff --git a/spec/unit/pops/issues_spec.rb b/spec/unit/pops/issues_spec.rb
new file mode 100644
index 0000000..091bd9c
--- /dev/null
+++ b/spec/unit/pops/issues_spec.rb
@@ -0,0 +1,26 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+describe "Puppet::Pops::Issues" do
+  include Puppet::Pops::Issues
+
+  it "should have an issue called NAME_WITH_HYPHEN" do
+    x = Puppet::Pops::Issues::NAME_WITH_HYPHEN
+    x.class.should == Puppet::Pops::Issues::Issue
+    x.issue_code.should == :NAME_WITH_HYPHEN
+  end
+
+  it "should should format a message that requires an argument" do
+    x = Puppet::Pops::Issues::NAME_WITH_HYPHEN
+    x.format(:name => 'Boo-Hoo',
+      :label => Puppet::Pops::Model::ModelLabelProvider.new,
+      :semantic => "dummy"
+      ).should == "A Ruby String may not have a name contain a hyphen. The name 'Boo-Hoo' is not legal"
+  end
+
+  it "should should format a message that does not require an argument" do
+    x = Puppet::Pops::Issues::NOT_TOP_LEVEL
+    x.format().should == "Classes, definitions, and nodes may only appear at toplevel or inside other classes"
+  end
+end
diff --git a/spec/unit/pops/label_provider_spec.rb b/spec/unit/pops/label_provider_spec.rb
new file mode 100644
index 0000000..3e82266
--- /dev/null
+++ b/spec/unit/pops/label_provider_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require 'puppet/pops'
+
+describe Puppet::Pops::LabelProvider do
+  let(:labeler) { Puppet::Pops::LabelProvider.new }
+
+  it "prefixes words that start with a vowel with an 'an'" do
+    labeler.a_an('owl').should == 'an owl'
+  end
+
+  it "prefixes words that start with a consonant with an 'a'" do
+    labeler.a_an('bear').should == 'a bear'
+  end
+
+  it "prefixes non-word characters with an 'a'" do
+    labeler.a_an('[] expression').should == 'a [] expression'
+  end
+
+  it "ignores a single quote leading the word" do
+    labeler.a_an("'owl'").should == "an 'owl'"
+  end
+
+  it "ignores a double quote leading the word" do
+    labeler.a_an('"owl"').should == 'an "owl"'
+  end
+
+  it "capitalizes the indefinite article for a word when requested" do
+    labeler.a_an_uc('owl').should == 'An owl'
+  end
+
+  it "raises an error when missing a character to work with" do
+    expect {
+      labeler.a_an('"')
+    }.to raise_error(Puppet::DevError, /<"> does not appear to contain a word/)
+  end
+
+  it "raises an error when given an empty string" do
+    expect {
+      labeler.a_an('')
+    }.to raise_error(Puppet::DevError, /<> does not appear to contain a word/)
+  end
+end
diff --git a/spec/unit/pops/model/ast_transformer_spec.rb b/spec/unit/pops/model/ast_transformer_spec.rb
new file mode 100644
index 0000000..f2b1c9b
--- /dev/null
+++ b/spec/unit/pops/model/ast_transformer_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+require File.join(File.dirname(__FILE__), '/../factory_rspec_helper')
+require 'puppet/pops'
+
+describe Puppet::Pops::Model::AstTransformer do
+  include FactoryRspecHelper
+
+  let(:filename) { "the-file.pp" }
+  let(:transformer) { Puppet::Pops::Model::AstTransformer.new(filename) }
+
+  context "literal numbers" do
+    it "converts a decimal number to a string Name" do
+      ast = transform(QNAME_OR_NUMBER("10"))
+
+      ast.should be_kind_of Puppet::Parser::AST::Name
+      ast.value.should == "10"
+    end
+
+    it "converts an octal number to a string Name" do
+      ast = transform(QNAME_OR_NUMBER("020"))
+
+      ast.should be_kind_of Puppet::Parser::AST::Name
+      ast.value.should == "020"
+    end
+
+    it "converts a hex number to a string Name" do
+      ast = transform(QNAME_OR_NUMBER("0x20"))
+
+      ast.should be_kind_of Puppet::Parser::AST::Name
+      ast.value.should == "0x20"
+    end
+
+    it "converts an unknown radix to an error string" do
+      ast = transform(Puppet::Pops::Model::Factory.new(Puppet::Pops::Model::LiteralNumber, 3, 2))
+
+      ast.should be_kind_of Puppet::Parser::AST::Name
+      ast.value.should == "bad radix:3"
+    end
+  end
+
+  it "preserves the file location" do
+    model = literal(1)
+    model.record_position(location(3, 1, 10), location(3, 2, 11))
+
+    ast = transform(model)
+
+    ast.file.should == filename
+    ast.line.should == 3
+    ast.pos.should == 1
+  end
+
+  def transform(model)
+    transformer.transform(model)
+  end
+
+  def location(line, column, offset)
+    position = Puppet::Pops::Adapters::SourcePosAdapter.new
+    position.line = line
+    position.pos = column
+    position.offset = offset
+
+    position
+  end
+end
diff --git a/spec/unit/pops/model/model_spec.rb b/spec/unit/pops/model/model_spec.rb
new file mode 100644
index 0000000..5014a64
--- /dev/null
+++ b/spec/unit/pops/model/model_spec.rb
@@ -0,0 +1,37 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+describe Puppet::Pops::Model do
+  it "should be possible to create an instance of a model object" do
+    nop = Puppet::Pops::Model::Nop.new
+    nop.class.should == Puppet::Pops::Model::Nop
+  end
+end
+
+describe Puppet::Pops::Model::Factory do
+  Factory = Puppet::Pops::Model::Factory
+  Model = Puppet::Pops::Model
+
+  it "construct an arithmetic expression" do
+    x = Factory.literal(10) + Factory.literal(20)
+    x.is_a?(Factory).should == true
+    current = x.current
+    current.is_a?(Model::ArithmeticExpression).should == true
+    current.operator.should == :'+'
+    current.left_expr.class.should == Model::LiteralNumber
+    current.right_expr.class.should == Model::LiteralNumber
+    current.left_expr.value.should == 10
+    current.right_expr.value.should == 20
+  end
+
+  it "should be easy to compare using a model tree dumper" do
+    x = Factory.literal(10) + Factory.literal(20)
+    Puppet::Pops::Model::ModelTreeDumper.new.dump(x.current).should == "(+ 10 20)"
+  end
+
+  it "builder should apply precedence" do
+    x = Factory.literal(2) * Factory.literal(10) + Factory.literal(20)
+    Puppet::Pops::Model::ModelTreeDumper.new.dump(x.current).should == "(+ (* 2 10) 20)"
+  end
+end
diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb
new file mode 100755
index 0000000..d5232e9
--- /dev/null
+++ b/spec/unit/pops/parser/lexer_spec.rb
@@ -0,0 +1,884 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+require 'puppet/pops'
+
+# This is a special matcher to match easily lexer output
+RSpec::Matchers.define :be_like do |*expected|
+  match do |actual|
+    diffable
+    expected.zip(actual).all? { |e,a| !e or a[0] == e or (e.is_a? Array and a[0] == e[0] and (a[1] == e[1] or (a[1].is_a?(Hash) and a[1][:value] == e[1]))) }
+  end
+end
+__ = nil
+
+module EgrammarLexerSpec
+  def self.tokens_scanned_from(s)
+    lexer = Puppet::Pops::Parser::Lexer.new
+    lexer.string = s
+    lexer.fullscan[0..-2]
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer do
+  include EgrammarLexerSpec
+
+  describe "when reading strings" do
+    before { @lexer = Puppet::Pops::Parser::Lexer.new }
+
+    it "should increment the line count for every carriage return in the string" do
+      @lexer.string = "'this\nis\natest'"
+      @lexer.fullscan[0..-2]
+
+      line = @lexer.line
+      line.should == 3
+    end
+
+    it "should not increment the line count for escapes in the string" do
+      @lexer.string = "'this\\nis\\natest'"
+      @lexer.fullscan[0..-2]
+
+      @lexer.line.should == 1
+    end
+
+    it "should not think the terminator is escaped, when preceeded by an even number of backslashes" do
+      @lexer.string = "'here\nis\nthe\nstring\\\\'with\nextra\njunk"
+      @lexer.fullscan[0..-2]
+
+      @lexer.line.should == 6
+    end
+
+    {
+      'r'  => "\r",
+      'n'  => "\n",
+      't'  => "\t",
+      's'  => " "
+    }.each do |esc, expected_result|
+      it "should recognize \\#{esc} sequence" do
+        @lexer.string = "\\#{esc}'"
+        @lexer.slurpstring("'")[0].should == expected_result
+      end
+    end
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::Token, "when initializing" do
+  it "should create a regex if the first argument is a string" do
+    Puppet::Pops::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something}
+  end
+
+  it "should set the string if the first argument is one" do
+    Puppet::Pops::Parser::Lexer::Token.new("something", :NAME).string.should == "something"
+  end
+
+  it "should set the regex if the first argument is one" do
+    Puppet::Pops::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something}
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TokenList do
+  before do
+    @list = Puppet::Pops::Parser::Lexer::TokenList.new
+  end
+
+  it "should have a method for retrieving tokens by the name" do
+    token = @list.add_token :name, "whatever"
+    @list[:name].should equal(token)
+  end
+
+  it "should have a method for retrieving string tokens by the string" do
+    token = @list.add_token :name, "whatever"
+    @list.lookup("whatever").should equal(token)
+  end
+
+  it "should add tokens to the list when directed" do
+    token = @list.add_token :name, "whatever"
+    @list[:name].should equal(token)
+  end
+
+  it "should have a method for adding multiple tokens at once" do
+    @list.add_tokens "whatever" => :name, "foo" => :bar
+    @list[:name].should_not be_nil
+    @list[:bar].should_not be_nil
+  end
+
+  it "should fail to add tokens sharing a name with an existing token" do
+    @list.add_token :name, "whatever"
+    expect { @list.add_token :name, "whatever" }.to raise_error(ArgumentError)
+  end
+
+  it "should set provided options on tokens being added" do
+    token = @list.add_token :name, "whatever", :skip_text => true
+    token.skip_text.should == true
+  end
+
+  it "should define any provided blocks as a :convert method" do
+    token = @list.add_token(:name, "whatever")  do "foo" end
+    token.convert.should == "foo"
+  end
+
+  it "should store all string tokens in the :string_tokens list" do
+    one = @list.add_token(:name, "1")
+    @list.string_tokens.should be_include(one)
+  end
+
+  it "should store all regex tokens in the :regex_tokens list" do
+    one = @list.add_token(:name, %r{one})
+    @list.regex_tokens.should be_include(one)
+  end
+
+  it "should not store string tokens in the :regex_tokens list" do
+    one = @list.add_token(:name, "1")
+    @list.regex_tokens.should_not be_include(one)
+  end
+
+  it "should not store regex tokens in the :string_tokens list" do
+    one = @list.add_token(:name, %r{one})
+    @list.string_tokens.should_not be_include(one)
+  end
+
+  it "should sort the string tokens inversely by length when asked" do
+    one = @list.add_token(:name, "1")
+    two = @list.add_token(:other, "12")
+    @list.sort_tokens
+    @list.string_tokens.should == [two, one]
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS do
+  before do
+    @lexer = Puppet::Pops::Parser::Lexer.new
+  end
+
+  {
+    :LBRACK => '[',
+    :RBRACK => ']',
+#    :LBRACE => '{',
+    :RBRACE => '}',
+    :LPAREN => '(',
+    :RPAREN => ')',
+    :EQUALS => '=',
+    :ISEQUAL => '==',
+    :GREATEREQUAL => '>=',
+    :GREATERTHAN => '>',
+    :LESSTHAN => '<',
+    :LESSEQUAL => '<=',
+    :NOTEQUAL => '!=',
+    :NOT => '!',
+    :COMMA => ',',
+    :DOT => '.',
+    :COLON => ':',
+    :AT => '@',
+    :LLCOLLECT => '<<|',
+    :RRCOLLECT => '|>>',
+    :LCOLLECT => '<|',
+    :RCOLLECT => '|>',
+    :SEMIC => ';',
+    :QMARK => '?',
+    :BACKSLASH => '\\',
+    :FARROW => '=>',
+    :PARROW => '+>',
+    :APPENDS => '+=',
+    :PLUS => '+',
+    :MINUS => '-',
+    :DIV => '/',
+    :TIMES => '*',
+    :LSHIFT => '<<',
+    :RSHIFT => '>>',
+    :MATCH => '=~',
+    :NOMATCH => '!~',
+    :IN_EDGE => '->',
+    :OUT_EDGE => '<-',
+    :IN_EDGE_SUB => '~>',
+    :OUT_EDGE_SUB => '<~',
+    :PIPE => '|',
+  }.each do |name, string|
+    it "should have a token named #{name.to_s}" do
+      Puppet::Pops::Parser::Lexer::TOKENS[name].should_not be_nil
+    end
+
+    it "should match '#{string}' for the token #{name.to_s}" do
+      Puppet::Pops::Parser::Lexer::TOKENS[name].string.should == string
+    end
+  end
+
+  {
+    "case" => :CASE,
+    "class" => :CLASS,
+    "default" => :DEFAULT,
+    "define" => :DEFINE,
+#    "import" => :IMPORT, # done as a function in egrammar
+    "if" => :IF,
+    "elsif" => :ELSIF,
+    "else" => :ELSE,
+    "inherits" => :INHERITS,
+    "node" => :NODE,
+    "and"  => :AND,
+    "or"   => :OR,
+    "undef"   => :UNDEF,
+    "false" => :FALSE,
+    "true" => :TRUE,
+    "in" => :IN,
+    "unless" => :UNLESS,
+  }.each do |string, name|
+    it "should have a keyword named #{name.to_s}" do
+      Puppet::Pops::Parser::Lexer::KEYWORDS[name].should_not be_nil
+    end
+
+    it "should have the keyword for #{name.to_s} set to #{string}" do
+      Puppet::Pops::Parser::Lexer::KEYWORDS[name].string.should == string
+    end
+  end
+
+  # These tokens' strings don't matter, just that the tokens exist.
+  [:STRING, :DQPRE, :DQMID, :DQPOST, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :MLCOMMENT,
+    :LBRACE, :LAMBDA,
+    :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name|
+    it "should have a token named #{name.to_s}" do
+      Puppet::Pops::Parser::Lexer::TOKENS[name].should_not be_nil
+    end
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:CLASSREF] do
+  before { @token = Puppet::Pops::Parser::Lexer::TOKENS[:CLASSREF] }
+
+  it "should match against single upper-case alpha-numeric terms" do
+    @token.regex.should =~ "One"
+  end
+
+  it "should match against upper-case alpha-numeric terms separated by double colons" do
+    @token.regex.should =~ "One::Two"
+  end
+
+  it "should match against many upper-case alpha-numeric terms separated by double colons" do
+    @token.regex.should =~ "One::Two::Three::Four::Five"
+  end
+
+  it "should match against upper-case alpha-numeric terms prefixed by double colons" do
+    @token.regex.should =~ "::One"
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:NAME] do
+  before { @token = Puppet::Pops::Parser::Lexer::TOKENS[:NAME] }
+
+  it "should match against lower-case alpha-numeric terms" do
+    @token.regex.should =~ "one-two"
+  end
+
+  it "should return itself and the value if the matched term is not a keyword" do
+    Puppet::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil)
+    @token.convert(stub("lexer"), "myval").should == [Puppet::Pops::Parser::Lexer::TOKENS[:NAME], "myval"]
+  end
+
+  it "should return the keyword token and the value if the matched term is a keyword" do
+    keyword = stub 'keyword', :name => :testing
+    Puppet::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword)
+    @token.convert(stub("lexer"), "myval").should == [keyword, "myval"]
+  end
+
+  it "should return the BOOLEAN token and 'true' if the matched term is the string 'true'" do
+    keyword = stub 'keyword', :name => :TRUE
+    Puppet::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword)
+    @token.convert(stub('lexer'), "true").should == [Puppet::Pops::Parser::Lexer::TOKENS[:BOOLEAN], true]
+  end
+
+  it "should return the BOOLEAN token and 'false' if the matched term is the string 'false'" do
+    keyword = stub 'keyword', :name => :FALSE
+    Puppet::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword)
+    @token.convert(stub('lexer'), "false").should == [Puppet::Pops::Parser::Lexer::TOKENS[:BOOLEAN], false]
+  end
+
+  it "should match against lower-case alpha-numeric terms separated by double colons" do
+    @token.regex.should =~ "one::two"
+  end
+
+  it "should match against many lower-case alpha-numeric terms separated by double colons" do
+    @token.regex.should =~ "one::two::three::four::five"
+  end
+
+  it "should match against lower-case alpha-numeric terms prefixed by double colons" do
+    @token.regex.should =~ "::one"
+  end
+
+  it "should match against nested terms starting with numbers" do
+    @token.regex.should =~ "::1one::2two::3three"
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:NUMBER] do
+  before do
+    @token = Puppet::Pops::Parser::Lexer::TOKENS[:NUMBER]
+    @regex = @token.regex
+  end
+
+  it "should match against numeric terms" do
+    @regex.should =~ "2982383139"
+  end
+
+  it "should match against float terms" do
+    @regex.should =~ "29823.235"
+  end
+
+  it "should match against hexadecimal terms" do
+    @regex.should =~ "0xBEEF0023"
+  end
+
+  it "should match against float with exponent terms" do
+    @regex.should =~ "10e23"
+  end
+
+  it "should match against float terms with negative exponents" do
+    @regex.should =~ "10e-23"
+  end
+
+  it "should match against float terms with fractional parts and exponent" do
+    @regex.should =~ "1.234e23"
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:COMMENT] do
+  before { @token = Puppet::Pops::Parser::Lexer::TOKENS[:COMMENT] }
+
+  it "should match against lines starting with '#'" do
+    @token.regex.should =~ "# this is a comment"
+  end
+
+  it "should be marked to get skipped" do
+    @token.skip?.should be_true
+  end
+
+  it "'s block should return the comment without the #" do
+    @token.convert(@lexer,"# this is a comment")[1].should == "this is a comment"
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:MLCOMMENT] do
+  before do
+    @token = Puppet::Pops::Parser::Lexer::TOKENS[:MLCOMMENT]
+    @lexer = stub 'lexer', :line => 0
+  end
+
+  it "should match against lines enclosed with '/*' and '*/'" do
+    @token.regex.should =~ "/* this is a comment */"
+  end
+
+  it "should match multiple lines enclosed with '/*' and '*/'" do
+    @token.regex.should =~ """/*
+      this is a comment
+      */"""
+  end
+
+#  # TODO: REWRITE THIS TEST TO NOT BE BASED ON INTERNALS
+#  it "should increase the lexer current line number by the amount of lines spanned by the comment" do
+#    @lexer.expects(:line=).with(2)
+#    @token.convert(@lexer, "1\n2\n3")
+#  end
+
+  it "should not greedily match comments" do
+    match = @token.regex.match("/* first */ word /* second */")
+    match[1].should == " first "
+  end
+
+  it "'s block should return the comment without the comment marks" do
+    @lexer.stubs(:line=).with(0)
+
+    @token.convert(@lexer,"/* this is a comment */")[1].should == "this is a comment"
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:RETURN] do
+  before { @token = Puppet::Pops::Parser::Lexer::TOKENS[:RETURN] }
+
+  it "should match against carriage returns" do
+    @token.regex.should =~ "\n"
+  end
+
+  it "should be marked to initiate text skipping" do
+    @token.skip_text.should be_true
+  end
+end
+
+shared_examples_for "handling `-` in standard variable names for egrammar" do |prefix|
+  # Watch out - a regex might match a *prefix* on these, not just the whole
+  # word, so make sure you don't have false positive or negative results based
+  # on that.
+  legal   = %w{f foo f::b foo::b f::bar foo::bar 3 foo3 3foo}
+  illegal = %w{f- f-o -f f::-o f::o- f::o-o}
+
+  ["", "::"].each do |global_scope|
+    legal.each do |name|
+      var = prefix + global_scope + name
+      it "should accept #{var.inspect} as a valid variable name" do
+        (subject.regex.match(var) || [])[0].should == var
+      end
+    end
+
+    illegal.each do |name|
+      var = prefix + global_scope + name
+      it "when `variable_with_dash` is disabled it should NOT accept #{var.inspect} as a valid variable name" do
+        Puppet[:allow_variables_with_dashes] = false
+        (subject.regex.match(var) || [])[0].should_not == var
+      end
+
+      it "when `variable_with_dash` is enabled it should NOT accept #{var.inspect} as a valid variable name" do
+        Puppet[:allow_variables_with_dashes] = true
+        (subject.regex.match(var) || [])[0].should_not == var
+      end
+    end
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR] do
+  its(:skip_text) { should be_false }
+
+  it_should_behave_like "handling `-` in standard variable names for egrammar", '$'
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:VARIABLE] do
+  its(:skip_text) { should be_false }
+
+  it_should_behave_like "handling `-` in standard variable names for egrammar", ''
+end
+
+describe "the horrible deprecation / compatibility variables with dashes" do
+  ENamesWithDashes = %w{f- f-o -f f::-o f::o- f::o-o}
+
+  { Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR_WITH_DASH] => '$',
+    Puppet::Pops::Parser::Lexer::TOKENS[:VARIABLE_WITH_DASH]   => ''
+  }.each do |token, prefix|
+    describe token do
+      its(:skip_text) { should be_false }
+
+      context "when compatibly is disabled" do
+        before :each do Puppet[:allow_variables_with_dashes] = false end
+        Puppet::Pops::Parser::Lexer::TOKENS.each do |name, value|
+          it "should be unacceptable after #{name}" do
+            token.acceptable?(:after => name).should be_false
+          end
+        end
+
+        # Yes, this should still *match*, just not be acceptable.
+        ENamesWithDashes.each do |name|
+          ["", "::"].each do |global_scope|
+            var = prefix + global_scope + name
+            it "should match #{var.inspect}" do
+              subject.regex.match(var).to_a.should == [var]
+            end
+          end
+        end
+      end
+
+      context "when compatibility is enabled" do
+        before :each do Puppet[:allow_variables_with_dashes] = true end
+
+        it "should be acceptable after DQPRE" do
+          token.acceptable?(:after => :DQPRE).should be_true
+        end
+
+        ENamesWithDashes.each do |name|
+          ["", "::"].each do |global_scope|
+            var = prefix + global_scope + name
+            it "should match #{var.inspect}" do
+              subject.regex.match(var).to_a.should == [var]
+            end
+          end
+        end
+      end
+    end
+  end
+
+  context "deprecation warnings" do
+    before :each do Puppet[:allow_variables_with_dashes] = true end
+
+    it "should match a top level variable" do
+      Puppet.expects(:deprecation_warning).once
+
+      EgrammarLexerSpec.tokens_scanned_from('$foo-bar').should == [
+        [:VARIABLE, {:value=>"foo-bar", :line=>1, :pos=>1, :offset=>0, :length=>8}]
+      ]
+    end
+
+    it "does not warn about a variable without a dash" do
+      Puppet.expects(:deprecation_warning).never
+
+      EgrammarLexerSpec.tokens_scanned_from('$c').should == [
+        [:VARIABLE, {:value=>"c", :line=>1, :pos=>1, :offset=>0, :length=>2}]
+      ]
+    end
+
+    it "does not warn about referencing a class name that contains a dash" do
+      Puppet.expects(:deprecation_warning).never
+
+      EgrammarLexerSpec.tokens_scanned_from('foo-bar').should == [
+        [:NAME, {:value=>"foo-bar", :line=>1, :pos=>1, :offset=>0, :length=>7}]
+      ]
+    end
+
+    it "warns about reference to variable" do
+      Puppet.expects(:deprecation_warning).once
+
+      EgrammarLexerSpec.tokens_scanned_from('$::foo-bar::baz-quux').should == [
+        [:VARIABLE, {:value=>"::foo-bar::baz-quux", :line=>1, :pos=>1, :offset=>0, :length=>20}]
+      ]
+    end
+
+    it "warns about reference to variable interpolated in a string" do
+      Puppet.expects(:deprecation_warning).once
+
+      EgrammarLexerSpec.tokens_scanned_from('"$::foo-bar::baz-quux"').should == [
+        [:DQPRE,    {:value=>"", :line=>1, :pos=>1, :offset=>0, :length=>2}],  # length since preamble includes start and terminator
+        [:VARIABLE, {:value=>"::foo-bar::baz-quux", :line=>1, :pos=>3, :offset=>2, :length=>19}],
+        [:DQPOST,   {:value=>"", :line=>1, :pos=>22, :offset=>21, :length=>1}],
+      ]
+    end
+
+    it "warns about reference to variable interpolated in a string as an expression" do
+      Puppet.expects(:deprecation_warning).once
+
+      EgrammarLexerSpec.tokens_scanned_from('"${::foo-bar::baz-quux}"').should == [
+        [:DQPRE,    {:value=>"", :line=>1, :pos=>1, :offset=>0, :length=>3}],
+        [:VARIABLE, {:value=>"::foo-bar::baz-quux", :line=>1, :pos=>4, :offset=>3, :length=>19}],
+        [:DQPOST,   {:value=>"", :line=>1, :pos=>23, :offset=>22, :length=>2}],
+      ]
+    end
+  end
+end
+
+
+describe Puppet::Pops::Parser::Lexer,"when lexing strings" do
+  {
+    %q{'single quoted string')}                                     => [[:STRING,'single quoted string']],
+    %q{"double quoted string"}                                      => [[:STRING,'double quoted string']],
+    %q{'single quoted string with an escaped "\\'"'}                => [[:STRING,'single quoted string with an escaped "\'"']],
+    %q{'single quoted string with an escaped "\$"'}                 => [[:STRING,'single quoted string with an escaped "\$"']],
+    %q{'single quoted string with an escaped "\."'}                 => [[:STRING,'single quoted string with an escaped "\."']],
+    %q{'single quoted string with an escaped "\r\n"'}               => [[:STRING,'single quoted string with an escaped "\r\n"']],
+    %q{'single quoted string with an escaped "\n"'}                 => [[:STRING,'single quoted string with an escaped "\n"']],
+    %q{'single quoted string with an escaped "\\\\"'}               => [[:STRING,'single quoted string with an escaped "\\\\"']],
+    %q{"string with an escaped '\\"'"}                              => [[:STRING,"string with an escaped '\"'"]],
+    %q{"string with an escaped '\\$'"}                              => [[:STRING,"string with an escaped '$'"]],
+    %Q{"string with a line ending with a backslash: \\\nfoo"}       => [[:STRING,"string with a line ending with a backslash: foo"]],
+    %q{"string with $v (but no braces)"}                            => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' (but no braces)']],
+    %q["string with ${v} in braces"]                                => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' in braces']],
+    %q["string with ${qualified::var} in braces"]                   => [[:DQPRE,"string with "],[:VARIABLE,'qualified::var'],[:DQPOST,' in braces']],
+    %q{"string with $v and $v (but no braces)"}                     => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," (but no braces)"]],
+    %q["string with ${v} and ${v} in braces"]                       => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," in braces"]],
+    %q["string with ${'a nested single quoted string'} inside it."] => [[:DQPRE,"string with "],[:STRING,'a nested single quoted string'],[:DQPOST,' inside it.']],
+    %q["string with ${['an array ',$v2]} in it."]                   => [[:DQPRE,"string with "],:LBRACK,[:STRING,"an array "],:COMMA,[:VARIABLE,"v2"],:RBRACK,[:DQPOST," in it."]],
+    %q{a simple "scanner" test}                                     => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"scanner"],[:NAME,"test"]],
+    %q{a simple 'single quote scanner' test}                        => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"single quote scanner"],[:NAME,"test"]],
+    %q{a harder 'a $b \c"'}                                         => [[:NAME,"a"],[:NAME,"harder"], [:STRING,'a $b \c"']],
+    %q{a harder "scanner test"}                                     => [[:NAME,"a"],[:NAME,"harder"], [:STRING,"scanner test"]],
+    %q{a hardest "scanner \"test\""}                                => [[:NAME,"a"],[:NAME,"hardest"],[:STRING,'scanner "test"']],
+    %Q{a hardestest "scanner \\"test\\"\n"}                         => [[:NAME,"a"],[:NAME,"hardestest"],[:STRING,%Q{scanner "test"\n}]],
+    %q{function("call")}                                            => [[:NAME,"function"],[:LPAREN,"("],[:STRING,'call'],[:RPAREN,")"]],
+    %q["string with ${(3+5)/4} nested math."]                       => [[:DQPRE,"string with "],:LPAREN,[:NAME,"3"],:PLUS,[:NAME,"5"],:RPAREN,:DIV,[:NAME,"4"],[:DQPOST," nested math."]],
+    %q["$$$$"]                                                      => [[:STRING,"$$$$"]],
+    %q["$variable"]                                                 => [[:DQPRE,""],[:VARIABLE,"variable"],[:DQPOST,""]],
+    %q["$var$other"]                                                => [[:DQPRE,""],[:VARIABLE,"var"],[:DQMID,""],[:VARIABLE,"other"],[:DQPOST,""]],
+    %q["foo$bar$"]                                                  => [[:DQPRE,"foo"],[:VARIABLE,"bar"],[:DQPOST,"$"]],
+    %q["foo$$bar"]                                                  => [[:DQPRE,"foo$"],[:VARIABLE,"bar"],[:DQPOST,""]],
+    %q[""]                                                          => [[:STRING,""]],
+    %q["123 456 789 0"]                                             => [[:STRING,"123 456 789 0"]],
+    %q["${123} 456 $0"]                                             => [[:DQPRE,""],[:VARIABLE,"123"],[:DQMID," 456 "],[:VARIABLE,"0"],[:DQPOST,""]],
+    %q["$foo::::bar"]                                               => [[:DQPRE,""],[:VARIABLE,"foo"],[:DQPOST,"::::bar"]]
+  }.each { |src,expected_result|
+    it "should handle #{src} correctly" do
+      EgrammarLexerSpec.tokens_scanned_from(src).should be_like(*expected_result)
+    end
+  }
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR] do
+  before { @token = Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR] }
+
+  it "should match against alpha words prefixed with '$'" do
+    @token.regex.should =~ '$this_var'
+  end
+
+  it "should return the VARIABLE token and the variable name stripped of the '$'" do
+    @token.convert(stub("lexer"), "$myval").should == [Puppet::Pops::Parser::Lexer::TOKENS[:VARIABLE], "myval"]
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer::TOKENS[:REGEX] do
+  before { @token = Puppet::Pops::Parser::Lexer::TOKENS[:REGEX] }
+
+  it "should match against any expression enclosed in //" do
+    @token.regex.should =~ '/this is a regex/'
+  end
+
+  it 'should not match if there is \n in the regex' do
+    @token.regex.should_not =~ "/this is \n a regex/"
+  end
+
+  describe "when scanning" do
+    it "should not consider escaped slashes to be the end of a regex" do
+      EgrammarLexerSpec.tokens_scanned_from("$x =~ /this \\/ foo/").should be_like(__,__,[:REGEX,%r{this / foo}])
+    end
+
+    it "should not lex chained division as a regex" do
+      EgrammarLexerSpec.tokens_scanned_from("$x = $a/$b/$c").collect { |name, data| name }.should_not be_include( :REGEX )
+    end
+
+    it "should accept a regular expression after NODE" do
+      EgrammarLexerSpec.tokens_scanned_from("node /www.*\.mysite\.org/").should be_like(__,[:REGEX,Regexp.new("www.*\.mysite\.org")])
+    end
+
+    it "should accept regular expressions in a CASE" do
+      s = %q{case $variable {
+        "something": {$othervar = 4096 / 2}
+        /regex/: {notice("this notably sucks")}
+        }
+      }
+      EgrammarLexerSpec.tokens_scanned_from(s).should be_like(
+        :CASE,:VARIABLE,:LBRACE,:STRING,:COLON,:LBRACE,:VARIABLE,:EQUALS,:NAME,:DIV,:NAME,:RBRACE,[:REGEX,/regex/],:COLON,:LBRACE,:NAME,:LPAREN,:STRING,:RPAREN,:RBRACE,:RBRACE
+      )
+    end
+  end
+
+  it "should return the REGEX token and a Regexp" do
+    @token.convert(stub("lexer"), "/myregex/").should == [Puppet::Pops::Parser::Lexer::TOKENS[:REGEX], Regexp.new(/myregex/)]
+  end
+end
+
+describe Puppet::Pops::Parser::Lexer, "when lexing comments" do
+  before { @lexer = Puppet::Pops::Parser::Lexer.new }
+
+  it "should skip whitespace before lexing the next token after a non-token" do
+    EgrammarLexerSpec.tokens_scanned_from("/* 1\n\n */ \ntest").should be_like([:NAME, "test"])
+  end
+end
+
+# FIXME: We need to rewrite all of these tests, but I just don't want to take the time right now.
+describe "Puppet::Pops::Parser::Lexer in the old tests" do
+  before { @lexer = Puppet::Pops::Parser::Lexer.new }
+
+  it "should do simple lexing" do
+    {
+      %q{\\}                      => [[:BACKSLASH,"\\"]],
+      %q{simplest scanner test}   => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"]],
+      %Q{returned scanner test\n} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"]]
+    }.each { |source,expected|
+      EgrammarLexerSpec.tokens_scanned_from(source).should be_like(*expected)
+    }
+  end
+
+  it "should fail usefully" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('^') }.to raise_error(RuntimeError)
+  end
+
+  it "should fail if the string is not set" do
+    expect { @lexer.fullscan }.to raise_error(Puppet::LexError)
+  end
+
+  it "should correctly identify keywords" do
+    EgrammarLexerSpec.tokens_scanned_from("case").should be_like([:CASE, "case"])
+  end
+
+  it "should correctly parse class references" do
+    %w{Many Different Words A Word}.each { |t| EgrammarLexerSpec.tokens_scanned_from(t).should be_like([:CLASSREF,t])}
+  end
+
+  # #774
+  it "should correctly parse namespaced class refernces token" do
+    %w{Foo ::Foo Foo::Bar ::Foo::Bar}.each { |t| EgrammarLexerSpec.tokens_scanned_from(t).should be_like([:CLASSREF, t]) }
+  end
+
+  it "should correctly parse names" do
+    %w{this is a bunch of names}.each { |t| EgrammarLexerSpec.tokens_scanned_from(t).should be_like([:NAME,t]) }
+  end
+
+  it "should correctly parse names with numerals" do
+    %w{1name name1 11names names11}.each { |t| EgrammarLexerSpec.tokens_scanned_from(t).should be_like([:NAME,t]) }
+  end
+
+  it "should correctly parse empty strings" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = ""') }.to_not raise_error
+  end
+
+  it "should correctly parse virtual resources" do
+    EgrammarLexerSpec.tokens_scanned_from("@type {").should be_like([:AT, "@"], [:NAME, "type"], [:LBRACE, "{"])
+  end
+
+  it "should correctly deal with namespaces" do
+    @lexer.string = %{class myclass}
+    @lexer.fullscan
+    @lexer.namespace.should == "myclass"
+
+    @lexer.namepop
+    @lexer.namespace.should == ""
+
+    @lexer.string = "class base { class sub { class more"
+    @lexer.fullscan
+    @lexer.namespace.should == "base::sub::more"
+
+    @lexer.namepop
+    @lexer.namespace.should == "base::sub"
+  end
+
+  it "should not put class instantiation on the namespace" do
+    @lexer.string = "class base { class sub { class { mode"
+    @lexer.fullscan
+    @lexer.namespace.should == "base::sub"
+  end
+
+  it "should correctly handle fully qualified names" do
+    @lexer.string = "class base { class sub::more {"
+    @lexer.fullscan
+    @lexer.namespace.should == "base::sub::more"
+
+    @lexer.namepop
+    @lexer.namespace.should == "base"
+  end
+
+  it "should correctly lex variables" do
+    ["$variable", "$::variable", "$qualified::variable", "$further::qualified::variable"].each do |string|
+      EgrammarLexerSpec.tokens_scanned_from(string).should be_like([:VARIABLE,string.sub(/^\$/,'')])
+    end
+  end
+
+  it "should end variables at `-`" do
+    EgrammarLexerSpec.tokens_scanned_from('$hyphenated-variable').
+      should be_like [:VARIABLE, "hyphenated"], [:MINUS, '-'], [:NAME, 'variable']
+  end
+
+  it "should not include whitespace in a variable" do
+    EgrammarLexerSpec.tokens_scanned_from("$foo bar").should_not be_like([:VARIABLE, "foo bar"])
+  end
+  it "should not include excess colons in a variable" do
+    EgrammarLexerSpec.tokens_scanned_from("$foo::::bar").should_not be_like([:VARIABLE, "foo::::bar"])
+  end
+end
+
+describe "Puppet::Pops::Parser::Lexer in the old tests when lexing example files" do
+  my_fixtures('*.pp') do |file|
+    it "should correctly lex #{file}" do
+      lexer = Puppet::Pops::Parser::Lexer.new
+      lexer.file = file
+      expect { lexer.fullscan }.to_not raise_error
+    end
+  end
+end
+
+describe "when trying to lex an non-existent file" do
+  include PuppetSpec::Files
+
+  it "should return an empty list of tokens" do
+    lexer = Puppet::Pops::Parser::Lexer.new
+    lexer.file = nofile = tmpfile('lexer')
+    File.exists?(nofile).should == false
+
+    lexer.fullscan.should == [[false,false]]
+  end
+end
+
+describe "when string quotes are not closed" do
+  it "should report with message including an \" opening quote" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = "') }.to raise_error(/after '"'/)
+  end
+
+  it "should report with message including an \' opening quote" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = \'') }.to raise_error(/after "'"/)
+  end
+
+  it "should report <eof> if immediately followed by eof" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = "') }.to raise_error(/followed by '<eof>'/)
+  end
+
+  it "should report max 5 chars following quote" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = "123456') }.to raise_error(/followed by '12345...'/)
+  end
+
+  it "should escape control chars" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = "12\n3456') }.to raise_error(/followed by '12\\n3...'/)
+  end
+
+  it "should resport position of opening quote" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = "123456') }.to raise_error(/at line 1:8/)
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var =  "123456') }.to raise_error(/at line 1:9/)
+  end
+end
+
+describe "when lexing number, bad input should not go unpunished" do
+  it "should slap bad octal as such" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = 0778') }.to raise_error(/Not a valid octal/)
+  end
+
+  it "should slap bad hex as such" do
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = 0xFG') }.to raise_error(/Not a valid hex/)
+    expect { EgrammarLexerSpec.tokens_scanned_from('$var = 0xfg') }.to raise_error(/Not a valid hex/)
+  end
+  # Note, bad decimals are probably impossible to enter, as they are not recognized as complete numbers, instead,
+  # the error will be something else, depending on what follows some initial digit.
+  #
+end
+
+describe "when lexing interpolation detailed positioning should be correct" do
+  it "should correctly position a string without interpolation" do
+    EgrammarLexerSpec.tokens_scanned_from('"not interpolated"').should be_like(
+      [:STRING, {:value=>"not interpolated", :line=>1, :offset=>0, :pos=>1, :length=>18}])
+  end
+
+  it "should correctly position a string with false start in interpolation" do
+    EgrammarLexerSpec.tokens_scanned_from('"not $$$ rpolated"').should be_like(
+      [:STRING, {:value=>"not $$$ rpolated", :line=>1, :offset=>0, :pos=>1, :length=>18}])
+  end
+
+  it "should correctly position pre-mid-end interpolation " do
+    EgrammarLexerSpec.tokens_scanned_from('"pre $x mid $y end"').should be_like(
+      [:DQPRE,    {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>6}],
+      [:VARIABLE, {:value=>"x", :line=>1, :offset=>6, :pos=>7, :length=>1}],
+      [:DQMID,    {:value=>" mid ", :line=>1, :offset=>7, :pos=>8, :length=>6}],
+      [:VARIABLE, {:value=>"y", :line=>1, :offset=>13, :pos=>14, :length=>1}],
+      [:DQPOST,   {:value=>" end", :line=>1, :offset=>14, :pos=>15, :length=>5}]
+    )
+  end
+
+  it "should correctly position pre-mid-end interpolation using ${} " do
+    EgrammarLexerSpec.tokens_scanned_from('"pre ${x} mid ${y} end"').should be_like(
+      [:DQPRE,    {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
+      [:VARIABLE, {:value=>"x", :line=>1, :offset=>7, :pos=>8, :length=>1}],
+      [:DQMID,    {:value=>" mid ", :line=>1, :offset=>8, :pos=>9, :length=>8}],
+      [:VARIABLE, {:value=>"y", :line=>1, :offset=>16, :pos=>17, :length=>1}],
+      [:DQPOST,   {:value=>" end", :line=>1, :offset=>17, :pos=>18, :length=>6}]
+    )
+  end
+
+  it "should correctly position pre-end interpolation using ${} with f call" do
+    EgrammarLexerSpec.tokens_scanned_from('"pre ${x()} end"').should be_like(
+      [:DQPRE,    {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
+      [:NAME,     {:value=>"x",    :line=>1, :offset=>7, :pos=>8, :length=>1}],
+      [:LPAREN,   {:value=>"(",    :line=>1, :offset=>8, :pos=>9, :length=>1}],
+      [:RPAREN,   {:value=>")",    :line=>1, :offset=>9, :pos=>10, :length=>1}],
+      [:DQPOST,   {:value=>" end", :line=>1, :offset=>10, :pos=>11, :length=>6}]
+    )
+  end
+
+  it "should correctly position pre-end interpolation using ${} with $x" do
+    EgrammarLexerSpec.tokens_scanned_from('"pre ${$x} end"').should be_like(
+      [:DQPRE,    {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
+      [:VARIABLE, {:value=>"x",    :line=>1, :offset=>7, :pos=>8, :length=>2}],
+      [:DQPOST,   {:value=>" end", :line=>1, :offset=>9, :pos=>10, :length=>6}]
+    )
+  end
+
+  it "should correctly position pre-end interpolation across lines" do
+    EgrammarLexerSpec.tokens_scanned_from(%Q["pre ${\n$x} end"]).should be_like(
+      [:DQPRE,    {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
+      [:VARIABLE, {:value=>"x",    :line=>2, :offset=>8, :pos=>1, :length=>2}],
+      [:DQPOST,   {:value=>" end", :line=>2, :offset=>10, :pos=>3, :length=>6}]
+    )
+  end
+
+  it "should correctly position interpolation across lines when strings have embedded newlines" do
+    EgrammarLexerSpec.tokens_scanned_from(%Q["pre \n\n${$x}\n mid$y"]).should be_like(
+      [:DQPRE,    {:value=>"pre \n\n", :line=>1, :offset=>0, :pos=>1, :length=>9}],
+      [:VARIABLE, {:value=>"x",    :line=>3, :offset=>9, :pos=>3, :length=>2}],
+      [:DQMID,   {:value=>"\n mid", :line=>3, :offset=>11, :pos=>5, :length=>7}],
+      [:VARIABLE, {:value=>"y",    :line=>4, :offset=>18, :pos=>6, :length=>1}]
+    )
+  end
+end
diff --git a/spec/unit/pops/parser/parse_basic_expressions_spec.rb b/spec/unit/pops/parser/parse_basic_expressions_spec.rb
new file mode 100644
index 0000000..6560e62
--- /dev/null
+++ b/spec/unit/pops/parser/parse_basic_expressions_spec.rb
@@ -0,0 +1,248 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/parser_rspec_helper')
+
+describe "egrammar parsing basic expressions" do
+  include ParserRspecHelper
+
+  context "When the parser parses arithmetic" do
+    context "with Integers" do
+      it "$a = 2 + 2"   do; dump(parse("$a = 2 + 2")).should == "(= $a (+ 2 2))"      ; end
+      it "$a = 7 - 3"   do; dump(parse("$a = 7 - 3")).should == "(= $a (- 7 3))"      ; end
+      it "$a = 6 * 3"   do; dump(parse("$a = 6 * 3")).should == "(= $a (* 6 3))"      ; end
+      it "$a = 6 / 3"   do; dump(parse("$a = 6 / 3")).should == "(= $a (/ 6 3))"      ; end
+      it "$a = 6 % 3"   do; dump(parse("$a = 6 % 3")).should == "(= $a (% 6 3))"      ; end
+      it "$a = -(6/3)"  do; dump(parse("$a = -(6/3)")).should == "(= $a (- (/ 6 3)))" ; end
+      it "$a = -6/3"    do; dump(parse("$a = -6/3")).should == "(= $a (/ (- 6) 3))"   ; end
+      it "$a = 8 >> 1 " do; dump(parse("$a = 8 >> 1")).should == "(= $a (>> 8 1))"    ; end
+      it "$a = 8 << 1 " do; dump(parse("$a = 8 << 1")).should == "(= $a (<< 8 1))"    ; end
+    end
+
+    context "with Floats" do
+      it "$a = 2.2 + 2.2"  do; dump(parse("$a = 2.2 + 2.2")).should == "(= $a (+ 2.2 2.2))"      ; end
+      it "$a = 7.7 - 3.3"  do; dump(parse("$a = 7.7 - 3.3")).should == "(= $a (- 7.7 3.3))"      ; end
+      it "$a = 6.1 * 3.1"  do; dump(parse("$a = 6.1 - 3.1")).should == "(= $a (- 6.1 3.1))"      ; end
+      it "$a = 6.6 / 3.3"  do; dump(parse("$a = 6.6 / 3.3")).should == "(= $a (/ 6.6 3.3))"      ; end
+      it "$a = -(6.0/3.0)" do; dump(parse("$a = -(6.0/3.0)")).should == "(= $a (- (/ 6.0 3.0)))" ; end
+      it "$a = -6.0/3.0"   do; dump(parse("$a = -6.0/3.0")).should == "(= $a (/ (- 6.0) 3.0))"   ; end
+      it "$a = 3.14 << 2"  do; dump(parse("$a = 3.14 << 2")).should == "(= $a (<< 3.14 2))"      ; end
+      it "$a = 3.14 >> 2"  do; dump(parse("$a = 3.14 >> 2")).should == "(= $a (>> 3.14 2))"      ; end
+    end
+
+    context "with hex and octal Integer values" do
+      it "$a = 0xAB + 0xCD" do; dump(parse("$a = 0xAB + 0xCD")).should == "(= $a (+ 0xAB 0xCD))"  ; end
+      it "$a = 0777 - 0333" do; dump(parse("$a = 0777 - 0333")).should == "(= $a (- 0777 0333))"  ; end
+    end
+
+    context "with strings requiring boxing to Numeric" do
+      # Test that numbers in string form does not turn into numbers
+      it "$a = '2' + '2'"       do; dump(parse("$a = '2' + '2'")).should == "(= $a (+ '2' '2'))"             ; end
+      it "$a = '2.2' + '0.2'"   do; dump(parse("$a = '2.2' + '0.2'")).should == "(= $a (+ '2.2' '0.2'))"     ; end
+      it "$a = '0xab' + '0xcd'" do; dump(parse("$a = '0xab' + '0xcd'")).should == "(= $a (+ '0xab' '0xcd'))" ; end
+      it "$a = '0777' + '0333'" do; dump(parse("$a = '0777' + '0333'")).should == "(= $a (+ '0777' '0333'))" ; end
+    end
+
+    context "precedence should be correct" do
+      it "$a = 1 + 2 * 3" do; dump(parse("$a = 1 + 2 * 3")).should == "(= $a (+ 1 (* 2 3)))"; end
+      it "$a = 1 + 2 % 3" do; dump(parse("$a = 1 + 2 % 3")).should == "(= $a (+ 1 (% 2 3)))"; end
+      it "$a = 1 + 2 / 3" do; dump(parse("$a = 1 + 2 / 3")).should == "(= $a (+ 1 (/ 2 3)))"; end
+      it "$a = 1 + 2 << 3" do; dump(parse("$a = 1 + 2 << 3")).should == "(= $a (<< (+ 1 2) 3))"; end
+      it "$a = 1 + 2 >> 3" do; dump(parse("$a = 1 + 2 >> 3")).should == "(= $a (>> (+ 1 2) 3))"; end
+    end
+
+    context "parentheses alter precedence" do
+      it "$a = (1 + 2) * 3" do; dump(parse("$a = (1 + 2) * 3")).should == "(= $a (* (+ 1 2) 3))"; end
+      it "$a = (1 + 2) / 3" do; dump(parse("$a = (1 + 2) / 3")).should == "(= $a (/ (+ 1 2) 3))"; end
+    end
+  end
+
+  context "When the evaluator performs boolean operations" do
+    context "using operators AND OR NOT" do
+      it "$a = true  and true" do; dump(parse("$a = true and true")).should == "(= $a (&& true true))"; end
+      it "$a = true  or true"  do; dump(parse("$a = true or true")).should == "(= $a (|| true true))" ; end
+      it "$a = !true"          do; dump(parse("$a = !true")).should == "(= $a (! true))"              ; end
+    end
+
+    context "precedence should be correct" do
+      it "$a = false or true and true" do
+        dump(parse("$a = false or true and true")).should == "(= $a (|| false (&& true true)))"
+      end
+
+      it "$a = (false or true) and true" do
+        dump(parse("$a = (false or true) and true")).should == "(= $a (&& (|| false true) true))"
+      end
+
+      it "$a = !true or true and true" do
+        dump(parse("$a = !false or true and true")).should == "(= $a (|| (! false) (&& true true)))"
+      end
+    end
+
+    # Possibly change to check of literal expressions
+    context "on values requiring boxing to Boolean" do
+      it "'x'            == true" do
+        dump(parse("! 'x'")).should == "(! 'x')"
+      end
+
+      it "''             == false" do
+        dump(parse("! ''")).should == "(! '')"
+      end
+
+      it ":undef         == false" do
+        dump(parse("! undef")).should == "(! :undef)"
+      end
+    end
+  end
+
+  context "When parsing comparisons" do
+    context "of string values" do
+      it "$a = 'a' == 'a'"  do; dump(parse("$a = 'a' == 'a'")).should == "(= $a (== 'a' 'a'))"   ; end
+      it "$a = 'a' != 'a'"  do; dump(parse("$a = 'a' != 'a'")).should == "(= $a (!= 'a' 'a'))"   ; end
+      it "$a = 'a' < 'b'"   do; dump(parse("$a = 'a' < 'b'")).should == "(= $a (< 'a' 'b'))"     ; end
+      it "$a = 'a' > 'b'"   do; dump(parse("$a = 'a' > 'b'")).should == "(= $a (> 'a' 'b'))"     ; end
+      it "$a = 'a' <= 'b'"  do; dump(parse("$a = 'a' <= 'b'")).should == "(= $a (<= 'a' 'b'))"   ; end
+      it "$a = 'a' >= 'b'"  do; dump(parse("$a = 'a' >= 'b'")).should == "(= $a (>= 'a' 'b'))"   ; end
+    end
+
+    context "of integer values" do
+      it "$a = 1 == 1"  do; dump(parse("$a = 1 == 1")).should == "(= $a (== 1 1))"   ; end
+      it "$a = 1 != 1"  do; dump(parse("$a = 1 != 1")).should == "(= $a (!= 1 1))"   ; end
+      it "$a = 1 < 2"   do; dump(parse("$a = 1 < 2")).should == "(= $a (< 1 2))"     ; end
+      it "$a = 1 > 2"   do; dump(parse("$a = 1 > 2")).should == "(= $a (> 1 2))"     ; end
+      it "$a = 1 <= 2"  do; dump(parse("$a = 1 <= 2")).should == "(= $a (<= 1 2))"   ; end
+      it "$a = 1 >= 2"  do; dump(parse("$a = 1 >= 2")).should == "(= $a (>= 1 2))"   ; end
+    end
+
+    context "of regular expressions (parse errors)" do
+      # Not supported in concrete syntax
+      it "$a = /.*/ == /.*/" do
+        expect {  parse("$a = /.*/ == /.*/") }.to raise_error(Puppet::ParseError)
+      end
+
+      it "$a = /.*/ != /a.*/" do
+        expect {  parse("$a = /.*/ != /.*/") }.to raise_error(Puppet::ParseError)
+      end
+    end
+  end
+
+  context "When parsing Regular Expression matching" do
+    it "$a = 'a' =~ /.*/"    do; dump(parse("$a = 'a' =~ /.*/")).should == "(= $a (=~ 'a' /.*/))"      ; end
+    it "$a = 'a' =~ '.*'"    do; dump(parse("$a = 'a' =~ '.*'")).should == "(= $a (=~ 'a' '.*'))"      ; end
+    it "$a = 'a' !~ /b.*/"   do; dump(parse("$a = 'a' !~ /b.*/")).should == "(= $a (!~ 'a' /b.*/))"    ; end
+    it "$a = 'a' !~ 'b.*'"   do; dump(parse("$a = 'a' !~ 'b.*'")).should == "(= $a (!~ 'a' 'b.*'))"    ; end
+  end
+
+  context "When parsing Lists" do
+    it "$a = []" do
+      dump(parse("$a = []")).should == "(= $a ([]))"
+    end
+
+    it "$a = [1]" do
+      dump(parse("$a = [1]")).should == "(= $a ([] 1))"
+    end
+
+    it "$a = [1,2,3]" do
+      dump(parse("$a = [1,2,3]")).should == "(= $a ([] 1 2 3))"
+    end
+
+    it "[...[...[]]] should create nested arrays without trouble" do
+      dump(parse("$a = [1,[2.0, 2.1, [2.2]],[3.0, 3.1]]")).should == "(= $a ([] 1 ([] 2.0 2.1 ([] 2.2)) ([] 3.0 3.1)))"
+    end
+
+    it "$a = [2 + 2]" do
+      dump(parse("$a = [2+2]")).should == "(= $a ([] (+ 2 2)))"
+    end
+
+    it "$a [1,2,3] == [1,2,3]" do
+      dump(parse("$a = [1,2,3] == [1,2,3]")).should == "(= $a (== ([] 1 2 3) ([] 1 2 3)))"
+    end
+  end
+
+  context "When parsing indexed access" do
+    it "$a = $b[2]" do
+      dump(parse("$a = $b[2]")).should == "(= $a (slice $b 2))"
+    end
+
+    it "$a = [1, 2, 3][2]" do
+      dump(parse("$a = [1,2,3][2]")).should == "(= $a (slice ([] 1 2 3) 2))"
+    end
+
+    it "$a = {'a' => 1, 'b' => 2}['b']" do
+      dump(parse("$a = {'a'=>1,'b' =>2}[b]")).should == "(= $a (slice ({} ('a' 1) ('b' 2)) b))"
+    end
+  end
+
+  context "When parsing assignments" do
+    it "Should allow simple assignment" do
+      dump(parse("$a = 10")).should == "(= $a 10)"
+    end
+
+    it "Should allow chained assignment" do
+      dump(parse("$a = $b = 10")).should == "(= $a (= $b 10))"
+    end
+
+    it "Should allow chained assignment with expressions" do
+      dump(parse("$a = 1 + ($b = 10)")).should == "(= $a (+ 1 (= $b 10)))"
+    end
+  end
+
+  context "When parsing Hashes" do
+    it "should create a  Hash when evaluating a LiteralHash" do
+      dump(parse("$a = {'a'=>1,'b'=>2}")).should == "(= $a ({} ('a' 1) ('b' 2)))"
+    end
+
+    it "$a = {...{...{}}} should create nested hashes without trouble" do
+      dump(parse("$a = {'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}")).should == "(= $a ({} ('a' 1) ('b' ({} ('x' 2.1) ('y' 2.2)))))"
+    end
+
+    it "$a = {'a'=> 2 + 2} should evaluate values in entries" do
+      dump(parse("$a = {'a'=>2+2}")).should == "(= $a ({} ('a' (+ 2 2))))"
+    end
+
+    it "$a = {'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2}" do
+      dump(parse("$a = {'a'=>1,'b'=>2} == {'a'=>1,'b'=>2}")).should == "(= $a (== ({} ('a' 1) ('b' 2)) ({} ('a' 1) ('b' 2))))"
+    end
+
+    it "$a = {'a'=> 1, 'b'=>2} != {'x'=> 1, 'y'=>3}" do
+      dump(parse("$a = {'a'=>1,'b'=>2} != {'a'=>1,'b'=>2}")).should == "(= $a (!= ({} ('a' 1) ('b' 2)) ({} ('a' 1) ('b' 2))))"
+    end
+  end
+
+  context "When parsing the 'in' operator" do
+    it "with integer in a list" do
+      dump(parse("$a = 1 in [1,2,3]")).should == "(= $a (in 1 ([] 1 2 3)))"
+    end
+
+    it "with string key in a hash" do
+      dump(parse("$a = 'a' in {'x'=>1, 'a'=>2, 'y'=> 3}")).should == "(= $a (in 'a' ({} ('x' 1) ('a' 2) ('y' 3))))"
+    end
+
+    it "with substrings of a string" do
+      dump(parse("$a = 'ana' in 'bananas'")).should == "(= $a (in 'ana' 'bananas'))"
+    end
+
+    it "with sublist in a list" do
+      dump(parse("$a = [2,3] in [1,2,3]")).should == "(= $a (in ([] 2 3) ([] 1 2 3)))"
+    end
+  end
+
+  context "When parsing string interpolation" do
+    it "should interpolate a bare word as a variable name, \"${var}\"" do
+      dump(parse("$a = \"$var\"")).should == "(= $a (cat '' (str $var) ''))"
+    end
+
+    it "should interpolate a variable in a text expression, \"${$var}\"" do
+      dump(parse("$a = \"${$var}\"")).should == "(= $a (cat '' (str $var) ''))"
+    end
+
+    it "should interpolate a variable, \"yo${var}yo\"" do
+      dump(parse("$a = \"yo${var}yo\"")).should == "(= $a (cat 'yo' (str $var) 'yo'))"
+    end
+
+    it "should interpolate any expression in a text expression, \"${var*2}\"" do
+      dump(parse("$a = \"yo${var+2}yo\"")).should == "(= $a (cat 'yo' (str (+ $var 2)) 'yo'))"
+    end
+  end
+end
diff --git a/spec/unit/pops/parser/parse_calls_spec.rb b/spec/unit/pops/parser/parse_calls_spec.rb
new file mode 100644
index 0000000..62012a4
--- /dev/null
+++ b/spec/unit/pops/parser/parse_calls_spec.rb
@@ -0,0 +1,93 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/parser_rspec_helper')
+
+describe "egrammar parsing function calls" do
+  include ParserRspecHelper
+
+  context "When parsing calls as statements" do
+    context "in top level scope" do
+      it "foo()" do
+        dump(parse("foo()")).should == "(invoke foo)"
+      end
+
+      it "foo bar" do
+        dump(parse("foo bar")).should == "(invoke foo bar)"
+      end
+
+      it "foo(bar)" do
+        dump(parse("foo(bar)")).should == "(invoke foo bar)"
+      end
+
+      it "foo(bar,)" do
+        dump(parse("foo(bar,)")).should == "(invoke foo bar)"
+      end
+
+      it "foo(bar, fum,)" do
+        dump(parse("foo(bar,fum,)")).should == "(invoke foo bar fum)"
+      end
+    end
+
+    context "in nested scopes" do
+      it "if true { foo() }" do
+        dump(parse("if true {foo()}")).should == "(if true\n  (then (invoke foo)))"
+      end
+
+      it "if true { foo bar}" do
+        dump(parse("if true {foo bar}")).should == "(if true\n  (then (invoke foo bar)))"
+      end
+    end
+  end
+
+  context "When parsing calls as expressions" do
+    it "$a = foo()" do
+      dump(parse("$a = foo()")).should == "(= $a (call foo))"
+    end
+
+    it "$a = foo(bar)" do
+      dump(parse("$a = foo()")).should == "(= $a (call foo))"
+    end
+
+    #    # For regular grammar where a bare word can not be a "statement"
+    #    it "$a = foo bar # illegal, must have parentheses" do
+    #      expect { dump(parse("$a = foo bar"))}.to raise_error(Puppet::ParseError)
+    #    end
+
+    # For egrammar where a bare word can be a "statement"
+    it "$a = foo bar # illegal, must have parentheses" do
+      dump(parse("$a = foo bar")).should == "(block (= $a foo) bar)"
+    end
+
+    context "in nested scopes" do
+      it "if true { $a = foo() }" do
+        dump(parse("if true { $a = foo()}")).should == "(if true\n  (then (= $a (call foo))))"
+      end
+
+      it "if true { $a= foo(bar)}" do
+        dump(parse("if true {$a = foo(bar)}")).should == "(if true\n  (then (= $a (call foo bar))))"
+      end
+    end
+  end
+
+  context "When parsing method calls" do
+    it "$a.foo" do
+      dump(parse("$a.foo")).should == "(call-method (. $a foo))"
+    end
+
+    it "$a.foo {|| }" do
+      dump(parse("$a.foo || { }")).should == "(call-method (. $a foo) (lambda ()))"
+    end
+
+    it "$a.foo {|$x| }" do
+      dump(parse("$a.foo {|$x| }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))"
+    end
+
+    it "$a.foo {|$x| }" do
+      dump(parse("$a.foo {|$x| $b = $x}")).should ==
+      "(call-method (. $a foo) (lambda (parameters x) (block (= $b $x))))"
+    end
+  end
+end
diff --git a/spec/unit/pops/parser/parse_conditionals_spec.rb b/spec/unit/pops/parser/parse_conditionals_spec.rb
new file mode 100644
index 0000000..b0cb36b
--- /dev/null
+++ b/spec/unit/pops/parser/parse_conditionals_spec.rb
@@ -0,0 +1,159 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/parser_rspec_helper')
+
+describe "egrammar parsing conditionals" do
+  include ParserRspecHelper
+
+  context "When parsing if statements" do
+    it "if true { $a = 10 }" do
+      dump(parse("if true { $a = 10 }")).should == "(if true\n  (then (= $a 10)))"
+    end
+
+    it "if true { $a = 10 } else {$a = 20}" do
+      dump(parse("if true { $a = 10 } else {$a = 20}")).should ==
+      ["(if true",
+        "  (then (= $a 10))",
+        "  (else (= $a 20)))"].join("\n")
+    end
+
+    it "if true { $a = 10 } elsif false { $a = 15} else {$a = 20}" do
+      dump(parse("if true { $a = 10 } elsif false { $a = 15} else {$a = 20}")).should ==
+      ["(if true",
+        "  (then (= $a 10))",
+        "  (else (if false",
+        "      (then (= $a 15))",
+        "      (else (= $a 20)))))"].join("\n")
+    end
+
+    it "if true { $a = 10 $b = 10 } else {$a = 20}" do
+      dump(parse("if true { $a = 10 $b = 20} else {$a = 20}")).should ==
+      ["(if true",
+        "  (then (block (= $a 10) (= $b 20)))",
+        "  (else (= $a 20)))"].join("\n")
+    end
+
+    it "allows a parenthesized conditional expression" do
+      dump(parse("if (true) { 10 }")).should == "(if true\n  (then 10))"
+    end
+
+    it "allows a parenthesized elsif conditional expression" do
+      dump(parse("if true { 10 } elsif (false) { 20 }")).should ==
+        ["(if true",
+         "  (then 10)",
+         "  (else (if false",
+         "      (then 20))))"].join("\n")
+    end
+  end
+
+  context "When parsing unless statements" do
+    it "unless true { $a = 10 }" do
+      dump(parse("unless true { $a = 10 }")).should == "(unless true\n  (then (= $a 10)))"
+    end
+
+    it "unless true { $a = 10 } else {$a = 20}" do
+      dump(parse("unless true { $a = 10 } else {$a = 20}")).should ==
+      ["(unless true",
+        "  (then (= $a 10))",
+        "  (else (= $a 20)))"].join("\n")
+    end
+
+    it "allows a parenthesized conditional expression" do
+      dump(parse("unless (true) { 10 }")).should == "(unless true\n  (then 10))"
+    end
+
+    it "unless true { $a = 10 } elsif false { $a = 15} else {$a = 20} # is illegal" do
+      expect { parse("unless true { $a = 10 } elsif false { $a = 15} else {$a = 20}")}.to raise_error(Puppet::ParseError)
+    end
+  end
+
+  context "When parsing selector expressions" do
+    it "$a = $b ? banana => fruit " do
+      dump(parse("$a = $b ? banana => fruit")).should ==
+      "(= $a (? $b (banana => fruit)))"
+    end
+
+    it "$a = $b ? { banana => fruit}" do
+      dump(parse("$a = $b ? { banana => fruit }")).should ==
+      "(= $a (? $b (banana => fruit)))"
+    end
+
+    it "does not fail on a trailing blank line" do
+      dump(parse("$a = $b ? { banana => fruit }\n\n")).should ==
+      "(= $a (? $b (banana => fruit)))"
+    end
+
+    it "$a = $b ? { banana => fruit, grape => berry }" do
+      dump(parse("$a = $b ? {banana => fruit, grape => berry}")).should ==
+      "(= $a (? $b (banana => fruit) (grape => berry)))"
+    end
+
+    it "$a = $b ? { banana => fruit, grape => berry, default => wat }" do
+      dump(parse("$a = $b ? {banana => fruit, grape => berry, default => wat}")).should ==
+      "(= $a (? $b (banana => fruit) (grape => berry) (:default => wat)))"
+    end
+
+    it "$a = $b ? { default => wat, banana => fruit, grape => berry,  }" do
+      dump(parse("$a = $b ? {default => wat, banana => fruit, grape => berry}")).should ==
+      "(= $a (? $b (:default => wat) (banana => fruit) (grape => berry)))"
+    end
+  end
+
+  context "When parsing case statements" do
+    it "case $a { a : {}}" do
+      dump(parse("case $a { a : {}}")).should ==
+      ["(case $a",
+        "  (when (a) (then ())))"
+      ].join("\n")
+    end
+
+    it "allows a parenthesized value expression" do
+      dump(parse("case ($a) { a : {}}")).should ==
+      ["(case $a",
+        "  (when (a) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { /.*/ : {}}" do
+      dump(parse("case $a { /.*/ : {}}")).should ==
+      ["(case $a",
+        "  (when (/.*/) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { a, b : {}}" do
+      dump(parse("case $a { a, b : {}}")).should ==
+      ["(case $a",
+        "  (when (a b) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { a, b : {} default : {}}" do
+      dump(parse("case $a { a, b : {} default : {}}")).should ==
+      ["(case $a",
+        "  (when (a b) (then ()))",
+        "  (when (:default) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { a : {$b = 10 $c = 20}}" do
+      dump(parse("case $a { a : {$b = 10 $c = 20}}")).should ==
+      ["(case $a",
+        "  (when (a) (then (block (= $b 10) (= $c 20)))))"
+      ].join("\n")
+    end
+  end
+
+  context "When parsing imports" do
+    it "import 'foo'" do
+      dump(parse("import 'foo'")).should == "(import 'foo')"
+    end
+
+    it "import 'foo', 'bar'" do
+      dump(parse("import 'foo', 'bar'")).should == "(import 'foo' 'bar')"
+    end
+  end
+end
diff --git a/spec/unit/pops/parser/parse_containers_spec.rb b/spec/unit/pops/parser/parse_containers_spec.rb
new file mode 100644
index 0000000..a6b9f20
--- /dev/null
+++ b/spec/unit/pops/parser/parse_containers_spec.rb
@@ -0,0 +1,175 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/parser_rspec_helper')
+
+describe "egrammar parsing containers" do
+  include ParserRspecHelper
+
+  context "When parsing file scope" do
+    it "$a = 10 $b = 20" do
+      dump(parse("$a = 10 $b = 20")).should == "(block (= $a 10) (= $b 20))"
+    end
+
+    it "$a = 10" do
+      dump(parse("$a = 10")).should == "(= $a 10)"
+    end
+  end
+
+  context "When parsing class" do
+    it "class foo {}" do
+      dump(parse("class foo {}")).should == "(class foo ())"
+    end
+
+    it "class foo::bar {}" do
+      dump(parse("class foo::bar {}")).should == "(class foo::bar ())"
+    end
+
+    it "class foo inherits bar {}" do
+      dump(parse("class foo inherits bar {}")).should == "(class foo (inherits bar) ())"
+    end
+
+    it "class foo($a) {}" do
+      dump(parse("class foo($a) {}")).should == "(class foo (parameters a) ())"
+    end
+
+    it "class foo($a, $b) {}" do
+      dump(parse("class foo($a, $b) {}")).should == "(class foo (parameters a b) ())"
+    end
+
+    it "class foo($a, $b=10) {}" do
+      dump(parse("class foo($a, $b=10) {}")).should == "(class foo (parameters a (= b 10)) ())"
+    end
+
+    it "class foo($a, $b) inherits belgo::bar {}" do
+      dump(parse("class foo($a, $b) inherits belgo::bar{}")).should == "(class foo (inherits belgo::bar) (parameters a b) ())"
+    end
+
+    it "class foo {$a = 10 $b = 20}" do
+      dump(parse("class foo {$a = 10 $b = 20}")).should == "(class foo (block (= $a 10) (= $b 20)))"
+    end
+
+    context "it should handle '3x weirdness'" do
+      it "class class {} # a class named 'class'" do
+        # Not as much weird as confusing that it is possible to name a class 'class'. Can have
+        # a very confusing effect when resolving relative names, getting the global hardwired "Class"
+        # instead of some foo::class etc.
+        # This is allowed in 3.x.
+        dump(parse("class class {}")).should == "(class class ())"
+      end
+
+      it "class default {} # a class named 'default'" do
+        # The weirdness here is that a class can inherit 'default' but not declare a class called default.
+        # (It will work with relative names i.e. foo::default though). The whole idea with keywords as
+        # names is flawed to begin with - it generally just a very bad idea.
+        expect { dump(parse("class default {}")).should == "(class default ())" }.to raise_error(Puppet::ParseError)
+      end
+
+      it "class foo::default {} # a nested name 'default'" do
+        dump(parse("class foo::default {}")).should == "(class foo::default ())"
+      end
+
+      it "class class inherits default {} # inherits default", :broken => true do
+        dump(parse("class class inherits default {}")).should == "(class class (inherits default) ())"
+      end
+
+      it "class class inherits default {} # inherits default" do
+        # TODO: See previous test marked as :broken=>true, it is actually this test (result) that is wacky,
+        # this because a class is named at parse time (since class evaluation is lazy, the model must have the
+        # full class name for nested classes - only, it gets this wrong when a class is named "class" - or at least
+        # I think it is wrong.)
+        #
+        dump(parse("class class inherits default {}")).should == "(class class::class (inherits default) ())"
+      end
+
+      it "class foo inherits class" do
+        dump(parse("class foo inherits class {}")).should == "(class foo (inherits class) ())"
+      end
+    end
+  end
+
+  context "When the parser parses define" do
+    it "define foo {}" do
+      dump(parse("define foo {}")).should == "(define foo ())"
+    end
+
+    it "define foo::bar {}" do
+      dump(parse("define foo::bar {}")).should == "(define foo::bar ())"
+    end
+
+    it "define foo($a) {}" do
+      dump(parse("define foo($a) {}")).should == "(define foo (parameters a) ())"
+    end
+
+    it "define foo($a, $b) {}" do
+      dump(parse("define foo($a, $b) {}")).should == "(define foo (parameters a b) ())"
+    end
+
+    it "define foo($a, $b=10) {}" do
+      dump(parse("define foo($a, $b=10) {}")).should == "(define foo (parameters a (= b 10)) ())"
+    end
+
+    it "define foo {$a = 10 $b = 20}" do
+      dump(parse("define foo {$a = 10 $b = 20}")).should == "(define foo (block (= $a 10) (= $b 20)))"
+    end
+
+    context "it should handle '3x weirdness'" do
+      it "define class {} # a define named 'class'" do
+        # This is weird because Class already exists, and instantiating this define will probably not
+        # work
+        dump(parse("define class {}")).should == "(define class ())"
+      end
+
+      it "define default {} # a define named 'default'" do
+        # Check unwanted ability to define 'default'.
+        # The expression below is not allowed (which is good).
+        #
+        expect { dump(parse("define default {}")).should == "(define default ())"}.to raise_error(Puppet::ParseError)
+      end
+    end
+  end
+
+  context "When parsing node" do
+    it "node foo {}" do
+      dump(parse("node foo {}")).should == "(node (matches foo) ())"
+    end
+
+    it "node foo, x::bar, default {}" do
+      dump(parse("node foo, x::bar, default {}")).should == "(node (matches foo x::bar :default) ())"
+    end
+
+    it "node 'foo' {}" do
+      dump(parse("node 'foo' {}")).should == "(node (matches 'foo') ())"
+    end
+
+    it "node foo inherits x::bar {}" do
+      dump(parse("node foo inherits x::bar {}")).should == "(node (matches foo) (parent x::bar) ())"
+    end
+
+    it "node foo inherits 'bar' {}" do
+      dump(parse("node foo inherits 'bar' {}")).should == "(node (matches foo) (parent 'bar') ())"
+    end
+
+    it "node foo inherits default {}" do
+      dump(parse("node foo inherits default {}")).should == "(node (matches foo) (parent :default) ())"
+    end
+
+    it "node /web.*/ {}" do
+      dump(parse("node /web.*/ {}")).should == "(node (matches /web.*/) ())"
+    end
+
+    it "node /web.*/, /do\.wop.*/, and.so.on {}" do
+      dump(parse("node /web.*/, /do\.wop.*/, 'and.so.on' {}")).should == "(node (matches /web.*/ /do\.wop.*/ 'and.so.on') ())"
+    end
+
+    it "node wat inherits /apache.*/ {}" do
+      expect { parse("node wat inherits /apache.*/ {}")}.to raise_error(Puppet::ParseError)
+    end
+
+    it "node foo inherits bar {$a = 10 $b = 20}" do
+      dump(parse("node foo inherits bar {$a = 10 $b = 20}")).should == "(node (matches foo) (parent bar) (block (= $a 10) (= $b 20)))"
+    end
+  end
+end
diff --git a/spec/unit/pops/parser/parse_resource_spec.rb b/spec/unit/pops/parser/parse_resource_spec.rb
new file mode 100644
index 0000000..7d2b54d
--- /dev/null
+++ b/spec/unit/pops/parser/parse_resource_spec.rb
@@ -0,0 +1,228 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/parser_rspec_helper')
+
+describe "egrammar parsing resource declarations" do
+  include ParserRspecHelper
+
+  context "When parsing regular resource" do
+    it "file { 'title': }" do
+      dump(parse("file { 'title': }")).should == [
+        "(resource file",
+        "  ('title'))"
+      ].join("\n")
+    end
+
+    it "file { 'title': path => '/somewhere', mode => 0777}" do
+      dump(parse("file { 'title': path => '/somewhere', mode => 0777}")).should == [
+        "(resource file",
+        "  ('title'",
+        "    (path => '/somewhere')",
+        "    (mode => 0777)))"
+      ].join("\n")
+    end
+
+    it "file { 'title': path => '/somewhere', }" do
+      dump(parse("file { 'title': path => '/somewhere', }")).should == [
+        "(resource file",
+        "  ('title'",
+        "    (path => '/somewhere')))"
+      ].join("\n")
+    end
+
+    it "file { 'title': , }" do
+      dump(parse("file { 'title': , }")).should == [
+        "(resource file",
+        "  ('title'))"
+      ].join("\n")
+    end
+
+    it "file { 'title': ; }" do
+      dump(parse("file { 'title': ; }")).should == [
+        "(resource file",
+        "  ('title'))"
+      ].join("\n")
+    end
+
+    it "file { 'title': ; 'other_title': }" do
+      dump(parse("file { 'title': ; 'other_title': }")).should == [
+        "(resource file",
+        "  ('title')",
+        "  ('other_title'))"
+      ].join("\n")
+    end
+
+    it "file { 'title1': path => 'x'; 'title2': path => 'y'}" do
+      dump(parse("file { 'title1': path => 'x'; 'title2': path => 'y'}")).should == [
+        "(resource file",
+        "  ('title1'",
+        "    (path => 'x'))",
+        "  ('title2'",
+        "    (path => 'y')))",
+      ].join("\n")
+    end
+  end
+
+  context "When parsing resource defaults" do
+    it "File {  }" do
+      dump(parse("File { }")).should == "(resource-defaults file)"
+    end
+
+    it "File { mode => 0777 }" do
+      dump(parse("File { mode => 0777}")).should == [
+        "(resource-defaults file",
+        "  (mode => 0777))"
+      ].join("\n")
+    end
+  end
+
+  context "When parsing resource override" do
+    it "File['x'] {  }" do
+      dump(parse("File['x'] { }")).should == "(override (slice file 'x'))"
+    end
+
+    it "File['x'] { x => 1 }" do
+      dump(parse("File['x'] { x => 1}")).should == "(override (slice file 'x')\n  (x => 1))"
+    end
+
+    it "File['x', 'y'] { x => 1 }" do
+      dump(parse("File['x', 'y'] { x => 1}")).should == "(override (slice file ('x' 'y'))\n  (x => 1))"
+    end
+
+    it "File['x'] { x => 1, y => 2 }" do
+      dump(parse("File['x'] { x => 1, y=> 2}")).should == "(override (slice file 'x')\n  (x => 1)\n  (y => 2))"
+    end
+
+    it "File['x'] { x +> 1 }" do
+      dump(parse("File['x'] { x +> 1}")).should == "(override (slice file 'x')\n  (x +> 1))"
+    end
+  end
+
+  context "When parsing virtual and exported resources" do
+    it "@@file { 'title': }" do
+      dump(parse("@@file { 'title': }")).should ==  "(exported-resource file\n  ('title'))"
+    end
+
+    it "@file { 'title': }" do
+      dump(parse("@file { 'title': }")).should ==  "(virtual-resource file\n  ('title'))"
+    end
+
+    it "@file { mode => 0777 }" do
+      # Defaults are not virtualizeable
+      expect {
+        dump(parse("@file { mode => 0777 }")).should ==  ""
+      }.to raise_error(Puppet::ParseError, /Defaults are not virtualizable/)
+    end
+  end
+
+  context "When parsing class resource" do
+    it "class { 'cname': }" do
+      dump(parse("class { 'cname': }")).should == [
+        "(resource class",
+        "  ('cname'))"
+      ].join("\n")
+    end
+
+    it "class { 'cname': x => 1, y => 2}" do
+      dump(parse("class { 'cname': x => 1, y => 2}")).should == [
+        "(resource class",
+        "  ('cname'",
+        "    (x => 1)",
+        "    (y => 2)))"
+      ].join("\n")
+    end
+
+    it "class { 'cname1': x => 1; 'cname2': y => 2}" do
+      dump(parse("class { 'cname1': x => 1; 'cname2': y => 2}")).should == [
+        "(resource class",
+        "  ('cname1'",
+        "    (x => 1))",
+        "  ('cname2'",
+        "    (y => 2)))",
+      ].join("\n")
+    end
+  end
+
+  context "reported issues in 3.x" do
+    it "should not screw up on brackets in title of resource #19632" do
+      dump(parse('notify { "thisisa[bug]": }')).should == [
+        "(resource notify",
+        "  ('thisisa[bug]'))",
+      ].join("\n")
+    end
+  end
+
+  context "When parsing Relationships" do
+    it "File[a] -> File[b]" do
+      dump(parse("File[a] -> File[b]")).should == "(-> (slice file a) (slice file b))"
+    end
+
+    it "File[a] <- File[b]" do
+      dump(parse("File[a] <- File[b]")).should == "(<- (slice file a) (slice file b))"
+    end
+
+    it "File[a] ~> File[b]" do
+      dump(parse("File[a] ~> File[b]")).should == "(~> (slice file a) (slice file b))"
+    end
+
+    it "File[a] <~ File[b]" do
+      dump(parse("File[a] <~ File[b]")).should == "(<~ (slice file a) (slice file b))"
+    end
+
+    it "Should chain relationships" do
+      dump(parse("a -> b -> c")).should ==
+      "(-> (-> a b) c)"
+    end
+
+    it "Should chain relationships" do
+      dump(parse("File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]")).should ==
+      "(<~ (<- (~> (-> (slice file a) (slice file b)) (slice file c)) (slice file d)) (slice file e))"
+    end
+
+    it "should create relationships between collects" do
+      dump(parse("File <| mode == 0644 |> -> File <| mode == 0755 |>")).should ==
+      "(-> (collect file\n  (<| |> (== mode 0644))) (collect file\n  (<| |> (== mode 0755))))"
+    end
+  end
+
+  context "When parsing collection" do
+    context "of virtual resources" do
+      it "File <| |>" do
+        dump(parse("File <| |>")).should == "(collect file\n  (<| |>))"
+      end
+    end
+
+    context "of exported resources" do
+      it "File <<| |>>" do
+        dump(parse("File <<| |>>")).should == "(collect file\n  (<<| |>>))"
+      end
+    end
+
+    context "queries are parsed with correct precedence" do
+      it "File <| tag == 'foo' |>" do
+        dump(parse("File <| tag == 'foo' |>")).should == "(collect file\n  (<| |> (== tag 'foo')))"
+      end
+
+      it "File <| tag == 'foo' and mode != 0777 |>" do
+        dump(parse("File <| tag == 'foo' and mode != 0777 |>")).should == "(collect file\n  (<| |> (&& (== tag 'foo') (!= mode 0777))))"
+      end
+
+      it "File <| tag == 'foo' or mode != 0777 |>" do
+        dump(parse("File <| tag == 'foo' or mode != 0777 |>")).should == "(collect file\n  (<| |> (|| (== tag 'foo') (!= mode 0777))))"
+      end
+
+      it "File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>" do
+        dump(parse("File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>")).should ==
+        "(collect file\n  (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode 0777)))))"
+      end
+
+      it "File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>" do
+        dump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>")).should ==
+        "(collect file\n  (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode 0777))))"
+      end
+    end
+  end
+end
diff --git a/spec/unit/pops/parser/parser_rspec_helper.rb b/spec/unit/pops/parser/parser_rspec_helper.rb
new file mode 100644
index 0000000..4b2e7a0
--- /dev/null
+++ b/spec/unit/pops/parser/parser_rspec_helper.rb
@@ -0,0 +1,11 @@
+require 'puppet/pops'
+
+require File.join(File.dirname(__FILE__), '/../factory_rspec_helper')
+
+module ParserRspecHelper
+  include FactoryRspecHelper
+  def parse(code)
+    parser = Puppet::Pops::Parser::Parser.new()
+    parser.parse_string(code)
+  end
+end
diff --git a/spec/unit/pops/parser/parser_spec.rb b/spec/unit/pops/parser/parser_spec.rb
new file mode 100644
index 0000000..86d3d6d
--- /dev/null
+++ b/spec/unit/pops/parser/parser_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+require 'puppet/pops'
+
+describe Puppet::Pops::Parser::Parser do
+  it "should instantiate a parser" do
+    parser = Puppet::Pops::Parser::Parser.new()
+    parser.class.should == Puppet::Pops::Parser::Parser
+  end
+
+  it "should parse a code string and return a model" do
+    parser = Puppet::Pops::Parser::Parser.new()
+    model = parser.parse_string("$a = 10").current
+    model.class.should == Model::AssignmentExpression
+  end
+end
diff --git a/spec/unit/pops/parser/rgen_sanitycheck_spec.rb b/spec/unit/pops/parser/rgen_sanitycheck_spec.rb
new file mode 100644
index 0000000..dc71691
--- /dev/null
+++ b/spec/unit/pops/parser/rgen_sanitycheck_spec.rb
@@ -0,0 +1,16 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/parser_rspec_helper')
+
+describe "RGen working with hashes" do
+  it "should be possible to create an empty hash after having required the files above" do
+    # If this fails, it means the rgen addition to Array is not monkey patched as it
+    # should (it will return an array instead of fail in a method_missing), and thus
+    # screw up Hash's check if it can do "to_hash' or not.
+    #
+    Hash[[]]
+  end
+end
diff --git a/spec/unit/pops/transformer/transform_basic_expressions_spec.rb b/spec/unit/pops/transformer/transform_basic_expressions_spec.rb
new file mode 100644
index 0000000..11a2c76
--- /dev/null
+++ b/spec/unit/pops/transformer/transform_basic_expressions_spec.rb
@@ -0,0 +1,243 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/transformer_rspec_helper')
+
+describe "transformation to Puppet AST for basic expressions" do
+  include TransformerRspecHelper
+
+  context "When transforming arithmetic" do
+    context "with Integers" do
+      it "$a = 2 + 2"   do; astdump(parse("$a = 2 + 2")).should == "(= $a (+ 2 2))"      ; end
+      it "$a = 7 - 3"   do; astdump(parse("$a = 7 - 3")).should == "(= $a (- 7 3))"      ; end
+      it "$a = 6 * 3"   do; astdump(parse("$a = 6 * 3")).should == "(= $a (* 6 3))"      ; end
+      it "$a = 6 / 3"   do; astdump(parse("$a = 6 / 3")).should == "(= $a (/ 6 3))"      ; end
+      it "$a = 6 % 3"   do; astdump(parse("$a = 6 % 3")).should == "(= $a (% 6 3))"      ; end
+      it "$a = -(6/3)"  do; astdump(parse("$a = -(6/3)")).should == "(= $a (- (/ 6 3)))" ; end
+      it "$a = -6/3"    do; astdump(parse("$a = -6/3")).should == "(= $a (/ (- 6) 3))"   ; end
+      it "$a = 8 >> 1 " do; astdump(parse("$a = 8 >> 1")).should == "(= $a (>> 8 1))"    ; end
+      it "$a = 8 << 1 " do; astdump(parse("$a = 8 << 1")).should == "(= $a (<< 8 1))"    ; end
+    end
+
+    context "with Floats" do
+      it "$a = 2.2 + 2.2"  do; astdump(parse("$a = 2.2 + 2.2")).should == "(= $a (+ 2.2 2.2))"      ; end
+      it "$a = 7.7 - 3.3"  do; astdump(parse("$a = 7.7 - 3.3")).should == "(= $a (- 7.7 3.3))"      ; end
+      it "$a = 6.1 * 3.1"  do; astdump(parse("$a = 6.1 - 3.1")).should == "(= $a (- 6.1 3.1))"      ; end
+      it "$a = 6.6 / 3.3"  do; astdump(parse("$a = 6.6 / 3.3")).should == "(= $a (/ 6.6 3.3))"      ; end
+      it "$a = -(6.0/3.0)" do; astdump(parse("$a = -(6.0/3.0)")).should == "(= $a (- (/ 6.0 3.0)))" ; end
+      it "$a = -6.0/3.0"   do; astdump(parse("$a = -6.0/3.0")).should == "(= $a (/ (- 6.0) 3.0))"   ; end
+      it "$a = 3.14 << 2"  do; astdump(parse("$a = 3.14 << 2")).should == "(= $a (<< 3.14 2))"      ; end
+      it "$a = 3.14 >> 2"  do; astdump(parse("$a = 3.14 >> 2")).should == "(= $a (>> 3.14 2))"      ; end
+    end
+
+    context "with hex and octal Integer values" do
+      it "$a = 0xAB + 0xCD" do; astdump(parse("$a = 0xAB + 0xCD")).should == "(= $a (+ 0xAB 0xCD))"  ; end
+      it "$a = 0777 - 0333" do; astdump(parse("$a = 0777 - 0333")).should == "(= $a (- 0777 0333))"  ; end
+    end
+
+    context "with strings requiring boxing to Numeric" do
+      # In AST, there is no difference, the ast dumper prints all numbers without quotes - they are still
+      # strings
+      it "$a = '2' + '2'"       do; astdump(parse("$a = '2' + '2'")).should == "(= $a (+ 2 2))"             ; end
+      it "$a = '2.2' + '0.2'"   do; astdump(parse("$a = '2.2' + '0.2'")).should == "(= $a (+ 2.2 0.2))"     ; end
+      it "$a = '0xab' + '0xcd'" do; astdump(parse("$a = '0xab' + '0xcd'")).should == "(= $a (+ 0xab 0xcd))" ; end
+      it "$a = '0777' + '0333'" do; astdump(parse("$a = '0777' + '0333'")).should == "(= $a (+ 0777 0333))" ; end
+    end
+
+    context "precedence should be correct" do
+      it "$a = 1 + 2 * 3" do; astdump(parse("$a = 1 + 2 * 3")).should == "(= $a (+ 1 (* 2 3)))"; end
+      it "$a = 1 + 2 % 3" do; astdump(parse("$a = 1 + 2 % 3")).should == "(= $a (+ 1 (% 2 3)))"; end
+      it "$a = 1 + 2 / 3" do; astdump(parse("$a = 1 + 2 / 3")).should == "(= $a (+ 1 (/ 2 3)))"; end
+      it "$a = 1 + 2 << 3" do; astdump(parse("$a = 1 + 2 << 3")).should == "(= $a (<< (+ 1 2) 3))"; end
+      it "$a = 1 + 2 >> 3" do; astdump(parse("$a = 1 + 2 >> 3")).should == "(= $a (>> (+ 1 2) 3))"; end
+    end
+
+    context "parentheses alter precedence" do
+      it "$a = (1 + 2) * 3" do; astdump(parse("$a = (1 + 2) * 3")).should == "(= $a (* (+ 1 2) 3))"; end
+      it "$a = (1 + 2) / 3" do; astdump(parse("$a = (1 + 2) / 3")).should == "(= $a (/ (+ 1 2) 3))"; end
+    end
+  end
+
+  context "When transforming boolean operations" do
+    context "using operators AND OR NOT" do
+      it "$a = true  and true" do; astdump(parse("$a = true and true")).should == "(= $a (&& true true))"; end
+      it "$a = true  or true"  do; astdump(parse("$a = true or true")).should == "(= $a (|| true true))" ; end
+      it "$a = !true"          do; astdump(parse("$a = !true")).should == "(= $a (! true))"              ; end
+    end
+
+    context "precedence should be correct" do
+      it "$a = false or true and true" do
+        astdump(parse("$a = false or true and true")).should == "(= $a (|| false (&& true true)))"
+      end
+
+      it "$a = (false or true) and true" do
+        astdump(parse("$a = (false or true) and true")).should == "(= $a (&& (|| false true) true))"
+      end
+
+      it "$a = !true or true and true" do
+        astdump(parse("$a = !false or true and true")).should == "(= $a (|| (! false) (&& true true)))"
+      end
+    end
+
+    # Possibly change to check of literal expressions
+    context "on values requiring boxing to Boolean" do
+      it "'x'            == true" do
+        astdump(parse("! 'x'")).should == "(! 'x')"
+      end
+
+      it "''             == false" do
+        astdump(parse("! ''")).should == "(! '')"
+      end
+
+      it ":undef         == false" do
+        astdump(parse("! undef")).should == "(! :undef)"
+      end
+    end
+  end
+
+  context "When transforming comparisons" do
+    context "of string values" do
+      it "$a = 'a' == 'a'"  do; astdump(parse("$a = 'a' == 'a'")).should == "(= $a (== 'a' 'a'))"   ; end
+      it "$a = 'a' != 'a'"  do; astdump(parse("$a = 'a' != 'a'")).should == "(= $a (!= 'a' 'a'))"   ; end
+      it "$a = 'a' < 'b'"   do; astdump(parse("$a = 'a' < 'b'")).should == "(= $a (< 'a' 'b'))"     ; end
+      it "$a = 'a' > 'b'"   do; astdump(parse("$a = 'a' > 'b'")).should == "(= $a (> 'a' 'b'))"     ; end
+      it "$a = 'a' <= 'b'"  do; astdump(parse("$a = 'a' <= 'b'")).should == "(= $a (<= 'a' 'b'))"   ; end
+      it "$a = 'a' >= 'b'"  do; astdump(parse("$a = 'a' >= 'b'")).should == "(= $a (>= 'a' 'b'))"   ; end
+    end
+
+    context "of integer values" do
+      it "$a = 1 == 1"  do; astdump(parse("$a = 1 == 1")).should == "(= $a (== 1 1))"   ; end
+      it "$a = 1 != 1"  do; astdump(parse("$a = 1 != 1")).should == "(= $a (!= 1 1))"   ; end
+      it "$a = 1 < 2"   do; astdump(parse("$a = 1 < 2")).should == "(= $a (< 1 2))"     ; end
+      it "$a = 1 > 2"   do; astdump(parse("$a = 1 > 2")).should == "(= $a (> 1 2))"     ; end
+      it "$a = 1 <= 2"  do; astdump(parse("$a = 1 <= 2")).should == "(= $a (<= 1 2))"   ; end
+      it "$a = 1 >= 2"  do; astdump(parse("$a = 1 >= 2")).should == "(= $a (>= 1 2))"   ; end
+    end
+
+    context "of regular expressions (parse errors)" do
+      # Not supported in concrete syntax
+      it "$a = /.*/ == /.*/" do
+        expect {  parse("$a = /.*/ == /.*/") }.to raise_error(Puppet::ParseError)
+      end
+
+      it "$a = /.*/ != /a.*/" do
+        expect {  parse("$a = /.*/ != /.*/") }.to raise_error(Puppet::ParseError)
+      end
+    end
+  end
+
+  context "When transforming Regular Expression matching" do
+    it "$a = 'a' =~ /.*/"    do; astdump(parse("$a = 'a' =~ /.*/")).should == "(= $a (=~ 'a' /.*/))"      ; end
+    it "$a = 'a' =~ '.*'"    do; astdump(parse("$a = 'a' =~ '.*'")).should == "(= $a (=~ 'a' '.*'))"      ; end
+    it "$a = 'a' !~ /b.*/"   do; astdump(parse("$a = 'a' !~ /b.*/")).should == "(= $a (!~ 'a' /b.*/))"    ; end
+    it "$a = 'a' !~ 'b.*'"   do; astdump(parse("$a = 'a' !~ 'b.*'")).should == "(= $a (!~ 'a' 'b.*'))"    ; end
+  end
+
+  context "When transforming Lists" do
+    it "$a = []" do
+      astdump(parse("$a = []")).should == "(= $a ([]))"
+    end
+
+    it "$a = [1]" do
+      astdump(parse("$a = [1]")).should == "(= $a ([] 1))"
+    end
+
+    it "$a = [1,2,3]" do
+      astdump(parse("$a = [1,2,3]")).should == "(= $a ([] 1 2 3))"
+    end
+
+    it "[...[...[]]] should create nested arrays without trouble" do
+      astdump(parse("$a = [1,[2.0, 2.1, [2.2]],[3.0, 3.1]]")).should == "(= $a ([] 1 ([] 2.0 2.1 ([] 2.2)) ([] 3.0 3.1)))"
+    end
+
+    it "$a = [2 + 2]" do
+      astdump(parse("$a = [2+2]")).should == "(= $a ([] (+ 2 2)))"
+    end
+
+    it "$a [1,2,3] == [1,2,3]" do
+      astdump(parse("$a = [1,2,3] == [1,2,3]")).should == "(= $a (== ([] 1 2 3) ([] 1 2 3)))"
+    end
+  end
+
+  context "When transforming indexed access" do
+    it "$a = $b[2]" do
+      astdump(parse("$a = $b[2]")).should == "(= $a (slice $b 2))"
+    end
+
+    it "$a = [1, 2, 3][2]" do
+      astdump(parse("$a = [1,2,3][2]")).should == "(= $a (slice ([] 1 2 3) 2))"
+    end
+
+    it "$a = {'a' => 1, 'b' => 2}['b']" do
+      astdump(parse("$a = {'a'=>1,'b' =>2}[b]")).should == "(= $a (slice ({} ('a' 1) ('b' 2)) b))"
+    end
+  end
+
+  context "When transforming Hashes" do
+    it "should create a  Hash when evaluating a LiteralHash" do
+      astdump(parse("$a = {'a'=>1,'b'=>2}")).should == "(= $a ({} ('a' 1) ('b' 2)))"
+    end
+
+    it "$a = {...{...{}}} should create nested hashes without trouble" do
+      astdump(parse("$a = {'a'=>1,'b'=>{'x'=>2.1,'y'=>2.2}}")).should == "(= $a ({} ('a' 1) ('b' ({} ('x' 2.1) ('y' 2.2)))))"
+    end
+
+    it "$a = {'a'=> 2 + 2} should evaluate values in entries" do
+      astdump(parse("$a = {'a'=>2+2}")).should == "(= $a ({} ('a' (+ 2 2))))"
+    end
+
+    it "$a = {'a'=> 1, 'b'=>2} == {'a'=> 1, 'b'=>2}" do
+      astdump(parse("$a = {'a'=>1,'b'=>2} == {'a'=>1,'b'=>2}")).should == "(= $a (== ({} ('a' 1) ('b' 2)) ({} ('a' 1) ('b' 2))))"
+    end
+
+    it "$a = {'a'=> 1, 'b'=>2} != {'x'=> 1, 'y'=>3}" do
+      astdump(parse("$a = {'a'=>1,'b'=>2} != {'a'=>1,'b'=>2}")).should == "(= $a (!= ({} ('a' 1) ('b' 2)) ({} ('a' 1) ('b' 2))))"
+    end
+  end
+
+  context "When transforming the 'in' operator" do
+    it "with integer in a list" do
+      astdump(parse("$a = 1 in [1,2,3]")).should == "(= $a (in 1 ([] 1 2 3)))"
+    end
+
+    it "with string key in a hash" do
+      astdump(parse("$a = 'a' in {'x'=>1, 'a'=>2, 'y'=> 3}")).should == "(= $a (in 'a' ({} ('a' 2) ('x' 1) ('y' 3))))"
+    end
+
+    it "with substrings of a string" do
+      astdump(parse("$a = 'ana' in 'bananas'")).should == "(= $a (in 'ana' 'bananas'))"
+    end
+
+    it "with sublist in a list" do
+      astdump(parse("$a = [2,3] in [1,2,3]")).should == "(= $a (in ([] 2 3) ([] 1 2 3)))"
+    end
+  end
+
+  context "When transforming string interpolation" do
+    it "should interpolate a bare word as a variable name, \"${var}\"" do
+      astdump(parse("$a = \"$var\"")).should == "(= $a (cat '' (str $var) ''))"
+    end
+
+    it "should interpolate a variable in a text expression, \"${$var}\"" do
+      astdump(parse("$a = \"${$var}\"")).should == "(= $a (cat '' (str $var) ''))"
+    end
+
+    it "should interpolate two variables in a text expression" do
+      astdump(parse(%q{$a = "xxx $x and $y end"})).should == "(= $a (cat 'xxx ' (str $x) ' and ' (str $y) ' end'))"
+    end
+
+    it "should interpolate one variables  followed by parentheses" do
+      astdump(parse(%q{$a = "xxx ${x} (yay)"})).should == "(= $a (cat 'xxx ' (str $x) ' (yay)'))"
+    end
+
+    it "should interpolate a variable, \"yo${var}yo\"" do
+      astdump(parse("$a = \"yo${var}yo\"")).should == "(= $a (cat 'yo' (str $var) 'yo'))"
+    end
+
+    it "should interpolate any expression in a text expression, \"${var*2}\"" do
+      astdump(parse("$a = \"yo${var+2}yo\"")).should == "(= $a (cat 'yo' (str (+ $var 2)) 'yo'))"
+    end
+  end
+end
diff --git a/spec/unit/pops/transformer/transform_calls_spec.rb b/spec/unit/pops/transformer/transform_calls_spec.rb
new file mode 100644
index 0000000..f2303db
--- /dev/null
+++ b/spec/unit/pops/transformer/transform_calls_spec.rb
@@ -0,0 +1,80 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/transformer_rspec_helper')
+
+describe "transformation to Puppet AST for function calls" do
+  include TransformerRspecHelper
+
+  context "When transforming calls as statements" do
+    context "in top level scope" do
+      it "foo()" do
+        astdump(parse("foo()")).should == "(invoke foo)"
+      end
+
+      it "foo bar" do
+        astdump(parse("foo bar")).should == "(invoke foo bar)"
+      end
+    end
+
+    context "in nested scopes" do
+      it "if true { foo() }" do
+        astdump(parse("if true {foo()}")).should == "(if true\n  (then (invoke foo)))"
+      end
+
+      it "if true { foo bar}" do
+        astdump(parse("if true {foo bar}")).should == "(if true\n  (then (invoke foo bar)))"
+      end
+    end
+  end
+
+  context "When transforming calls as expressions" do
+    it "$a = foo()" do
+      astdump(parse("$a = foo()")).should == "(= $a (call foo))"
+    end
+
+    it "$a = foo(bar)" do
+      astdump(parse("$a = foo()")).should == "(= $a (call foo))"
+    end
+
+    # For egrammar where a bare word can be a "statement"
+    it "$a = foo bar # assignment followed by bare word is ok in egrammar" do
+      astdump(parse("$a = foo bar")).should == "(block (= $a foo) bar)"
+    end
+
+    context "in nested scopes" do
+      it "if true { $a = foo() }" do
+        astdump(parse("if true { $a = foo()}")).should == "(if true\n  (then (= $a (call foo))))"
+      end
+
+      it "if true { $a= foo(bar)}" do
+        astdump(parse("if true {$a = foo(bar)}")).should == "(if true\n  (then (= $a (call foo bar))))"
+      end
+    end
+  end
+
+  context "When transforming method calls" do
+    it "$a.foo" do
+      astdump(parse("$a.foo")).should == "(call-method (. $a foo))"
+    end
+
+    it "$a.foo {|| }" do
+      astdump(parse("$a.foo || { }")).should == "(call-method (. $a foo) (lambda ()))"
+    end
+
+    it "$a.foo {|| []} # check transformation to block with empty array" do
+      astdump(parse("$a.foo || { []}")).should == "(call-method (. $a foo) (lambda (block ([]))))"
+    end
+
+    it "$a.foo {|$x| }" do
+      astdump(parse("$a.foo {|$x| }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))"
+    end
+
+    it "$a.foo {|$x| $b = $x}" do
+      astdump(parse("$a.foo {|$x| $b = $x}")).should ==
+      "(call-method (. $a foo) (lambda (parameters x) (block (= $b $x))))"
+    end
+  end
+end
diff --git a/spec/unit/pops/transformer/transform_conditionals_spec.rb b/spec/unit/pops/transformer/transform_conditionals_spec.rb
new file mode 100644
index 0000000..6eefa51
--- /dev/null
+++ b/spec/unit/pops/transformer/transform_conditionals_spec.rb
@@ -0,0 +1,132 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/transformer_rspec_helper')
+
+describe "transformation to Puppet AST for conditionals" do
+  include TransformerRspecHelper
+
+  context "When transforming if statements" do
+    it "if true { $a = 10 }" do
+      astdump(parse("if true { $a = 10 }")).should == "(if true\n  (then (= $a 10)))"
+    end
+
+    it "if true { $a = 10 } else {$a = 20}" do
+      astdump(parse("if true { $a = 10 } else {$a = 20}")).should ==
+      ["(if true",
+        "  (then (= $a 10))",
+        "  (else (= $a 20)))"].join("\n")
+    end
+
+    it "if true { $a = 10 } elsif false { $a = 15} else {$a = 20}" do
+      astdump(parse("if true { $a = 10 } elsif false { $a = 15} else {$a = 20}")).should ==
+      ["(if true",
+        "  (then (= $a 10))",
+        "  (else (if false",
+        "      (then (= $a 15))",
+        "      (else (= $a 20)))))"].join("\n")
+    end
+
+    it "if true { $a = 10 $b = 10 } else {$a = 20}" do
+      astdump(parse("if true { $a = 10 $b = 20} else {$a = 20}")).should ==
+      ["(if true",
+        "  (then (block (= $a 10) (= $b 20)))",
+        "  (else (= $a 20)))"].join("\n")
+    end
+  end
+
+  context "When transforming unless statements" do
+    # Note that Puppet 3.1 does not have an "unless x", it is encoded as "if !x"
+    it "unless true { $a = 10 }" do
+      astdump(parse("unless true { $a = 10 }")).should == "(if (! true)\n  (then (= $a 10)))"
+    end
+
+    it "unless true { $a = 10 } else {$a = 20}" do
+      astdump(parse("unless true { $a = 10 } else {$a = 20}")).should ==
+      ["(if (! true)",
+        "  (then (= $a 10))",
+        "  (else (= $a 20)))"].join("\n")
+    end
+
+    it "unless true { $a = 10 } elsif false { $a = 15} else {$a = 20} # is illegal" do
+      expect { parse("unless true { $a = 10 } elsif false { $a = 15} else {$a = 20}")}.to raise_error(Puppet::ParseError)
+    end
+  end
+
+  context "When transforming selector expressions" do
+    it "$a = $b ? banana => fruit " do
+      astdump(parse("$a = $b ? banana => fruit")).should ==
+      "(= $a (? $b (banana => fruit)))"
+    end
+
+    it "$a = $b ? { banana => fruit}" do
+      astdump(parse("$a = $b ? { banana => fruit }")).should ==
+      "(= $a (? $b (banana => fruit)))"
+    end
+
+    it "$a = $b ? { banana => fruit, grape => berry }" do
+      astdump(parse("$a = $b ? {banana => fruit, grape => berry}")).should ==
+      "(= $a (? $b (banana => fruit) (grape => berry)))"
+    end
+
+    it "$a = $b ? { banana => fruit, grape => berry, default => wat }" do
+      astdump(parse("$a = $b ? {banana => fruit, grape => berry, default => wat}")).should ==
+      "(= $a (? $b (banana => fruit) (grape => berry) (:default => wat)))"
+    end
+
+    it "$a = $b ? { default => wat, banana => fruit, grape => berry,  }" do
+      astdump(parse("$a = $b ? {default => wat, banana => fruit, grape => berry}")).should ==
+      "(= $a (? $b (:default => wat) (banana => fruit) (grape => berry)))"
+    end
+  end
+
+  context "When transforming case statements" do
+    it "case $a { a : {}}" do
+      astdump(parse("case $a { a : {}}")).should ==
+      ["(case $a",
+        "  (when (a) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { /.*/ : {}}" do
+      astdump(parse("case $a { /.*/ : {}}")).should ==
+      ["(case $a",
+        "  (when (/.*/) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { a, b : {}}" do
+      astdump(parse("case $a { a, b : {}}")).should ==
+      ["(case $a",
+        "  (when (a b) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { a, b : {} default : {}}" do
+      astdump(parse("case $a { a, b : {} default : {}}")).should ==
+      ["(case $a",
+        "  (when (a b) (then ()))",
+        "  (when (:default) (then ())))"
+      ].join("\n")
+    end
+
+    it "case $a { a : {$b = 10 $c = 20}}" do
+      astdump(parse("case $a { a : {$b = 10 $c = 20}}")).should ==
+      ["(case $a",
+        "  (when (a) (then (block (= $b 10) (= $c 20)))))"
+      ].join("\n")
+    end
+  end
+
+  context "When transforming imports" do
+    it "import 'foo'" do
+      astdump(parse("import 'foo'")).should == ":nop"
+    end
+
+    it "import 'foo', 'bar'" do
+      astdump(parse("import 'foo', 'bar'")).should == ":nop"
+    end
+  end
+end
diff --git a/spec/unit/pops/transformer/transform_containers_spec.rb b/spec/unit/pops/transformer/transform_containers_spec.rb
new file mode 100644
index 0000000..682860f
--- /dev/null
+++ b/spec/unit/pops/transformer/transform_containers_spec.rb
@@ -0,0 +1,182 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/transformer_rspec_helper')
+
+describe "transformation to Puppet AST for containers" do
+  include TransformerRspecHelper
+
+  context "When transforming file scope" do
+    it "$a = 10 $b = 20" do
+      astdump(parse("$a = 10 $b = 20")).should == "(block (= $a 10) (= $b 20))"
+    end
+
+    it "$a = 10" do
+      astdump(parse("$a = 10")).should == "(= $a 10)"
+    end
+  end
+
+  context "When transforming class" do
+    it "class foo {}" do
+      astdump(parse("class foo {}")).should == "(class foo ())"
+    end
+
+    it "class foo::bar {}" do
+      astdump(parse("class foo::bar {}")).should == "(class foo::bar ())"
+    end
+
+    it "class foo inherits bar {}" do
+      astdump(parse("class foo inherits bar {}")).should == "(class foo (inherits bar) ())"
+    end
+
+    it "class foo($a) {}" do
+      astdump(parse("class foo($a) {}")).should == "(class foo (parameters a) ())"
+    end
+
+    it "class foo($a, $b) {}" do
+      astdump(parse("class foo($a, $b) {}")).should == "(class foo (parameters a b) ())"
+    end
+
+    it "class foo($a, $b=10) {}" do
+      astdump(parse("class foo($a, $b=10) {}")).should == "(class foo (parameters a (= b 10)) ())"
+    end
+
+    it "class foo($a, $b) inherits belgo::bar {}" do
+      astdump(parse("class foo($a, $b) inherits belgo::bar{}")).should == "(class foo (inherits belgo::bar) (parameters a b) ())"
+    end
+
+    it "class foo {$a = 10 $b = 20}" do
+      astdump(parse("class foo {$a = 10 $b = 20}")).should == "(class foo (block (= $a 10) (= $b 20)))"
+    end
+
+    context "it should handle '3x weirdness'" do
+      it "class class {} # a class named 'class'" do
+        # Not as much weird as confusing that it is possible to name a class 'class'. Can have
+        # a very confusing effect when resolving relative names, getting the global hardwired "Class"
+        # instead of some foo::class etc.
+        # This is allowed in 3.x.
+        astdump(parse("class class {}")).should == "(class class ())"
+      end
+
+      it "class default {} # a class named 'default'" do
+        # The weirdness here is that a class can inherit 'default' but not declare a class called default.
+        # (It will work with relative names i.e. foo::default though). The whole idea with keywords as
+        # names is flawed to begin with - it generally just a very bad idea.
+        expect { dump(parse("class default {}")).should == "(class default ())" }.to raise_error(Puppet::ParseError)
+      end
+
+      it "class foo::default {} # a nested name 'default'" do
+        astdump(parse("class foo::default {}")).should == "(class foo::default ())"
+      end
+
+      it "class class inherits default {} # inherits default", :broken => true do
+        astdump(parse("class class inherits default {}")).should == "(class class (inherits default) ())"
+      end
+
+      it "class class inherits default {} # inherits default" do
+        # TODO: See previous test marked as :broken=>true, it is actually this test (result) that is wacky,
+        # this because a class is named at parse time (since class evaluation is lazy, the model must have the
+        # full class name for nested classes - only, it gets this wrong when a class is named "class" - or at least
+        # I think it is wrong.)
+        #
+        astdump(parse("class class inherits default {}")).should == "(class class::class (inherits default) ())"
+      end
+
+      it "class foo inherits class" do
+        astdump(parse("class foo inherits class {}")).should == "(class foo (inherits class) ())"
+      end
+    end
+  end
+
+  context "When transforming define" do
+    it "define foo {}" do
+      astdump(parse("define foo {}")).should == "(define foo ())"
+    end
+
+    it "define foo::bar {}" do
+      astdump(parse("define foo::bar {}")).should == "(define foo::bar ())"
+    end
+
+    it "define foo($a) {}" do
+      astdump(parse("define foo($a) {}")).should == "(define foo (parameters a) ())"
+    end
+
+    it "define foo($a, $b) {}" do
+      astdump(parse("define foo($a, $b) {}")).should == "(define foo (parameters a b) ())"
+    end
+
+    it "define foo($a, $b=10) {}" do
+      astdump(parse("define foo($a, $b=10) {}")).should == "(define foo (parameters a (= b 10)) ())"
+    end
+
+    it "define foo {$a = 10 $b = 20}" do
+      astdump(parse("define foo {$a = 10 $b = 20}")).should == "(define foo (block (= $a 10) (= $b 20)))"
+    end
+
+    context "it should handle '3x weirdness'" do
+      it "define class {} # a define named 'class'" do
+        # This is weird because Class already exists, and instantiating this define will probably not
+        # work
+        astdump(parse("define class {}")).should == "(define class ())"
+      end
+
+      it "define default {} # a define named 'default'" do
+        # Check unwanted ability to define 'default'.
+        # The expression below is not allowed (which is good).
+        #
+        expect { dump(parse("define default {}")).should == "(define default ())"}.to raise_error(Puppet::ParseError)
+      end
+    end
+  end
+
+  context "When transforming node" do
+    it "node foo {}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node foo {}")).should == "(node (matches 'foo') ())"
+    end
+
+    it "node foo, x.bar, default {}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node foo, x_bar, default {}")).should == "(node (matches 'foo' 'x_bar' :default) ())"
+    end
+
+    it "node 'foo' {}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node 'foo' {}")).should == "(node (matches 'foo') ())"
+    end
+
+    it "node foo inherits x::bar {}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node foo inherits x_bar {}")).should == "(node (matches 'foo') (parent x_bar) ())"
+    end
+
+    it "node foo inherits 'bar' {}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node foo inherits 'bar' {}")).should == "(node (matches 'foo') (parent 'bar') ())"
+    end
+
+    it "node foo inherits default {}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node foo inherits default {}")).should == "(node (matches 'foo') (parent :default) ())"
+    end
+
+    it "node /web.*/ {}" do
+      astdump(parse("node /web.*/ {}")).should == "(node (matches /web.*/) ())"
+    end
+
+    it "node /web.*/, /do\.wop.*/, and.so.on {}" do
+      astdump(parse("node /web.*/, /do\.wop.*/, 'and.so.on' {}")).should == "(node (matches /web.*/ /do\.wop.*/ 'and.so.on') ())"
+    end
+
+    it "node wat inherits /apache.*/ {}" do
+      expect { parse("node wat inherits /apache.*/ {}")}.to raise_error(Puppet::ParseError)
+    end
+
+    it "node foo inherits bar {$a = 10 $b = 20}" do
+      # AST can not differentiate between bare word and string
+      astdump(parse("node foo inherits bar {$a = 10 $b = 20}")).should == "(node (matches 'foo') (parent bar) (block (= $a 10) (= $b 20)))"
+    end
+  end
+end
diff --git a/spec/unit/pops/transformer/transform_resource_spec.rb b/spec/unit/pops/transformer/transform_resource_spec.rb
new file mode 100644
index 0000000..251ef2f
--- /dev/null
+++ b/spec/unit/pops/transformer/transform_resource_spec.rb
@@ -0,0 +1,185 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '/transformer_rspec_helper')
+
+describe "transformation to Puppet AST for resource declarations" do
+  include TransformerRspecHelper
+
+  context "When transforming regular resource" do
+    it "file { 'title': }" do
+      astdump(parse("file { 'title': }")).should == [
+        "(resource file",
+        "  ('title'))"
+      ].join("\n")
+    end
+
+    it "file { 'title': ; 'other_title': }" do
+      astdump(parse("file { 'title': ; 'other_title': }")).should == [
+        "(resource file",
+        "  ('title')",
+        "  ('other_title'))"
+      ].join("\n")
+    end
+
+    it "file { 'title': path => '/somewhere', mode => 0777}" do
+      astdump(parse("file { 'title': path => '/somewhere', mode => 0777}")).should == [
+        "(resource file",
+        "  ('title'",
+        "    (path => '/somewhere')",
+        "    (mode => 0777)))"
+      ].join("\n")
+    end
+
+    it "file { 'title1': path => 'x'; 'title2': path => 'y'}" do
+      astdump(parse("file { 'title1': path => 'x'; 'title2': path => 'y'}")).should == [
+        "(resource file",
+        "  ('title1'",
+        "    (path => 'x'))",
+        "  ('title2'",
+        "    (path => 'y')))",
+      ].join("\n")
+    end
+  end
+
+  context "When transforming resource defaults" do
+    it "File {  }" do
+      astdump(parse("File { }")).should == "(resource-defaults file)"
+    end
+
+    it "File { mode => 0777 }" do
+      astdump(parse("File { mode => 0777}")).should == [
+        "(resource-defaults file",
+        "  (mode => 0777))"
+      ].join("\n")
+    end
+  end
+
+  context "When transforming resource override" do
+    it "File['x'] {  }" do
+      astdump(parse("File['x'] { }")).should == "(override (slice file 'x'))"
+    end
+
+    it "File['x'] { x => 1 }" do
+      astdump(parse("File['x'] { x => 1}")).should == "(override (slice file 'x')\n  (x => 1))"
+    end
+
+    it "File['x', 'y'] { x => 1 }" do
+      astdump(parse("File['x', 'y'] { x => 1}")).should == "(override (slice file ('x' 'y'))\n  (x => 1))"
+    end
+
+    it "File['x'] { x => 1, y => 2 }" do
+      astdump(parse("File['x'] { x => 1, y=> 2}")).should == "(override (slice file 'x')\n  (x => 1)\n  (y => 2))"
+    end
+
+    it "File['x'] { x +> 1 }" do
+      astdump(parse("File['x'] { x +> 1}")).should == "(override (slice file 'x')\n  (x +> 1))"
+    end
+  end
+
+  context "When transforming virtual and exported resources" do
+    it "@@file { 'title': }" do
+      astdump(parse("@@file { 'title': }")).should ==  "(exported-resource file\n  ('title'))"
+    end
+
+    it "@file { 'title': }" do
+      astdump(parse("@file { 'title': }")).should ==  "(virtual-resource file\n  ('title'))"
+    end
+  end
+
+  context "When transforming class resource" do
+    it "class { 'cname': }" do
+      astdump(parse("class { 'cname': }")).should == [
+        "(resource class",
+        "  ('cname'))"
+      ].join("\n")
+    end
+
+    it "class { 'cname': x => 1, y => 2}" do
+      astdump(parse("class { 'cname': x => 1, y => 2}")).should == [
+        "(resource class",
+        "  ('cname'",
+        "    (x => 1)",
+        "    (y => 2)))"
+      ].join("\n")
+    end
+
+    it "class { 'cname1': x => 1; 'cname2': y => 2}" do
+      astdump(parse("class { 'cname1': x => 1; 'cname2': y => 2}")).should == [
+        "(resource class",
+        "  ('cname1'",
+        "    (x => 1))",
+        "  ('cname2'",
+        "    (y => 2)))",
+      ].join("\n")
+    end
+  end
+
+  context "When transforming Relationships" do
+    it "File[a] -> File[b]" do
+      astdump(parse("File[a] -> File[b]")).should == "(-> (slice file a) (slice file b))"
+    end
+
+    it "File[a] <- File[b]" do
+      astdump(parse("File[a] <- File[b]")).should == "(<- (slice file a) (slice file b))"
+    end
+
+    it "File[a] ~> File[b]" do
+      astdump(parse("File[a] ~> File[b]")).should == "(~> (slice file a) (slice file b))"
+    end
+
+    it "File[a] <~ File[b]" do
+      astdump(parse("File[a] <~ File[b]")).should == "(<~ (slice file a) (slice file b))"
+    end
+
+    it "Should chain relationships" do
+      astdump(parse("a -> b -> c")).should ==
+      "(-> (-> a b) c)"
+    end
+
+    it "Should chain relationships" do
+      astdump(parse("File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]")).should ==
+      "(<~ (<- (~> (-> (slice file a) (slice file b)) (slice file c)) (slice file d)) (slice file e))"
+    end
+  end
+
+  context "When transforming collection" do
+    context "of virtual resources" do
+      it "File <| |>" do
+        astdump(parse("File <| |>")).should == "(collect file\n  (<| |>))"
+      end
+    end
+
+    context "of exported resources" do
+      it "File <<| |>>" do
+        astdump(parse("File <<| |>>")).should == "(collect file\n  (<<| |>>))"
+      end
+    end
+
+    context "queries are parsed with correct precedence" do
+      it "File <| tag == 'foo' |>" do
+        astdump(parse("File <| tag == 'foo' |>")).should == "(collect file\n  (<| |> (== tag 'foo')))"
+      end
+
+      it "File <| tag == 'foo' and mode != 0777 |>" do
+        astdump(parse("File <| tag == 'foo' and mode != 0777 |>")).should == "(collect file\n  (<| |> (&& (== tag 'foo') (!= mode 0777))))"
+      end
+
+      it "File <| tag == 'foo' or mode != 0777 |>" do
+        astdump(parse("File <| tag == 'foo' or mode != 0777 |>")).should == "(collect file\n  (<| |> (|| (== tag 'foo') (!= mode 0777))))"
+      end
+
+      it "File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>" do
+        astdump(parse("File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>")).should ==
+        "(collect file\n  (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode 0777)))))"
+      end
+
+      it "File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>" do
+        astdump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>")).should ==
+        "(collect file\n  (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode 0777))))"
+      end
+    end
+  end
+end
diff --git a/spec/unit/pops/transformer/transformer_rspec_helper.rb b/spec/unit/pops/transformer/transformer_rspec_helper.rb
new file mode 100644
index 0000000..94c3f02
--- /dev/null
+++ b/spec/unit/pops/transformer/transformer_rspec_helper.rb
@@ -0,0 +1,27 @@
+require 'puppet/pops'
+require 'puppet/parser/ast'
+
+require File.join(File.dirname(__FILE__), '/../factory_rspec_helper')
+
+module TransformerRspecHelper
+  include FactoryRspecHelper
+  # Dumps the AST to string form
+  #
+  def astdump(ast)
+    ast = transform(ast) unless ast.kind_of?(Puppet::Parser::AST)
+    Puppet::Pops::Model::AstTreeDumper.new.dump(ast)
+  end
+
+  # Transforms the Pops model to an AST model
+  #
+  def transform(model)
+    Puppet::Pops::Model::AstTransformer.new.transform(model)
+  end
+
+  # Parses the string code to a Pops model
+  #
+  def parse(code)
+    parser = Puppet::Pops::Parser::Parser.new()
+    parser.parse_string(code)
+  end
+end
diff --git a/spec/unit/pops/visitor_spec.rb b/spec/unit/pops/visitor_spec.rb
new file mode 100644
index 0000000..c5858c2
--- /dev/null
+++ b/spec/unit/pops/visitor_spec.rb
@@ -0,0 +1,94 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+
+describe Puppet::Pops::Visitor do
+  describe "A visitor and a visitable in a configuration with min and max args set to 0" do
+    class DuckProcessor
+      def initialize
+        @friend_visitor = Puppet::Pops::Visitor.new(self, "friend", 0, 0)
+      end
+
+      def hi(o, *args)
+        @friend_visitor.visit(o, *args)
+      end
+
+      def friend_Duck(o)
+        "Hi #{o.class}"
+      end
+
+      def friend_Numeric(o)
+        "Howdy #{o.class}"
+      end
+    end
+
+    class Duck
+      include Puppet::Pops::Visitable
+    end
+
+    it "should select the expected method when there are no arguments" do
+      duck = Duck.new
+      duck_processor = DuckProcessor.new
+      duck_processor.hi(duck).should == "Hi Duck"
+    end
+
+    it "should fail if there are too many arguments" do
+      duck = Duck.new
+      duck_processor = DuckProcessor.new
+      expect { duck_processor.hi(duck, "how are you?") }.to raise_error(/^Visitor Error: Too many.*/)
+    end
+
+    it "should select method for superclass" do
+      duck_processor = DuckProcessor.new
+      duck_processor.hi(42).should == "Howdy Fixnum"
+    end
+
+    it "should select method for superclass" do
+      duck_processor = DuckProcessor.new
+      duck_processor.hi(42.0).should == "Howdy Float"
+    end
+
+    it "should fail if class not handled" do
+      duck_processor = DuckProcessor.new
+      expect { duck_processor.hi("wassup?") }.to raise_error(/Visitor Error: the configured.*/)
+    end
+  end
+
+  describe "A visitor and a visitable in a configuration with min =1, and max args set to 2" do
+    class DuckProcessor2
+      def initialize
+        @friend_visitor = Puppet::Pops::Visitor.new(self, "friend", 1, 2)
+      end
+
+      def hi(o, *args)
+        @friend_visitor.visit(o, *args)
+      end
+
+      def friend_Duck(o, drink, eat="grain")
+        "Hi #{o.class}, drink=#{drink}, eat=#{eat}"
+      end
+    end
+
+    class Duck
+      include Puppet::Pops::Visitable
+    end
+
+    it "should select the expected method when there are is one arguments" do
+      duck = Duck.new
+      duck_processor = DuckProcessor2.new
+      duck_processor.hi(duck, "water").should == "Hi Duck, drink=water, eat=grain"
+    end
+
+    it "should fail if there are too many arguments" do
+      duck = Duck.new
+      duck_processor = DuckProcessor2.new
+      expect { duck_processor.hi(duck, "scotch", "soda", "peanuts") }.to raise_error(/^Visitor Error: Too many.*/)
+    end
+
+    it "should fail if there are too few arguments" do
+      duck = Duck.new
+      duck_processor = DuckProcessor2.new
+      expect { duck_processor.hi(duck) }.to raise_error(/^Visitor Error: Too few.*/)
+    end
+  end
+end
diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb
index e80e903..f891d5c 100755
--- a/spec/unit/resource/type_spec.rb
+++ b/spec/unit/resource/type_spec.rb
@@ -715,13 +715,13 @@ def code(str)
       dest.doc.should == "foonessyayness"
     end
 
-    it "should turn its code into an ASTArray if necessary" do
+    it "should turn its code into a BlockExpression if necessary" do
       dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo"))
       source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar"))
 
       dest.merge(source)
 
-      dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray)
+      dest.code.should be_instance_of(Puppet::Parser::AST::BlockExpression)
     end
 
     it "should set the other class's code as its code if it has none" do
@@ -742,7 +742,7 @@ def code(str)
 
       dest.merge(source)
 
-      dest.code.children.collect { |l| l.value }.should == %w{dest source}
+      dest.code.children.collect { |l| l[0].value }.should == %w{dest source}
     end
   end
 end

    

weareth...@puppetlabs.com

unread,
Apr 5, 2013, 7:45:15 PM4/5/13
to puppet...@googlegroups.com

On Fri Apr 05 23:18:04 UTC 2013 pull request #1584 was closed.

(#11331) A whole new parser and some iteration goodness requested by (zaphod42)

The pull request was merged by: zaphod42

Reply all
Reply to author
Forward
0 new messages