Skip to content

Instantly share code, notes, and snippets.

@p-baleine
Last active August 30, 2020 22:32
Show Gist options
  • Save p-baleine/61981ef969c96a5e4e72ee68f4ef76a7 to your computer and use it in GitHub Desktop.
Save p-baleine/61981ef969c96a5e4e72ee68f4ef76a7 to your computer and use it in GitHub Desktop.
libjq と戯れる、その 1

準備

libjq をビルドする

git clone  https://github.com/stedolan/jq.git
cd jq
git submodule update --init
autoreconf -fi
./configure --with-oniguruma=builtin
make LDFLAGS=-all-static -j8
cp .libs/libjq.a ../

cd modules/oniguruma
make
cp src/.libs/libonig.a ../../

試してみる:

make
./bin/01_hello_jq
hello jq!
#include <iostream>
#include <string>
extern "C" {
#include <jv.h>
#include <jq.h>
}
using namespace std;
/*
* libjq の基本を理解したい。
*
* 以下、jq の Wiki ページより
* https://github.com/stedolan/jq/wiki/C-API:-jq-program-invocation
*
* Hint: the API in jv.h is for dealing with parsed JSON values, while the API in
* jq.h is for evaluating jq programs. Use jq_init() to allocate a jq_state *
* value, then use jq_compile_args() to compile a jq program, then use jq_start()
* to get ready to evaluate it on one input value, and then repeatedly call
* jq_next() to extract all the output values that the program produces.
* Termination is indicated by jq_next() outputting an invalid value (without a
* message), while errors are indicated by jq_next() outputting an invalid value
* with a message. When done, use jq_teardown() to release the jq_state * value.
*
* まずはこの基本の処理を実装してみる。
*/
int main() {
// テスト用の文字列
const string input = R"EOS(
[
{
"name": "Ness",
"age": 12,
"origin": { "country": "Eagleland", "town": "Onett" }
},
{
"name": "Paula",
"age": 11,
"origin": { "country": "Eagleland", "town": "Twoson" }
}
]
)EOS";
// jq プログラム
const string program = ".[] | {town: .origin.town}";
int ret = 0;
// jq_state* の領域確保するために jq_init をコールする
jq_state* jq = jq_init();
// jq プログラムをコンパイルする
int compiled = jq_compile_args(jq, program.c_str(), jv_object());
if (!compiled) {
cerr << "jq_compile_args failed." << endl;
ret = 1;
} else {
// input のパーサーを準備する
jv_parser* parser = jv_parser_new(/* flag */ 0); // TODO: flag の意味を調べる
jv_parser_set_buf(parser, input.c_str(), input.size(), /* is_partial */ 0); // TODO: is_partial の意味を調べる
/*
* jq の main.c:main() ではここでループしているけれど、それは複数の
* 入力を可能にするため
* ここでは入力は input 一つなのでループしない
*/
// 次の入力値を得る
jv value = jv_parser_next(parser);
if (jv_is_valid(value)) {
// 入力値を評価するための準備をするために jq_start をコールする
jq_start(jq, value, /* flags */ 0); // TODO: flags の意味を調べる
// jq プログラムの出力値を得るために繰り返し jq_next をコールする
jv result = jq_next(jq);
// jq_is_valid な限り jq_next をコールする
while (jv_is_valid(result)) {
// 標準出力に出力したいので jv_dump_string する
jv dumped = jv_dump_string(jv_copy(result), /* flags */ 0); // TODO: flags の意味を調べる
cout << jv_string_value(dumped) << endl;
jv_free(dumped);
jv_free(result);
result = jq_next(jq);
}
} else if (jv_invalid_has_msg(jv_copy(value))) {
// パースエラー
jv msg = jv_invalid_get_msg(value);
cerr << jv_string_value(msg) << endl;
jv_free(msg);
ret = 2;
} else {
// System error
ret = 3;
}
}
// jq_state* を解放するために jq_teardown をコールする
jq_teardown(&jq);
return ret;
}
CC = $(shell ls *.cc)
BIN = $(CC:%.cc=bin/%)
LINK_FLAGS = -ljq -lpthread -lonig
all: $(BIN)
bin/%: %.cc libjq.a libonig.a
clang++ -v -Wall -O2 -o $@ -L. -I. $< ${LINK_FLAGS}
%.o: %.cc
clang++ -c -I. $<
.PHONY: clean
clean:
rm -f *.o bin/*
@p-baleine
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment