parser.y

Path: lib/tmail/parser.y
Last Update: Thu Feb 14 19:07:22 +1100 2008

# # parser.y # # Copyright (c) 1998-2007 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU Lesser General Public License version 2.1. #

class TMail::Parser

  options no_result_var

rule

  content   : DATETIME      datetime   { val[1] }
            | RECEIVED      received   { val[1] }
            | MADDRESS      addrs_TOP  { val[1] }
            | RETPATH       retpath    { val[1] }
            | KEYWORDS      keys       { val[1] }
            | ENCRYPTED     enc        { val[1] }
            | MIMEVERSION   version    { val[1] }
            | CTYPE         ctype      { val[1] }
            | CENCODING     cencode    { val[1] }
            | CDISPOSITION  cdisp      { val[1] }
            | ADDRESS       addr_TOP   { val[1] }
            | MAILBOX       mbox       { val[1] }

  datetime  : day DIGIT ATOM DIGIT hour zone
            # 0   1     2    3     4    5
            #     date month year
                {
                  t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0)
                  (t + val[4] - val[5]).localtime
                }

  day       :  /* none */
            | ATOM ','

  hour      : DIGIT ':' DIGIT
                {
                  (val[0].to_i * 60 * 60) +
                  (val[2].to_i * 60)
                }
            | DIGIT ':' DIGIT ':' DIGIT
                {
                  (val[0].to_i * 60 * 60) +
                  (val[2].to_i * 60) +
                  (val[4].to_i)
                }

  zone      : ATOM
                {
                  timezone_string_to_unixtime(val[0])
                }

  received  : from by via with id for received_datetime
                {
                  val
                }

  from      : /* none */
            | FROM received_domain
                {
                  val[1]
                }

  by        :  /* none */
            | BY received_domain
                {
                  val[1]
                }

  received_domain
            : domain
                {
                  join_domain(val[0])
                }
            | domain '@' domain
                {
                  join_domain(val[2])
                }
            | domain DOMLIT
                {
                  join_domain(val[0])
                }

  via       :  /* none */
            | VIA ATOM
                {
                  val[1]
                }

  with      : /* none */
                {
                  []
                }
            | with WITH ATOM
                {
                  val[0].push val[2]
                  val[0]
                }

  id        :  /* none */
            | ID msgid
                {
                  val[1]
                }
            | ID ATOM
                {
                  val[1]
                }

  for       :  /* none */
            | FOR received_addrspec
                {
                  val[1]
                }

  received_addrspec
            : routeaddr
                {
                  val[0].spec
                }
            | spec
                {
                  val[0].spec
                }

  received_datetime
            :  /* none */
            | ';' datetime
                {
                  val[1]
                }

  addrs_TOP : addrs
            | group_bare
            | addrs commas group_bare

  addr_TOP  : mbox
            | group
            | group_bare

  retpath   : addrs_TOP
            | '<' '>' { [ Address.new(nil, nil) ] }

  addrs     : addr
                {
                  val
                }
            | addrs commas addr
                {
                  val[0].push val[2]
                  val[0]
                }

  addr      : mbox
            | group

  mboxes    : mbox
                {
                  val
                }
            | mboxes commas mbox
                {
                  val[0].push val[2]
                  val[0]
                }

  mbox      : spec
            | routeaddr
            | addr_phrase routeaddr
                {
                  val[1].phrase = Decoder.decode(val[0])
                  val[1]
                }

  group     : group_bare ';'

  group_bare: addr_phrase ':' mboxes
                {
                  AddressGroup.new(val[0], val[2])
                }
            | addr_phrase ':' { AddressGroup.new(val[0], []) }

  addr_phrase
            : local_head             { val[0].join('.') }
            | addr_phrase local_head { val[0] << ' ' << val[1].join('.') }

  routeaddr : '<' routes spec '>'
                {
                  val[2].routes.replace val[1]
                  val[2]
                }
            | '<' spec '>'
                {
                  val[1]
                }

  routes    : at_domains ':'

  at_domains: '@' domain                { [ val[1].join('.') ] }
            | at_domains ',' '@' domain { val[0].push val[3].join('.'); val[0] }

  spec      : local '@' domain { Address.new( val[0], val[2] ) }
            | local            { Address.new( val[0], nil ) }

  local: local_head
       | local_head '.' { val[0].push ''; val[0] }

  local_head: word
                { val }
            | local_head dots word
                {
                  val[1].times do
                    val[0].push ''
                  end
                  val[0].push val[2]
                  val[0]
                }

  domain    : domword
                { val }
            | domain dots domword
                {
                  val[1].times do
                    val[0].push ''
                  end
                  val[0].push val[2]
                  val[0]
                }

  dots      : '.'     { 0 }
            | '.' '.' { 1 }

  word      : atom
            | QUOTED
            | DIGIT

  domword   : atom
            | DOMLIT
            | DIGIT

  commas    : ','
            | commas ','

  msgid     : '<' spec '>'
                {
                  val[1] = val[1].spec
                  val.join('')
                }

  keys      : phrase          { val }
            | keys ',' phrase { val[0].push val[2]; val[0] }

  phrase    : word
            | phrase word { val[0] << ' ' << val[1] }

  enc       : word
                {
                  val.push nil
                  val
                }
            | word word
                {
                  val
                }

  version   : DIGIT '.' DIGIT
                {
                  [ val[0].to_i, val[2].to_i ]
                }

  ctype     : TOKEN '/' TOKEN params opt_semicolon
                {
                  [ val[0].downcase, val[2].downcase, decode_params(val[3]) ]
                }
            | TOKEN params opt_semicolon
                {
                  [ val[0].downcase, nil, decode_params(val[1]) ]
                }

  params    : /* none */
                {
                  {}
                }
            | params ';' TOKEN '=' QUOTED
                {
                  val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"')
                  val[0]
                }
            | params ';' TOKEN '=' TOKEN
                {
                  val[0][ val[2].downcase ] = val[4]
                  val[0]
                }

  cencode   : TOKEN
                {
                  val[0].downcase
                }

  cdisp     : TOKEN params opt_semicolon
                {
                  [ val[0].downcase, decode_params(val[1]) ]
                }

  opt_semicolon
            :
            | ';'

  atom      : ATOM
            | FROM
            | BY
            | VIA
            | WITH
            | ID
            | FOR

end

[Validate]