//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   30 Jul 08  Brian Frank  Creation
//

**
** SyntaxDoc models a full document as a series of SyntaxLines.
**
class SyntaxDoc
{

  **
  ** Parse an input stream into a document using given rules.
  ** The input stream is guaranteed to be closed.
  **
  static SyntaxDoc parse(SyntaxRules rules, InStream in)
  {
    try
      return SyntaxParser(rules).parse(in)
    finally
      in.close
  }

  ** Internal constructor
  internal new make(SyntaxRules rules)
  {
    this.rules = rules
  }

  ** Rules used to parse this document.
  SyntaxRules rules { private set }

  ** Iterate each line of the document.
  Void eachLine(|SyntaxLine| f)
  {
    for (x := lines; x != null; x = x.next) f(x)
  }

  internal SyntaxLine? lines
}

**************************************************************************
** SyntaxLine
**************************************************************************

**
** SyntaxLine models one parsed line of code
**
class SyntaxLine
{
  ** Internal constructor
  internal new make(Int num) { this.num = num }

  ** One based line number
  const Int num

  ** Iterate each segment span of text in the line
  Void eachSegment(|SyntaxType type, Str text| f)
  {
    for (i:=0; i<segments.size; i+=2)
    {
      f(segments[i], segments[i+1])
    }
  }

  internal SyntaxLine? next
  internal Obj[] segments := [,]  // SyntaxType/Str pairs
}

**************************************************************************
** SyntaxType
**************************************************************************

**
** SyntaxType models a syntax specific segment type such keyword or comment
**
enum class SyntaxType
{
  ** Normal text
  text(null),

  ** Bracket such as '{', '}', '(', ')', '[', or ']'
  bracket("b"),

  ** Language specific keyword
  keyword("i"),

  ** String literal
  literal("em"),

  ** Comment section either to end of line or multi-line block
  comment("q")

  private new make(Str? html) { this.html = html }

  ** HTML element to use for styled output
  internal const Str? html
}