From 20f9ad3f551b1060be05105118412dc5687fdf21 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 18 Apr 2022 11:01:47 -0700 Subject: [PATCH] implement basic VT parser -- only print/execute handled --- src/terminal/Parser.zig | 87 +++++++++++++++++++++++++----------- src/terminal/parse_table.zig | 2 +- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 0272dc86a..47d65c07d 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -27,7 +27,10 @@ pub const State = enum { sos_pm_apc_string, }; -pub const Action = enum { +/// Transition action is an action that can be taken during a state +/// transition. This is more of an internal action, not one used by +/// end users, typically. +pub const TransitionAction = enum { none, ignore, print, @@ -45,6 +48,16 @@ pub const Action = enum { osc_end, }; +/// Action is the action that a caller of the parser is expected to +/// take as a result of some input character. +pub const Action = union(enum) { + /// Draw character to the screen. + print: u8, + + /// Execute the C0 or C1 function. + execute: u8, +}; + /// Current state of the state machine state: State = .ground, @@ -52,7 +65,10 @@ pub fn init() Parser { return .{}; } -pub fn next(self: *Parser, c: u8) void { +/// Next consums the next character c and returns the actions to execute. +/// Up to 3 actions may need to be exected -- in order -- representing +/// the state exit, transition, and entry actions. +pub fn next(self: *Parser, c: u8) [3]?Action { const effect = effect: { // First look up the transition in the anywhere table. const anywhere = table[c][@enumToInt(State.anywhere)]; @@ -65,43 +81,62 @@ pub fn next(self: *Parser, c: u8) void { const next_state = effect.state; const action = effect.action; + // After generating the actions, we set our next state. + defer self.state = next_state; + // When going from one state to another, the actions take place in this order: // // 1. exit action from old state // 2. transition action // 3. entry action to new state + return [3]?Action{ + switch (self.state) { + .osc_string => @panic("TODO"), // TODO: osc_end + .dcs_passthrough => @panic("TODO"), // TODO: unhook + else => null, + }, - // Perform exit actions. "The action associated with the exit event happens - // when an incoming symbol causes a transition from this state to another - // state (or even back to the same state)." - switch (self.state) { - .osc_string => {}, // TODO: osc_end - .dcs_passthrough => {}, // TODO: unhook - else => {}, - } + self.doAction(action, c), - // Perform the transition action - self.doAction(action); - - // Perform the entry action - // TODO: when _first_ entered only? - switch (self.state) { - .escape, .dcs_entry, .csi_entry => {}, // TODO: clear - .osc_string => {}, // TODO: osc_start - .dcs_passthrough => {}, // TODO: hook - else => {}, - } - - self.state = next_state; + switch (self.state) { + .escape, .dcs_entry, .csi_entry => @panic("TODO"), // TODO: clear + .osc_string => @panic("TODO"), // TODO: osc_start + .dcs_passthrough => @panic("TODO"), // TODO: hook + else => null, + }, + }; } -fn doAction(self: *Parser, action: Action) void { +fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action { _ = self; - _ = action; + return switch (action) { + .none, .ignore => null, + .print => return Action{ .print = c }, + .execute => return Action{ .execute = c }, + else => @panic("TODO"), + }; } test { var p = init(); - p.next(0x9E); + _ = p.next(0x9E); try testing.expect(p.state == .sos_pm_apc_string); + _ = p.next(0x9C); + try testing.expect(p.state == .ground); + + { + const a = p.next('a'); + try testing.expect(p.state == .ground); + try testing.expect(a[0] == null); + try testing.expect(a[1].? == .print); + try testing.expect(a[2] == null); + } + + { + const a = p.next(0x19); + try testing.expect(p.state == .ground); + try testing.expect(a[0] == null); + try testing.expect(a[1].? == .execute); + try testing.expect(a[2] == null); + } } diff --git a/src/terminal/parse_table.zig b/src/terminal/parse_table.zig index d0f19f8e8..2fc4b662e 100644 --- a/src/terminal/parse_table.zig +++ b/src/terminal/parse_table.zig @@ -5,7 +5,7 @@ const std = @import("std"); const builtin = @import("builtin"); const parser = @import("Parser.zig"); const State = parser.State; -const Action = parser.Action; +const Action = parser.TransitionAction; /// The state transition table. The type is [u8][State]Transition but /// comptime-generated to be exactly-sized.