CodeTools::AST << {

# A Ruby code generation visitor
ToRuby < ::Myco::Object {
  var lines: [""]

  # The currently held string of generated ruby code
  to_s: lines.join("\n")

  # Add to the current line the given list of Strings
  # and/or objects that respond to :to_ruby
  add: |*strings| strings.each |string| {
    string.is_a?(String)
      &? lines.last.concat(string.dup)
      ?? string.to_ruby(self)
  }

  # Start a new line, optionally adding a list of Strings
  # and/or objects that respond to :to_ruby
  line: |*strings|
    lines.push(indents.last.dup); add(*strings)

  # Add the given items to the current line,
  # using the given separator between items.
  # If a block is given, it is called on
  # each iteration with the item instead.
  # Returns true if any items were written, else false.
  list: |items, separator="", auto_lines:false, &block| {
    items.any? && (
      items = items.dup
      last_item = items.pop

      use_lines = items.any? && auto_lines
      use_lines && self.push_indent

      items.each |item| {
        use_lines && self.line
        block &? block.call(item) ?? add(item)
        add(separator)
      }
      use_lines && self.line
      block &? block.call(last_item) ?? add(last_item)

      use_lines && self.pop_indent
      use_lines && self.line
    )
  }

  # Stack of indent levels (as strings to be prefixed)
  var indents: [""]
  var indent_amount: 2
  pop_indent: indents.pop
  push_indent: |amount=indent_amount|
    indents.push(indents.last + " " * amount)

  # Stack of every AST node in hierarchy that can hold local variables
  var var_scopes: []
  var_scope: var_scopes.last

  with_nested_var_scope: |node, &block| {
    var_scopes.push(node)
    block.call
    var_scopes.pop
  }

  var_scope_declare_local: |name|
    var_scope.variables[name] = true

  var_scope_declare_locals: |*names| {
    names.each |name| { var_scope_declare_local(name) }
  }

  var_scope_has_local?: |name| {
    !!var_scopes.reverse.detect |scope| {
      scope.variables.has_key?(name)
    }
  }

  # Helper methods

  # Return false if the given indent might be problematic in generated Ruby
  # TODO: make comprehensive
  easy_ident?: |sym| {
    str = sym.to_s
    (str =~ Regexp.new("^[a-z_][A-Za-z_]*$"))
    && (![:if,:unless,:and,:or,:def,:do,:class,
          :module,:end,:break,:begin,:rescue,:ensure].include?(sym))
  }
}

# Allow any Node to be visited with the to_ruby meme
Node << {
  to_ruby_code: {
    g = ToRuby.new
    to_ruby(g)
    g.to_s
  }
}

Self         << { to_ruby: |g| g.add("self") }
NullLiteral  << { to_ruby: |g| g.add("nil") }
VoidLiteral  << { to_ruby: |g| g.add("::Myco::Void") }
TrueLiteral  << { to_ruby: |g| g.add("true") }
FalseLiteral << { to_ruby: |g| g.add("false") }

NumericLiteral << { to_ruby: |g| g.add(self.value.inspect) }
StringLiteral  << { to_ruby: |g| g.add(self.value.inspect) }
SymbolLiteral  << { to_ruby: |g| g.add(self.value.inspect) }

StringCompose << {
  to_ruby: |g| {
    g.add('"')
      self.body.each_slice(2) |string, to_string| {
        g.add(string.value.to_s.inspect.slice(Range.new(1, -2)))
        to_string && (g.add('#{'); g.add(to_string); g.add('}'))
      }
    g.add('"')
  }
}

SymbolCompose << {
  to_ruby: |g| {
    g.add(':"')
      self.body.each_slice(2) |string, to_string| {
        g.add(string.value.to_s.inspect.slice(Range.new(1, -2)))
        to_string && (g.add('#{'); g.add(to_string); g.add('}'))
      }
    g.add('"')
  }
}

ArrayAssembly << {
  to_ruby: |g|
    g.add("["); g.list(self.body, ",", auto_lines:true); g.add("]")
}

KeywordAssembly << {
  to_ruby: |g| {
    g.add("{")
    g.list(self.body.each_slice(2).to_a, ",", auto_lines:true) |pair| {
      g.add(pair.first, " => ", pair.last)
    }
    g.add("}")
  }
}

ConstantAccess << {
  to_ruby: |g| {
    name_list = self.names.map(&:to_sym)
    first_name = name_list.shift

    self.toplevel &? (
      first_name case(
        :Myco,     &{ g.add("::Myco") },
        :Ruby,     &{ g.add("::Object") },
        :Rubinius, &{ g.add("::Rubinius") },
                   &{ g.add("::Myco::"first_name"") }
      )
    ) ?? (
      g.add("::Myco.find_constant("first_name.inspect")")
    )
    name_list.each |name| { g.add("::"name"") }
  }
}

ConstantAssignment << {
  to_ruby: |g| {
    name_list = self.constant.names.map(&:to_sym)
    last_name = name_list.pop
    first_name = name_list.any? && name_list.shift

    self.constant.toplevel &? (
      first_name &? (
        first_name case(
          :Myco,     &{ g.add("::Myco") },
          :Ruby,     &{ g.add("::Object") },
          :Rubinius, &{ g.add("::Rubinius") },
                     &{ g.add("::Myco::"first_name"") }
        )
      ) ?? (
        g.add("::Myco")
      )
    ) ?? (
      first_name &? (
        g.add("::Myco.find_constant("first_name.inspect")")
      ) ?? (
        g.add("::Myco.cscope.for_method_definition")
      )
    )
    name_list.each |name| { g.add("::"name"") }
    g.add("::"last_name" = ")

    g.add(self.value)
  }
}

ConstantDefine << {
  to_ruby: |g| {
    g.add(implementation)
    g.line(".tap { |__c__| __c__.__name__ = ")
      g.add(self.constant.names.last.inspect)
    g.add(" }")
  }
}

DeclareExtension << {
  to_ruby: |g| {
    g.add(self.constant); g.add(".component_eval {")
    g.add(self.body); g.add("}")
  }
}

DeclaredScope << {
  to_ruby: |g|
    g.with_nested_var_scope(self) { g.add(self.body) }
}

Script      << { to_ruby: |g| g.add(self.body) }
DeclareFile << { to_ruby: |g| g.line; implementation.to_ruby(g); g.line }

DeclareObject << {
  to_ruby: |g| {
    g.add("::Myco::Component.new("); g.add(self.types); g.add(", ::Myco.cscope.for_method_definition, __FILE__, __LINE__)")
    g.line(".tap { |__c__| __c__.__last__ = __c__.component_eval {"); g.add(scope_implementation); g.add("}}")
    self.create && g.add(".instance")
  }
}

DeclareString << { to_ruby: |g| g.add(implementation) }

DeclareCategory << {
  to_ruby: |g| {
    g.add("__category__("self.name.inspect")")
    g.add(".component_eval {"); g.add(self.body); g.add("}")
  }
}

DeclareMeme << {
  to_ruby: |g| {
    name = self.decorations.last
    name.node_type == :symbol &? (
      g.add("declare_meme(")
        g.list([
          self.effective_name
          self.effective_decorations
          self.effective_body
        ], ",", auto_lines: true)
      g.add(")")
    ) ?? (
      g.add(ConstantAssignment.new(
        line:     self.line
        constant: self.effective_name
        value:    self.effective_body.block.body
      ))
    )
  }
}

RequiredParameter << { to_ruby: |g| g.add(""self.name"") }
OptionalParameter << { to_ruby: |g| g.add(""self.name"="); g.add(self.value) }
RestParameter     << { to_ruby: |g| g.add("*"self.name"") }

KeywordRequiredParameter << { to_ruby: |g| g.add(""self.name":") }
KeywordOptionalParameter << { to_ruby: |g| g.add(""self.name":"); g.add(self.value) }
KeywordRestParameter     << { to_ruby: |g| g.add("**"self.name"") }

BlockParameter    << { to_ruby: |g| g.add("&"self.name"") }

ParameterAssembly << {
  to_ruby: |g|
    g.add("|"); g.list(self.all_params, ", "); g.add("|")
}

Sequence << {
  to_ruby: |g|
    g.add("("); g.list(self.array, "", auto_lines:true); g.add(")")
}

Invoke << { to_ruby: |g| g.add(implementation) }

InvokeMethod << {
  to_ruby: |g| {
    list = self.arguments &? self.arguments.body.dup ?? []
    self.arguments.block.is_a?(BlockArgument) && # TODO: receive block as part of arguments list
      list.push(self.arguments.block)

    g.add(self.receiver)

    g.easy_ident?(self.name) &? (
      g.add("."self.name"")
    ) ?? (
      g.add(".__send__")
      list.unshift(SymbolLiteral.new(line:self.line, value:self.name))
    )

    list.any? && (
      g.add("(")
        g.list(list, ",", auto_lines:true)
      g.add(")")
    )

    self.arguments.block.is_a?(Block) && (
      g.add(" {"); g.add(self.arguments.block); g.add("}")
    )
  }
}

LocalVariableAssignment << {
  to_ruby: |g| {
    g.var_scope_declare_local(self.name)
    g.var_scope.variables[self.name] = true
    g.add(self.name.to_s); g.add(" = "); g.add(self.value)
  }
}

LocalVariableAccessAmbiguous << {
  to_ruby: |g| {
    g.var_scope_has_local?(self.name) &? (
      g.add(self.name.to_s)
    ) ?? (
      g.add("self")

      g.easy_ident?(self.name) &? (
        g.add("."self.name"")
      ) ?? (
        g.add(".__send__("self.name.inspect")")
      )
    )
  }
}

SplatValue << {
  to_ruby: |g| {
    self.value.is_a?(self.class) &? (
      self.value.to_ruby(g)
    ) ?? (
      g.add("*")
      self.value.to_ruby(g)
    )
  }
}

BlockArgument << {
  to_ruby: |g| g.add("&"); g.add(self.body)
}

Block << {
  to_ruby: |g| {
    g.with_nested_var_scope(self) {
      g.var_scope_declare_locals(*self.effective_parameters.names)

      g.add(" "); g.add(self.effective_parameters); g.add(" "); g.add(self.body)
    }
  }
}

BlockLiteral << { to_ruby: |g| g.add(implementation) }

Quest << {
  to_ruby: |g| {
    # TODO: make this output more verbose, but more correct, and without duping the questable
    associated_questable = self.questable.dup
    associated_questable.receiver = self.receiver

    g.add("(")
      g.add("Rubinius::Type.object_respond_to?(")
        g.add(self.receiver)
        g.add(", "self.questable.name.inspect").false?")
      g.add(" ? ::Myco::Void : ")
      g.add(associated_questable)
    g.add(")")
  }
}

BranchOperator << {
  to_ruby: |g| {
    g.add('::Myco.branch_op(')
      g.add(self.type.inspect); g.add(', ')
      g.add(self.left); g.add(') {')
      g.add(self.right); g.add('}')
  }
}

PipeOperator << {
  to_ruby: |g| g.add(implementation)
}

}