From ebb7b677ba661c980e0a9d582aac27785ae5256d Mon Sep 17 00:00:00 2001 From: Dan Hirsch <thequux@upstandinghackers.com> Date: Sat, 4 Jan 2014 22:11:32 +0100 Subject: [PATCH] Added handwritten test for h_action to C# --- src/bindings/dotnet/ext/hammer.cs | 78 ++++++++++++++++--- src/bindings/dotnet/hammer.i | 36 +++++++++ src/bindings/dotnet/test/hammer_hand_tests.cs | 18 +++++ src/hammer.h | 6 +- src/registry.c | 18 ++--- 5 files changed, 132 insertions(+), 24 deletions(-) create mode 100644 src/bindings/dotnet/test/hammer_hand_tests.cs diff --git a/src/bindings/dotnet/ext/hammer.cs b/src/bindings/dotnet/ext/hammer.cs index 9b816ef..00f217f 100644 --- a/src/bindings/dotnet/ext/hammer.cs +++ b/src/bindings/dotnet/ext/hammer.cs @@ -1,9 +1,23 @@ using Hammer.Internal; using System; using System.Runtime.InteropServices; +using System.Collections; 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 { internal HParser wrapped; @@ -19,7 +33,7 @@ namespace Hammer pins.Add(o); return this; } - + public Object Parse(byte[] str) { byte[] strp; @@ -27,15 +41,20 @@ namespace Hammer strp = new byte[1]; else strp = str; - unsafe { - fixed(byte* b = &strp[0]) { - HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length); - if (res != null) { - return Unmarshal(res.ast); - } else { - return null; + try { + unsafe { + fixed(byte* b = &strp[0]) { + HParseResult res = hammer.h_parse(wrapped, (IntPtr)b, (uint)str.Length); + if (res != null) { + // TODO: free the PR + return Unmarshal(res.ast); + } else { + throw new ParseError(); + } } } + } catch (ParseError e) { + return null; } } @@ -65,6 +84,22 @@ namespace Hammer return ret; } 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"); } } @@ -83,10 +118,25 @@ namespace Hammer hammer.h_bind_indirect(this.wrapped, p.wrapped); } } - + 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) { // Probably not what you want unless you're parsing binary data. @@ -104,7 +154,7 @@ namespace Hammer IntPtr[] rlist = new IntPtr[parsers.Length+1]; 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; return rlist; @@ -307,7 +357,11 @@ namespace Hammer { 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); + } } } \ No newline at end of file diff --git a/src/bindings/dotnet/hammer.i b/src/bindings/dotnet/hammer.i index b959bb9..98ef59b 100644 --- a/src/bindings/dotnet/hammer.i +++ b/src/bindings/dotnet/hammer.i @@ -42,3 +42,39 @@ } %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); +} + } diff --git a/src/bindings/dotnet/test/hammer_hand_tests.cs b/src/bindings/dotnet/test/hammer_hand_tests.cs new file mode 100644 index 0000000..0d88528 --- /dev/null +++ b/src/bindings/dotnet/test/hammer_hand_tests.cs @@ -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]))); + + } + } +} \ No newline at end of file diff --git a/src/hammer.h b/src/hammer.h index 2914b8f..dc403c0 100644 --- a/src/hammer.h +++ b/src/hammer.h @@ -666,13 +666,13 @@ void h_benchmark_report(FILE* stream, HBenchmarkResults* results); // {{{ Token type registry /// 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 -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 -const char* h_get_token_type_name(int token_type); +const char* h_get_token_type_name(HTokenType token_type); // }}} #ifdef __cplusplus diff --git a/src/registry.c b/src/registry.c index c59b6ea..60aa886 100644 --- a/src/registry.c +++ b/src/registry.c @@ -22,14 +22,14 @@ typedef struct Entry_ { const char* name; - int value; + HTokenType value; } Entry; static void *tt_registry = 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 -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. @@ -45,12 +45,12 @@ static int compare_entries(const void* v1, const void* v2) { 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)); new_entry->name = name; - new_entry->value = -1; + new_entry->value = 0; Entry* probe = *(Entry**)tsearch(new_entry, &tt_registry, compare_entries); - if (probe->value != -1) { + if (probe->value != 0) { // Token type already exists... // TODO: treat this as a bug? free(new_entry); @@ -70,16 +70,16 @@ int h_allocate_token_type(const char* name) { return probe->value; } } -int h_get_token_type_number(const char* name) { +HTokenType h_get_token_type_number(const char* name) { Entry e; e.name = name; Entry **ret = (Entry**)tfind(&e, &tt_registry, compare_entries); if (ret == NULL) - return -1; + return 0; else 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) return NULL; else -- GitLab