I've written a simple grammar for a language meant to be used in graph-based dialogue systems (primarily for video games).
Here are the grammars:
parser grammar DialogueScriptParser;
options {
tokenVocab = DialogueScriptLexer;
}
// Entry Point
script: scheduled_block* EOF;
// Scheduled Blocks
scheduled_block:
scheduled_block_open block scheduled_block_close;
scheduled_block_open: LT flag_list? LT;
scheduled_block_close: GT flag_list? GT;
block: statement*;
// Statements
statement:
if_statement
| switch_statement
| compound_statement
| expression_statement
| declaration_statement;
// Compound Statement
compound_statement: LBRACE statement_list? RBRACE;
statement_list: statement+;
// Expression Statement
expression_statement: expression SEMI;
// If Statement
if_statement:
IF LPAREN expression RPAREN statement (ELSE statement)?;
// Switch Statement
switch_statement: SWITCH LPAREN expression LPAREN switch_block;
switch_block: LBRACE switch_label* RBRACE;
switch_label: CASE expression COLON | DEFAULT COLON;
// Declaration Statement
declaration_statement: type declarator_init SEMI;
declarator_init: declarator (ASSIGN expression)?;
declarator: IDENTIFIER;
// Expression
expression_list: expression (COMMA expression);
expression:
name
| literal
| LPAREN expression RPAREN
| expression (INC | DEC)
| expression LBRACK expression RBRACK
| expression LPAREN expression_list? RPAREN
| expression LBRACE expression_list? RBRACE
| (SUB | ADD | INC | DEC | NOT | BIT_NOT) expression
| expression TURNARY expression COLON expression
| expression mul_div_mod_operator expression
| expression add_sub_operator expression
| <assoc = right> expression concat_operator expression
| expression relational_operator expression
| expression and_operator expression
| expression or_operator expression
| expression bitwise_operator expression;
// Operators
concat_operator: CONCAT;
and_operator: AND;
or_operator: OR;
add_sub_operator: ADD | SUB;
mul_div_mod_operator: MUL | DIV | MOD;
relational_operator: GT | LT | LE | GE;
equality_operator: EQUAL | NOTEQUAL;
bitwise_operator:
| '<' '<'
| '>' '>'
| BIT_AND
| BIT_OR
| BIT_XOR;
assignment_operator:
ASSIGN
| ADD_ASSIGN
| SUB_ASSIGN
| MUL_ASSIGN
| DIV_ASSIGN
| AND_ASSIGN
| OR_ASSIGN
| XOR_ASSIGN
| MOD_ASSIGN
| LSHIFT_ASSIGN
| RSHIFT_ASSIGN;
// Types
type:
primitive_type
| type LBRACK RBRACK
| name ('<' type (COMMA type)* '>')?;
primitive_type:
TYPE_BOOLEAN
| TYPE_CHAR
| TYPE_FLOAT_DEFAULT
| TYPE_FLOAT32
| TYPE_FLOAT64
| TYPE_INT_DEFAULT
| TYPE_INT8
| TYPE_INT16
| TYPE_INT32
| TYPE_INT64
| TYPE_UINT_DEFAULT
| TYPE_UINT8
| TYPE_UINT16
| TYPE_UINT32
| TYPE_UINT64
| TYPE_STRING;
// Name
name: namespace? IDENTIFIER (DOT IDENTIFIER)*;
// Namespace
namespace: IDENTIFIER COLONCOLON (namespace)*;
// Flags
flag_list: IDENTIFIER (COMMA IDENTIFIER)*;
// Literals
literal:
INTEGER_LITERAL
| FLOATING_POINT_LITERAL
| BOOLEAN_LITERAL
| CHARACTER_LITERAL
| STRING_LITERAL
| NULL_LITERAL;
and:
lexer grammar DialogueScriptLexer;
// Keywords
TYPE_BOOLEAN: 'bool';
TYPE_CHAR: 'char'; // 16 bits
TYPE_FLOAT_DEFAULT: 'float'; // 32 bits
TYPE_FLOAT32: 'float32';
TYPE_FLOAT64: 'float64';
TYPE_INT_DEFAULT: 'int'; // 32 bits
TYPE_INT8: 'int8';
TYPE_INT16: 'int16';
TYPE_INT32: 'int32';
TYPE_INT64: 'int64';
TYPE_UINT_DEFAULT: 'uint'; // 32 bits
TYPE_UINT8: 'uint8';
TYPE_UINT16: 'uint16';
TYPE_UINT32: 'uint32';
TYPE_UINT64: 'uint64';
TYPE_STRING: 'string'; // 16 bits per character
BREAK: 'break'; // used for switch
CASE: 'case'; // used for switch
DEFAULT: 'default'; // used for switch
IF: 'if';
ELSE: 'else';
SWITCH: 'switch';
// Integer Literals
INTEGER_LITERAL:
DecIntegerLiteral
| HexIntegerLiteral
| OctalIntegerLiteral
| BinaryIntegerLiteral;
fragment DecIntegerLiteral: '0' | NonZeroDigit Digit*;
fragment HexIntegerLiteral: '0' [xX] HexDigit+;
fragment OctalIntegerLiteral: '0' Digit+;
fragment BinaryIntegerLiteral: '0' [bB] BinaryDigit+;
// Floating-Point Literals
FLOATING_POINT_LITERAL: DecFloatingPointLiteral;
fragment DecFloatingPointLiteral:
DecIntegerLiteral? ('.' Digits) FloatTypeSuffix?
| DecIntegerLiteral FloatTypeSuffix?;
// Boolean Literals
BOOLEAN_LITERAL: 'true' | 'false';
// Character Literals
CHARACTER_LITERAL:
'\'' SingleCharacter '\''
| '\'' EscapeSequence '\'';
fragment SingleCharacter: ~['\\\r\n];
fragment EscapeSequence:
'\\\''
| '\\"'
| '\\\\'
| '\\0'
| '\\a'
| '\\b'
| '\\f'
| '\\n'
| '\\r'
| '\\t'
| '\\v';
// String Literals
STRING_LITERAL: '"' StringCharacters? '"';
fragment StringCharacters: StringCharacter+;
fragment StringCharacter: ~["\\\r\n] | EscapeSequence;
// Null Literal
NULL_LITERAL: 'null';
// Separators
LPAREN: '(';
RPAREN: ')';
LBRACE: '{';
RBRACE: '}';
LBRACK: '[';
RBRACK: ']';
SEMI: ';';
COMMA: ',';
DOT: '.';
COLON: ':';
COLONCOLON: '::';
// Operators
ASSIGN: '=';
ADD_ASSIGN: '+=';
SUB_ASSIGN: '-=';
MUL_ASSIGN: '*=';
DIV_ASSIGN: '/=';
AND_ASSIGN: '&=';
OR_ASSIGN: '|=';
XOR_ASSIGN: '^=';
MOD_ASSIGN: '%=';
LSHIFT_ASSIGN: '<<=';
RSHIFT_ASSIGN: '>>=';
GT: '>';
LT: '<';
EQUAL: '==';
LE: '<=';
GE: '>=';
NOTEQUAL: '!=';
NOT: '!';
BIT_NOT: '~';
BIT_AND: '&';
BIT_OR: '|';
BIT_XOR: '^';
/* Defining these here make recognizing scheduled blocks difficult BIT_SHIFT_L: '<<'; BIT_SHIFT_R:
'>>';
*/
AND: '&&';
OR: '||';
INC: '++';
DEC: '--';
ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/';
MOD: '%';
CONCAT: '..';
TURNARY: '?';
// Identifiers
/* Order affects precedence IDENTFIER must come last. */
IDENTIFIER: Letter LetterOrDigit*;
fragment LetterOrDigit: Letter | Digit;
fragment Digits: Digit+;
fragment Digit: '0' | NonZeroDigit;
fragment NonZeroDigit: [1-9];
fragment HexDigit: [0-9a-fA-F];
fragment BinaryDigit: [01];
fragment Letter: [a-zA-Z_];
fragment FloatTypeSuffix: [fFdD];
// Whitespace and Comments
WHITESPACE: [ \t\r\n\u000C]+ -> skip;
COMMENT_BLOCK: '/*' .*? '*/' -> channel(HIDDEN);
COMMENT_LINE: '//' ~[\r\n]* -> channel(HIDDEN);
Project: https://github.com/Sahasrara/DialogueScript
The grammar is working for the most part, but it's struggling to parse certain types of expressions.
Example:
<<
if (intVar == 10 && globalFunc() || "string lit" .. "concat string" == stringVar)
{
anotherFunc();
}
>>
Here's the output tree:
I know there's a precedence issue here, but I'm not entirely sure how to resolve it. Would someone mind pointing me in the right direction?
You are not using the equality_operator rule that contains the == operator. Place it somewhere in your expression rule:
expression
: ...
| expression add_sub_operator expression
| expression equality_operator expression
| <assoc = right> expression concat_operator expression
| ...
;
When placed there, it will have a lower precedence than + and -, and a higher precedence than ..:
Also note that the assignment_operator is not used currently.
TL;DR : How compile/use FBExport on Ubuntu / how export Firebird query result to csv file.
I would to like export query result from firebird database to csv file.
On Windows I do similary job using FBExport.
Unfortunetly I don't know use this tool on Ubuntu.
I downloaded pack from http://www.firebirdfaq.org/fbexport.php
When I try run ./fbexport i got error:
./fbexport: error while loading shared libraries: libfbclient.so.2:
cannot open shared object file: No such file or directory
Also I tried compile pack.
First I changed make file from:
###############################################################################
.SUFFIXES: .o .cpp
OBJECTS_FBE=fbexport/ParseArgs.o fbexport/FBExport.o fbexport/cli-main.o
OBJECTS_FBC=fbcopy/args.o fbcopy/fbcopy.o fbcopy/TableDependency.o fbcopy/main.o
# Compiler & linker flags
COMPILE_FLAGS=-O1 -DIBPP_LINUX -DIBPP_GCC -Iibpp
LINK_FLAGS=-pthread -lfbclient
#COMPILE_FLAGS=-O1 -DIBPP_WINDOWS -DIBPP_GCC -Iibpp
#LINK_FLAGS=
all: exe/fbcopy exe/fbexport
exe/fbexport: $(OBJECTS_FBE) ibpp/all_in_one.o
g++ $(LINK_FLAGS) ibpp/all_in_one.o $(OBJECTS_FBE) -oexe/fbexport
exe/fbcopy: $(OBJECTS_FBC) ibpp/all_in_one.o
g++ $(LINK_FLAGS) ibpp/all_in_one.o $(OBJECTS_FBC) -oexe/fbcopy
# Linux only
# FB2.0: g++ -pthread -lfbclient $(OBJECTS) -o$(EXENAME)
# FB1.5: g++ -lfbclient $(OBJECTS) -o$(EXENAME)
# FB1.0: g++ -lgds -lcrypt -lm $(OBJECTS) -o$(EXENAME)
install:
install exe/fbcopy /usr/bin/fbcopy
install exe/fbexport /usr/bin/fbexport
.cpp.o:
g++ -c $(COMPILE_FLAGS) -o $# $<
clean:
rm -f fbcopy/*.o
rm -f ibpp/all_in_one.o
rm -f exe/fbcopy*
rm -f fbexport/*.o
rm -f exe/fbexport*
#EOF
to:
###############################################################################
.SUFFIXES: .o .cpp
OBJECTS_FBE=fbexport/ParseArgs.o fbexport/FBExport.o fbexport/cli-main.o
# Compiler & linker flags
COMPILE_FLAGS=-O1 -DIBPP_LINUX -DIBPP_GCC -Iibpp
LINK_FLAGS=-pthread -lfbclient
#COMPILE_FLAGS=-O1 -DIBPP_WINDOWS -DIBPP_GCC -Iibpp
#LINK_FLAGS=
all: exe/fbexport
exe/fbexport: $(OBJECTS_FBE) ibpp/all_in_one.o
g++ $(LINK_FLAGS) ibpp/all_in_one.o $(OBJECTS_FBE) -oexe/fbexport
# Linux only
# FB2.0: g++ -pthread -lfbclient $(OBJECTS) -o$(EXENAME)
# FB1.5: g++ -lfbclient $(OBJECTS) -o$(EXENAME)
# FB1.0: g++ -lgds -lcrypt -lm $(OBJECTS) -o$(EXENAME)
install:
install exe/fbexport /usr/bin/fbexport
.cpp.o:
g++ -c $(COMPILE_FLAGS) -o $# $<
clean:
rm -f ibpp/all_in_one.o
rm -f fbexport/*.o
rm -f exe/fbexport*
#EOF
(because I like to compile only FBExport (excluding FBCopy))
After this change I tried run make in main folder.
Output:
user#apiserver:~/fbexport-1.90$ make
g++ -c -O1 -DIBPP_LINUX -DIBPP_GCC -Iibpp -o fbexport/ParseArgs.o fbexport/ParseArgs.cpp
g++ -c -O1 -DIBPP_LINUX -DIBPP_GCC -Iibpp -o fbexport/FBExport.o fbexport/FBExport.cpp
fbexport/FBExport.cpp: In member function ‘std::string FBExport::CreateHumanString(IBPP::Statement&, int)’:
fbexport/FBExport.cpp:318:29: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 3 has type ‘int’ [-Wformat=]
318 | sprintf(str, "%ld", x);
| ~~^ ~
| | |
| | int
| long int
| %d
fbexport/FBExport.cpp:40:21: warning: format ‘%lli’ expects argument of type ‘long long int’, but argument 3 has type ‘int64_t’ {aka ‘long int’} [-Wformat=]
40 | #define INT64FORMAT "%lli"
| ^~~~~~
fbexport/FBExport.cpp:351:26: note: in expansion of macro ‘INT64FORMAT’
351 | sprintf(str, INT64FORMAT, int64val);
| ^~~~~~~~~~~
fbexport/FBExport.cpp:40:25: note: format string is defined here
40 | #define INT64FORMAT "%lli"
| ~~~^
| |
| long long int
| %li
fbexport/FBExport.cpp: In member function ‘bool FBExport::CreateString(IBPP::Statement&, int, std::string&)’:
fbexport/FBExport.cpp:429:29: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 3 has type ‘int’ [-Wformat=]
429 | sprintf(str, "%ld", x);
| ~~^ ~
| | |
| | int
| long int
| %d
fbexport/FBExport.cpp:435:29: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 3 has type ‘int’ [-Wformat=]
435 | sprintf(str, "%ld", d.GetDate());
| ~~^ ~~~~~~~~~~~
| | |
| long int int
| %d
fbexport/FBExport.cpp:440:29: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 3 has type ‘int’ [-Wformat=]
440 | sprintf(str, "%ld", t.GetTime());
| ~~^ ~~~~~~~~~~~
| | |
| long int int
| %d
fbexport/FBExport.cpp:40:21: warning: format ‘%lli’ expects argument of type ‘long long int’, but argument 3 has type ‘int64_t’ {aka ‘long int’} [-Wformat=]
40 | #define INT64FORMAT "%lli"
| ^~~~~~
fbexport/FBExport.cpp:462:26: note: in expansion of macro ‘INT64FORMAT’
462 | sprintf(str, INT64FORMAT, int64val);
| ^~~~~~~~~~~
fbexport/FBExport.cpp:40:25: note: format string is defined here
40 | #define INT64FORMAT "%lli"
| ~~~^
| |
| long long int
| %li
fbexport/FBExport.cpp: In member function ‘int FBExport::Export(IBPP::Statement&, FILE*)’:
fbexport/FBExport.cpp:487:18: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
487 | register int fc = st->Columns();
| ^~
fbexport/FBExport.cpp:491:23: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
491 | for (register int i=1; i<=fc; i++)
| ^
fbexport/FBExport.cpp:505:27: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
505 | for (register int i=1; i<=fc; i++) // ... export all fields to file.
| ^
fbexport/FBExport.cpp: In member function ‘int FBExport::ExportHuman(IBPP::Statement&, FILE*)’:
fbexport/FBExport.cpp:829:18: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
829 | register int fc = st->Columns();
| ^~
fbexport/FBExport.cpp:835:27: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
835 | for (register int i=1; i<=fc; i++) // output CSV header.
| ^
fbexport/FBExport.cpp:847:27: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
847 | for (register int i=1; i<=fc; i++) // ... export all fields to file.
| ^
fbexport/FBExport.cpp:860:27: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
860 | for (register int i=1; i<=fc; i++) // output CSV header.
| ^
fbexport/FBExport.cpp:875:27: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
875 | for (register int i=1; i<=fc; i++) // ... export all fields to file.
| ^
fbexport/FBExport.cpp: In function ‘int statement_length(FILE*)’:
fbexport/FBExport.cpp:1335:24: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
1335 | register int c = 0, tmp = 0;
| ^
fbexport/FBExport.cpp:1335:31: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
1335 | register int c = 0, tmp = 0;
| ^~~
fbexport/FBExport.cpp:1336:24: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
1336 | register int l = 0;
| ^
fbexport/FBExport.cpp: In function ‘char* read_statement(char*, int, FILE*)’:
fbexport/FBExport.cpp:1376:24: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
1376 | register int c = 0, tmp = 0;
| ^
fbexport/FBExport.cpp:1376:31: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
1376 | register int c = 0, tmp = 0;
| ^~~
fbexport/FBExport.cpp:1377:25: warning: ISO C++17 does not allow ‘register’ storage class specifier [-Wregister]
1377 | register char *P;
| ^
fbexport/FBExport.cpp: In member function ‘std::string FBExport::CreateHumanString(IBPP::Statement&, int)’:
fbexport/FBExport.cpp:339:17: warning: ignoring return value of ‘char* gcvt(double, int, char*)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
339 | gcvt(fval, 19, str);
| ~~~~^~~~~~~~~~~~~~~
fbexport/FBExport.cpp:345:17: warning: ignoring return value of ‘char* gcvt(double, int, char*)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
345 | gcvt(dval, 19, str);
| ~~~~^~~~~~~~~~~~~~~
fbexport/FBExport.cpp: In member function ‘bool FBExport::CreateString(IBPP::Statement&, int, std::string&)’:
fbexport/FBExport.cpp:452:17: warning: ignoring return value of ‘char* gcvt(double, int, char*)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
452 | gcvt(fval, 19, str);
| ~~~~^~~~~~~~~~~~~~~
fbexport/FBExport.cpp:457:17: warning: ignoring return value of ‘char* gcvt(double, int, char*)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
457 | gcvt(dval, 19, str);
| ~~~~^~~~~~~~~~~~~~~
fbexport/FBExport.cpp: In member function ‘int FBExport::Import(IBPP::Statement&, FILE*)’:
fbexport/FBExport.cpp:706:26: warning: ignoring return value of ‘size_t fread(void*, size_t, size_t, FILE*)’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
706 | fread(buff, size, 1, fp);
| ~~~~~^~~~~~~~~~~~~~~~~~~
fbexport/FBExport.cpp: In member function ‘int FBExport::Init(Arguments*)’:
fbexport/FBExport.cpp:1211:41: warning: ‘__builtin___sprintf_chk’ may write a terminating nul past the end of the destination [-Wformat-overflow=]
1211 | sprintf(num, "%d", i+1);
| ^
In file included from /usr/include/stdio.h:888,
from /usr/include/c++/11/cstdio:42,
from /usr/include/c++/11/ext/string_conversions.h:43,
from /usr/include/c++/11/bits/basic_string.h:6606,
from /usr/include/c++/11/string:55,
from ibpp/ibpp.h:91,
from fbexport/FBExport.cpp:44:
/usr/include/x86_64-linux-gnu/bits/stdio2.h:38:34: note: ‘__builtin___sprintf_chk’ output between 2 and 11 bytes into a destination of size 10
38 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 | __glibc_objsize (__s), __fmt,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40 | __va_arg_pack ());
| ~~~~~~~~~~~~~~~~~
g++ -c -O1 -DIBPP_LINUX -DIBPP_GCC -Iibpp -o fbexport/cli-main.o fbexport/cli-main.cpp
g++ -c -O1 -DIBPP_LINUX -DIBPP_GCC -Iibpp -o ibpp/all_in_one.o ibpp/all_in_one.cpp
g++ -pthread -lfbclient ibpp/all_in_one.o fbexport/ParseArgs.o fbexport/FBExport.o fbexport/cli-main.o -oexe/fbexport
What can I do in this case?
Regards
Tomasz
Since version 1.5 the jq data processing language has a library module system. A module consists of optional metadata and a set of functions. For instance
module { name: "util", version: "1.0.0" };
def digitsum: tostring|split("")|map(tonumber)|add;
stored as file util.jq can be used like this:
$ echo '789' | jq -L. 'include "util"; digitsum'
24
Modules can use other modules and the dependencies are tracked by the modulemeta directive but how to express and check for a minimum version of a module number? For instance:
module {
name: "math",
version: "0.1.0",
};
include "util"; # TODO: require at least version 1.0.0!
def digitroot:
(.|digitsum) as $sum |
if $sum<10 then $sum else $sum|digitroot end;
The support for modules in jq is currently (June 2019) still very minimal, though on github there is a module management system for jq: https://github.com/joelpurra/jqnpm
Without using such an external module management system, what can be done in jq itself? Extending the given example, the following illustrates one approach to supporting version requirements. Notice the additional key named dependencies in the metadata of the math module. (Currently, this key cannot be named deps as jq overwrites it.)
Files
dependencies.jq
# Recursively check specified version constraints
module { name: "dependencies", version: "0.0.2" };
# parents of a module as defined by its .deps
def parents:
. as $in
| if type == "array" then map(parents) | add
else modulemeta | .deps | map(.relpath)
end ;
# ancestors of a single module or an array of modules.
# The array of "ancestors" of a module includes itself.
def ancestors:
# input and $visited should be arrays of strings
def ancestors($visited):
. as $in
| ($in - $visited) as $new
| if $new == [] then $visited
else $new | parents | ancestors($visited + $new | unique)
end;
if type == "array" then . else [.] end
| ancestors([]) ;
def versionsort:
def parse:
sub("(?<a>(alpha|beta|gamma))"; "\(.a).")
| [splits("[-.]")]
| map(tonumber? // .) ;
sort_by(parse);
# Input: a module name
# Emit empty if the constraints for the given module are satisfied, otherwise raise an error
def dependencies($version):
def le($y): (. == $y) or ([.,$y] | . == versionsort);
modulemeta
| .version as $mv
| if (($mv == null) or ($version | le($mv))) then empty
else ("module \(.name) dependencies version \($version) vs \($mv)" | error)
end ;
# Input: a module name or array of module names
# Check the module-version dependencies in .dependencies, proceeding up the chain as defined by .deps
def dependencies:
def check:
modulemeta
| select(has("dependencies"))
| all( .dependencies | to_entries[];
.key as $m | .value as $v | ($m | dependencies($v) ))
| empty;
ancestors[] | check;
util.jq
module { name: "util", version: "1.0.0" };
def digitsum: tostring|split("")|map(tonumber)|add;
math.jq
module {
name: "math",
version: "0.1.0",
dependencies: {"util": "1.0.0"} };
include "util" ;
def digitroot:
digitsum as $sum
| if $sum<10 then $sum
else $sum|digitroot
end;
Invocation
jq -n -L . '
include "dependencies";
include "math";
"math" | dependencies,
(123|digitroot) '
So this is my grammar:
grammar Test;
prog: stmt_list;
stmt_list
: stmt_list stmt ';'
| stmt ';'
;
stmt
: assignment
| bind
;
assignment: 'var' IDENTIFIER ('=' | '+=' | '-=' | '*=' | '/=') expression;
type
: IDENTIFIER
| primitiveType
;
primitiveType
: 'int'
| 'float'
| 'string'
| 'bool'
;
expression
: atom
| expression ('*' | '/') expression
| expression ('+' | '-') expression
;
atom
: '(' expression ')'
| IDENTIFIER
| INT
| STRING
;
IDENTIFIER: [A-z_][A-z_0-9]*;
INT: [1-9][0-9]*;
STRING: '"' [A-z] '"';
WS: [\t\r\n]+ -> channel(HIDDEN);
I can compile it with antlr and everything works fine. When I test it with grun it will compile but it throws a "token recognition error" whenever there's a whitespace. For example with this input:
var a = b + c;
I get:
line 1:3 token recognition error at: ' '
line 1:5 token recognition error at: ' '
line 1:7 token recognition error at: ' '
line 1:9 token recognition error at: ' '
line 1:11 token recognition error at: ' '
Besides this everything works but it would still be nice if I could get rid of these messages.
You're only putting tabs and line break chars to the hidden channel, not spaces.
Instead of:
WS: [\t\r\n]+ -> channel(HIDDEN);
do:
WS: [ \t\r\n]+ -> channel(HIDDEN);
I have a grammar that includes this rule:
expr:
unaryExpr '(' (stat | expr | constant) ')' #labelUnaryExpr
| binaryExpr '(' (stat | expr | constant) ',' (stat | expr | constant) ')' #labelBinaryExpr
| multipleExpr '(' (stat | expr | constant) (',' (stat | expr | constant))+ ')' #labelMultipleExpr
;
For expr, I can access the value of unaryExpr by calling ctx.unaryStat(). How can I access (stat | expr | constant) similarly? Is there a solution that doesn't require modifying my grammar by adding another rule for the group?
Since you've labelled you alternatives, you can access the (stat | expr | constant) in its respective listener/visitor method:
#Override
public void enterLabelUnaryExpr(#NotNull ExprParser.LabelUnaryExprContext ctx) {
// one of these will return something other than null
System.out.println(ctx.stat());
System.out.println(ctx.expr());
System.out.println(ctx.constant());
}