xaizek / zograscope (License: AGPLv3 only) (since 2018-12-07)
Mainly a syntax-aware diff that also provides a number of additional tools.
Commit ca6e9ed8edcde3658d2e81c31d61e167d362f73d

Fill in bash grammar
Author: xaizek
Author date (UTC): 2022-08-28 12:32
Committer name: xaizek
Committer date (UTC): 2022-08-28 12:33
Parent(s): 71bbd5354b4766db2a0ed1f6e0a4d88db7712768
Signing key: 99DC5E4DB05F6BE2
Tree: ee45caf89d85ac78f6b4929b7505dd55a82f84ca
File Lines added Lines deleted
src/ts/bash/TSBashLanguage.cpp 258 12
src/ts/bash/TSBashSType.hpp 59 0
tests/tests.cpp 7 0
tests/tests.hpp 10 0
tests/ts/bash/ts-bash-parsing.cpp 10 12
File src/ts/bash/TSBashLanguage.cpp changed (mode: 100644) (index e63dea4..93f6613)
... ... TsBashLanguage::TsBashLanguage() : tsLanguage(*tree_sitter_bash())
36 36 { "comment", +TSBashSType::Comment }, { "comment", +TSBashSType::Comment },
37 37
38 38 { "program", +TSBashSType::Program }, { "program", +TSBashSType::Program },
39
40 { "command", +TSBashSType::Command },
41 { "declaration_command", +TSBashSType::DeclarationCommand },
42 { "negated_command", +TSBashSType::NegatedCommand },
43 { "test_command", +TSBashSType::TestCommand },
44 { "unset_command", +TSBashSType::UnsetCommand },
45
46 { "_statement", +TSBashSType::Statement },
47 { "c_style_for_statement", +TSBashSType::CStyleForStatement },
48 { "case_statement", +TSBashSType::CaseStatement },
49 { "compound_statement", +TSBashSType::CompoundStatement },
50 { "for_statement", +TSBashSType::ForStatement },
51 { "if_statement", +TSBashSType::IfStatement },
52 { "while_statement", +TSBashSType::WhileStatement },
53
54 { "variable_assignment", +TSBashSType::VariableAssignment },
55
56 { "_expression", +TSBashSType::Expression },
57 { "_primary_expression", +TSBashSType::PrimaryExpression },
58 { "array", +TSBashSType::Array },
59 { "binary_expression", +TSBashSType::BinaryExpression },
60 { "case_item", +TSBashSType::CaseItem },
61 { "command_name", +TSBashSType::CommandName },
62 { "command_substitution", +TSBashSType::CommandSubstitution },
63 { "concatenation", +TSBashSType::Concatenation },
64 { "do_group", +TSBashSType::DoGroup },
65 { "elif_clause", +TSBashSType::ElifClause },
66 { "else_clause", +TSBashSType::ElseClause },
67 { "expansion", +TSBashSType::Expansion },
68 { "expansion_flags", +TSBashSType::ExpansionFlags },
69 { "file_redirect", +TSBashSType::FileRedirect },
70 { "function_definition", +TSBashSType::FunctionDefinition },
71 { "heredoc_body", +TSBashSType::HeredocBody },
72 { "heredoc_redirect", +TSBashSType::HeredocRedirect },
73 { "herestring_redirect", +TSBashSType::HerestringRedirect },
74 { "list", +TSBashSType::List },
75 { "line_continuation", +TSBashSType::LineContinuation },
76 { "parenthesized_expression", +TSBashSType::ParenthesizedExpression },
77 { "pipeline", +TSBashSType::Pipeline },
78 { "postfix_expression", +TSBashSType::PostfixExpression },
79 { "process_substitution", +TSBashSType::ProcessSubstitution },
80 { "redirected_statement", +TSBashSType::RedirectedStatement },
81 { "simple_expansion", +TSBashSType::SimpleExpansion },
82 { "string", +TSBashSType::String },
83 { "string_content", +TSBashSType::StringContent },
84 { "string_expansion", +TSBashSType::StringExpansion },
85 { "subscript", +TSBashSType::Subscript },
86 { "subshell", +TSBashSType::Subshell },
87 { "ternary_expression", +TSBashSType::TernaryExpression },
88 { "unary_expression", +TSBashSType::UnaryExpression },
89 { "word", +TSBashSType::Word },
90 { "ansii_c_string", +TSBashSType::AnsiiCString },
91 { "comment", +TSBashSType::Comment },
92 { "file_descriptor", +TSBashSType::FileDescriptor },
93 { "heredoc_start", +TSBashSType::HeredocStart },
94 { "raw_string", +TSBashSType::RawString },
95 { "regex", +TSBashSType::Regex },
96 { "special_variable_name", +TSBashSType::SpecialVariableName },
97 { "test_operator", +TSBashSType::TestOperator },
98 { "variable_name", +TSBashSType::VariableName },
39 99 }; };
40 100
41 101 types = { types = {
102 { "comment", Type::Comments },
103 { "command_name", Type::Directives },
104
105 { "special_variable_name", Type::UserTypes },
106 { "variable_name", Type::UserTypes },
107 { "$", Type::UserTypes },
108
109 { "ansii_c_string", Type::StrConstants },
110 { "raw_string", Type::StrConstants },
111 { "string_content", Type::StrConstants },
112 { "\"", Type::StrConstants },
113
114 { "test_operator", Type::Operators },
115
116 { "case", Type::Keywords },
117 { "declare", Type::Keywords },
118 { "do", Type::Keywords },
119 { "done", Type::Keywords },
120 { "elif", Type::Keywords },
121 { "else", Type::Keywords },
122 { "esac", Type::Keywords },
123 { "export", Type::Keywords },
124 { "fi", Type::Keywords },
125 { "for", Type::Keywords },
126 { "function", Type::Keywords },
127 { "if", Type::Keywords },
128 { "in", Type::Keywords },
129 { "local", Type::Keywords },
130 { "readonly", Type::Keywords },
131 { "select", Type::Keywords },
132 { "then", Type::Keywords },
133 { "typeset", Type::Keywords },
134 { "unset", Type::Keywords },
135 { "unsetenv", Type::Keywords },
136 { "until", Type::Keywords },
137 { "while", Type::Keywords },
138
139 { "&>", Type::Specifiers },
140 { "&>>", Type::Specifiers },
141 { ">", Type::Specifiers },
142 { ">&", Type::Specifiers },
143 { ">(", Type::Specifiers },
144 { ">>", Type::Specifiers },
145 { ">|", Type::Specifiers },
146
147 { "==", Type::Comparisons },
148 { "!=", Type::Comparisons },
149 { "=~", Type::Comparisons },
150 { "<=", Type::Comparisons },
151 { ">=", Type::Comparisons },
152
153 { "|", Type::Operators },
154 { "|&", Type::Operators },
155 { "||", Type::Operators },
156 { "&&", Type::Operators },
157 { "+", Type::Operators },
158 { "++", Type::Operators },
159 { "-", Type::Operators },
160 { "--", Type::Operators },
161
162 { "[", Type::LeftBrackets },
163 { "]", Type::RightBrackets },
164
165 { "[[", Type::LeftBrackets },
166 { "]]", Type::RightBrackets },
167
168 { "<(", Type::LeftBrackets },
169 { "$(", Type::LeftBrackets },
170 { "${", Type::LeftBrackets },
171 { "(", Type::LeftBrackets },
172 { ")", Type::RightBrackets },
173
174 { "((", Type::LeftBrackets },
175 { "))", Type::RightBrackets },
176
177 { "{", Type::LeftBrackets },
178 { "}", Type::RightBrackets },
179
180 { "=", Type::Assignments },
181 { "+=", Type::Assignments },
182 { "-=", Type::Assignments },
42 183 }; };
184
185 // Unused: "!", "#", "%", "&", "/", ":", ":-", ":?", ";", ";&", ";;", ";;&",
186 // "<", "<&", "<<", "<<-", "<<<", "?", "`"
187
188 badNodes = { "\n" };
43 189 } }
44 190
45 191 Type Type
 
