mirror of
https://github.com/emersion/kanshi
synced 2024-09-18 09:51:36 +02:00
Add basic parser
This commit is contained in:
parent
c1f6c85052
commit
ddb8682b9e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/build
|
||||||
|
/build-*
|
20
include/config.h
Normal file
20
include/config.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef KANSHI_CONFIG_H
|
||||||
|
#define KANSHI_CONFIG_H
|
||||||
|
|
||||||
|
#include <wayland-util.h>
|
||||||
|
|
||||||
|
struct kanshi_profile_output {
|
||||||
|
struct wl_list link;
|
||||||
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kanshi_profile {
|
||||||
|
struct wl_list link;
|
||||||
|
struct wl_list outputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kanshi_config {
|
||||||
|
struct wl_list profiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
27
include/parser.h
Normal file
27
include/parser.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef KANSHI_PARSER_H
|
||||||
|
#define KANSHI_PARSER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct kanshi_config;
|
||||||
|
|
||||||
|
enum kanshi_token_type {
|
||||||
|
KANSHI_TOKEN_LBRACKET,
|
||||||
|
KANSHI_TOKEN_RBRACKET,
|
||||||
|
KANSHI_TOKEN_STR,
|
||||||
|
KANSHI_TOKEN_NEWLINE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kanshi_parser {
|
||||||
|
FILE *f;
|
||||||
|
int next;
|
||||||
|
int line, col;
|
||||||
|
|
||||||
|
enum kanshi_token_type tok_type;
|
||||||
|
char tok_str[1024];
|
||||||
|
size_t tok_str_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kanshi_config *parse_config(const char *path);
|
||||||
|
|
||||||
|
#endif
|
12
main.c
Normal file
12
main.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct kanshi_config *config = parse_config("/home/simon/.config/kanshi/config");
|
||||||
|
if (config == NULL) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
50
meson.build
Normal file
50
meson.build
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
project(
|
||||||
|
'kanshi',
|
||||||
|
'c',
|
||||||
|
version: '0.0.0',
|
||||||
|
license: 'MIT',
|
||||||
|
meson_version: '>=0.47.0',
|
||||||
|
default_options: [
|
||||||
|
'c_std=c99',
|
||||||
|
'warning_level=3',
|
||||||
|
'werror=true',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
|
||||||
|
add_project_arguments(cc.get_supported_arguments([
|
||||||
|
'-Wundef',
|
||||||
|
'-Wlogical-op',
|
||||||
|
'-Wmissing-include-dirs',
|
||||||
|
'-Wold-style-definition',
|
||||||
|
'-Wpointer-arith',
|
||||||
|
'-Winit-self',
|
||||||
|
'-Wfloat-equal',
|
||||||
|
'-Wstrict-prototypes',
|
||||||
|
'-Wredundant-decls',
|
||||||
|
'-Wimplicit-fallthrough=2',
|
||||||
|
'-Wendif-labels',
|
||||||
|
'-Wstrict-aliasing=2',
|
||||||
|
'-Woverflow',
|
||||||
|
'-Wformat=2',
|
||||||
|
|
||||||
|
'-Wno-missing-braces',
|
||||||
|
'-Wno-missing-field-initializers',
|
||||||
|
'-Wno-unused-parameter',
|
||||||
|
]), language: 'c')
|
||||||
|
|
||||||
|
wayland_client = dependency('wayland-client')
|
||||||
|
|
||||||
|
kanshi_inc = include_directories('include')
|
||||||
|
|
||||||
|
executable(
|
||||||
|
meson.project_name(),
|
||||||
|
files(
|
||||||
|
'main.c',
|
||||||
|
'parser.c',
|
||||||
|
),
|
||||||
|
include_directories: kanshi_inc,
|
||||||
|
dependencies: [wayland_client],
|
||||||
|
install: true,
|
||||||
|
)
|
235
parser.c
Normal file
235
parser.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
const char *token_type_str(enum kanshi_token_type t) {
|
||||||
|
switch (t) {
|
||||||
|
case KANSHI_TOKEN_LBRACKET:
|
||||||
|
return "'{'";
|
||||||
|
case KANSHI_TOKEN_RBRACKET:
|
||||||
|
return "'}'";
|
||||||
|
case KANSHI_TOKEN_STR:
|
||||||
|
return "string";
|
||||||
|
case KANSHI_TOKEN_NEWLINE:
|
||||||
|
return "newline";
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int parser_read_char(struct kanshi_parser *parser) {
|
||||||
|
if (parser->next >= 0) {
|
||||||
|
int ch = parser->next;
|
||||||
|
parser->next = -1;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ch = fgetc(parser->f);
|
||||||
|
if (ch == EOF) {
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "fgetc failed: %s\n", strerror(errno));
|
||||||
|
} else {
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '\n') {
|
||||||
|
parser->line++;
|
||||||
|
parser->col = 0;
|
||||||
|
} else {
|
||||||
|
parser->col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parser_peek_char(struct kanshi_parser *parser) {
|
||||||
|
int ch = parser_read_char(parser);
|
||||||
|
parser->next = ch;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parser_read_str(struct kanshi_parser *parser) {
|
||||||
|
while (1) {
|
||||||
|
int ch = parser_peek_char(parser);
|
||||||
|
if (ch < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isspace(ch) || ch == '{' || ch == '}') {
|
||||||
|
parser->tok_str[parser->tok_str_len] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always keep enough room for a terminating NULL char
|
||||||
|
if (parser->tok_str_len + 1 >= sizeof(parser->tok_str)) {
|
||||||
|
fprintf(stderr, "string too long\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parser->tok_str[parser->tok_str_len] = parser_read_char(parser);
|
||||||
|
parser->tok_str_len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parser_next_token(struct kanshi_parser *parser) {
|
||||||
|
while (1) {
|
||||||
|
int ch = parser_read_char(parser);
|
||||||
|
if (ch < 0) {
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '{') {
|
||||||
|
parser->tok_type = KANSHI_TOKEN_LBRACKET;
|
||||||
|
return true;
|
||||||
|
} else if (ch == '}') {
|
||||||
|
parser->tok_type = KANSHI_TOKEN_RBRACKET;
|
||||||
|
return true;
|
||||||
|
} else if (ch == '\n') {
|
||||||
|
parser->tok_type = KANSHI_TOKEN_NEWLINE;
|
||||||
|
return true;
|
||||||
|
} else if (isspace(ch)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
parser->tok_type = KANSHI_TOKEN_STR;
|
||||||
|
parser->tok_str[0] = ch;
|
||||||
|
parser->tok_str_len = 1;
|
||||||
|
return parser_read_str(parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parser_expect_token(struct kanshi_parser *parser,
|
||||||
|
enum kanshi_token_type want) {
|
||||||
|
if (!parser_next_token(parser)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (parser->tok_type != want) {
|
||||||
|
fprintf(stderr, "expected %s, got %s\n",
|
||||||
|
token_type_str(want), token_type_str(parser->tok_type));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_profile_output *parse_profile_output(struct kanshi_parser *parser) {
|
||||||
|
struct kanshi_profile_output *output = calloc(1, sizeof(*output));
|
||||||
|
|
||||||
|
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
output->name = strdup(parser->tok_str);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (!parser_next_token(parser)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parser->tok_type) {
|
||||||
|
case KANSHI_TOKEN_STR:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
case KANSHI_TOKEN_NEWLINE:
|
||||||
|
return output;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "unexpected %s in output\n",
|
||||||
|
token_type_str(parser->tok_type));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
|
||||||
|
if (!parser_expect_token(parser, KANSHI_TOKEN_LBRACKET)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_profile *profile = calloc(1, sizeof(*profile));
|
||||||
|
wl_list_init(&profile->outputs);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (!parser_next_token(parser)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parser->tok_type) {
|
||||||
|
case KANSHI_TOKEN_RBRACKET:
|
||||||
|
return profile;
|
||||||
|
case KANSHI_TOKEN_STR:
|
||||||
|
if (strcmp(parser->tok_str, "output") == 0) {
|
||||||
|
struct kanshi_profile_output *output =
|
||||||
|
parse_profile_output(parser);
|
||||||
|
if (output == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
wl_list_insert(&profile->outputs, &output->link);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "unexpected directive '%s' in profile\n",
|
||||||
|
parser->tok_str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KANSHI_TOKEN_NEWLINE:
|
||||||
|
break; // No-op
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "unexpected %s in profile\n",
|
||||||
|
token_type_str(parser->tok_type));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_config *_parse_config(struct kanshi_parser *parser) {
|
||||||
|
struct kanshi_config *config = calloc(1, sizeof(*config));
|
||||||
|
wl_list_init(&config->profiles);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int ch = parser_peek_char(parser);
|
||||||
|
if (ch < 0) {
|
||||||
|
return NULL;
|
||||||
|
} else if (ch == 0) {
|
||||||
|
return config;
|
||||||
|
} else if (isspace(ch)) {
|
||||||
|
parser_read_char(parser);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_profile *profile = parse_profile(parser);
|
||||||
|
if (!profile) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_insert(&config->profiles, &profile->link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_config *parse_config(const char *path) {
|
||||||
|
FILE *f = fopen(path, "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
fprintf(stderr, "failed to open file\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kanshi_parser parser = {
|
||||||
|
.f = f,
|
||||||
|
.next = -1,
|
||||||
|
.line = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kanshi_config *config = _parse_config(&parser);
|
||||||
|
fclose(f);
|
||||||
|
if (config == NULL) {
|
||||||
|
fprintf(stderr, "failed to parse config file: "
|
||||||
|
"error on line %d, column %d\n", parser.line, parser.col);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user