lib/opal/nodes/literal.rb



require 'opal/nodes/base'

module Opal
  module Nodes
    class ValueNode < Base
      handle :true, :false, :self, :nil

      def compile
        push type.to_s
      end
    end

    class NumericNode < Base
      handle :int, :float

      children :value

      def compile
        push value.to_s
        wrap '(', ')' if recv?
      end
    end

    class StringNode < Base
      handle :str

      children :value

      def compile
        push value.inspect
      end
    end

    class SymbolNode < Base
      handle :sym

      children :value

      def compile
        push value.to_s.inspect
      end
    end

    class RegexpNode < Base
      handle :regexp

      children :value, :flags

      def compile
        case value
        when ''
          push('/^/')
        when %r{\?\<\w+\>}
          message = "named captures are not supported in javascript: #{value.inspect}"
          push "self.$raise(new SyntaxError('#{message}'))"
        else
          push "#{Regexp.new(value).inspect}#{flags}"
        end
      end
    end

    module XStringLineSplitter
      def compile_split_lines(value, sexp)
        idx = 0
        value.each_line do |line|
          if idx == 0
            push line
          else
            line_sexp = s()
            line_sexp.source = [sexp.line + idx, 0]
            frag = Fragment.new(line, line_sexp)
            push frag
          end

          idx += 1
        end
      end
    end

    class XStringNode < Base
      include XStringLineSplitter

      handle :xstr

      children :value

      def needs_semicolon?
        stmt? and !value.to_s.include?(';')
      end

      def compile
        compile_split_lines(value.to_s, @sexp)

        push ';' if needs_semicolon?

        wrap '(', ')' if recv?
      end

      def start_line
        @sexp.line
      end
    end

    class DynamicStringNode < Base
      handle :dstr

      def compile
        children.each_with_index do |part, idx|
          push " + " unless idx == 0

          if String === part
            push part.inspect
          elsif part.type == :evstr
            push "("
            push expr(part[1])
            push ")"
          elsif part.type == :str
            push part[1].inspect
          elsif part.type == :dstr
            push "("
            push expr(part)
            push ")"
          else
            raise "Bad dstr part #{part.inspect}"
          end

          wrap '(', ')' if recv?
        end
      end
    end

    class DynamicSymbolNode < Base
      handle :dsym

      def compile
        children.each_with_index do |part, idx|
          push " + " unless idx == 0

          if String === part
            push part.inspect
          elsif part.type == :evstr
            push expr(s(:call, part.last, :to_s, s(:arglist)))
          elsif part.type == :str
            push part.last.inspect
          else
            raise "Bad dsym part"
          end
        end

        wrap '(', ')'
      end
    end

    class DynamicXStringNode < Base
      include XStringLineSplitter

      handle :dxstr

      def requires_semicolon(code)
        stmt? and !code.include?(';')
      end

      def compile
        needs_semicolon = false

        children.each do |part|
          if String === part
            compile_split_lines(part.to_s, @sexp)

            needs_semicolon = true if requires_semicolon(part.to_s)
          elsif part.type == :evstr
            push expr(part[1])
          elsif part.type == :str
            compile_split_lines(part.last.to_s, part)
            needs_semicolon = true if requires_semicolon(part.last.to_s)
          else
            raise "Bad dxstr part"
          end
        end

        push ';' if needs_semicolon
        wrap '(', ')' if recv?
      end
    end

    class DynamicRegexpNode < Base
      handle :dregx

      def compile
        children.each_with_index do |part, idx|
          push " + " unless idx == 0

          if String === part
            push part.inspect
          elsif part.type == :str
            push part[1].inspect
          else
            push expr(part[1])
          end
        end

        wrap '(new RegExp(', '))'
      end
    end

    class InclusiveRangeNode < Base
      handle :irange

      children :start, :finish

      def compile
        helper :range

        push '$range(', expr(start), ', ', expr(finish), ', false)'
      end
    end

    class ExclusiveRangeNode < Base
      handle :erange

      children :start, :finish

      def compile
        helper :range

        push '$range(', expr(start), ', ', expr(finish), ', true)'
      end
    end
  end
end