Last active
June 23, 2021 15:58
-
-
Save Faheetah/c002fbcbd48ece95a9adf423afc0604d to your computer and use it in GitHub Desktop.
An example of parsing C with Elixir nimble_parsec, specifically Libvirt XDRs
This file contains hidden or 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
defmodule StructParser do | |
import NimbleParsec | |
@space 0x0020 | |
@tab 0x009 | |
whitespace = utf8_char([@space, @tab, ?\n]) | |
ignore_whitespace = | |
lookahead(whitespace) | |
|> repeat(ignore(whitespace)) | |
ignore_comment = | |
ignore(string("/*")) | |
|> optional(ignore(string("*"))) | |
|> concat(ignore_whitespace) | |
|> repeat( | |
lookahead_not(string("*/")) | |
|> utf8_char([]) | |
) | |
|> ignore() | |
|> ignore(string("*/")) | |
procedure = | |
utf8_string([?A..?Z, ?_], min: 1) | |
|> ignore(whitespace) | |
|> ignore(string("=")) | |
|> ignore(whitespace) | |
|> integer(min: 1) | |
|> optional(ignore(string(","))) | |
|> tag(:procedure) | |
remote_procedures = | |
ignore(string("enum remote_procedure {")) | |
|> repeat( | |
choice([ignore_comment, ignore_whitespace, procedure]) | |
) | |
|> ignore(string("}")) | |
struct_value = | |
optional(ignore_whitespace) | |
|> repeat( | |
lookahead_not(string(";")) | |
|> choice([ | |
ignore_whitespace, | |
utf8_string([?a..?z, ?A..?Z, ?_, ?<, ?>], min: 1) | |
]) | |
) | |
|> ignore(string(";")) | |
|> optional(ignore_whitespace) | |
|> optional(ignore_comment) | |
|> optional(ignore_whitespace) | |
|> wrap() | |
struct = | |
ignore(string("struct")) | |
|> concat(ignore_whitespace) | |
|> utf8_string([?a..?z, ?_], min: 1) | |
|> optional(ignore_whitespace) | |
|> ignore(string("{")) | |
|> optional(ignore_whitespace) | |
|> optional(ignore_comment) | |
|> optional(ignore_whitespace) | |
|> tag(repeat(struct_value), :fields) | |
|> tag(:struct) | |
defparsec :parse, | |
choice([remote_procedures, struct, ignore(utf8_char([]))]) | |
|> repeat() | |
end | |
c_code = """ | |
/* -*- c -*- | |
* remote_protocol.x: private protocol for communicating between | |
* remote_internal driver and libvirtd. This protocol is | |
* internal and may change at any time. | |
* | |
* Copyright (C) 2006-2015 Red Hat, Inc. | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2.1 of the License, or (at your option) any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library. If not, see | |
* <http://www.gnu.org/licenses/>. | |
*/ | |
/* Notes: | |
* | |
* (1) The protocol is internal and may change at any time, without | |
* notice. Do not use it. Instead link to libvirt and use the remote | |
* driver. | |
* | |
* (2) See bottom of this file for a description of the home-brew RPC. | |
* | |
* (3) Authentication/encryption is done outside this protocol. | |
* | |
* (4) For namespace reasons, all exported names begin 'remote_' or | |
* 'REMOTE_'. This makes names quite long. | |
*/ | |
%#include <libvirt/libvirt.h> | |
%#include "internal.h" | |
%#include "virxdrdefs.h" | |
%#include "virsocket.h" | |
/*----- Data types. -----*/ | |
/* Length of long, but not unbounded, strings. | |
* This is an arbitrary limit designed to stop the decoder from trying | |
* to allocate unbounded amounts of memory when fed with a bad message. | |
*/ | |
const REMOTE_STRING_MAX = 4194304; | |
struct remote_node_get_memory_stats { | |
remote_nonnull_string field; | |
unsigned hyper value; | |
}; | |
struct remote_domain_disk_error { | |
remote_nonnull_string disk; | |
int error; | |
enum remote_procedure { | |
/* Each function must be preceded by a comment providing one or | |
* more annotations: | |
*/ | |
/** | |
* @generate: none | |
* @priority: high | |
* @acl: connect:getattr | |
*/ | |
REMOTE_PROC_CONNECT_OPEN = 1, | |
/** | |
* @generate: none | |
*/ | |
REMOTE_PROC_CONNECT_FOO = 2 | |
}; | |
struct remote_connect_get_hostname_ret { | |
remote_nonnull_string hostname; | |
}; | |
struct remote_domain_set_interface_parameters_args { | |
remote_nonnull_domain dom; | |
remote_nonnull_string device; | |
remote_typed_param params<REMOTE_DOMAIN_INTERFACE_PARAMETERS_MAX>; | |
unsigned int flags; | |
}; | |
""" | |
{_, content, _, _, _, _} = StructParser.parse(c_code) | |
IO.inspect(content) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment