| package ansiterm |
| |
| import ( |
| "errors" |
| "log" |
| "os" |
| ) |
| |
| type AnsiParser struct { |
| currState state |
| eventHandler AnsiEventHandler |
| context *ansiContext |
| csiEntry state |
| csiParam state |
| dcsEntry state |
| escape state |
| escapeIntermediate state |
| error state |
| ground state |
| oscString state |
| stateMap []state |
| |
| logf func(string, ...interface{}) |
| } |
| |
| type Option func(*AnsiParser) |
| |
| func WithLogf(f func(string, ...interface{})) Option { |
| return func(ap *AnsiParser) { |
| ap.logf = f |
| } |
| } |
| |
| func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser { |
| ap := &AnsiParser{ |
| eventHandler: evtHandler, |
| context: &ansiContext{}, |
| } |
| for _, o := range opts { |
| o(ap) |
| } |
| |
| if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { |
| logFile, _ := os.Create("ansiParser.log") |
| logger := log.New(logFile, "", log.LstdFlags) |
| if ap.logf != nil { |
| l := ap.logf |
| ap.logf = func(s string, v ...interface{}) { |
| l(s, v...) |
| logger.Printf(s, v...) |
| } |
| } else { |
| ap.logf = logger.Printf |
| } |
| } |
| |
| if ap.logf == nil { |
| ap.logf = func(string, ...interface{}) {} |
| } |
| |
| ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}} |
| ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}} |
| ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}} |
| ap.escape = escapeState{baseState{name: "Escape", parser: ap}} |
| ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}} |
| ap.error = errorState{baseState{name: "Error", parser: ap}} |
| ap.ground = groundState{baseState{name: "Ground", parser: ap}} |
| ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}} |
| |
| ap.stateMap = []state{ |
| ap.csiEntry, |
| ap.csiParam, |
| ap.dcsEntry, |
| ap.escape, |
| ap.escapeIntermediate, |
| ap.error, |
| ap.ground, |
| ap.oscString, |
| } |
| |
| ap.currState = getState(initialState, ap.stateMap) |
| |
| ap.logf("CreateParser: parser %p", ap) |
| return ap |
| } |
| |
| func getState(name string, states []state) state { |
| for _, el := range states { |
| if el.Name() == name { |
| return el |
| } |
| } |
| |
| return nil |
| } |
| |
| func (ap *AnsiParser) Parse(bytes []byte) (int, error) { |
| for i, b := range bytes { |
| if err := ap.handle(b); err != nil { |
| return i, err |
| } |
| } |
| |
| return len(bytes), ap.eventHandler.Flush() |
| } |
| |
| func (ap *AnsiParser) handle(b byte) error { |
| ap.context.currentChar = b |
| newState, err := ap.currState.Handle(b) |
| if err != nil { |
| return err |
| } |
| |
| if newState == nil { |
| ap.logf("WARNING: newState is nil") |
| return errors.New("New state of 'nil' is invalid.") |
| } |
| |
| if newState != ap.currState { |
| if err := ap.changeState(newState); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func (ap *AnsiParser) changeState(newState state) error { |
| ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) |
| |
| // Exit old state |
| if err := ap.currState.Exit(); err != nil { |
| ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err) |
| return err |
| } |
| |
| // Perform transition action |
| if err := ap.currState.Transition(newState); err != nil { |
| ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err) |
| return err |
| } |
| |
| // Enter new state |
| if err := newState.Enter(); err != nil { |
| ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err) |
| return err |
| } |
| |
| ap.currState = newState |
| return nil |
| } |