mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-09-20 16:36:19 +02:00
8d76d3500c
With the exception of text_decoder.gd the code in these files follows the original so closely that it doesn't qualify as an original work and so there is nothing new to copyright. Instead, the original license text is kept with a note mentioning the port to GDScript.
329 lines
9.5 KiB
GDScript
329 lines
9.5 KiB
GDScript
# Copyright (c) 2018 The xterm.js authers. All rights reserved.
|
|
# Ported to GDScript by the GodotXterm authors.
|
|
# License MIT
|
|
extends Reference
|
|
|
|
const Constants = preload("res://addons/godot_xterm/parser/constants.gd")
|
|
const TransitionTable = preload("res://addons/godot_xterm/parser/transition_table.gd")
|
|
const VT500TransitionTable = preload("res://addons/godot_xterm/parser/vt500_transition_table.gd")
|
|
const DcsParser = preload("res://addons/godot_xterm/parser/dcs_parser.gd")
|
|
const Params = preload("res://addons/godot_xterm/parser/params.gd")
|
|
|
|
const TableAccess = TransitionTable.TableAccess
|
|
const NON_ASCII_PRINTABLE = Constants.NON_ASCII_PRINTABLE
|
|
const ParserState = Constants.ParserState
|
|
const ParserAction = Constants.ParserAction
|
|
|
|
var initial_state
|
|
var current_state
|
|
var preceding_codepoint
|
|
|
|
var _transitions
|
|
|
|
# buffers over several parse calls
|
|
var _params
|
|
var _collect
|
|
|
|
# handler lookup containers
|
|
var _print_handler
|
|
var _execute_handlers
|
|
var _csi_handlers
|
|
var _esc_handlers
|
|
var _osc_parser
|
|
var _dcs_parser
|
|
var _error_handler
|
|
|
|
# fallback handlers
|
|
var _print_handler_fb
|
|
var _execute_handler_fb
|
|
var _csi_handler_fb
|
|
var _esc_handler_fb
|
|
var _error_handler_fb
|
|
|
|
|
|
# Default do noting fallback handler.
|
|
# Allows a variable number of arguments from 0 - 7.
|
|
func noop(a = null, b = null, c = null, d = null, e = null, f = null, g = null):
|
|
pass
|
|
|
|
|
|
func _init(transitions = VT500TransitionTable.new().table):
|
|
initial_state = ParserState.GROUND
|
|
current_state = initial_state
|
|
_transitions = transitions
|
|
_params = Params.new() # Defaults to 32 storable params/subparams
|
|
_params.add_param(0) # ZDM (Zero Default Mode
|
|
_collect = 0
|
|
preceding_codepoint = 0
|
|
|
|
# set default fallback handlers and handler lookup containers
|
|
var noop = {'target': self, 'method': 'noop'}
|
|
_print_handler_fb = noop
|
|
_execute_handler_fb = noop
|
|
_csi_handler_fb = noop
|
|
_esc_handler_fb = noop
|
|
_error_handler_fb = noop
|
|
_print_handler = _print_handler_fb
|
|
_execute_handlers = {}
|
|
_csi_handlers = {}
|
|
_esc_handlers = {}
|
|
_osc_parser = null # TODO OscParser.new()
|
|
_dcs_parser = DcsParser.new()
|
|
_error_handler = _error_handler_fb
|
|
|
|
# swallow 7bit ST (ESC+\)
|
|
set_esc_handler({'final': '\\'}, self, 'noop')
|
|
|
|
|
|
static func identifier(id: Dictionary, final_range: Array = [0x40, 0x7e]):
|
|
var res = 0
|
|
|
|
var prefix = id.get('prefix')
|
|
var intermediates = id.get('intermediates')
|
|
var final = id.get('final')
|
|
|
|
if prefix:
|
|
if prefix.length() > 1:
|
|
push_error("only one byte prefix supported")
|
|
res = prefix.to_ascii()[0]
|
|
if res and 0x3c > res or res > 0x3f:
|
|
push_error("prefix must be in the range 0x3c-0x3f")
|
|
|
|
if intermediates:
|
|
if intermediates.length() > 2:
|
|
push_error("only two bytes as intermediates are supported")
|
|
for intermediate in intermediates:
|
|
var im = intermediate.to_ascii()[0]
|
|
if 0x20 > im or im > 0x2f:
|
|
push_error("intermediate must be in the range 0x20-0x2f")
|
|
res = res << 8
|
|
res = res | im
|
|
|
|
if final.length() != 1:
|
|
push_error("final must be a single byte")
|
|
var final_code = final.to_ascii()[0]
|
|
if final_range[0] > final_code or final_code > final_range[1]:
|
|
push_error("final must be in the range " + String(final_range[0]) + "-" + String(final_range[1]))
|
|
res = res << 8
|
|
res = res | final_code
|
|
|
|
return res
|
|
|
|
static func ident_to_string(ident: int):
|
|
var res = PoolStringArray([])
|
|
while ident:
|
|
res.append(PoolByteArray([ident & 0xFF]).get_string_from_ascii())
|
|
ident >>= 8
|
|
res.invert()
|
|
return res.join('')
|
|
|
|
func set_print_handler(target: Object, method: String):
|
|
_print_handler = { 'target': target, 'method': method }
|
|
|
|
|
|
func add_esc_handler(id, target, method):
|
|
var ident = identifier(id, [0x30, 0x7e])
|
|
if not _esc_handlers.has(ident):
|
|
_esc_handlers[ident] = []
|
|
var handler_list = _esc_handlers[ident]
|
|
handler_list.append({'target': target, 'method': method})
|
|
|
|
|
|
func set_csi_handler(id: Dictionary, target: Object, method: String):
|
|
_csi_handlers[identifier(id)] = [{ 'target': target, 'method': method }]
|
|
|
|
|
|
func set_csi_handler_fallback(target, method):
|
|
_csi_handler_fb = { 'target': target, 'method': method }
|
|
|
|
|
|
func set_execute_handler(flag: int, target: Object, method: String):
|
|
_execute_handlers[flag] = { 'target': target, 'method': method }
|
|
|
|
|
|
func set_execute_handler_fallback(target: Object, method: String):
|
|
_execute_handler_fb = { 'target': target, 'method': method }
|
|
|
|
|
|
func set_esc_handler(id, target, method):
|
|
_esc_handlers[identifier(id, [0x30, 0x7e])] = [{'target': target, 'method': method}]
|
|
|
|
|
|
func set_esc_handler_fallback(target: Object, method: String):
|
|
_esc_handler_fb = {'target': target, 'method': method}
|
|
|
|
|
|
func add_dcs_handler(id, target, method):
|
|
pass
|
|
# TODO!!!
|
|
|
|
func set_dcs_handler(id, target: Object, method: String):
|
|
_dcs_parser.set_handler(id, {'target': target, 'method': method})
|
|
|
|
func set_dcs_handler_fallback(target: Object, method: String):
|
|
_dcs_parser.set_handler_fallback(target, method)
|
|
|
|
func reset():
|
|
current_state = initial_state
|
|
_params.reset()
|
|
_params.add_param(0) # ZDM
|
|
_collect = 0
|
|
preceding_codepoint = 0
|
|
|
|
func parse(data: Array, length: int):
|
|
var code = 0
|
|
var transition = 0
|
|
var _current_state = current_state
|
|
var dcs = _dcs_parser
|
|
var collect = _collect
|
|
var params = _params
|
|
|
|
#print("table", table)
|
|
|
|
#print("parse -> data: ", data, " length: ", length)
|
|
|
|
# Process input string.
|
|
var i = 0
|
|
while i < length:
|
|
#print("i: ", i)
|
|
code = data[i]
|
|
|
|
#print("code: ", code)
|
|
|
|
# Normal transition and action lookup.
|
|
transition = _transitions[_current_state << TableAccess.INDEX_STATE_SHIFT | code if code < 0xa0 else NON_ASCII_PRINTABLE]
|
|
|
|
#print ("transition: ", transition)
|
|
#print("current state: ", current_state)
|
|
|
|
match transition >> TableAccess.TRANSITION_ACTION_SHIFT:
|
|
ParserAction.PRINT:
|
|
# read ahead with loop unrolling
|
|
# # Note: 0x20 (SP) is included, 0x7F (DEL) is excluded
|
|
var j = i + 1
|
|
while true:
|
|
code = data[j] if j < data.size() else 0
|
|
if j >= length or code < 0x20 or (code > 0x7e and code < NON_ASCII_PRINTABLE):
|
|
_print_handler['target'].call(_print_handler['method'], data, i, j)
|
|
i = j - 1
|
|
break
|
|
j += 1
|
|
code = data[j] if j < data.size() else 0
|
|
if j >= length or code < 0x20 or (code > 0x7e and code < NON_ASCII_PRINTABLE):
|
|
_print_handler['target'].call(_print_handler['method'], data, i, j)
|
|
i = j - 1
|
|
break
|
|
j += 1
|
|
code = data[j] if j < data.size() else 0
|
|
if j >= length or code < 0x20 or (code > 0x7e and code < NON_ASCII_PRINTABLE):
|
|
_print_handler['target'].call(_print_handler['method'], data, i, j)
|
|
i = j - 1
|
|
break
|
|
j += 1
|
|
code = data[j] if j < data.size() else 0
|
|
if j >= length or code < 0x20 or (code > 0x7e and code < NON_ASCII_PRINTABLE):
|
|
_print_handler['target'].call(_print_handler['method'], data, i, j)
|
|
i = j - 1
|
|
break
|
|
j += 1
|
|
ParserAction.EXECUTE:
|
|
var handler = _execute_handlers.get(code)
|
|
if handler:
|
|
handler['target'].call(handler['method'])
|
|
elif _execute_handler_fb:
|
|
_execute_handler_fb['target'].call(_execute_handler_fb['method'], code)
|
|
preceding_codepoint = 0
|
|
ParserAction.IGNORE:
|
|
pass
|
|
ParserAction.ERROR:
|
|
print("Parser error!")
|
|
|
|
ParserAction.CSI_DISPATCH:
|
|
# Trigger CSI Handler
|
|
var handlers = _csi_handlers.get((collect << 8 | code), [])
|
|
handlers.invert()
|
|
for handler in handlers:
|
|
# undefined or true means success and to stop bubbling
|
|
if handler['target'].call(handler['method'], params.to_array()):
|
|
continue
|
|
handlers.invert()
|
|
if handlers.empty():
|
|
_csi_handler_fb['target'].call(_csi_handler_fb['method'], collect << 8 | code, params.to_array())
|
|
preceding_codepoint = 0
|
|
|
|
|
|
ParserAction.PARAM:
|
|
# Inner loop digits (0x30 - 0x39) and ; (0x3b) and : (0x3a)
|
|
var do = true
|
|
while do:
|
|
match code:
|
|
0x3b:
|
|
params.add_param(0)
|
|
0x3a:
|
|
params.add_sub_param(-1)
|
|
_:
|
|
params.add_digit(code - 48)
|
|
i += 1
|
|
code = data[i] if i < data.size() else 0
|
|
do = i < length and code > 0x2f and code < 0x3c
|
|
i-=1
|
|
|
|
ParserAction.COLLECT:
|
|
collect <<= 8
|
|
collect |= code
|
|
|
|
ParserAction.ESC_DISPATCH:
|
|
var handlers = _esc_handlers.get((collect << 8 | code), [])
|
|
handlers.invert()
|
|
for handler in handlers:
|
|
# undefined or true means success and to stop bubbling
|
|
if handler['target'].call(handler['method']) != false:
|
|
continue
|
|
handlers.invert()
|
|
if handlers.empty():
|
|
_esc_handler_fb['target'].call(_esc_handler_fb['method'], collect << 8 | code)
|
|
preceding_codepoint = 0
|
|
|
|
ParserAction.CLEAR:
|
|
params.reset()
|
|
params.add_param(0) # ZDM
|
|
collect = 0
|
|
|
|
ParserAction.DCS_HOOK:
|
|
dcs.hook(collect << 8 | code, params.to_array())
|
|
|
|
ParserAction.DCS_PUT:
|
|
# inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
|
|
# unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort)
|
|
for j in range(i + 1, length + 1):
|
|
code = data[j]
|
|
if code == 0x18 or code == 0x1a or code == 0x1b or (code > 0x7f and code < NON_ASCII_PRINTABLE):
|
|
dcs.put(data, i, j)
|
|
i = j - 1
|
|
break
|
|
break
|
|
ParserAction.DCS_UNHOOK:
|
|
_dcs_parser.unhook(code != 0x18 and code != 0x1a)
|
|
if code == 0x1b:
|
|
transition |= ParserState.ESCAPE
|
|
params.reset()
|
|
params.add_param(0) # ZDM
|
|
collect = 0;
|
|
preceding_codepoint = 0
|
|
ParserAction.OSC_START:
|
|
pass
|
|
|
|
ParserAction.OSC_PUT:
|
|
pass
|
|
|
|
ParserAction.OSC_END:
|
|
pass
|
|
|
|
_current_state = transition & TableAccess.TRANSITION_STATE_MASK
|
|
i += 1
|
|
|
|
# save collected intermediates
|
|
_collect = collect
|
|
|
|
# save state
|
|
current_state = _current_state
|