From 5c3bbdf1147e1097d4543216c18b1642839214d1 Mon Sep 17 00:00:00 2001 From: Hayodea Hakol Date: Tue, 7 Jan 2025 14:08:17 -0400 Subject: [PATCH] Devspec: Add lexer and parser for devSpecs This grammar looks simple but it consumed 2 days of my time and it's giving me bugs all the same, but mostly working nicely. See the syntax documentation in /docs --- hcore/deviceManager/Makefile.am | 16 +++- hcore/deviceManager/deviceSpecl.ll | 34 +++++++++ hcore/deviceManager/deviceSpecp.yy | 118 +++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 hcore/deviceManager/deviceSpecl.ll create mode 100644 hcore/deviceManager/deviceSpecp.yy diff --git a/hcore/deviceManager/Makefile.am b/hcore/deviceManager/Makefile.am index bc5f355..65a6ca3 100644 --- a/hcore/deviceManager/Makefile.am +++ b/hcore/deviceManager/Makefile.am @@ -1,4 +1,18 @@ AM_CPPFLAGS+= -I"$(top_srcdir)/hcore/include" +AM_YFLAGS = -d noinst_LIBRARIES = libdeviceManager.a -libdeviceManager_a_SOURCES = deviceManager.cpp deviceSpecParser.cpp +libdeviceManager_a_SOURCES = deviceSpecp.yy deviceSpecl.ll \ + deviceManager.cpp deviceSpecParser.cpp + +deviceSpecl.cc: deviceSpecl.ll +deviceSpecl.o: AM_LFLAGS += --header-file=deviceSpecl.hh \ + -o deviceSpecl.cc +deviceSpecp.cc deviceSpecp.hh: deviceSpecp.yy +deviceSpecp.o: AM_YFLAGS += -p deviceSpecp \ + --header=deviceSpecp.hh -o deviceSpecp.cc + +deviceSpecParser.o: AM_CXXFLAGS+=-Wno-ignored-attributes + +CLEANFILES=deviceSpecp.cc deviceSpecp.hh \ + deviceSpecl.cc deviceSpecl.hh diff --git a/hcore/deviceManager/deviceSpecl.ll b/hcore/deviceManager/deviceSpecl.ll new file mode 100644 index 0000000..0187e07 --- /dev/null +++ b/hcore/deviceManager/deviceSpecl.ll @@ -0,0 +1,34 @@ +%option prefix="deviceSpecl" +%{ +#include +#include +#include "deviceSpecp.hh" +%} + +%% +^a { + deviceSpecplval.chr = yytext[0]; + return KEYWORD_SPECTYPE_ACTUATOR; + } +^e { + deviceSpecplval.chr = yytext[0]; + return KEYWORD_SPECTYPE_EXTROSPECTOR; + } +^i { + deviceSpecplval.chr = yytext[0]; + return KEYWORD_SPECTYPE_INTEROSPECTOR; + } +"||" { return DOUBLE_PIPE; } +"|" { return PIPE; } +"(" { return LPAREN; } +")" { return RPAREN; } +[^\|\(\) \t\r\n]+ { deviceSpecplval.str = strdup(yytext); return STRING; } +\r?\n { /* ignore newlines */ } +[ \t]+ { /* ignore whitespace */ } +. { return yytext[0]; } +%% + +int deviceSpeclwrap(void) +{ + return 1; // Indicate end of input +} diff --git a/hcore/deviceManager/deviceSpecp.yy b/hcore/deviceManager/deviceSpecp.yy new file mode 100644 index 0000000..e1e867b --- /dev/null +++ b/hcore/deviceManager/deviceSpecp.yy @@ -0,0 +1,118 @@ +%{ +#include +#include +#include +#include +#include +#include +#include + +#ifndef yylex +/* We use different prefixes for the lexer and parser. + * * Our lexer's prefix is deviceSpecl. + * * Our parser's prefix is deviceSpecp. + * + * Yacc and Bison don't have a way to handle the scenario where the lexer has + * a different prefix from the parser that they generate. They assume that the + * lexer must have the same prefix as the parser they generate. So we just use + * this #define below to override yacc/bison's presumed prefix for the lexer. + */ +#error "Yacc should have defined yylex, and we need to override it to tell yacc the name of our lex function." +#endif +#undef yylex +#define yylex deviceSpecllex + +// Declare the symbols that our lexer will export. +int yylex(void); +void yyerror(const char *message) +{ + throw std::runtime_error( + std::string("deviceSpec parser error: ") + + std::string(message)) + yytext; +} + +%} + +%union { + char* str; + char chr; + DeviceManager::SensorDeviceSpec* sensorSpec; + DeviceManager::InteroceptorDeviceSpec* interoceptorSpec; + DeviceManager::ExtrospectorDeviceSpec* extrospectorSpec; + std::vector* stringVector; +} + +%token STRING +%token PIPE DOUBLE_PIPE LPAREN RPAREN +%token KEYWORD_SPECTYPE_ACTUATOR +%token KEYWORD_SPECTYPE_EXTROSPECTOR KEYWORD_SPECTYPE_INTEROSPECTOR + +%type params opt_params +%type spec_body +%type interoceptor_spec +%type extrospector_spec + +%% + +file: /* NOTHING */ + | sensor_specs + ; + +sensor_specs: + sensor_spec + | sensor_specs DOUBLE_PIPE sensor_spec + ; + +sensor_spec: + interoceptor_spec + | extrospector_spec + ; + +interoceptor_spec: + KEYWORD_SPECTYPE_INTEROSPECTOR PIPE spec_body { + DeviceManager::InteroceptorDeviceSpec *spec = + static_cast($3); + + spec->sensorType = $1; + DeviceManager::interoceptorDeviceSpecs.push_back(*spec); + delete spec; + } + ; + +extrospector_spec: + KEYWORD_SPECTYPE_EXTROSPECTOR PIPE spec_body { + DeviceManager::ExtrospectorDeviceSpec *spec = + static_cast($3); + + spec->sensorType = $1; + DeviceManager::extrospectorDeviceSpecs.push_back(*spec); + delete spec; + } + ; + +spec_body: + STRING PIPE STRING LPAREN opt_params RPAREN PIPE STRING LPAREN opt_params RPAREN PIPE STRING { + $$ = new DeviceManager::SensorDeviceSpec(); + $$->sensorType = '\0'; + $$->implexor = std::string($1); + $$->api = std::string($3); + $$->apiParams = std::move(*$5); + $$->server = std::string($8); + $$->serverParams = std::move(*$10); + $$->deviceSelector = std::string($13); + delete $5; + delete $10; + } + ; + +opt_params: + params + | /* empty */ { $$ = new std::vector(); } + ; + +params: + STRING { $$ = new std::vector{ $1 }; } + | params PIPE STRING { $$ = $1; $$->push_back($3); } + ; + +%%