zOs/REXX/SCANWIN

/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
**********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanRead', 'm',
        , "oReset call scanWinReset m, arg, arg2",
        , "jOpen call scanWinOpen m, arg(3)",
        , "jClose call scanReadClose m",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)',
        , 'isWindow 1'
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner ------------------------------*/
scanWin: procedure expose m.
parse arg rdr, wOpts
    return oNew(m.class_ScanWin, rdr, wOpts)

/*--- set the reader and window attributes of scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, winOpt
    return scanSqlOpt(scanWinOpt(oMutate(m, m.class_ScanWin), winOpt))

/*--- set the window scanner attributes -----------------------------*/
scanWinOpt: procedure expose m.
parse arg m, cuLe wiLi wiBa
    if pos('@', cuLe) > 0 then
        parse var cuLe cuLe '@' m.m.cutPos
    else
        m.m.cutPos = 1
    cuLe = word(cuLe 72, 1)
    m.m.cutLen = cuLe                      /* fix recLen */
    wiLe = cuLe * (1 + word(wiLi 5, 1))
    m.m.posMin = word(wiba 3, 1) * cuLe    /* room to go back */
    m.m.posLim = m.m.posMin + wiLe
    m.m.winTot = m.m.posLim + wiLe
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, line0
    call jOpen m.m.rdr, '<'
    if line0 == '' then
        return scanSetPos0(m, 1 1)
    if length(line0) // m.m.cutLen \== 0 then
        line0 = line0||left('', m.m.cutLen - length(line0)//m.m.cutLen)
    return scanSetPos0(m, (1 - length(line0) % m.m.cutLen) 1, line0)
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left -----------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - ((m.m.pos-1) // m.m.cutLen + 1 + m.m.posMin)
        call assert 'dlt >= 0 & dlt // m.m.cutLen = 0','dlt m.m.cutLen'
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    r = m.m.rdr
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(r) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.r, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot',
              , 'm.m.winTot length(m.m.src) m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start ----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment --------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if m.m.scanComment \== '' then do
        cl = length(m.m.scanComment)
        if scanLook(m, cl) == m.m.scanComment then do
            np = scanWinNlPos(m)
            if np = m.m.pos then
                np = np +  m.m.cutLen
            if np >= m.m.pos + cl then do
                m.m.tok = substr(m.m.src, m.m.pos, np - m.m.pos)
                m.m.pos = np
                return 1
                end
            end
        end
    if m.m.scanNestCom then
        if scanLit(m, '/*') then do
            tk = substr(m.m.src, m.m.pos, m.m.cutLen + 2)
            call scanTextCom m, , '*/'
            if \ scanLit(m, '*/') then
                 call scanErr m, 'nested comment after /* not finished'
            if pos('*/', tk) < 1 then
                m.m.tok = left(tk, m.m.cutLen - 5)'...*/'
            else
                m.m.tok = left(tk, pos('*/', tk) + 1)
            return 1
            end
    m.m.tok = ''
    return 0
endProcedure scanWinCom


/*--- scan nl -------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    m.m.tok = ''
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np = m.m.pos then
        return 0
    if unCond == '?' then
        return 1
    m.m.tok = substr(m.m.pos, np-m.m.pos)
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position -------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if scanEnd(m) then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        p = word(p, 1)
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   ************************************************/