/*
 * hpricot_css.rl
 * ragel -C hpricot_css.rl -o hpricot_css.c
 *
 * Copyright (C) 2008 why the lucky stiff
 */
#include <ruby.h>

#define FILTER(id) \
  rb_funcall2(mod, rb_intern("" # id), fargs, fvals); \
  rb_ary_clear(tmpt); \
  fargs = 1
#define FILTERAUTO() \
  char filt[10]; \
  sprintf(filt, "%.*s", te - ts, ts); \
  rb_funcall2(mod, rb_intern(filt), fargs, fvals); \
  rb_ary_clear(tmpt); \
  fargs = 1
#ifdef HAVE_RUBY_ENCODING_H
#define STRNEW(a, len)  rb_external_str_new((a), (len))
#else
#define STRNEW(a, len)  rb_str_new((a), (len))
#endif
#define PUSH(aps, ape) rb_ary_push(tmpt, fvals[fargs++] = STRNEW(aps, ape - aps))
#define P(id) printf(id ": %.*s\n", te - ts, ts);

%%{
  machine hpricot_css;

  action a {
    aps = p;
  }

  action b {
    ape = p;
    PUSH(aps, ape);
  }

  action c {
    ape = p;
    aps2 = p;
  }

  action d {
    ape2 = p;
    PUSH(aps, ape);
    PUSH(aps2, ape2);
  }

  commas    = space* "," space*;
  traverse  = [>+~];
  sdot      = "\\.";
  utfw      = alnum | "_" | "-" |
              (0xc4 0xa8..0xbf) | (0xc5..0xdf 0x80..0xbf) |
              (0xe0..0xef 0x80..0xbf 0x80..0xbf) |
              (0xf0..0xf4 0x80..0xbf 0x80..0xbf 0x80..0xbf);
  utfword   = utfw+;
  utfname   = (utfw | sdot)+;
  quote1    = "'" [^']* "'";
  quote2    = '"' [^"]* '"';

  cssid     = "#" %a utfname %b;
  cssclass  = "." %a utfname %b;
  cssname   = "[name=" %a utfname %b "]";
  cssattr   = "[" %a utfname %c space* [^ \n\t]? "=" %d space* (quote1 | quote2 | [^\]]+) "]";
  csstag    = utfname >a %b;
  cssmod    = ("even" | "odd" | (digit | "n" | "+" | "-")* );
  csschild  = ":" %a ("only" | "nth" | "last" | "first") "-child" %b ("(" %a cssmod %b ")")?;
  csspos    = ":" %a ("nth" | "eq" | "gt" | "lt" | "first" | "last" | "even" | "odd") %b ("(" %a digit+ %b ")")?;
  pseudop   = "(" [^)]+ ")";
  pseudoq   = "'" (pseudop+ | [^'()]*) "'" |
              '"' (pseudop+ | [^"()]*) '"' |
                  (pseudop+ | [^"()]*);
  pseudo    = ":" %a utfname %b ("(" %a pseudoq %b ")")?;

  main     := |*
    cssid      => { FILTER(ID); };
    cssclass   => { FILTER(CLASS); };
    cssname    => { FILTER(NAME); };
    cssattr    => { FILTER(ATTR); };
    csstag     => { FILTER(TAG); };
    cssmod     => { FILTER(MOD); };
    csschild   => { FILTER(CHILD); };
    csspos     => { FILTER(POS); };
    pseudo     => { FILTER(PSUEDO); };
    commas     => { focus = rb_ary_new3(1, node); };
    traverse   => { FILTERAUTO(); };
    space;
  *|;

  write data nofinal;
}%%

VALUE hpricot_css(VALUE self, VALUE mod, VALUE str, VALUE node)
{
  int cs, act, eof;
  char *p, *pe, *ts, *te, *aps, *ape, *aps2, *ape2;

  int fargs = 1;
  VALUE fvals[6];
  VALUE focus = rb_ary_new3(1, node);
  VALUE tmpt = rb_ary_new();
  rb_gc_register_address(&focus);
  rb_gc_register_address(&tmpt);
  fvals[0] = focus;

  if (TYPE(str) != T_STRING)
    rb_raise(rb_eArgError, "bad CSS selector, String only please.");

  StringValue(str);
  p = RSTRING_PTR(str);
  pe = p + RSTRING_LEN(str);

  %% write init;
  %% write exec;

  rb_gc_unregister_address(&focus);
  rb_gc_unregister_address(&tmpt);
  return focus;
}
