/'* \file fb-doc_emitters.bas
\brief Default emitter to create pseudo C source, \#INCLUDEs the other emitters

This file is the main file for emitters. It contains some helper 
functions to extract original source code and comments. And it 
contains the standard emitter to translate the FB source to pseudo C 
source.

The pseudo C emitter is designed to create C source out of the FB 
code including the documentation comments. Comments may be multi 
line comment blocks or line end comments. The emitter tries to place 
the comments similar as in the FB source (but a comment inside a 
statement will be placed at the end of the corresponding C statement).

The emitter is designed to be used to generate output for the 
documentation tool-chain back-end. This may be written to the StdOut 
pipe (default) or to one or more files (option \em --fileoutout). It 
also can be used to translate an FB header to C syntax by unsing 
option \em --cstyle.

'/

#INCLUDE ONCE "fb-doc_emitters.bi"

/'* \brief Convenience macro for output '/
#DEFINE Code(_T_) PRINT #.Ocha, _T_;


/'* \brief Handler for exporting comments
\param P the parser calling this handler
\param Stop_ the end position in the input buffer Parser::Buf

Export the comments between the 'last position' and the Stop_ 
position. Then, the Stop_ position gets the new 'last position'.

'/
SUB cEmitComments CDECL(BYVAL P AS Parser PTR, BYVAL Stop_ AS INTEGER)
  WITH *P
    VAR i = .SrcBgn
    WHILE i <= Stop_
      SELECT CASE AS CONST .Buf[i]
      CASE 0 : EXIT WHILE
      CASE ASC(!"\n") : Code(NL)
      CASE ASC("""")
        SCAN_QUOTE(.Buf,i)
      CASE ASC("'")
        VAR c = i + 1
        SCAN_SL_COMM(.Buf,i)
        IF .Buf[c] = OPT->JoComm THEN c += 1 : Code("//!" & MID(.Buf, c + 1, i - c))
        IF .Buf[c] = OPT->AdLine THEN c += 1 : Code(MID(.Buf, c + 1, i - c))
        CONTINUE WHILE
      CASE ASC("/") : IF .Buf[i + 1] <> ASC("'") THEN EXIT SELECT
        i += 2
        VAR c = IIF(.Buf[i] = OPT->JoComm, i + 1, 0)
        IF c THEN Code("/**")
        DO
          SELECT CASE AS CONST .Buf[i]
          CASE 0 : EXIT DO
          CASE ASC(!"\n") : IF 0 = c THEN Code(NL) : EXIT SELECT
            Code(MID(.Buf, c + 1, i - c + 1))
            c = i + 1
            IF OPT->Asterix = OPT->GTK_DOC THEN Code("* ")
          CASE ASC("'")
            SELECT CASE AS CONST .Buf[i + 1]
            CASE 0 : EXIT SUB
            CASE ASC("/") : i += 1 : EXIT DO
            END SELECT
          END SELECT : i += 1
        LOOP
        IF c THEN Code(MID(.Buf, c + 1, i - c - 1) & "*/")
      END SELECT : i += 1
    WEND : IF .SrcBgn < i THEN .SrcBgn = i
  END WITH
END SUB


/'* \brief Export the name (including double colons for member functions)
\param P the parser calling this emitter

This property reads the name of a construct from the input buffer 
and emits all words, concatenated by double colons.

\returns the name including dots (colons)

'/
SUB cppNam CDECL(BYVAL P AS Parser PTR)
  WITH *P
    VAR t = .NamTok
    Code(.SubStr(t))
    WHILE t < .TkEnd
      t += 3
      SELECT CASE AS CONST *t
      CASE  .TOK_DOT : Code("::")
      CASE .TOK_WORD : Code(.SubStr(t))
      CASE ELSE : EXIT WHILE
      END SELECT
    WEND
  END WITH
END SUB


/'* \brief Export the name (including dots for member functions)
\param P the parser calling this emitter

This property reads the name of a construct from the input buffer 
and emits all words as in the original source.

\returns the name including dots (colons)

'/
SUB cNam CDECL(BYVAL P AS Parser PTR)
  WITH *P
    VAR t = .NamTok, a = *(t + 1), l = *(t + 2)
    WHILE t < .TkEnd
      t += 3
      SELECT CASE AS CONST *t
      CASE .TOK_DOT, .TOK_WORD : l += *(t + 2)
      CASE ELSE : EXIT WHILE
      END SELECT
    WEND : Code(MID(.Buf, a + 1, l))
  END WITH
END SUB


/'* \brief Create pseude C declaration
\param P the parser calling this emitter

Create a declaration for the construct at the current parser 
position using FB style. All FB keywords gets mangled to a single 
word to get a FreeBasic look-and-feel in the documentation. Ie we emit

 - "SUB Name();" (instead of "void Name(void);")
 - "INTEGER varnam" (instead of "int varname")
 - "BYREF_AS_STRING strng" (instead of "char** strng")
 - "FUNCTION_CDECL_AS_SINGLE xyz CDECL(BYVAL_AS_BYTE C)" (instead of 
   "float xyz(char C)")
 - ...

The C lexer of the back-end (gtk-doc or Doxygen) interprets this 
single word as a C type declaration (or macro) and can handle FB 
source code that way.

Exeptions handled in this SUB:

 - TypTok = 0: no type, emit only the name (with a space in from)
 - NymTok = 0: no name, emit type and exit

'/
SUB cppCreateTypNam CDECL(BYVAL P AS Parser PTR)
  WITH *P
    IF .TypTok THEN
      IF .Co1Tok THEN Code(.SubStr(.Co1Tok) & "_")
                      Code(.SubStr(.TypTok))
      IF .Co2Tok THEN Code("_" & .SubStr(.Co2Tok))
      FOR i AS INTEGER = 1 TO .PtrCount
                      Code("_" & .SubStr(.PtrTok)) : NEXT
      IF .NamTok THEN Code(" ") ELSE EXIT SUB
    ELSE
      Code(" ")
    END IF

    IF .NamTok > .TypTok _
      THEN cEmitComments(P, *(.NamTok + 1))

    cppNam(P)
    IF .DimTok THEN   Code(.Bracket(.DimTok))
    IF .BitTok THEN   Code(.BitIni)
    IF .IniTok THEN   Code(.VarIni)
  END WITH
END SUB


/'* \brief The dimension of a variable
\param P the parser calling this emitter

This property reads the dimension of a construct from the input buffer.

\returns the name including round or squared brackets

'/
SUB cArrDim CDECL(BYVAL P AS Parser PTR)
  WITH *P
    VAR t = .DimTok, kl = 0
    DO
      SELECT CASE AS CONST *t
      CASE .TOK_COMMA :           Code(" + 1][")
      CASE .TOK_BROPN : kl += 1 : Code("[")
      CASE .TOK_BRCLO : kl -= 1 : Code(" + 1]") : IF kl <= 0 THEN EXIT DO
      CASE ELSE                 : Code(.SubStr(t))
      END SELECT : t += 3
    LOOP UNTIL t >= .TkEnd
  END WITH
END SUB


/'* \brief Create C declaration
\param P the parser calling this emitter

Create a C declaration for the construct at the current parser 
position. All FB keywords gets translated to their C expressions. Ie 
we emit

 - "void Name(void);" (instead of "SUB Name();")
 - "int varname" (instead of "INTEGER varnam")
 - "char** strng" (instead of "BYREF_AS_STRING strng") 
 - "float xyz(char C)" (instead of "FUNCTION_CDECL_AS_SINGLE 
    xyz CDECL(BYVAL_AS_BYTE C)")
 - ...

The C source code is very useful when you use a library compiled 
with FreeBasic in a other language like C or C++. fb-doc can 
auto-generate the header files (just check initializers and array 
dimensioning manually).

Exeptions handled in this SUB:

 - TypTok = 0: no type, emit only the name (with a space in from)
 - NymTok = 0: no name, emit type and exit

'/
SUB cCreateTypNam CDECL(BYVAL P AS Parser PTR)
  WITH *P
    IF .TypTok THEN
      IF .Co1Tok THEN            Code("const ")

      SELECT CASE AS CONST *.TypTok
      CASE            .TOK_BYTE : Code("signed char")
      CASE            .TOK_UBYT : Code("unsigned char")
      CASE            .TOK_SHOR : Code("short")
      CASE            .TOK_USHO : Code("unsigned short")
      CASE            .TOK_STRI : Code("char*") ' ??? FB_STRING, g_string
      CASE            .TOK_SING : Code("float")
      CASE            .TOK_DOUB : Code("double")
      CASE            .TOK_ZSTR : Code("char")
      CASE            .TOK_WSTR : Code("wchar")
      CASE .TOK_INT,  .TOK_LONG : Code("int")
      CASE .TOK_UINT, .TOK_ULNG : Code("unsigned int")
      CASE            .TOK_LINT : Code("long int")
      CASE            .TOK_ULIN : Code("unsigned long int")
      CASE ELSE                 : Code(.SubStr(.TypTok))
      END SELECT

      IF .Co2Tok THEN             Code(" " & .SubStr(.Co2Tok))
      IF .By_Tok ANDALSO *.By_Tok = .TOK_BYRE THEN .PtrCount += 1
      FOR i AS INTEGER = 1 TO .PtrCount
                                  Code("*") : NEXT
      IF .NamTok THEN Code(" ") ELSE EXIT SUB
    END IF

    IF .NamTok > .TypTok _
      THEN cEmitComments(P, *(.NamTok + 1))

    SELECT CASE AS CONST *.StaTok
    CASE .TOK_TYPE, .TOK_DIM, .TOK_RDIM, .TOK_COMM, .TOK_EXRN
      IF .FunTok THEN Code("(*") : cNam(P) : Code(")") : EXIT SUB
    END SELECT
    cNam(P)
    IF .DimTok THEN cArrDim(P)
    IF .BitTok THEN Code(.BitIni)
    IF .IniTok THEN Code(.VarIni)
  END WITH
END SUB


DECLARE SUB cppCreateFunction CDECL(BYVAL AS Parser PTR)
/'* \brief Handler for a parameter declaration
\param P the parser calling this handler

Generate a declaration for a parameter list. The declaration may be 
empty (), may have no name (prototype declaration) or may by an 
ellipsis ( ... ). Initializers get emitted "as-is".

We emit a space in front and a comma behind the parameter. When done 
the first space gets replaced by a '(' and the last comma gets 
replaced by a ')'.

'/
SUB cppEntryListParameter CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    IF .FunTok THEN
      IF .By_Tok THEN Code(.SubStr(.By_Tok) & "_")
      IF .CalTok THEN Code("_" & .SubStr(.CalTok))
      IF .DivTok THEN Code("_" & .SubStr(.DivTok))
      cppCreateFunction(P)
    ELSEIF .TypTok THEN
      IF .By_Tok THEN Code(.SubStr(.By_Tok) & "_")
      Code(.SubStr(.As_Tok) & "_")
      cppCreateTypNam(P)
    ELSEIF *.NamTok = .TOK_TRIDO THEN
      Code("...)") : EXIT SUB
    END IF

    IF *.CurTok <> .TOK_BRCLO      THEN Code(", ") : EXIT SUB
    cEmitComments(P, *(.CurTok + 1)) : Code(")")
  END WITH
END SUB


DECLARE SUB cCreateFunction CDECL(BYVAL AS Parser PTR)
/'* \brief Handler for a parameter declaration
\param P the parser calling this handler

Generate a declaration for a parameter list. The declaration may be 
empty (), may have no name (prototype declaration) or may by an 
ellipsis ( ... ). Initializers get emitted "as-is", this means you 
have to check if they contain FB keywords (and translate manually, if
so).

We emit a space in front and a comma behind the parameter. When done 
the first space gets replaced by a '(' and the last comma gets 
replaced by a ')'.

'/
SUB cEntryListParameter CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    IF .FunTok THEN
      cCreateFunction(P)
    ELSEIF .TypTok THEN
      cCreateTypNam(P)
      IF .NamTok ANDALSO .IniTok THEN Code(.VarIni)
    ELSEIF .NamTok = .TOK_TRIDO THEN
      Code("...")
    ELSE
      Code("void")
    END IF

    IF *.CurTok <> .TOK_BRCLO      THEN Code(", ") : EXIT SUB
    cEmitComments(P, *(.CurTok + 1)) : Code(")")
  END WITH
END SUB


/'* \brief Create a function declaration
\param P the parser calling this handler

Generate a declaration for a function (SUB, FUNCTION, DESTRUCTOR, 
CONSTRUCTOR, PROPERTY). We emit a type, a name and a parameter list.

Exceptions:

 - in a declaration a CONSTRUCTOR or DESTRUCTOR have no name

'/
SUB cppCreateFunction CDECL(BYVAL P AS Parser PTR)
  WITH *P
    IF .DivTok THEN Code(.SubStr(.DivTok) & " ")

    SELECT CASE AS CONST *.FunTok
    CASE .TOK_CTOR
      IF .NamTok THEN Code(.SubStr(.NamTok) & "::" & .SubStr(.NamTok)) ELSE _
                      Code(.BlockNam)
    CASE .TOK_DTOR
      IF .NamTok THEN Code(.SubStr(.NamTok) & "::~" & .SubStr(.NamTok)) ELSE _
                      Code("~" & .BlockNam)
    CASE ELSE 
                      Code(.SubStr(.FunTok))
      IF .CalTok THEN Code("_" & .SubStr(.CalTok))
      IF .AliTok THEN Code("_" & .SubStr(.AliTok))
      IF .TypTok ANDALSO .As_Tok THEN _
                      Code("_" & .SubStr(.As_Tok) & "_")
      cppCreateTypNam(P)
    END SELECT : IF 0 = .ParTok THEN Code("()") : EXIT SUB

    cEmitComments(P, *(.ParTok + 1))
    Code(" (")
    .parseListPara(@cppEntryListParameter)
    IF .ListCount <= 0 THEN Code(")")
  END WITH
END SUB


/'* \brief Create a function declaration
\param P the parser calling this handler

Generate a declaration for a function (SUB, FUNCTION, DESTRUCTOR, 
CONSTRUCTOR, PROPERTY). We emit a type, a name and a parameter list.

Exceptions:

 - in a declaration a CONSTRUCTOR or DESTRUCTOR have no name

'/
SUB cCreateFunction CDECL(BYVAL P AS Parser PTR)
  WITH *P
    SELECT CASE AS CONST *.FunTok
    CASE .TOK_CTOR
      Code(.SubStr(.FunTok) & " ")
      IF .NamTok THEN Code(.SubStr(.NamTok) & "." & .SubStr(.NamTok)) _
                 ELSE Code(.BlockNam)
    CASE .TOK_DTOR
      Code(.SubStr(.FunTok) & " ")
      IF .NamTok THEN Code(.SubStr(.NamTok) & "." & .SubStr(.NamTok)) _
                 ELSE Code(.BlockNam)
    CASE ELSE 
      IF 0 = .TypTok THEN Code("void ")
      cCreateTypNam(P)
    END SELECT : IF 0 = .ParTok THEN Code("(void)") : EXIT SUB

    cEmitComments(P, *(.ParTok + 1))
    Code(" (")
    .parseListPara(@cEntryListParameter)
    IF .ListCount <= 0 THEN Code("void)")
  END WITH
END SUB


/'* \brief Handler for an include line
\param P the parser calling this handler

Generate an \#include line. This line are useful for diagrams in 
Doxygen.

'/
/'* \brief Emitter to generate an \#INCLUDE translation
\param P the parser calling this emitter

This emitter gets called when the parser finds an \#INCLUDE 
statement. It creates a C translation and sends it to the output 
stream. When option \em --tree is given it checks if the file has 
been done allready. If not, it creates a new #Parser and starts its 
scanning process.

'/
SUB c_include CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))
    VAR n = .SubStr(.NamTok)
    Code("#include " & n)
    IF 0 = OPT->InTree THEN EXIT SUB

    STATIC AS STRING done, o_path
    n = MID(n, 2, LEN(n) - 2)
    VAR f = !"\n" & CURDIR & SLASH & n & !"\n"
    IF .DivTok THEN IF INSTR(done, f) THEN EXIT SUB
    done &= f

    VAR fnr = FREEFILE : IF OPEN(n FOR INPUT AS #fnr) THEN EXIT SUB ELSE CLOSE #fnr
    VAR oldpath = CURDIR
    VAR c = INSTRREV(n, SLASH) : IF c THEN CHDIR(LEFT(n, c - 1))
    VAR pars = NEW Parser(OPT->EmitIF)

    SELECT CASE AS CONST OPT->RunMode
    CASE OPT->FILE_OUT
      VAR o_pathlen = LEN(o_path)
      IF 0 = o_pathlen THEN
        VAR d = CURDIR
        VAR r = OPT->checkDir(OPT->OutPath)
        o_path = CURDIR & SLASH
        CHDIR d
        IF r THEN ERROUT(" --> Couldn't create path: " & OPT->OutPath) : EXIT SUB
        o_pathlen = LEN(o_path)
      END IF

      PRINT : MSG_LINE(n)
      VAR f = o_path & LEFT(n, INSTRREV(n, "."))
      IF LCASE(RIGHT(n, 3)) = ".bi" THEN f &= "h" ELSE f &= "c"

      IF c THEN
        o_path &= LEFT(n, c)
        VAR d = CURDIR
        VAR r = OPT->checkDir(o_path)
        CHDIR d
        IF r THEN ERROUT(" --> Couldn't create path.") : EXIT SELECT
      END IF

      pars->Ocha = FREEFILE
      IF OPEN(f FOR OUTPUT, AS #pars->Ocha) THEN
        ERROUT("Couldn't write " & f)
      ELSE
        pars->File_(MID(n, c + 1))
        CLOSE #pars->Ocha
        ERROUT(" --> " & f)
      END IF
      IF c THEN o_path = LEFT(o_path, o_pathlen)
    CASE OPT->SCAN_LIST
      PRINT : MSG_LINE(n)
      pars->Ocha = .Ocha
      pars->File_(MID(n, c + 1))
      ERROUT(" --> scanned " & f)
    CASE ELSE 
      pars->Ocha = .Ocha
      pars->File_(MID(n, c + 1))
    END SELECT

    DELETE pars : IF c THEN CHDIR(oldpath)
  END WITH 
END SUB


/'* \brief Emitter to generate a macro translation
\param P the parser calling this emitter

This emitter gets called when the parser finds a macro (\#DEFINE / 
\#MACRO). It generates a C translation of the macro and sends it 
to the output stream.

'/
SUB c_defi_ CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    Code("#define ")
    VAR a = *(.NamTok + 1), e = *(.TkEnd + 1)
    IF *.StaTok = .TOK_MACR THEN
      VAR l = *(.CurTok + 1) - a
      Code(MID(.Buf, a + 1, l) & "  /* (multi line FreeBasic #MACRO) ")
      a += l
      Code(MID(.Buf, a + 1, e - a) & " */ ")
    ELSE
      Code(MID(.Buf, a + 1, e - a))
    END IF
    .SrcBgn = e
  END WITH 
END SUB


/'* \brief Emitter to generate a function translation
\param P the parser calling this emitter

This emitter gets called when the parser finds a function (SUB / 
FUNCTION / PROPERTY / CONSTRUCTOR / DESTRUCTOR). It translates a 
function and its parameter list to C-like code and sends it to the 
output stream. The function body is either empty or contains pseudo 
calls.

'/
SUB c_func_ CDECL(BYVAL P AS Parser PTR) ' ToDo: internal function calls for diagrams
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    OPT->Function_(P)
    Code(" {")

    IF OPT->AllCallees THEN
      VAR t = .CurTok, e = .TkEnd - 6
      WHILE t < e
        VAR c = *(t + 1) - 1
        IF *t >= .TOK_ABST ANDALSO .Buf[c] <> ASC("@") THEN
          VAR l = *(t + 2), s = UCASE(MID(.Buf, c + 2, l)), x = l
          FOR i AS INTEGER = LEN(OPT->Callees) - 2 TO 0 STEP -1
            x = l - 1
            WHILE s[x] = (OPT->Callees[i] AND &b11011111)
              x -= 1
              i -= 1
              IF x < 0 THEN
                SELECT CASE AS CONST OPT->Callees[i]
                CASE ASC(!"\n")
                CASE ASC(  ".")
                  x = i
                  DO : i -= 1 : LOOP UNTIL OPT->Callees[i] = ASC(!"\n") ORELSE i < 1
                  l += x - i
                CASE ELSE : EXIT WHILE
                END SELECT
                cEmitComments(P, c + 1)
                Code(" " & MID(OPT->Callees, i + 2, l) & "();")
                EXIT FOR
              END IF
            WEND : DO : i -= 1 : LOOP UNTIL OPT->Callees[i] = ASC(!"\n") ORELSE i < 1
          NEXT
        END IF : t += 3
      WEND
    END IF

    cEmitComments(P, *(.TkEnd + 1) - 1)
    Code("};")
  END WITH
END SUB


/'* \brief Emitter to generate a declaration translation
\param P the parser calling this emitter

This emitter gets called when the parser is in a declaration (VAR / 
DIM / CONST / COMMON / EXTERN / STATIC). It generates a C 
translation for each variable name and sends it (them) to the output 
stream. Documantation comments get emitted at the appropriate place. 
Each declaration get a single line, even if the original source code 
is a comma-separated list. (This may destroy line synchonisation, so 
it's better to place each declaration in a single line.)

'/
SUB c_decl_ CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    IF 0 = .ListCount THEN
      SELECT CASE AS CONST *.StaTok
      CASE .TOK_CONS : Code("const ")
      CASE .TOK_STAT : Code("static ")
      CASE .TOK_COMM : Code("common ")
      CASE .TOK_EXRN : Code("extern ")
      CASE .TOK_TYPE : Code("typedef ")
        IF 0 = .FunTok ANDALSO .TypTok > .NamTok THEN Code("struct ")
      END SELECT
    END IF

    IF     .FunTok THEN : OPT->Function_(P)
    ELSEIF .TypTok THEN : OPT->Variable_(P)
    ELSE
      IF 0 = .ListCount THEN Code("VAR ")
                             Code(.SubStr(.NamTok))
      IF .BitTok THEN        Code(.BitIni)
      IF .IniTok THEN        Code(.VarIni)
    END IF
    IF *.CurTok <= .TOK_EOS THEN Code(";") : EXIT SUB
    IF .NamTok > .TypTok _
      THEN Code(", ") _
      ELSE Code("; ")
  END WITH 
END SUB


/'* \brief Handler for an enumerator entry (inside ENUM block)
\param P the parser calling this handler

Generate an enumerator in an enum block. Name, initializers and 
documentation comments are emitted. Logical operators like SHL or 
AND are not handled jet.

'/
SUB cEntryBlockENUM CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    IF 0 = .ListCount THEN Code(STRING(.LevelCount * 2, " "))
    Code(.SubStr(.NamTok))
    IF .IniTok THEN Code(.VarIni)
    IF *.CurTok <> .TOK_END THEN Code(", ")
  END WITH
END SUB


DECLARE SUB c_Block CDECL(BYVAL AS Parser PTR)

/'* \brief Handler for a context line (TYPE / UNION block)
\param P the parser calling this handler

Generate a line in an struct or union block. Type, name, 
initializers and documentation comments are emitted. Logical 
operators like SHL or AND are not handled jet.

'/
SUB cEntryBlockTypeUnion CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    SELECT CASE AS CONST *.Tk1
    CASE .TOK_PRIV : Code("private:")
    CASE .TOK_PROT : Code("protected:")
    CASE .TOK_PUBL : Code("public:")
    CASE .TOK_ENUM, .TOK_UNIO, .TOK_TYPE, .TOK_CLAS : c_Block(P)
    CASE ELSE
      IF 0 = .ListCount       THEN Code(STRING(.LevelCount * 2, " "))
      IF *.Tk1 = .TOK_DECL THEN OPT->Function_(P) : Code(";") : EXIT SUB
      IF .FunTok THEN OPT->Function_(P) _
                 ELSE OPT->Variable_(P)
      IF *.CurTok <= .TOK_EOS THEN Code(";") : EXIT SUB
      IF .NamTok > .TypTok THEN Code(",") ELSE Code("; ")
    END SELECT
  END WITH
END SUB


/'* \brief Emitter to generate a block translation
\param P the parser calling this emitter

This emitter gets called when the parser finds a block (TYPE / UNION 
/ ENUM). It generates a C translation of the block and sends it to 
the output stream.

'/
SUB c_Block CDECL(BYVAL P AS Parser PTR)
  WITH *P
    cEmitComments(P, *(.Tk1 + 1))

    IF .LevelCount THEN Code(STRING(.LevelCount * 2, " "))
    SELECT CASE AS CONST *.Tk1
    CASE .TOK_TYPE, .TOK_CLAS
      IF OPT->Types = OPT->FB_STYLE THEN
        Code("class " & .BlockNam & "{")
        .parseBlockTyUn(@cEntryBlockTypeUnion)
        .BlockNam = ""
      ELSE
        IF 0 = .LevelCount ANDALSO LEN(.BlockNam) THEN Code("typedef ")
        Code("struct " & .BlockNam & "{")
        .parseBlockTyUn(@cEntryBlockTypeUnion)
      END IF
    CASE .TOK_UNIO
      IF 0 = .LevelCount ANDALSO LEN(.BlockNam) THEN Code("typedef ")
      Code("union " & .BlockNam & "{")
      .parseBlockTyUn(@cEntryBlockTypeUnion)
    CASE .TOK_ENUM
      IF 0 = .LevelCount ANDALSO LEN(.BlockNam) THEN Code("typedef ")
      Code("enum " & .BlockNam & "{")
      .parseBlockEnum(@cEntryBlockENUM)
    CASE ELSE : Code("-???-")
    END SELECT

    cEmitComments(P, *(.Tk1 + 1))
    IF .LevelCount THEN Code(STRING(.LevelCount * 2, " "))
    IF LEN(.BlockNam) THEN Code("} " & .BlockNam & ";") _
                      ELSE Code("};")
  END WITH
END SUB


/'* \brief Emitter for an error message
\param P the parser calling this handler

Generate an error output. When the parser detects an error it calls 
this function. Depending on the run-mode we do or do not emit an 
information. In \em --geany mode the error message gets shown in the 
status line (or in the debug window).

'/
SUB c_error CDECL(BYVAL P AS Parser PTR)
  WITH *P
    SELECT CASE AS CONST OPT->RunMode
    CASE OPT->GEANYFILTER ': EXIT SUB ' or shall we output?
      Code(NL & "'!!! " & PROG_NAME & .ErrMsg & "!" & NL)
    CASE ELSE
      ERROUT("==> " & PROG_NAME & .ErrMsg & "!")
    END SELECT
  END WITH
END SUB


/'* \brief Emitter to be calles before parsing
\param P the parser calling this emitter

This emitter gets called before the parser starts its parsing 
process. It initializes the FB source code emission.

'/
SUB c_Init CDECL(BYVAL P AS Parser PTR)
  P->SrcBgn = 0
END SUB


/'* \brief Emitter to be calles after parsing
\param P the parser calling this emitter

This emitter gets called after the parser ands its parsing process. 
It sends the rest of the FB source code to the output stream.

'/
SUB c_exit CDECL(BYVAL P AS Parser PTR)
  cEmitComments(P, P->Fin)
END SUB


' place the handlers in the emitter interface
WITH_NEW_EMITTER("C_Source")

  ._Error_ = @c_error
  
    .Defi_ = @c_defi_
    .Incl_ = @c_include
    .Func_ = @c_func_
    .Decl_ = @c_decl_
    .Enum_ = @c_Block
    .Unio_ = @c_Block
    .Clas_ = @c_Block
  
   ._Init_ = @c_Init
   ._Exit_ = @c_exit
END WITH


/'* \brief Handler for exporting FreeBasic source code
\param P the parser calling this handler
\param E the end position in the input buffer Parser::Buf

Extract original source code from the input buffer Parser::Buf. The 
code starts at the last position and gets extracted up to the line end 
before the given position. This line end gets stored as the new 'last
position'.

'/
SUB cEmitSource CDECL(BYVAL P AS Parser PTR, BYVAL E AS INTEGER)
  WITH *P
    DO : IF E > 0 THEN E -= 1 ELSE EXIT SUB
    LOOP UNTIL .Buf[E] = ASC(!"\n")
    VAR a = .SrcBgn, l = E - a : .SrcBgn = E + 1
    Code(MID(.Buf, a + 1, l + 1))
  END WITH
END SUB


/'* \brief Handler for FIXME
\param P the parser calling this handler

Generate FIXME

'/
SUB geanyInit CDECL(BYVAL P AS Parser PTR)
  P->SrcBgn = 0
END SUB


/'* \brief Handler for FIXME
\param P the parser calling this handler

Generate FIXME

'/
SUB geanyExit CDECL(BYVAL P AS Parser PTR)
  WITH *P
    Code(MID(.Buf, .SrcBgn + 1))
  END WITH
END SUB


#INCLUDE ONCE "fb-doc_emit_callees.bas"
#INCLUDE ONCE "fb-doc_emit_gtk.bas"
#INCLUDE ONCE "fb-doc_emit_doxy.bas"

