Skip to content

Commit

Permalink
feat(c): introduce C parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Mephistophiles committed Sep 22, 2024
1 parent 45b6455 commit d2b4f8c
Show file tree
Hide file tree
Showing 26 changed files with 906 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pathdiff = "0.2.1"
tempfile = "3.12.0"
titlecase = "3.3.0"
tree-sitter = "0.23.0"
tree-sitter-c = "0.23.0"
tree-sitter-c-sharp = "0.23.0"
tree-sitter-go = "0.23.1"
tree-sitter-python = "0.23.2"
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,37 @@ Options (global):
of times this flag is given, maxing out at 'trace' verbosity.

Language scopes:
--c <C>
Scope C code using a prepared query.
[env: C=]
[aliases: c]

Possible values:
- comments: Comments (single- and multi-line)
- strings: Strings
- includes: Includes
- type-def: Type definitions
- enum: `enum` definitions
- struct: `struct` type definitions
- variable: Variable definitions
- function-def: Function definitions
- function-decl: Function declaration
- switch: `switch` blocks
- if: `if` blocks
- for: `for` blocks
- while: `while` blocks
- do: `do` blocks
- union: `union` blocks
- identifier: Identifier
- declaration: Declaration
- call-expression: Call expression

--c-query <TREE-SITTER-QUERY>
Scope C code using a custom tree-sitter query.
[env: C_QUERY=]

--csharp <CSHARP>
Scope C# code using a prepared query.
Expand Down
17 changes: 17 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use srgn::actions::{
};
#[cfg(feature = "symbols")]
use srgn::actions::{Symbols, SymbolsInversion};
use srgn::scoping::langs::c::{CQuery, C};
use srgn::scoping::langs::csharp::{CSharp, CSharpQuery};
use srgn::scoping::langs::go::{Go, GoQuery};
use srgn::scoping::langs::hcl::{Hcl, HclQuery};
Expand Down Expand Up @@ -830,6 +831,7 @@ fn get_language_scopers(args: &cli::Cli) -> Vec<Box<dyn LanguageScoper>> {
};
}

