Added handwritten test for h_action to C#
This commit is contained in:
parent
feaf1a7e06
commit
ebb7b677ba
5 changed files with 132 additions and 24 deletions
|
|
@ -1,9 +1,23 @@
|
||||||
using Hammer.Internal;
|
using Hammer.Internal;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Collections;
|
||||||
namespace Hammer
|
namespace Hammer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public delegate Object HAction(Object obj);
|
||||||
|
public delegate bool HPredicate(Object obj);
|
||||||
|
|
||||||
|
public class ParseError : Exception
|
||||||
|
{
|
||||||
|
public readonly string Reason;
|
||||||
|
public ParseError() : this(null) {}
|
||||||
|
public ParseError(string reason) : base() {
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class Parser
|
public class Parser
|
||||||
{
|
{
|
||||||
internal HParser wrapped;
|
internal HParser wrapped;
|
||||||
|
|
@ -19,7 +33,7 @@ namespace Hammer
|
||||||
pins.Add(o);
|
pins.Add(o);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object Parse(byte[] str)
|
public Object Parse(byte[] str)
|
||||||
{
|
{
|
||||||
byte[] strp;
|
byte[] strp;
|
||||||
|
|
@ -27,15 +41,20 @@ namespace Hammer
|
||||||
strp = new byte[1];
|
strp = new byte[1];
|
||||||
else
|
else
|
||||||
strp = str;
|
strp = str;
|
||||||
unsafe {
|
try {
|
||||||
fixed(byte* b = &strp[0]) {
|
unsafe {
|
||||||
HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length);
|
fixed(byte* b = &strp[0]) {
|
||||||
if (res != null) {
|
HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length);
|
||||||
return Unmarshal(res.ast);
|
if (res != null) {
|
||||||
} else {
|
// TODO: free the PR
|
||||||
return null;
|
return Unmarshal(res.ast);
|
||||||
|
} else {
|
||||||
|
throw new ParseError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (ParseError e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +84,22 @@ namespace Hammer
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
if (tok.token_type == Hammer.tt_dotnet)
|
||||||
|
{
|
||||||
|
HTaggedToken tagged = hammer.h_parsed_token_get_tagged_token(tok);
|
||||||
|
Object cb = Hammer.tag_to_action[tagged.label];
|
||||||
|
Object unmarshalled = Unmarshal(tagged.token);
|
||||||
|
if (cb is HAction) {
|
||||||
|
HAction act = (HAction)cb;
|
||||||
|
return act(unmarshalled);
|
||||||
|
} else if (cb is HPredicate) {
|
||||||
|
HPredicate pred = (HPredicate)cb;
|
||||||
|
if (!pred(unmarshalled))
|
||||||
|
throw new ParseError("Predicate failed");
|
||||||
|
else
|
||||||
|
return unmarshalled;
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new Exception("Should not reach here");
|
throw new Exception("Should not reach here");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,10 +118,25 @@ namespace Hammer
|
||||||
hammer.h_bind_indirect(this.wrapped, p.wrapped);
|
hammer.h_bind_indirect(this.wrapped, p.wrapped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Hammer
|
public class Hammer
|
||||||
{
|
{
|
||||||
|
internal static IDictionary tag_to_action;
|
||||||
|
internal static HTokenType tt_dotnet;
|
||||||
|
static Hammer()
|
||||||
|
{
|
||||||
|
tt_dotnet = hammer.h_allocate_token_type("com.upstandinghackers.hammer.dotnet.tagged");
|
||||||
|
hammer.h_set_dotnet_tagged_token_type(tt_dotnet);
|
||||||
|
tag_to_action = new System.Collections.Hashtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ulong RegisterAction(HAction action)
|
||||||
|
{
|
||||||
|
ulong newAction = (ulong)tag_to_action.Count;
|
||||||
|
tag_to_action[newAction] = action;
|
||||||
|
return newAction;
|
||||||
|
}
|
||||||
|
|
||||||
internal static byte[] ToBytes(string s)
|
internal static byte[] ToBytes(string s)
|
||||||
{
|
{
|
||||||
// Probably not what you want unless you're parsing binary data.
|
// Probably not what you want unless you're parsing binary data.
|
||||||
|
|
@ -104,7 +154,7 @@ namespace Hammer
|
||||||
IntPtr[] rlist = new IntPtr[parsers.Length+1];
|
IntPtr[] rlist = new IntPtr[parsers.Length+1];
|
||||||
for (int i = 0; i < parsers.Length; i++)
|
for (int i = 0; i < parsers.Length; i++)
|
||||||
{
|
{
|
||||||
rlist[i] = HParser.getCPtr(parsers[i].wrapped).Handle;
|
rlist[i] = HParser.getCPtr(parsers[i].wrapped).Handle;
|
||||||
}
|
}
|
||||||
rlist[parsers.Length] = IntPtr.Zero;
|
rlist[parsers.Length] = IntPtr.Zero;
|
||||||
return rlist;
|
return rlist;
|
||||||
|
|
@ -307,7 +357,11 @@ namespace Hammer
|
||||||
{
|
{
|
||||||
return new Parser(hammer.h_repeat_n(p.wrapped, count));
|
return new Parser(hammer.h_repeat_n(p.wrapped, count));
|
||||||
}
|
}
|
||||||
|
public static Parser Action(Parser p, HAction action)
|
||||||
|
{
|
||||||
|
ulong actionNo = Hammer.RegisterAction(action);
|
||||||
|
return new Parser(hammer.h_tag(p.wrapped, actionNo)).Pin(p).Pin(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -42,3 +42,39 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
%include "../swig/hammer.i";
|
%include "../swig/hammer.i";
|
||||||
|
|
||||||
|
%{
|
||||||
|
HTokenType dotnet_tagged_token_type;
|
||||||
|
%}
|
||||||
|
%inline {
|
||||||
|
void h_set_dotnet_tagged_token_type(HTokenType new_tt) {
|
||||||
|
dotnet_tagged_token_type = new_tt;
|
||||||
|
}
|
||||||
|
// Need this inline as well
|
||||||
|
struct HTaggedToken {
|
||||||
|
HParsedToken *token;
|
||||||
|
uint64_t label;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this is to make it easier to access via SWIG
|
||||||
|
struct HTaggedToken *h_parsed_token_get_tagged_token(HParsedToken* hpt) {
|
||||||
|
return (struct HTaggedToken*)hpt->token_data.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
HParsedToken *act_tag(const HParseResult* p, void* user_data) {
|
||||||
|
struct HTaggedToken *tagged = H_ALLOC(struct HTaggedToken);
|
||||||
|
tagged->label = *(uint64_t*)user_data;
|
||||||
|
tagged->token = p->ast;
|
||||||
|
return h_make(p->arena, dotnet_tagged_token_type, tagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
HParser *h_tag__m(HAllocator *mm__, HParser *p, uint64_t tag) {
|
||||||
|
uint64_t *tagptr = h_new(uint64_t, 1);
|
||||||
|
*tagptr = tag;
|
||||||
|
return h_action__m(mm__, p, act_tag, tagptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
HParser *h_tag(HParser *p, uint64_t tag) {
|
||||||
|
return h_tag__m(&system_allocator, p, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
18
src/bindings/dotnet/test/hammer_hand_tests.cs
Normal file
18
src/bindings/dotnet/test/hammer_hand_tests.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Hammer.Test
|
||||||
|
{
|
||||||
|
using NUnit.Framework;
|
||||||
|
[TestFixture]
|
||||||
|
public partial class HammerTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestAction()
|
||||||
|
{
|
||||||
|
Parser parser = Hammer.Action(Hammer.Sequence(Hammer.Choice(Hammer.Token("a"),
|
||||||
|
Hammer.Token("A")),
|
||||||
|
Hammer.Choice(Hammer.Token("b"),
|
||||||
|
Hammer.Token("B"))),
|
||||||
|
(HAction)(x => char.ToUpper(((string)x)[0])));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -666,13 +666,13 @@ void h_benchmark_report(FILE* stream, HBenchmarkResults* results);
|
||||||
|
|
||||||
// {{{ Token type registry
|
// {{{ Token type registry
|
||||||
/// Allocate a new, unused (as far as this function knows) token type.
|
/// Allocate a new, unused (as far as this function knows) token type.
|
||||||
int h_allocate_token_type(const char* name);
|
HTokenType h_allocate_token_type(const char* name);
|
||||||
|
|
||||||
/// Get the token type associated with name. Returns -1 if name is unkown
|
/// Get the token type associated with name. Returns -1 if name is unkown
|
||||||
int h_get_token_type_number(const char* name);
|
HTokenType h_get_token_type_number(const char* name);
|
||||||
|
|
||||||
/// Get the name associated with token_type. Returns NULL if the token type is unkown
|
/// Get the name associated with token_type. Returns NULL if the token type is unkown
|
||||||
const char* h_get_token_type_name(int token_type);
|
const char* h_get_token_type_name(HTokenType token_type);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,14 @@
|
||||||
|
|
||||||
typedef struct Entry_ {
|
typedef struct Entry_ {
|
||||||
const char* name;
|
const char* name;
|
||||||
int value;
|
HTokenType value;
|
||||||
} Entry;
|
} Entry;
|
||||||
|
|
||||||
static void *tt_registry = NULL;
|
static void *tt_registry = NULL;
|
||||||
static Entry** tt_by_id = NULL;
|
static Entry** tt_by_id = NULL;
|
||||||
static int tt_by_id_sz = 0;
|
static unsigned int tt_by_id_sz = 0;
|
||||||
#define TT_START TT_USER
|
#define TT_START TT_USER
|
||||||
static int tt_next = TT_START;
|
static HTokenType tt_next = TT_START;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO: These are for the extension registry, which does not yet have a good name.
|
// TODO: These are for the extension registry, which does not yet have a good name.
|
||||||
|
|
@ -45,12 +45,12 @@ static int compare_entries(const void* v1, const void* v2) {
|
||||||
return strcmp(e1->name, e2->name);
|
return strcmp(e1->name, e2->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int h_allocate_token_type(const char* name) {
|
HTokenType h_allocate_token_type(const char* name) {
|
||||||
Entry* new_entry = malloc(sizeof(*new_entry));
|
Entry* new_entry = malloc(sizeof(*new_entry));
|
||||||
new_entry->name = name;
|
new_entry->name = name;
|
||||||
new_entry->value = -1;
|
new_entry->value = 0;
|
||||||
Entry* probe = *(Entry**)tsearch(new_entry, &tt_registry, compare_entries);
|
Entry* probe = *(Entry**)tsearch(new_entry, &tt_registry, compare_entries);
|
||||||
if (probe->value != -1) {
|
if (probe->value != 0) {
|
||||||
// Token type already exists...
|
// Token type already exists...
|
||||||
// TODO: treat this as a bug?
|
// TODO: treat this as a bug?
|
||||||
free(new_entry);
|
free(new_entry);
|
||||||
|
|
@ -70,16 +70,16 @@ int h_allocate_token_type(const char* name) {
|
||||||
return probe->value;
|
return probe->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int h_get_token_type_number(const char* name) {
|
HTokenType h_get_token_type_number(const char* name) {
|
||||||
Entry e;
|
Entry e;
|
||||||
e.name = name;
|
e.name = name;
|
||||||
Entry **ret = (Entry**)tfind(&e, &tt_registry, compare_entries);
|
Entry **ret = (Entry**)tfind(&e, &tt_registry, compare_entries);
|
||||||
if (ret == NULL)
|
if (ret == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
else
|
else
|
||||||
return (*ret)->value;
|
return (*ret)->value;
|
||||||
}
|
}
|
||||||
const char* h_get_token_type_name(int token_type) {
|
const char* h_get_token_type_name(HTokenType token_type) {
|
||||||
if (token_type >= tt_next || token_type < TT_START)
|
if (token_type >= tt_next || token_type < TT_START)
|
||||||
return NULL;
|
return NULL;
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue