Skip to content

Commit

Permalink
feat: add support for C
Browse files Browse the repository at this point in the history
  • Loading branch information
kkoomen committed Oct 4, 2020
1 parent 027216a commit 77ae35b
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 97 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ RUN npm install -g tree-sitter
RUN npm install -g tree-sitter-php
RUN npm install -g tree-sitter-typescript
RUN npm install -g tree-sitter-python
RUN npm install -g tree-sitter-c

# Install addition python modules.
RUN pip3 install --upgrade pip vim-vint==0.3.15 setuptools
Expand Down
67 changes: 14 additions & 53 deletions ftplugin/c.vim
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ if &filetype !=? 'c'
finish
endif

let b:doge_pattern_single_line_comment = '\m\(\/\*.\{-}\*\/\|\/\/.\{-}$\)'
let b:doge_pattern_multi_line_comment = '\m\/\*.\{-}\*\/'
let b:doge_parser = 'c'
let b:doge_insert = 'above'

let b:doge_supported_doc_standards = doge#buffer#get_supported_doc_standards([
\ 'doxygen_javadoc',
Expand All @@ -26,21 +26,6 @@ let b:doge_supported_doc_standards = doge#buffer#get_supported_doc_standards([
let b:doge_doc_standard = doge#buffer#get_doc_standard('c')
let b:doge_patterns = doge#buffer#get_patterns()

" ==============================================================================
"
" Define our base for every pattern.
"
" ==============================================================================
let s:pattern_base = {
\ 'generator': {
\ 'file': 'libclang.py',
\ },
\ 'parameters': {
\ 'format': '@{param-type|param} {name} !description',
\ },
\ 'insert': 'above',
\}

" ==============================================================================
"
" Define the pattern types.
Expand All @@ -50,50 +35,38 @@ let s:pattern_base = {
" ------------------------------------------------------------------------------
" Matches regular functions.
" ------------------------------------------------------------------------------
" int add(int x, int y) {}
" template<class...T> void h(int i = 0, T... args) {}
" ------------------------------------------------------------------------------
let s:function_pattern = doge#helpers#deepextend(s:pattern_base, {
\ 'generator': {
\ 'args': ['FUNCTION_DECL'],
let s:function_pattern = {
\ 'node_types': ['function_definition', 'declaration'],
\ 'parameters': {
\ 'format': '@param {name} !description',
\ },
\})
\}

" ------------------------------------------------------------------------------
" Matches structs.
" ------------------------------------------------------------------------------
" struct foo { };
" ------------------------------------------------------------------------------
let s:struct_pattern = doge#helpers#deepextend(s:pattern_base, {
\ 'generator': {
\ 'args': ['STRUCT_DECL'],
\ },
\})
let s:struct_pattern = {
\ 'node_types': ['struct_specifier'],
\}

" ------------------------------------------------------------------------------
" Matches field declarations inside structs.
" ------------------------------------------------------------------------------
" struct foo {
" int bar;
" };
" ------------------------------------------------------------------------------
let s:field_pattern = doge#helpers#deepextend(s:pattern_base, {
\ 'generator': {
\ 'args': ['FIELD_DECL'],
\ },
let s:field_pattern = {
\ 'node_types': ['field_declaration'],
\ 'template': [
\ '/**',
\ ' * @{name} !description',
\ ' */',
\ ],
\})
unlet s:field_pattern['parameters']
\}

" ==============================================================================
"
" Define the doc standards.
"
" ==============================================================================