... ... TsBashLanguage::isDiffable(const Node *x) const
86 232
87 233 bool bool
88 234 TsBashLanguage::isStructural(const Node *x) const TsBashLanguage::isStructural(const Node *x) const
89 { return Language::isStructural(x); }
235 {
236 return Language::isStructural(x)
237 || -x->stype == TSBashSType::LineContinuation;
238 }
90 239
91 240 bool bool
92 TsBashLanguage::isEolContinuation(const Node */*x*/) const
93 { return false; }
241 TsBashLanguage::isEolContinuation(const Node *x) const
242 { return (-x->stype == TSBashSType::LineContinuation); }
94 243
95 244 bool bool
96 245 TsBashLanguage::alwaysMatches(const Node *x) const TsBashLanguage::alwaysMatches(const Node *x) const
 
... ... TsBashLanguage::isPseudoParameter(const Node */*x*/) const
101 250 { return false; } { return false; }
102 251
103 252 bool bool
104 TsBashLanguage::shouldSplice(SType /*parent*/, const Node */*childNode*/) const
105 { return false; }
253 TsBashLanguage::shouldSplice(SType parent, const Node *childNode) const
254 {
255 return -parent == TSBashSType::FunctionDefinition
256 && -childNode->stype == TSBashSType::CompoundStatement;
257 }
106 258
107 259 bool bool
108 260 TsBashLanguage::isValueNode(SType /*stype*/) const TsBashLanguage::isValueNode(SType /*stype*/) const
 
