Added handwritten test for h_action to C#

This commit is contained in:
Dan Hirsch 2014-01-04 22:11:32 +01:00
parent feaf1a7e06
commit ebb7b677ba
5 changed files with 132 additions and 24 deletions

View file

@ -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;
@ -27,17 +41,22 @@ namespace Hammer
strp = new byte[1]; strp = new byte[1];
else else
strp = str; strp = str;
try {
unsafe { unsafe {
fixed(byte* b = &strp[0]) { fixed(byte* b = &strp[0]) {
HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length); HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length);
if (res != null) { if (res != null) {
// TODO: free the PR
return Unmarshal(res.ast); return Unmarshal(res.ast);
} else { } else {
throw new ParseError();
}
}
}
} catch (ParseError e) {
return null; return null;
} }
} }
}
}
internal Object Unmarshal(HParsedToken tok) internal Object Unmarshal(HParsedToken tok)
{ {
@ -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");
} }
} }
@ -86,6 +121,21 @@ namespace Hammer
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)
{ {
@ -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);
}
} }
} }

View file

@ -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);
}
}

View 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])));
}
}
}

View file

@ -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

View file

@ -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