New parser

openid
Marcin Kulik 13 years ago
parent 6eeafdf699
commit 766fe57bf1

@ -5,257 +5,477 @@ class AsciiIo.VT
@data = ''
@resetTerminal()
@render()
@compilePatterns()
# @compilePatterns()
handleData: (data) ->
if data.match(/^\x1b[\x00-\x1f]/)
@handleControlCharacter(data[1])
return 2
else if match = data.match(/^(\x1b\x5d|\x9d).*?(\x1b\\\\|\x9c|\x07)/)
# OSC seq
return match[0].length
else if match = data.match(/^(\x1b[PX_^]|[\x90\x98\x9e\x9f]).*?(\x1b\\\\|\x9c)/)
# DCS/SOS/PM/APC seq
return match[0].length
else if match = data.match(/^(?:\x1b\x5b|\x9b)([\x30-\x3f]*?)[\x20-\x2f]*?[\x40-\x7e]/)
# Control sequences
@handleControlSequence(match[0], match[1], match)
return match[0].length
else if match = data.match(/^\x1b[\x20-\x2f]*?[\x30-\x3f]/)
@handlePrivateEscSeq(match[0])
return match[0].length
else if match = data.match(/^\x1b[\x20-\x2f]*?[\x40-\x5a\x5c-\x7e]/) # excluding \x5b "["
@handleStandardEscSeq(match[0])
return match[0].length
else if data.match(/^\x1b\x7f/) # DELETE
return 2
else if data.match(/^[\x00-\x1a\x1c-\x1f]/) # excluding \x1b "ESC"
@handleControlCharacter(data[0])
return 1
else if match = data.match(/^([\x20-\x7e]|\xe2..|[\xa1-\xfe])+/)
@handlePrintableCharacters(match[0])
return match[0].length
else if data[0] is "\x7f"
# DELETE, always and everywhere ignored
return 1
else if data.match(/^[\x80-\x9f]/)
@handleControlCharacter(data[0])
return 1
else if data[0] is "\xa0"
# Same as SPACE (\x20)
@handlePrintableCharacters(' ')
return 1
else if data[0] is "\xff"
# Same as DELETE (\x7f)
return 1
else
return 0
handleControlCharacter: (char) ->
switch char
when "\x07"
@bell()
when "\x08"
@backspace()
when "\x09"
@buffer.goToNextHorizontalTabStop()
# @tab()
when "\x0a"
@lineFeed()
when "\x0b"
@verticalTab()
when "\x0c"
@formFeed()
when "\x0d"
@carriageReturn()
when "\x84"
@index() # "D"
when "\x85"
@newLine() # "E"
when "\x88"
@setHorizontalTabStop() # "H"
when "\x8d"
@reverseIndex() # "M"
noop: ->
# handleC0ControlSet: (data) ->
# console.log 'handling C0'
handlePrintableCharacters: (text) ->
@buffer.print text
handleStandardEscSeq: (data) ->
last = data[data.length - 1]
if last.match(/[\x40-\x5f]/)
# convert to C1
char = String.fromCharCode(last.charCodeAt(0) + 0x40)
@handleControlCharacter(char)
else
switch last
when "c"
@resetTerminal()
handlePrivateEscSeq: (data) ->
last = data[data.length - 1]
switch last
when "7"
@saveTerminalState()
when "8"
@restoreTerminalState()
handleControlSequence: (data, params, match) ->
if params and params.match(/^[\x3c-\x3f]/)
@handlePrivateControlSequence(data, params)
else
@handleStandardControlSequence(data, params)
handleStandardControlSequence: (data, params) ->
term = data[data.length - 1]
numbers = @parseParams(params)
n = numbers[0]
m = numbers[1]
# handleC1ControlSet: (data) ->
# handleControlSequence: (data) ->
_C0_PATTERNS:
# C0 set of 7-bit control characters
"[\x00-\x1f]":
# bell
"\x07": (data) -> @bell()
# backspace
"\x08": (data) -> @backspace()
# Move the cursor to the next tab stop
"\x09": (data) ->
"\x0a": (data) -> @lineFeed()
"\x0d": (data) -> @carriageReturn()
"\x0e": (data) ->
"\x0f": (data) ->
# Reserved (?)
"\x82": (data) ->
# Cancel Character, ignore previous character
"\x94": (data) ->
# Escape sequence
"\x1b": _.extend({}, @_C0_PATTERNS, {
# Control sequence
"\x1b\\[": @_CS_PATTERNS
})
_CS_PATTERNS:
"sth": 1
_PATTERNS: _.extend({}, @_C0_PATTERNS, {
# Printable characters
"([\x20-\x7e])+": @handlePrintableCharacters
# "Delete", always and everywhere ignored
"[\x7f\xff]": @noop
# C1 control set
"[\x80-\x9f]":
# Control sequence
"\x9b": @_CS_PATTERNS
# G1 Displayable, 94 additional displayable characters
"[\xa1-\xfe]": @handlePrintableCharacters
# Always and everywhere a blank space
"\xa0": -> @handlePrintableCharacters(' ')
})
PATTERNS:
"\x00": (data) ->
"\x07": (data) -> @bell()
"\x08": (data) -> @backspace()
"\x09": (data) -> @buffer.goToNextHorizontalTabStop()
"\x0a": (data) -> @lineFeed()
"\x0b": (data) -> @verticalTab()
"\x0c": (data) -> @formFeed()
"\x0d": (data) -> @carriageReturn()
"\x0e": (data) ->
"\x0f": (data) ->
"\x82": (data) -> # Reserved (?)
"\x85": (data) -> @setHorizontalTabStop()
"\x94": (data) -> # Cancel Character, ignore previous character
# 20 - 7e
"([\x20-\x7e]|\xe2..|[\xc2\xc4\xc5].)+": (data, match) ->
@buffer.print match[0]
"\x1b\\(B": (data) -> # SCS (Set G0 Character SET)
"\x1b\\[([0-9;]*)([\x40-\x7e])": (data, match) ->
if match[1].length == 0
@params = []
else
@params = _(match[1].split(';')).map (n) -> if n is '' then undefined else parseInt(n)
@n = @params[0]
@m = @params[1]
@handleCS match[2]
# private standards
"\x1b\\[\\?([\x30-\x3f]+)([hlsr])": (data, match) ->
# h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
# l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
# 1001 + s = ?
# 1001 + r = ?
modes = match[1].split(";")
action = match[2]
mode = undefined
for mode in modes
if mode is "1"
# 1 + h / l = cursor keys stuff
else if mode is "5"
# Reverse/normal video - ignoring
else if mode is "7"
# Enables/disables autowrap mode
else if mode is "12"
if action is "h"
# blinking cursor
else action is "l"
# steady cursor
else if mode is "25"
if action is "h"
@showCursor()
else if action is "l"
@hideCursor()
else if mode is "47"
if action is "h"
@switchToAlternateBuffer()
else if action is "l"
@switchToNormalBuffer()
else if mode is "1000"
# Enables/disables normal mouse tracking
else if mode is "1001"
# pbly sth with mouse/keys...
else if mode is "1002"
# 2002 + h / l = mouse tracking stuff
else if mode is "1049"
if action is "h"
# Save cursor position, switch to alternate screen buffer, and clear screen.
@switchToAlternateBuffer()
@clearScreen()
else if action is "l"
# Clear screen, switch to normal screen buffer, and restore cursor position.
@clearScreen()
@switchToNormalBuffer()
else
throw "unknown mode: " + mode + action
"\x1b\x3d": (data) -> # DECKPAM - Set keypad to applications mode (ESCape instead of digits)
"\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
"\x1b\\\\": -> # String Terminator (VT125 exits graphics)
"\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
"\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)
"\x1bc": -> @resetTerminal()
"\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
"\x1bD": -> @index()
"\x1bE": -> @newLine()
"\x1bH": -> @setHorizontalTabStop()
"\x1bM": -> @reverseIndex()
"\x1bk": -> # NAPLPS lock-shift G1 to GR
"\x1b7": (data) -> # save cursor pos and char attrs
@saveTerminalState()
"\x1b8": (data) -> # restore cursor pos and char attrs
@restoreTerminalState()
handleCS: (term) ->
switch term
when "@"
@buffer.insertCharacters @n
@buffer.insertCharacters n
when "A"
@buffer.priorRow @n
@buffer.priorRow n
when "B"
@buffer.nextRow @n
@buffer.nextRow n
when "C"
@buffer.nextColumn @n
@buffer.nextColumn n
when "D"
@buffer.priorColumn @n
@buffer.priorColumn n
when "E"
@buffer.nextRowFirstColumn @n
@buffer.nextRowFirstColumn n
when "F"
@buffer.priorRowFirstColumn @n
@buffer.priorRowFirstColumn n
when "G"
@buffer.goToColumn @n
@buffer.goToColumn n
when "H"
@buffer.goToRowAndColumn @n, @m
@buffer.goToRowAndColumn n, m
when "I"
@buffer.goToNextHorizontalTabStop @n
@buffer.goToNextHorizontalTabStop n
when "J"
if @n is 2
if n is 2
@buffer.eraseScreen()
else if @n is 1
else if n is 1
@buffer.eraseFromScreenStart()
else
@buffer.eraseToScreenEnd()
when "K"
if @n is 2
if n is 2
@buffer.eraseRow()
else if @n is 1
else if n is 1
@buffer.eraseFromRowStart()
else
@buffer.eraseToRowEnd()
when "L"
@buffer.insertLine @n or 1
@buffer.insertLine n or 1
when "M"
@buffer.deleteLine @n or 1
@buffer.deleteLine n or 1
when "P" # DCH - Delete Character, from current position to end of field
@buffer.deleteCharacters @n or 1
@buffer.deleteCharacters n or 1
when "S"
@buffer.scrollUp @n
@buffer.scrollUp n
when "T"
@buffer.scrollDown @n
@buffer.scrollDown n
when "X"
@buffer.eraseCharacters @n
@buffer.eraseCharacters n
when "Z"
@buffer.goToPriorHorizontalTabStop @n
@buffer.goToPriorHorizontalTabStop n
when "b"
@buffer.repeatLastCharacter @n
@buffer.repeatLastCharacter n
when "d" # VPA - Vertical Position Absolute
@buffer.goToRow @n
@buffer.goToRow n
when "f"
@buffer.goToRowAndColumn @n, @m
@buffer.goToRowAndColumn n, m
when "g"
if !@n or @n is 0
if !n or n is 0
@buffer.clearHorizontalTabStop()
else if @n is 3
else if n is 3
@buffer.clearAllHorizontalTabStops()
when "l" # l, Reset mode
console.log "(TODO) reset: " + @n
console.log "(TODO) reset: " + n
when "m"
@handleSGR @params
@handleSGR numbers
when "n"
@reportRowAndColumn()
when "r" # Set top and bottom margins (scroll region on VT100)
@setScrollRegion @n or 0, @m or @lines - 1
when "^"
# reserved
# Privacy Message (password verification), terminated by ST
# TODO
@setScrollRegion n or 0, m or @lines - 1
else
throw "no handler for CSI term: " + term
handlePrivateControlSequence: (data, params) ->
action = data[data.length - 1]
modes = @parseParams(params)
console.log "private: #{@formattedData(data)}, action: #{action}, modes: #{modes}"
for mode in modes
if mode is 25
if action is "h"
@showCursor()
else if action is "l"
@hideCursor()
else if mode is 47
if action is "h"
@switchToAlternateBuffer()
else if action is "l"
@switchToNormalBuffer()
else if mode is 1049
if action is "h"
# Save cursor position, switch to alternate screen buffer, and clear screen.
console.log 'switching'
@switchToAlternateBuffer()
@clearScreen()
else if action is "l"
# Clear screen, switch to normal screen buffer, and restore cursor position.
console.log 'switching back'
@clearScreen()
@switchToNormalBuffer()
# else
# throw "unknown mode: " + mode + action
parseParams: (params) ->
if params.length is 0
numbers = []
else
numbers = _(params.replace(/[^0-9;]/, '').split(';')).map (n) -> if n is '' then undefined else parseInt(n)
numbers
# _C0_PATTERNS:
# default: (data) ->
# console.log "no handler for: #{@formattedData(data)}"
# # C0 set of 7-bit control characters
# "\x1b?[\x00-\x1f]":
# # bell
# "\x07": (data) -> @bell()
# # backspace
# "\x08": (data) -> @backspace()
# # Move the cursor to the next tab stop
# "\x09": (data) ->
# "\x0a": (data) -> @lineFeed()
# "\x0d": (data) -> @carriageReturn()
# "\x0e": (data) ->
# "\x0f": (data) ->
# # Reserved (?)
# "\x82": (data) ->
# # Cancel Character, ignore previous character
# "\x94": (data) ->
# # # Escape sequence
# # "\x1b": _.extend({}, @_C0_PATTERNS, {
# # Control sequence
# "\x1b\\[": @_CS_PATTERNS
# _CS_PATTERNS:
# "sth": 1
# _PATTERNS: _.extend({}, @_C0_PATTERNS, {
# # Printable characters
# "([\x20-\x7e])+": @handlePrintableCharacters
# # "Delete", always and everywhere ignored
# "[\x7f\xff]": @noop
# # C1 control set
# "[\x80-\x9f]":
# # Control sequence
# "\x9b": @_CS_PATTERNS
# # G1 Displayable, 94 additional displayable characters
# "[\xa1-\xfe]": @handlePrintableCharacters
# # Always and everywhere a blank space
# "\xa0": -> @handlePrintableCharacters(' ')
# })
# PATTERNS:
# "\x00": (data) ->
# "\x07": (data) -> @bell()
# "\x08": (data) -> @backspace()
# "\x09": (data) -> @buffer.goToNextHorizontalTabStop()
# "\x0a": (data) -> @lineFeed()
# "\x0b": (data) -> @verticalTab()
# "\x0c": (data) -> @formFeed()
# "\x0d": (data) -> @carriageReturn()
# "\x0e": (data) ->
# "\x0f": (data) ->
# # 20 - 7e
# "([\x20-\x7e]|\xe2..|[\xc2\xc4\xc5].)+": (data, match) ->
# @buffer.print match[0]
# "\x1b\\(B": (data) -> # SCS (Set G0 Character SET)
# "\x1b\\[([0-9;]*)([\x40-\x7e])": (data, match) ->
# if match[1].length == 0
# @params = []
# else
# @params = _(match[1].split(';')).map (n) -> if n is '' then undefined else parseInt(n)
# @n = @params[0]
# @m = @params[1]
# @handleCS match[2]
# # private standards
# "\x1b\\[\\?([\x30-\x3f]+)([hlsr])": (data, match) ->
# # h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
# # l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
# # 1001 + s = ?
# # 1001 + r = ?
# modes = match[1].split(";")
# action = match[2]
# mode = undefined
# for mode in modes
# if mode is "1"
# # 1 + h / l = cursor keys stuff
# else if mode is "5"
# # Reverse/normal video - ignoring
# else if mode is "7"
# # Enables/disables autowrap mode
# else if mode is "12"
# if action is "h"
# # blinking cursor
# else action is "l"
# # steady cursor
# else if mode is "25"
# if action is "h"
# @showCursor()
# else if action is "l"
# @hideCursor()
# else if mode is "47"
# if action is "h"
# @switchToAlternateBuffer()
# else if action is "l"
# @switchToNormalBuffer()
# else if mode is "1000"
# # Enables/disables normal mouse tracking
# else if mode is "1001"
# # pbly sth with mouse/keys...
# else if mode is "1002"
# # 2002 + h / l = mouse tracking stuff
# else if mode is "1049"
# if action is "h"
# # Save cursor position, switch to alternate screen buffer, and clear screen.
# @switchToAlternateBuffer()
# @clearScreen()
# else if action is "l"
# # Clear screen, switch to normal screen buffer, and restore cursor position.
# @clearScreen()
# @switchToNormalBuffer()
# else
# throw "unknown mode: " + mode + action
# "\x1b\x3d": (data) -> # DECKPAM - Set keypad to applications mode (ESCape instead of digits)
# "\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
# "\x1b\\\\": -> # String Terminator (VT125 exits graphics)
# "\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
# "\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)
# "\x1bc": -> @resetTerminal()
# "\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
# "\x1bD": -> @index()
# "\x1bE": -> @newLine()
# "\x1bH": -> @setHorizontalTabStop()
# "\x1bM": -> @reverseIndex()
# "\x1bk": -> # NAPLPS lock-shift G1 to GR
# "\x1b7": (data) -> # save cursor pos and char attrs
# @saveTerminalState()
# "\x1b8": (data) -> # restore cursor pos and char attrs
# @restoreTerminalState()
# handleCS: (term) ->
# switch term
# when "@"
# @buffer.insertCharacters @n
# when "A"
# @buffer.priorRow @n
# when "B"
# @buffer.nextRow @n
# when "C"
# @buffer.nextColumn @n
# when "D"
# @buffer.priorColumn @n
# when "E"
# @buffer.nextRowFirstColumn @n
# when "F"
# @buffer.priorRowFirstColumn @n
# when "G"
# @buffer.goToColumn @n
# when "H"
# @buffer.goToRowAndColumn @n, @m
# when "I"
# @buffer.goToNextHorizontalTabStop @n
# when "J"
# if @n is 2
# @buffer.eraseScreen()
# else if @n is 1
# @buffer.eraseFromScreenStart()
# else
# @buffer.eraseToScreenEnd()
# when "K"
# if @n is 2
# @buffer.eraseRow()
# else if @n is 1
# @buffer.eraseFromRowStart()
# else
# @buffer.eraseToRowEnd()
# when "L"
# @buffer.insertLine @n or 1
# when "M"
# @buffer.deleteLine @n or 1
# when "P" # DCH - Delete Character, from current position to end of field
# @buffer.deleteCharacters @n or 1
# when "S"
# @buffer.scrollUp @n
# when "T"
# @buffer.scrollDown @n
# when "X"
# @buffer.eraseCharacters @n
# when "Z"
# @buffer.goToPriorHorizontalTabStop @n
# when "b"
# @buffer.repeatLastCharacter @n
# when "d" # VPA - Vertical Position Absolute
# @buffer.goToRow @n
# when "f"
# @buffer.goToRowAndColumn @n, @m
# when "g"
# if !@n or @n is 0
# @buffer.clearHorizontalTabStop()
# else if @n is 3
# @buffer.clearAllHorizontalTabStops()
# when "l" # l, Reset mode
# console.log "(TODO) reset: " + @n
# when "m"
# @handleSGR @params
# when "n"
# @reportRowAndColumn()
# when "r" # Set top and bottom margins (scroll region on VT100)
# @setScrollRegion @n or 0, @m or @lines - 1
# when "^"
# # reserved
# # Privacy Message (password verification), terminated by ST
# # TODO
# else
# throw "no handler for CSI term: " + term
handleSGR: (numbers) ->
@buffer.setBrush @sgrInterpreter.buildBrush(@buffer.brush, numbers)
@ -263,35 +483,50 @@ class AsciiIo.VT
@view.visualBell()
# @trigger('bell')
compilePatterns: ->
@COMPILED_PATTERNS = ([new RegExp("^" + re), f] for re, f of @PATTERNS)
# compilePatterns: ->
# @COMPILED_PATTERNS = ([new RegExp("^" + re), f] for re, f of @PATTERNS)
feed: (data) ->
@data += data
while @data.length > 0
match = null
processed = @handleData(@data)
for pattern in @COMPILED_PATTERNS
match = pattern[0].exec(@data)
if processed is 0
# console.log "no kurwa: #{@formattedData(@data)}"
break
# throw 'up'
if match
handler = pattern[1]
handler.call(this, @data, match)
@data = @data.slice(match[0].length)
break
@data = @data.slice(processed)
break unless match
# match = null
@render()
# for pattern in @COMPILED_PATTERNS
# match = pattern[0].exec(@data)
if @data.length > 500
head = @data.slice(0, 100)
hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
console.log "failed matching: '" + Utf8.decode(head) + "' (" + hex.join() + ")" # [pos: " + (@dataIndex - count) + "]"
# if match
# handler = pattern[1]
# handler.call(this, @data, match)
# @data = @data.slice(match[0].length)
# break
# break unless match
@render()
@data.length is 0
# aaa: ->
# if @data.length > 500
# head = @data.slice(0, 100)
# hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
# console.log "failed matching: '" + Utf8.decode(head) + "' (" + hex.join() + ")" # [pos: " + (@dataIndex - count) + "]"
formattedData: (data) ->
head = data.slice(0, 100)
hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
Utf8.decode(head) + " (" + hex.join() + ")"
render: ->
@view.render(@buffer.changes(), @buffer.cursorX, @buffer.cursorY)
@buffer.clearChanges()
@ -391,3 +626,4 @@ class AsciiIo.VT
reportRowAndColumn: ->
# http://www.shaels.net/index.php/propterm/documents
# http://manpages.ubuntu.com/manpages/lucid/man7/urxvt.7.html

Loading…
Cancel
Save