3
\$\begingroup\$

To teach myself how to do meta-progaming using Prolog, I've been playing around with translating ABNF syntax into DCG syntax. I'm still at the lexing and tokenising stage, but hope to advance to the "yacc" stage soon.

Using Json, since it is defined in ABNF at https://trac.ietf.org/trac/json/browser/abnf/json.abnf?rev=2 (and something I find frustrating in compiler tutorials I've found online is they don't stick to context free grammars after boring students with the basics), I've written the following code which works OK on the simple examples I've tested.

:- use_module(library(dcg/basics)).

% value = false / null / true / object / array / number / string
value(l("false"))           --> blanks, [102, 97, 108, 115, 101], blanks.
value(l("null"))            --> blanks, [110, 117, 108, 108], blanks.
value(l("true"))            --> blanks, [116, 114, 117, 101], blanks.
% object = "{" [ member *( "," member ) ] "}"
value(o(Object))            --> blanks, [123], blanks, members(Object), blanks, [125], blanks.
% array = "[" [ value *( "," value ) ] "]"
value(a(Array))             --> blanks, [91], blanks, values(Array), blanks, [93], blanks.
% number = [ "-" ] int [ frac ] [ exp ]
value(n(Number))            --> number(Number).
% string = '"' *char '"'
value(s(String))            --> blanks, [34], chars(Codes), { string_codes(String, Codes) }. % needs a different pattern to handle escaping
% a string has several chars terminated by an unescaped double quote
chars([92,34|Codes])        --> [92,34], chars(Codes). % skip \"
chars([Code|Codes])         --> [Code], chars(Codes), { Code \= 34 }.
chars([])                   --> [34].
% member = string ":" value   abreviated to memb to avoid collision with prolog's builtin clause member
memb(m(s(String), Value))   --> blanks, value(s(String)), blanks, [58], blanks, value(Value), blanks.
% an object can have several comma separated members
members([Member|Members])   --> blanks, memb(Member), blanks, [44], blanks, members(Members).
members([Member])           --> blanks, memb(Member), blanks.
members([])                 --> [].
% an array can have several comma separated values
values([Value|Values])      --> blanks, value(Value), blanks, [44], blanks, values(Values).
values([Value])             --> blanks, value(Value), blanks.
values([])                  --> [].

tokenize(Filename, Tokens) :-
  phrase_from_file(value(Tokens), Filename), !.

I'd appreciate some tips on how to improve this from the gurus in this group.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.