... ... TsBashLanguage::isValueNode(SType /*stype*/) const
110 262
111 263 bool bool
112 264 TsBashLanguage::isLayerBreak(SType /*parent*/, SType stype) const TsBashLanguage::isLayerBreak(SType /*parent*/, SType stype) const
113 { return isValueNode(stype); }
265 {
266 return isValueNode(stype)
267 || -stype == TSBashSType::Command
268 || -stype == TSBashSType::DeclarationCommand
269 || -stype == TSBashSType::NegatedCommand
270 || -stype == TSBashSType::TestCommand
271 || -stype == TSBashSType::UnsetCommand
272 || -stype == TSBashSType::Statement
273 || -stype == TSBashSType::CStyleForStatement
274 || -stype == TSBashSType::CaseStatement
275 || -stype == TSBashSType::CompoundStatement
276 || -stype == TSBashSType::ForStatement
277 || -stype == TSBashSType::IfStatement
278 || -stype == TSBashSType::WhileStatement
279 || -stype == TSBashSType::VariableAssignment
280 || -stype == TSBashSType::FunctionDefinition;
281 }
114 282
115 283 bool bool
116 284 TsBashLanguage::shouldDropLeadingWS(SType /*stype*/) const TsBashLanguage::shouldDropLeadingWS(SType /*stype*/) const
 
... ... TsBashLanguage::classify(SType stype) const
127 295 case TSBashSType::Comment: case TSBashSType::Comment:
128 296 return MType::Comment; return MType::Comment;
129 297
298 case TSBashSType::DeclarationCommand:
299 return MType::Declaration;
300
301 case TSBashSType::Statement:
302 case TSBashSType::CStyleForStatement:
303 case TSBashSType::CaseStatement:
304 case TSBashSType::CompoundStatement:
305 case TSBashSType::ForStatement:
306 case TSBashSType::IfStatement:
307 case TSBashSType::WhileStatement:
308 return MType::Statement;
309
310 case TSBashSType::FunctionDefinition:
311 return MType::Function;
312
130 313 default: default:
131 314 return MType::Other; return MType::Other;
132 315 } }
 
