Skip to content

Instantly share code, notes, and snippets.

@Aidan63
Created January 7, 2023 19:01
Show Gist options
  • Save Aidan63/d1026d161ebc8c6176db6b69a0f82be4 to your computer and use it in GitHub Desktop.
Save Aidan63/d1026d161ebc8c6176db6b69a0f82be4 to your computer and use it in GitHub Desktop.
package hxcppdbg.gdb;
import haxe.Exception;
import tink.cli.Result;
import haxe.ds.Option;
import haxe.ds.Either;
import hxparse.Parser.parse as parse;
enum ResultClass {
RDone;
RRunning;
RConnected;
RError;
RExit;
}
enum AsyncClass {
AStopped;
}
enum StreamOutput {
Console;
Target;
Log;
}
enum AsyncOutput {
Exec;
Status;
Notify;
}
enum Token {
TResultClass(v:ResultClass);
TAsyncClass(v:AsyncClass);
TString(v:String);
TTupleOpen;
TTupleClose;
TListOpen;
TListClose;
TStreamRecord(v:StreamOutput);
TAsyncRecord(v:AsyncOutput);
TResult;
TToken(v:String);
TCString(v:String);
TComma;
TNewLine;
TGdb;
TEof;
}
enum Value {
Const(v : String);
Tuple(v : Map<String, Value>);
List(v : Either<Array<Value>, Array<Result>>);
}
class MiLexer extends hxparse.Lexer implements hxparse.RuleBuilder {
static var buf : StringBuf;
public static var tok = @:rule [
"{" => TTupleOpen,
"}" => TTupleClose,
"[" => TListOpen,
"]" => TListClose,
"*" => TAsyncRecord(Exec),
"+" => TAsyncRecord(Status),
"=" => TAsyncRecord(Notify),
"~" => TStreamRecord(Console),
"@" => TStreamRecord(Target),
"&" => TStreamRecord(Log),
"^" => TResult,
"," => TComma,
"\\(gdb\\)" => TGdb,
"-?(([1-9][0-9]*)|0)(.[0-9]+)?([eE][\\+\\-]?[0-9]+)?" => TToken(lexer.current),
"-?([a-zA-Z1-9_\\-]*)" => TString(lexer.current),
'"' => {
buf = new StringBuf();
lexer.token(string);
TCString(buf.toString());
},
"\n" => TNewLine,
"\r\n" => TNewLine,
"[\t ]" => lexer.token(tok),
"" => TEof
];
public static var string = @:rule [
"\\\\t" => {
buf.addChar("\t".code);
lexer.token(string);
},
"\\\\n" => {
buf.addChar("\n".code);
lexer.token(string);
},
"\\\\r" => {
buf.addChar("\r".code);
lexer.token(string);
},
'\\\\"' => {
buf.addChar('"'.code);
lexer.token(string);
},
"\\\\u[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]" => {
buf.add(String.fromCharCode(Std.parseInt("0x" +lexer.current.substr(2))));
lexer.token(string);
},
'"' => {
lexer.curPos().pmax;
},
'[^"]' => {
buf.add(lexer.current);
lexer.token(string);
},
];
}
class AsyncRecord {
public final token : Option<Int>;
public final output : AsyncOutput;
public final cls : String;
public final results : Map<String, Value>;
public function new(_token, _output, _cls, _results) {
token = _token;
output = _output;
cls = _cls;
results = _results;
}
}
class StreamRecord {
public final output : StreamOutput;
public final value : String;
public function new(_output, _value) {
output = _output;
value = _value;
}
}
class Result {
public final variable : String;
public final value : Value;
public function new(_variable, _value) {
variable = _variable;
value = _value;
}
}
enum OOBRecord {
Async(v : AsyncRecord);
Stream(v : StreamRecord);
}
class ResultRecord {
public final token : Option<Int>;
public final cls : ResultClass;
public final results : Map<String, Value>;
public function new(_token, _cls, _results) {
token = _token;
cls = _cls;
results = _results;
}
}
class MiParser extends hxparse.Parser<hxparse.LexerTokenSource<Token>, Token> {
public function new(_input) {
final lexer = new MiLexer(byte.ByteData.ofString(_input));
final source = new hxparse.LexerTokenSource(lexer, MiLexer.tok);
super(source);
}
public function parseLine() {
return if (hasOOB()) {
Left(parseOutOfBandRecord());
} else {
Right(parseResultRecord());
}
}
private function hasOOB() {
return switch peek(0) {
case TAsyncRecord(_), TStreamRecord(_):
true;
case TToken(_):
peek(1).match(TAsyncRecord(_));
case _:
false;
}
}
public function parseResultRecord() {
final token = switch peek(0) {
case TToken(v):
junk();
Some(Std.parseInt(v));
case _:
None;
}
return parse(switch stream {
case [ TResult, rc = parseResultClass() ]:
final acc = new Map<String, Value>();
while (peek(0) == TComma) {
junk();
final result = parseResult();
acc[result.variable] = result.value;
}
switch stream {
case [ TNewLine ]:
return new ResultRecord(token, rc, acc);
}
});
}
public function parseResultClass() {
return parse(switch stream {
case [ TString('done') ]:
ResultClass.RDone;
case [ TString('running') ]:
ResultClass.RRunning;
case [ TString('connected') ]:
ResultClass.RConnected;
case [ TString('error') ]:
ResultClass.RError;
case [ TString('exit') ]:
ResultClass.RExit;
});
}
public function parseAsyncOutput() {
return parse(switch stream {
case [ TString(cls) ]:
final acc = new Map<String, Value>();
while (peek(0) == TComma) {
junk();
final result = parseResult();
acc[result.variable] = result.value;
}
{ cls : cls, results : acc };
});
}
public function parseResult() : Result {
return parse(switch stream {
case [ TString(v1), TAsyncRecord(Notify), v2 = parseValue() ]:
new Result(v1, v2);
});
}
public function parseValue() : Value {
return parse(switch stream {
case [ TCString(v) ]:
Value.Const(v);
case [ TTupleOpen, tuple = parseTuple([]) ]:
Value.Tuple(tuple);
case [ TListOpen, list = parseList(null) ]:
Value.List(list);
});
}
public function parseTuple(acc : Map<String, Value>) {
return parse(switch stream {
case [ TTupleClose ]:
acc;
case [ v = parseResult() ]:
acc[v.variable] = v.value;
switch stream {
case [ TTupleClose ]:
acc;
case [ TComma ]:
parseTuple(acc);
}
});
}
public function parseList(acc : Null<Either<Array<Value>, Array<Result>>>) {
return parse(switch stream {
case [ TListClose ]:
acc;
case _:
final elt = switch [ peek(0), peek(1) ] {
case [ TString(_), TAsyncRecord(Notify) ]:
Left(parseResult());
case _:
Right(parseValue());
}
// if the acc structure is null create it based on the element we just parsed.
if (acc == null) {
acc = switch elt {
case Left(v): Right(new Array<Result>());
case Right(v): Left(new Array<Value>());
}
}
// If the element parsed is not fit on the acc structure just throw an exception.
switch acc {
case Left(values):
switch elt {
case Right(value):
values.push(value);
case _:
throw new Exception('result will not fit in a list of values');
}
case Right(results):
switch elt {
case Left(result):
results.push(result);
case _:
throw new Exception('value will not fit in a results map');
}
}
switch stream {
case [ TListClose ]:
acc;
case [ TComma ]:
parseList(acc);
}
});
}
public function parseOutOfBandRecord() {
return switch peek(0)
{
case TToken(_), TAsyncRecord(_):
Async(parseAsyncRecord());
case _:
Stream(parseStreamRecord());
}
}
public function parseStreamRecord() {
return parse(switch stream {
case [ TStreamRecord(output), TCString(v), TNewLine ]:
new StreamRecord(output, v);
});
}
public function parseAsyncRecord() {
final token = switch peek(0) {
case TToken(v):
junk();
Some(Std.parseInt(v));
case _:
None;
}
return parse(switch stream {
case [ TAsyncRecord(t), o = parseAsyncOutput(), TNewLine ]:
new AsyncRecord(token, t, o.cls, o.results);
});
}
}
import hxcppdbg.gdb.Parser.MiParser;
function main() {
testValue();
testResult();
testEmptyTuple();
testTuple();
testEmptyList();
testValueList();
testResultList();
testMixedList();
testStreamRecord();
testResultRecord();
testOutput();
}
function testValue() {
trace('value');
final parser = new MiParser('"myVal"');
trace(parser.parseValue());
}
function testResult() {
trace('result');
final parser = new MiParser('myVal="some val"');
trace(parser.parseResult());
}
function testEmptyTuple() {
trace('empty tuple');
final parser = new MiParser('{}');
trace(parser.parseValue());
}
function testTuple() {
trace('tuple');
final parser = new MiParser('{ myVal1="some val1", myVal2="some val2" }');
trace(parser.parseValue());
}
function testEmptyList() {
trace('empty list');
final parser = new MiParser('[]');
trace(parser.parseValue());
}
function testValueList() {
trace('value list');
final parser = new MiParser('[ "myVal1", "myVal2" ]');
trace(parser.parseValue());
}
function testResultList() {
trace('result list');
final parser = new MiParser('[ myVal1="some val1", myVal2="some val2" ]');
trace(parser.parseValue());
}
function testMixedList() {
trace('mixed list');
final parser = new MiParser('[ "my const", myVal1="some val1", { myVal1="some val1", myVal2="some val2" } ]');
trace(parser.parseValue());
}
function testStreamRecord() {
trace('console stream');
final parser = new MiParser('~"my console stream output"\n');
trace(parser.parseStreamRecord());
trace('target stream');
final parser = new MiParser('@"my target stream output"\n');
trace(parser.parseStreamRecord());
trace('log stream');
final parser = new MiParser('&"my log stream output"\n');
trace(parser.parseStreamRecord());
}
function testResultRecord() {
trace('result record');
final parser = new MiParser('12^running\n');
trace(parser.parseResultRecord());
}
function testOutput() {
trace('output');
final parser = new MiParser('=thread-group-started,id="i1",pid="22901"\n=thread-created,id="1",group-id="i1"\n=breakpoint-modified,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00005555555a63d7",func="Main_obj::main()",file="./src/Main.cpp",fullname="/mnt/d/programming/haxe/hxcppdbg/sample/bin/src/Main.cpp",line="43",thread-groups=["i1"],times="0",original-location="src/Main.cpp:43"}\n=library-loaded,id="/lib64/ld-linux-x86-64.so.2",target-name="/lib64/ld-linux-x86-64.so.2",host-name="/lib64/ld-linux-x86-64.so.2",symbols-loaded="0",thread-group="i1",ranges=[{from="0x00007ffff7fd0100",to="0x00007ffff7ff2674"}]\n^running\n*running,thread-id="all"\n(gdb)');
trace(parser.parseOutput());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment