# Syntax: Ruby

## Ruby Script #1

    #!/usr/bin/env ruby

    # Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
    #
    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    require 'rubygems'

    require 'rexec'
    require 'rexec/daemon'

    require 'rubygems'
    require 'rubydns'

    # To run this command, use the standard daemon syntax as root
    # ./daemon2.rb start

    # You should be able to see that the server has dropped priviledges
    #   # ps aux | grep daemon2.rb
    #   daemon   16555   0.4  0.0    81392   2024   ??  S     3:35am   0:00.28 ruby ../test/daemon2.rb start

    # Test using the following command
    # dig @localhost test.mydomain.org

    # You might need to change the user name "daemon". This can be a user name or a user id.
    RUN_AS = "daemon"

    # UDP Socket does per packet reverse lookups unless this is set.
    UDPSocket.do_not_reverse_lookup = true

    # We need to be root in order to bind to privileged port
    if RExec.current_user != "root"
      $stderr.puts "Sorry, this command needs to be run as root!"
      exit 1
    end

    # The Daemon itself
    class Server < RExec::Daemon::Base
      @@var_directory = File.dirname(__FILE__)

      def self.run
        # Bind to port 53 (UDP)
        socket = UDPSocket.new
        socket.bind("0.0.0.0", 53)

        # Drop priviledges
        RExec.change_user(RUN_AS)

        # Don't buffer output (for debug purposes)
        $stderr.sync = true

        # Use upstream DNS for name resolution (These ones are Orcon DNS in NZ)
        $R = Resolv::DNS.new(:nameserver => ["60.234.1.1", "60.234.2.2"])

        # Start the RubyDNS server
        RubyDNS::run_server(:listen => [socket]) do
          match("test.mydomain.org", :A) do |transaction|
            transaction.respond!("10.0.0.80")
          end

          # Default DNS handler
          otherwise do |transaction|
            logger.info "Passthrough: #{transaction}"
            transaction.passthrough!($R)
          end
        end
      end
    end

    # RExec daemon runner
    Server.daemonize

## Ruby Script #2

    #!/usr/bin/env ruby
    # Simple Operator Expression Parser

    require 'set'

    DEBUG = false

    class Array
      def map_to(with)
        r = {}

        each_with_index { |v,i| r[v] = with[i] }

        return r
      end
    end

    module Expression
      class Context
        def initialize(operators, values)
          @values = values
          @operators = operators
        end

        def value_of (key)
          @values[key]
        end

        def call (op, args)
          @operators.call(op, args)
        end
      end

      class Constant
        def initialize(value)
          @value = value
        end

        def evaluate (ctx)
          return @value
        end
      end

      class Identifier
        def initialize(name)
          @name = name
        end

        def evaluate (ctx)
          ctx.value_of(@name)
        end
      end

      class Operator
        def initialize(name, args = [])
          @name = name
          @args = args
        end

        def name
          @name
        end

        def evaluate (ctx)
          ctx.call(@name, @args.collect { |a| a.evaluate(ctx) })
        end

        def args
          @args
        end
      end

      class Brackets
        def initialize(node)
          @node = node
        end

        def evaluate (ctx)
          @node.evaluate(ctx)
        end
      end

      class Parser
        def initialize(ops, expr)
          @identifiers = []
          @operators = ops

          # Tokens and expressions line up
          @expressions = []
          @tokens = []

          @top = nil

          parse(expr)
        end

        def evaluate (ctx)
          @expressions.collect do |expr|
            expr != nil ? expr.evaluate(ctx) : nil
          end
        end

        def identifiers
          @identifiers
        end

        def tokens
          @tokens
        end
      private
        def parse(expr)
          symbols = @operators.keys + ["(", ")"]
          tokenizer = Regexp.union(Regexp.union(*symbols), /[A-Z]+/)

          @tokens = expr.scan(tokenizer)
          @expressions = [nil] * @tokens.size

          @identifiers = Set.new(expr.scan(/[A-Z]+/))

          @top, i = process_expression

          if DEBUG
            puts "Processed #{i} tokens..."
            puts "Tokens: " + @tokens.join(" ")
            puts @top.inspect
            puts @expressions.inspect
          end
        end

        def process_expression(i = 0)
          ast = []
          ops = {}
          while i < @tokens.size
            t = @tokens[i]

            if t == "("
              result, i = process_expression(i+1)
              ast += result
            elsif t == ")"
              break
            else
              result = process_token(i)
              ast << result
            end

            if result.class == Operator
              ops[result.name] ||= []
              # Store the index
              ops[result.name] << (ast.size - 1)
            end

            i += 1
          end

          #puts ast.inspect

          # We need to sort the list of operators now
          # [c, infix, prefix, c]

          @operators.order.each do |name|
            op_kind = @operators.kind(name)
            next unless ops[name]

            ops[name].each do |loc|
              op = ast[loc]

              if op_kind == :prefix
                rhs = find_subexpression(ast, loc, RHS_SEARCH)
                op.args << ast[rhs]
                ast[rhs] = nil
              elsif op_kind == :infix
                lhs = find_subexpression(ast, loc, LHS_SEARCH)
                rhs = find_subexpression(ast, loc, RHS_SEARCH)
                op.args << ast[lhs]
                op.args << ast[rhs]
                ast[lhs] = ast[rhs] = nil
              elsif op_kind == :postfix
                lhs = find_subexpression(ast, loc, LHS_SEARCH)
                op.args << ast[lhs]
                ast[rhs] = nil
              end
            end
          end

          return [ast.uniq, i]
        end

        RHS_SEARCH = 1
        LHS_SEARCH = -1

        def find_subexpression(ast, loc, dir)
          while loc >= 0 && loc < ast.size
            loc += dir
            return loc if ast[loc] != nil
          end

          return nil
        end

        def process_token(i)
          t = @tokens[i]

          if @operators.key? t
            tok = Operator.new(t)
          elsif t.match /[A-Z]+/
            tok = Identifier.new(t)
          else
            tok = Constant.new(t)
          end

          @expressions[i] = tok
          return tok
        end
      end

      TYPE = 0
      FUNC = 1
      class Operators
        def initialize
          @operators = {}
          @order = []
        end

        def add(sym, kind, &block)
          @operators[sym] = [kind, block]
          @order << sym
        end

        def order
          @order
        end

        def keys
          @operators.keys
        end

        def key? k
          @operators.key? k
        end

        def kind k
          @operators[k][0]
        end

        def call(k, args)
          @operators[k][1].call(*args)
        end
      end
    end