Skip to content

Instantly share code, notes, and snippets.

@Faheetah
Last active June 23, 2021 15:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Faheetah/c002fbcbd48ece95a9adf423afc0604d to your computer and use it in GitHub Desktop.
Save Faheetah/c002fbcbd48ece95a9adf423afc0604d to your computer and use it in GitHub Desktop.
An example of parsing C with Elixir nimble_parsec, specifically Libvirt XDRs
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