handle_language_scope!(c, c_query, CQuery, C);
handle_language_scope!(csharp, csharp_query, CSharpQuery, CSharp);
handle_language_scope!(hcl, hcl_query, HclQuery, Hcl);
handle_language_scope!(go, go_query, GoQuery, Go);
Expand Down Expand Up @@ -936,6 +938,7 @@ mod cli {
use clap::builder::ArgPredicate;
use clap::{ArgAction, Command, CommandFactory, Parser};
use clap_complete::{generate, Generator, Shell};
use srgn::scoping::langs::c::{CustomCQuery, PreparedCQuery};
use srgn::scoping::langs::csharp::{CustomCSharpQuery, PreparedCSharpQuery};
use srgn::scoping::langs::go::{CustomGoQuery, PreparedGoQuery};
use srgn::scoping::langs::hcl::{CustomHclQuery, PreparedHclQuery};
Expand Down Expand Up @@ -1224,6 +1227,8 @@ mod cli {
#[group(required = false, multiple = false)]
#[command(next_help_heading = "Language scopes")]
pub struct LanguageScopes {
#[command(flatten)]
pub c: Option<CScope>,
#[command(flatten)]
pub csharp: Option<CSharpScope>,
#[command(flatten)]
Expand All @@ -1238,6 +1243,18 @@ mod cli {
pub typescript: Option<TypeScriptScope>,
}

#[derive(Parser, Debug, Clone)]
#[group(required = false, multiple = false)]
pub struct CScope {
/// Scope C code using a prepared query.
#[arg(long, env, verbatim_doc_comment, visible_alias = "c")]
pub c: Vec<PreparedCQuery>,

/// Scope C code using a custom tree-sitter query.
#[arg(long, env, verbatim_doc_comment, value_name = TREE_SITTER_QUERY_VALUE_NAME)]
pub c_query: Vec<CustomCQuery>,
}

#[derive(Parser, Debug, Clone)]
#[group(required = false, multiple = false)]
pub struct CSharpScope {
Expand Down
130 changes: 130 additions & 0 deletions src/scoping/langs/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::fmt::Debug;
use std::path::Path;
use std::str::FromStr;

use clap::ValueEnum;
use tree_sitter::QueryError;

use super::{CodeQuery, Language, LanguageScoper, TSLanguage, TSQuery};
use crate::find::Find;

/// The C language.
pub type C = Language<CQuery>;
/// A query for C.
pub type CQuery = CodeQuery<CustomCQuery, PreparedCQuery>;

/// Prepared tree-sitter queries for C.
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum PreparedCQuery {
/// Comments (single- and multi-line).
Comments,
/// Strings.
Strings,
/// Includes.
Includes,
/// Type definitions.
TypeDef,
/// `enum` definitions.
Enum,
/// `struct` type definitions.
Struct,
/// Variable definitions.
Variable,
/// Function definitions.
FunctionDef,
/// Function declaration.
FunctionDecl,
/// `switch` blocks.
Switch,
/// `if` blocks.
If,
/// `for` blocks.
For,
/// `while` blocks.
While,
/// `do` blocks.
Do,
/// `union` blocks.
Union,
/// Identifier.
Identifier,
/// Declaration.
Declaration,
/// Call expression.
CallExpression,
}

impl From<PreparedCQuery> for TSQuery {
fn from(value: PreparedCQuery) -> Self {
Self::new(
&C::lang(),
match value {
PreparedCQuery::Comments => "(comment) @comment",
PreparedCQuery::Strings => "[(string_literal) (system_lib_string)] @string",
PreparedCQuery::Includes => "(preproc_include) @include",
PreparedCQuery::TypeDef => "(type_definition) @typedef",
PreparedCQuery::Enum => "(enum_specifier) @enum",
PreparedCQuery::Struct => "(struct_specifier) @struct",
PreparedCQuery::Variable => "(declaration) @var",
PreparedCQuery::FunctionDef => "(function_definition) @function_definition",
PreparedCQuery::FunctionDecl => "(function_declarator) @function_decl",
PreparedCQuery::Switch => "(switch_statement) @switch",
PreparedCQuery::If => "(if_statement) @if",
PreparedCQuery::For => "(for_statement) @for",
PreparedCQuery::While => "(while_statement) @while",
PreparedCQuery::Union => "(union_specifier) @union",
PreparedCQuery::Do => "(do_statement) @do",
PreparedCQuery::Identifier => "(identifier) @ident",
PreparedCQuery::Declaration => "(declaration) @decl",
PreparedCQuery::CallExpression => "(call_expression) @call",
},
)
.expect("Prepared queries to be valid")
}
}

/// A custom tree-sitter query for C.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CustomCQuery(String);

impl FromStr for CustomCQuery {
type Err = QueryError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match TSQuery::new(&C::lang(), s) {
Ok(_) => Ok(Self(s.to_string())),
Err(e) => Err(e),
}
}
}

impl From<CustomCQuery> for TSQuery {
fn from(value: CustomCQuery) -> Self {
Self::new(&C::lang(), &value.0)
.expect("Valid query, as object cannot be constructed otherwise")
}
}

impl LanguageScoper for C {
fn lang() -> TSLanguage {
tree_sitter_c::LANGUAGE.into()
}

fn pos_query(&self) -> &TSQuery {
&self.positive_query
}

fn neg_query(&self) -> Option<&TSQuery> {
self.negative_query.as_ref()
}
}

impl Find for C {
fn extensions(&self) -> &'static [&'static str] {
&["c", "h"]
}

fn is_path_invalid(&self, _: &Path) -> bool {
false
}
}
2 changes: 2 additions & 0 deletions src/scoping/langs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use crate::scoping::{
view::ScopedViewBuilder,
};

/// C.
pub mod c;
/// C#.
pub mod csharp;
/// Go.
Expand Down
77 changes: 77 additions & 0 deletions tests/langs/c/base.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <stdio.h>
#include "base.h"

typedef unsigned int uint;
typedef void (*callback)(void);

/* Multiline comment.
* New line.
*/
struct S {
int a;
int b;
const char *c;
callback cb;
};

union U {
char test[4];
int a;
};

/**
* Doxygen comment
*
*/
enum E {
A, ///< Doxygen comment.
B, /*< Doxygen comment. */
C,
};


extern int external_var;

const char *external_function_declaration(const void *ptr);

// Main function.
int main(void) {
int a = 0; /* C Stype comments */
struct S s;
struct S *sp;
union U u;

// Call a function.
printf("Hello, World!\n");
s.cb();
sp->cb();

if (a) {
printf("a\n");
} else if (sp) {
printf("b\n");
} else {
printf("c\n");
}

for (int a = 0; a < 10; a++) {
printf("for\n");
}

while (a++ < 100) {
printf("while\n");
}

do {
printf("do-while\n");
} while (0);

switch (a) {
case 1:
return 0;
default:
return -1;
}

return 0;
}
Loading

0 comments on commit d2b4f8c

Please sign in to comment.