call doge#buffer#register_doc_standard('doxygen_javadoc', [
\ doge#helpers#deepextend(s:function_pattern, {
\ 'template': [
Expand All @@ -109,8 +82,6 @@ call doge#buffer#register_doc_standard('doxygen_javadoc', [
\ 'template': [
\ '/**',
\ ' * struct {name} - !description',
\ '%(parameters| *)%',
\ '%(parameters| * {parameters})%',
\ ' */',
\ ],
\ }),
Expand All @@ -132,8 +103,6 @@ call doge#buffer#register_doc_standard('doxygen_javadoc_no_asterisk', [
\ 'template': [
\ '/**',
\ 'struct {name} - !description',
\ '%(parameters|)%',
\ '%(parameters|{parameters})%',
\ '*/',
\ ],
\ }),
Expand All @@ -155,8 +124,6 @@ call doge#buffer#register_doc_standard('doxygen_javadoc_banner', [
\ 'template': [
\ '/*******************************************************************************',
\ ' * struct {name} - !description',
\ '%(parameters| *)%',
\ '%(parameters| * {parameters})%',
\ ' ******************************************************************************/',
\ ],
\ }),
Expand All @@ -178,8 +145,6 @@ call doge#buffer#register_doc_standard('doxygen_qt', [
\ 'template': [
\ '/*!',
\ ' * struct {name} - !description',
\ '%(parameters| *)%',
\ '%(parameters| * {parameters})%',
\ ' */',
\ ],
\ }),
Expand All @@ -201,8 +166,6 @@ call doge#buffer#register_doc_standard('doxygen_qt_no_asterisk', [
\ 'template': [
\ '/*!',
\ 'struct {name} - !description',
\ '%(parameters|)%',
\ '%(parameters|{parameters})%',
\ '*/',
\ ],
\ }),
Expand All @@ -227,8 +190,6 @@ call doge#buffer#register_doc_standard('kernel_doc', [
\ 'template': [
\ '/**',
\ ' * struct {name} - !description',
\ '%(parameters|)%',
\ '%(parameters| * {parameters})%',
\ ' */',
\ ],
\ }),
Expand Down
8 changes: 8 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
},
"dependencies": {
"tree-sitter": "^0.17.0",
"tree-sitter-c": "^0.16.1",
"tree-sitter-php": "^0.16.2",
"tree-sitter-python": "^0.16.1",
"tree-sitter-typescript": "^0.16.1",
Expand Down
2 changes: 1 addition & 1 deletion run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ set -e
set -u

docker_image=kkoomen/doge
current_docker_image_id=c9c37fd778ad
current_docker_image_id=f00715cb8566
DOGE_DOCKER_IMAGE="$docker_image"
export DOGE_DOCKER_IMAGE

Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum Language {
PHP = 'php',
TYPESCRIPT = 'typescript',
PYTHON = 'python',
C = 'c',
}
2 changes: 2 additions & 0 deletions src/parser-languages.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import C from 'tree-sitter-c';
import PHP from 'tree-sitter-php';
import Python from 'tree-sitter-python';
import TypeScript from 'tree-sitter-typescript/typescript';
Expand All @@ -7,4 +8,5 @@ export const parserLanguages = {
[Language.PHP]: PHP,
[Language.TYPESCRIPT]: TypeScript,
[Language.PYTHON]: Python,
[Language.C]: C,
}
120 changes: 120 additions & 0 deletions src/parsers/c.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { SyntaxNode } from 'tree-sitter';
import { CustomParserService } from '../parser-service.interface';
import { BaseParserService } from './base-parser.service';

enum NodeType {
FUNCTION_DEFINITION = 'function_definition',
DECLARATION = 'declaration',
STRUCT_SPECIFIER = 'struct_specifier',
FIELD_DECLARATION = 'field_declaration',
}

export class CParserService extends BaseParserService implements CustomParserService {
constructor(
private readonly rootNode: SyntaxNode,
private readonly lineNumber: number,
private readonly nodeTypes: string[],
) {
super();
}

public traverse(node: SyntaxNode): void {
if (node.startPosition.row === this.lineNumber && this.nodeTypes.includes(node.type) && this.done === false) {
switch (node.type) {
case NodeType.FUNCTION_DEFINITION:
case NodeType.DECLARATION: {
this.result = {
name: null,
parameters: [],
returnType: null,
};
this.runNodeParser(this.parseFunction, node);
break;
}

case NodeType.STRUCT_SPECIFIER: {
this.result = { name: null };
this.runNodeParser(this.parseStruct, node);
break;
}

case NodeType.FIELD_DECLARATION: {
this.result = { name: null };
this.runNodeParser(this.parseFieldDeclaration, node);
break;
}

default: {
console.error(`Unable to handle node type: ${node.type}`)
break;
}
}
} else if (node.childCount > 0) {
node.children.forEach((childNode: SyntaxNode) => {
this.traverse(childNode);
});
}
}

private parseFunction(node: SyntaxNode): void {
node.children.forEach((childNode: SyntaxNode) => {
switch (childNode.type) {
case 'primitive_type':
case 'type_identifier': {
this.result.returnType = childNode.text;
break;
}

case 'function_declarator': {
this.result.name = childNode.children.filter((n: SyntaxNode) => n.type === 'identifier').shift()?.text;

childNode
.children
.filter((n: SyntaxNode) => ['parameter_list', 'parameter_declaration'].includes(n.type))
.map((n: SyntaxNode) => n.children.filter((cn: SyntaxNode) => cn.type === 'parameter_declaration'))
.reduce((items: SyntaxNode[], curr: SyntaxNode[]) => [...items, ...curr], [])
.forEach((n: SyntaxNode) => {
const param: Record<string, any> = { name: null, type: null };

n.children.forEach((cn: SyntaxNode) => {
if (['primitive_type', 'type_identifier'].includes(cn.type)) {
param.type = cn.text;
}

if (cn.type === 'pointer_declarator') {
param.name = cn.children.filter((cn: SyntaxNode) => cn.type === 'identifier').shift()?.text;
}

if (cn.type === 'identifier') {
param.name = cn.text;
}
})

this.result.parameters.push(param);
})
break;
}
}
});
}

private parseStruct(node: SyntaxNode): void {
node.children.forEach((childNode: SyntaxNode) => {
switch (childNode.type) {
case 'type_identifier': {
this.result.name = childNode.text;
}
}
});
}

private parseFieldDeclaration(node: SyntaxNode) {
node.children.forEach((childNode: SyntaxNode) => {
switch (childNode.type) {
case 'field_identifier': {
this.result.name = childNode.text;
}
}
});
}
}
5 changes: 5 additions & 0 deletions src/parsers/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { SyntaxNode } from 'tree-sitter';
import { Language } from '../constants';
import { ValueOf } from '../helpers';
import { CParserService } from './c.service';
import { PhpParserService } from './php.service';
import { PythonParserService } from './python.service';
import { TypeScriptParserService } from './typescript.service';

export type ParserService = PhpParserService
| TypeScriptParserService
| PythonParserService
| CParserService

export function getParserService(
language: ValueOf<Language>,
Expand All @@ -23,6 +25,9 @@ export function getParserService(
case Language.PYTHON:
return new PythonParserService(...args);

case Language.C:
return new CParserService(...args);

default:
console.error(`Could not get parser service for unknown language: ${language}`)
break;
Expand Down
10 changes: 4 additions & 6 deletions src/parsers/php.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,10 @@ export class PhpParserService extends BaseParserService implements CustomParserS
break;
}
}
} else {
if (node.childCount > 0) {
node.children.forEach((childNode: SyntaxNode) => {
this.traverse(childNode);
});
}
} else if (node.childCount > 0) {
node.children.forEach((childNode: SyntaxNode) => {
this.traverse(childNode);
});
}
}

Expand Down
10 changes: 4 additions & 6 deletions src/parsers/python.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,10 @@ export class PythonParserService extends BaseParserService implements CustomPars
break;
}
}
} else {
if (node.childCount > 0) {
node.children.forEach((childNode: SyntaxNode) => {
this.traverse(childNode);
});
}
} else if (node.childCount > 0) {
node.children.forEach((childNode: SyntaxNode) => {
this.traverse(childNode);
});
}
}

Expand Down
Loading

0 comments on commit 77ae35b

Please sign in to comment.