Created
January 7, 2023 19:01
-
-
Save Aidan63/d1026d161ebc8c6176db6b69a0f82be4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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