... ... TsBashLanguage::classify(SType stype) const
135 318 const char * const char *
136 319 TsBashLanguage::toString(SType stype) const TsBashLanguage::toString(SType stype) const
137 320 { {
138 switch (-stype) {
139 case TSBashSType::None: return "TSBashSType::None";
140
141 case TSBashSType::Separator: return "TSBashSType::Separator";
142 case TSBashSType::Comment: return "TSBashSType::Comment";
321 #define CASE(item) case TSBashSType::item: return "TSBashSType::" #item
143 322
144 case TSBashSType::Program: return "TSBashSType::Program";
323 switch (-stype) {
324 CASE(None);
325
326 CASE(Separator);
327 CASE(Comment);
328
329 CASE(Program);
330
331 CASE(Command);
332 CASE(DeclarationCommand);
333 CASE(NegatedCommand);
334 CASE(TestCommand);
335 CASE(UnsetCommand);
336
337 CASE(Statement);
338 CASE(CStyleForStatement);
339 CASE(CaseStatement);
340 CASE(CompoundStatement);
341 CASE(ForStatement);
342 CASE(IfStatement);
343 CASE(WhileStatement);
344
345 CASE(VariableAssignment);
346
347 CASE(Expression);
348 CASE(PrimaryExpression);
349 CASE(Array);
350 CASE(BinaryExpression);
351 CASE(CaseItem);
352 CASE(CommandName);
353 CASE(CommandSubstitution);
354 CASE(Concatenation);
355 CASE(DoGroup);
356 CASE(ElifClause);
357 CASE(ElseClause);
358 CASE(Expansion);
359 CASE(ExpansionFlags);
360 CASE(FileRedirect);
361 CASE(FunctionDefinition);
362 CASE(HeredocBody);
363 CASE(HeredocRedirect);
364 CASE(HerestringRedirect);
365 CASE(List);
366 CASE(LineContinuation);
367 CASE(ParenthesizedExpression);
368 CASE(Pipeline);
369 CASE(PostfixExpression);
370 CASE(ProcessSubstitution);
371 CASE(RedirectedStatement);
372 CASE(SimpleExpansion);
373 CASE(String);
374 CASE(StringContent);
375 CASE(StringExpansion);
376 CASE(Subscript);
377 CASE(Subshell);
378 CASE(TernaryExpression);
379 CASE(UnaryExpression);
380 CASE(Word);
381 CASE(AnsiiCString);
382 CASE(FileDescriptor);
383 CASE(HeredocStart);
384 CASE(RawString);
385 CASE(Regex);
386 CASE(SpecialVariableName);
387 CASE(TestOperator);
388 CASE(VariableName);
145 389 } }
146 390
147 391 assert(false && "Unhandled enumeration item"); assert(false && "Unhandled enumeration item");
148 392 return "<UNKNOWN>"; return "<UNKNOWN>";
393
394 #undef CASE
149 395 } }
File src/ts/bash/TSBashSType.hpp changed (mode: 100644) (index 768a32a..eed6c5c)
... ... enum class TSBashSType : std::uint8_t
33 33 Comment, Comment,
34 34
35 35 Program, Program,
36
37 Command,
38 DeclarationCommand,
39 NegatedCommand,
40 TestCommand,
41 UnsetCommand,
42
43 Statement,
44 CStyleForStatement,
45 CaseStatement,
46 CompoundStatement,
47 ForStatement,
48 IfStatement,
49 WhileStatement,
50
51 VariableAssignment,
52
53 Expression,
54 PrimaryExpression,
55 RedirectedStatement,
56 Array,
57 BinaryExpression,
58 CaseItem,
59 CommandName,
60 CommandSubstitution,
61 Concatenation,
62 DoGroup,
63 ElifClause,
64 ElseClause,
65 Expansion,
66 ExpansionFlags,
67 FileRedirect,
68 FunctionDefinition,
69 HeredocBody,
70 HeredocRedirect,
71 HerestringRedirect,
72 List,
73 LineContinuation,
74 ParenthesizedExpression,
75 Pipeline,
76 PostfixExpression,
77 ProcessSubstitution,
78 SimpleExpansion,
79 String,
80 StringContent,
81 StringExpansion,
82 Subscript,
83 Subshell,
84 TernaryExpression,
85 UnaryExpression,
86 Word,
87 AnsiiCString,
88 FileDescriptor,
89 HeredocStart,
90 RawString,
91 Regex,
92 SpecialVariableName,
93 TestOperator,
94 VariableName,
36 95 }; };
37 96
38 97 // "Conversion operator": TSBashSType -> SType. // "Conversion operator": TSBashSType -> SType.
File tests/tests.cpp changed (mode: 100644) (index 34ee087..899fafd)
... ... diffTsLua(const std::string &left, const std::string &right)
301 301 return diffSources(left, right, true, "test-input.lua", "--- "); return diffSources(left, right, true, "test-input.lua", "--- ");
302 302 } }
303 303
304 #undef diffTsBash
305 std::string
306 diffTsBash(const std::string &left, const std::string &right)
307 {
308 return diffSources(left, right, true, "test-input.sh", "### ");
309 }
310
304 311 // Compares two sources with expectation being embedded in them in form of // Compares two sources with expectation being embedded in them in form of
305 312 // trailing markers. Returns difference report. // trailing markers. Returns difference report.
306 313 static std::string static std::string
File tests/tests.hpp changed (mode: 100644) (index 4cf88d8..0853d55)
... ... std::string diffTsLua(const std::string &left, const std::string &right);
203 203 reportDiffFailure(difference); \ reportDiffFailure(difference); \
204 204 } while (false) } while (false)
205 205
206 // Compares two Bash sources with expectation being embedded in them in form of
207 // trailing `### <expectation>` markers. Returns difference report.
208 std::string diffTsBash(const std::string &left, const std::string &right);
209 // This is a wrapper that makes reported failure point be somewhere in the test.
210 #define diffTsBash(left, right) do { \
211 std::string difference = diffTsBash((left), (right)); \
212 CHECK(difference.empty()); \
213 reportDiffFailure(difference); \
214 } while (false)
215
206 216 // Prints report. This function is needed to make our custom output appear // Prints report. This function is needed to make our custom output appear
207 217 // after Catch's failure report. // after Catch's failure report.
208 218 void reportDiffFailure(const std::string &report); void reportDiffFailure(const std::string &report);
File tests/ts/bash/ts-bash-parsing.cpp copied from file tests/ts/lua/ts-lua-parsing.cpp (similarity 71%) (mode: 100644) (index e74b904..31838c3)
21 21
22 22 #include "tests.hpp" #include "tests.hpp"
23 23
24 TEST_CASE("Lua expressions are properly structured",
25 "[ts-lua][comparison][parsing]")
24 TEST_CASE("Bash splices braces into function", "[ts-bash][comparison][parsing]")
26 25 { {
27 diffTsLua(R"(
28 if a < b and
29 a > b and --- Deletions
30 a <= b or a >= c then
31 end
32 )", R"(
33 if a < b and
34 a <= b or a >= c then
35 end
36 )");
26 diffTsBash(R"raw(
27 function _editinplace() {
28 local tmp_file="$(mktemp "/tmp/bash.XXXXXX")" ### Deletions
29 }
30 )raw", R"raw(
31 function _editinplace() {
32 READLINE_LINE="$(< "$tmp_file")" ### Additions
33 }
34 )raw");
37 35 } }
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/zograscope

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/zograscope

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master