Skip to content

Instantly share code, notes, and snippets.

@mmalecki
Created November 27, 2011 19:20
Show Gist options
  • Save mmalecki/1398015 to your computer and use it in GitHub Desktop.
Save mmalecki/1398015 to your computer and use it in GitHub Desktop.
My finite state machine for parsing ANSI codes
var ansispan = function (str) {
}
ansispan.parse = function (str) {
//
// I'm terrible at writing parsers.
//
var matchingControl = null,
matchingData = null,
matchingText = null,
ansiState = [],
result = [],
state = {};
//
// General workflow for this thing is:
// \033\[33mText
// | | |
// | | matchingText
// | matchingData
// matchingControl
//
// In further steps we hope it's all going to be fine. It usually is.
//
for (var i = 0; i < str.length; i++) {
if (matchingControl != null) {
if (matchingControl == '\033' && str[i] == '\[') {
//
// We've matched full control code. Lets start matching formating data.
//
matchingControl = null;
matchingData = '';
}
else {
//
// We failed to match anything - most likely a bad control code. We
// go back to matching regular strings.
//
matchingControl = null;
}
continue;
}
else if (matchingData != null) {
if (str[i] == ';') {
ansiState.push(matchingData);
matchingData = '';
}
else if (str[i] == 'm') {
ansiState.push(matchingData);
matchingData = null;
matchingText = '';
//
// Convert matched formatting data into user-friendly state object
//
ansiState.forEach(function (ansiCode) {
if ((30 <= ansiCode) && (ansiCode <= 37)) {
state.foreground = ansiCode;
}
else if ((40 <= ansiCode) && (ansiCode <= 47)) {
state.background = ansiCode;
}
else if (ansiCode == 39) {
delete state.foreground;
}
else if (ansiCode == 49) {
delete state.background;
}
else if (ansiCode == 1) {
state.bold = true;
}
else if (ansiCode == 3) {
state.italic = true;
}
else if (ansiCode == 4) {
state.underline = true;
}
else if (ansiCode == 22) {
state.bold = false;
}
else if (ansiCode == 23) {
state.italic = false;
}
else if (ansiCode == 24) {
state.underline = false;
}
});
}
else {
matchingData += str[i];
}
continue;
}
if (str[i] == '\033') {
matchingControl = str[i];
//
// "emit" matched text with correct state
//
if (matchingText) {
state.text = matchingText;
result.push(state);
state = {};
}
}
else {
matchingText += str[i];
}
}
return result;
}
ansispan.foregroundColors = {
'30': 'black',
'31': 'red',
'32': 'green',
'33': 'yellow',
'35': 'purple',
'36': 'cyan',
'37': 'white'
};
ansispan.backgroundColors = {
'40': 'black',
'41': 'red',
'42': 'green',
'43': 'yellow',
'45': 'purple',
'46': 'cyan',
'47': 'white'
};
ansispan.styles = {
'1': 'bold',
'3': 'italics',
'4': 'underline'
};
if (typeof module == "object" && typeof window == "undefined") {
module.exports = ansispan;
}
require('colors');
console.dir(ansispan.parse('red'.red + ' normal ' + 'blue'.blue));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment