parser.go 3.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
package ansiterm

import (
	"errors"
	"io/ioutil"
	"os"

	"github.com/Sirupsen/logrus"
)

var logger *logrus.Logger

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
}

func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser {
	logFile := ioutil.Discard

	if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
		logFile, _ = os.Create("ansiParser.log")
	}

	logger = &logrus.Logger{
		Out:       logFile,
		Formatter: new(logrus.TextFormatter),
		Level:     logrus.InfoLevel,
	}

	parser := &AnsiParser{
		eventHandler: evtHandler,
		context:      &ansiContext{},
	}

	parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}}
	parser.csiParam = csiParamState{baseState{name: "CsiParam", parser: parser}}
	parser.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: parser}}
	parser.escape = escapeState{baseState{name: "Escape", parser: parser}}
	parser.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: parser}}
	parser.error = errorState{baseState{name: "Error", parser: parser}}
	parser.ground = groundState{baseState{name: "Ground", parser: parser}}
	parser.oscString = oscStringState{baseState{name: "OscString", parser: parser}}

	parser.stateMap = []state{
		parser.csiEntry,
		parser.csiParam,
		parser.dcsEntry,
		parser.escape,
		parser.escapeIntermediate,
		parser.error,
		parser.ground,
		parser.oscString,
	}

	parser.currState = getState(initialState, parser.stateMap)

	logger.Infof("CreateParser: parser %p", parser)
	return parser
}

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 {
		logger.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 {
	logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name())

	// Exit old state
	if err := ap.currState.Exit(); err != nil {
		logger.Infof("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
		return err
	}

	// Perform transition action
	if err := ap.currState.Transition(newState); err != nil {
		logger.Infof("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 {
		logger.Infof("Enter state '%s' failed with: '%v'", newState.Name(), err)
		return err
	}

	ap.currState = newState
	return nil
}