Commits

Anonymous committed 3835974

added an example + readme

Comments (0)

Files changed (5)

+go-clang
+========
+
+Naive Go bindings to the C-API of ``CLang``.
+
+Installation
+------------
+
+As there is no ``pkg-config`` entry for clang, you may have to tinker
+a bit the various CFLAGS and LDFLAGS options, or pass them via the
+shell:
+
+::
+
+  $ CGO_CFLAGS="-I/somewhere" \
+  CGO_LDFLAGS="-L/somewhere/else" \
+  go get bitbucket.org/binet/go-clang/pkg/clang
+
+
+Example
+-------
+
+An example on how to use the AST visitor of ``CLang`` is provided
+here:
+
+ 
+
+Limitations
+-----------
+
+- Only a subset of the C-API of ``CLang`` has been provided yet.
+  More will come as patches flow in and time goes by.
+
+- Go-doc documentation is lagging (but the doxygen docs from the C-API
+  of ``CLang`` are in the ``.go`` files)

cmd/go-clang-dump/main.go

+// go-clang-dump shows how to dump the AST of a C/C++ file via the Cursor
+// visitor API.
+//
+// ex:
+// $ go-clang-dump -fname=foo.cxx
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"bitbucket.org/binet/go-clang/pkg/clang"
+)
+
+var fname *string = flag.String("fname", "", "the file to analyze")
+
+func main() {
+	fmt.Printf(":: go-clang-dump...\n")
+	flag.Parse()
+	fmt.Printf(":: fname: %s\n", *fname)
+	fmt.Printf(":: args: %v\n", flag.Args())
+	if *fname == "" {
+		flag.Usage()
+		fmt.Printf("please provide a file name to analyze\n")
+		os.Exit(1)
+	}
+	idx := clang.NewIndex(0, 1)
+	defer idx.Dispose()
+
+	nidx := 0
+	args := []string{}
+	if len(flag.Args()) > 0 && flag.Args()[0] == "-" {
+		nidx = 1
+		args = make([]string, len(flag.Args()[nidx:]))
+		copy(args, flag.Args()[nidx:])
+	}
+
+	tu := idx.Parse(*fname, args, 0)
+
+	defer tu.Dispose()
+
+	fmt.Printf("tu: %s\n", tu.Spelling())
+	cursor := tu.ToCursor()
+	fmt.Printf("cursor-isnull: %v\n", cursor.IsNull())
+	fmt.Printf("cursor: %s\n", cursor.Spelling())
+	fmt.Printf("cursor-kind: %s\n", cursor.Kind().Spelling())
+
+	tu_fname := tu.File(*fname).Name()
+	fmt.Printf("tu-fname: %s\n", tu_fname)
+
+	fct := func(cursor, parent clang.Cursor) clang.ChildVisitResult {
+		if cursor.IsNull() {
+			fmt.Printf("cursor: <none>\n")
+			return clang.CVR_Continue
+		}
+		fmt.Printf("%s: %s (%s)\n", 
+			cursor.Kind().Spelling(), cursor.Spelling(), cursor.USR())
+		switch cursor.Kind() {
+		case clang.CK_ClassDecl, clang.CK_EnumDecl,
+			clang.CK_StructDecl, clang.CK_Namespace:
+			return clang.CVR_Recurse
+		}
+		return clang.CVR_Continue
+	}
+
+	cursor.Visit((clang.CursorVisitor) (fct))
+
+	fmt.Printf(":: bye.\n")
+}

pkg/clang/clang.go

 	"unsafe"
 )
 
+/**
+ * \brief Describes the availability of a particular entity, which indicates
+ * whether the use of this entity will result in a warning or error due to
+ * it being deprecated or unavailable.
+ */
+type AvailabilityKind uint32
+const (
+
+	/**
+	 * \brief The entity is available.
+	 */
+	Available AvailabilityKind = C.CXAvailability_Available
+
+  /**
+   * \brief The entity is available, but has been deprecated (and its use is
+   * not recommended).
+   */
+	Deprecated AvailabilityKind = C.CXAvailability_Deprecated
+	/**
+	 * \brief The entity is not available; any use of it will be an error.
+	 */
+	NotAvailable AvailabilityKind = C.CXAvailability_NotAvailable
+	/**
+	 * \brief The entity is available, but not accessible; any use of it will be
+	 * an error.
+	 */
+	NotAccessible AvailabilityKind = C.CXAvailability_NotAccessible
+)
+
 // An "index" that consists of a set of translation units that would
 // typically be linked together into an executable or library
 type Index struct {
 	return TranslationUnit{o}
 }
 
+/**
+ * \brief Return the CXTranslationUnit for a given source file and the provided
+ * command line arguments one would pass to the compiler.
+ *
+ * Note: The 'source_filename' argument is optional.  If the caller provides a
+ * NULL pointer, the name of the source file is expected to reside in the
+ * specified command line arguments.
+ *
+ * Note: When encountered in 'clang_command_line_args', the following options
+ * are ignored:
+ *
+ *   '-c'
+ *   '-emit-ast'
+ *   '-fsyntax-only'
+ *   '-o <output file>'  (both '-o' and '<output file>' are ignored)
+ *
+ * \param CIdx The index object with which the translation unit will be
+ * associated.
+ *
+ * \param source_filename - The name of the source file to load, or NULL if the
+ * source file is included in \p clang_command_line_args.
+ *
+ * \param num_clang_command_line_args The number of command-line arguments in
+ * \p clang_command_line_args.
+ *
+ * \param clang_command_line_args The command-line arguments that would be
+ * passed to the \c clang executable if it were being invoked out-of-process.
+ * These command-line options will be parsed and will affect how the translation
+ * unit is parsed. Note that the following options are ignored: '-c',
+ * '-emit-ast', '-fsyntex-only' (which is the default), and '-o <output file>'.
+ *
+ * \param num_unsaved_files the number of unsaved file entries in \p
+ * unsaved_files.
+ *
+ * \param unsaved_files the files that have not yet been saved to disk
+ * but may be required for code completion, including the contents of
+ * those files.  The contents and name of these files (as specified by
+ * CXUnsavedFile) are copied when necessary, so the client only needs to
+ * guarantee their validity until the call to this function returns.
+ */
+//FIXME: handle unsaved-files
+func (idx Index) CreateTranslationUnitFromSourceFile(fname string, args []string) TranslationUnit {
+	var c_fname *C.char = nil
+	if fname != "" {
+		c_fname = C.CString(fname)
+	}
+	defer C.free(unsafe.Pointer(c_fname))
+
+	c_nargs := C.int(len(args))
+	c_cmds := make([]*C.char, len(args))
+	for i,_ := range args {
+		cstr := C.CString(args[i])
+		defer C.free(unsafe.Pointer(cstr))
+		c_cmds[i] = cstr
+	}
+
+	o := C.clang_createTranslationUnitFromSourceFile(
+		idx.c,
+		c_fname,
+		c_nargs, &c_cmds[0],
+		C.uint(0), nil)
+	return TranslationUnit{o}
+
+}
+
+/**
+ * \brief Flags that control the creation of translation units.
+ *
+ * The enumerators in this enumeration type are meant to be bitwise
+ * ORed together to specify which options should be used when
+ * constructing the translation unit.
+ */
+type TranslationUnitFlags uint32
+const (
+  /**
+   * \brief Used to indicate that no special translation-unit options are
+   * needed.
+   */
+	TU_None = C.CXTranslationUnit_None
+
+  /**
+   * \brief Used to indicate that the parser should construct a "detailed"
+   * preprocessing record, including all macro definitions and instantiations.
+   *
+   * Constructing a detailed preprocessing record requires more memory
+   * and time to parse, since the information contained in the record
+   * is usually not retained. However, it can be useful for
+   * applications that require more detailed information about the
+   * behavior of the preprocessor.
+   */
+	TU_DetailedPreprocessingRecord = C.CXTranslationUnit_DetailedPreprocessingRecord
+
+  /**
+   * \brief Used to indicate that the translation unit is incomplete.
+   *
+   * When a translation unit is considered "incomplete", semantic
+   * analysis that is typically performed at the end of the
+   * translation unit will be suppressed. For example, this suppresses
+   * the completion of tentative declarations in C and of
+   * instantiation of implicitly-instantiation function templates in
+   * C++. This option is typically used when parsing a header with the
+   * intent of producing a precompiled header.
+   */
+	TU_Incomplete = C.CXTranslationUnit_Incomplete
+  
+  /**
+   * \brief Used to indicate that the translation unit should be built with an 
+   * implicit precompiled header for the preamble.
+   *
+   * An implicit precompiled header is used as an optimization when a
+   * particular translation unit is likely to be reparsed many times
+   * when the sources aren't changing that often. In this case, an
+   * implicit precompiled header will be built containing all of the
+   * initial includes at the top of the main file (what we refer to as
+   * the "preamble" of the file). In subsequent parses, if the
+   * preamble or the files in it have not changed, \c
+   * clang_reparseTranslationUnit() will re-use the implicit
+   * precompiled header to improve parsing performance.
+   */
+	TU_PrecompiledPreamble = C.CXTranslationUnit_PrecompiledPreamble
+  
+  /**
+   * \brief Used to indicate that the translation unit should cache some
+   * code-completion results with each reparse of the source file.
+   *
+   * Caching of code-completion results is a performance optimization that
+   * introduces some overhead to reparsing but improves the performance of
+   * code-completion operations.
+   */
+	TU_CacheCompletionResults = C.CXTranslationUnit_CacheCompletionResults
+
+	/**
+   * \brief DEPRECATED: Enable precompiled preambles in C++.
+   *
+   * Note: this is a *temporary* option that is available only while
+   * we are testing C++ precompiled preamble support. It is deprecated.
+   */
+	TU_CXXPrecompiledPreamble = C.CXTranslationUnit_CXXPrecompiledPreamble
+
+  /**
+   * \brief DEPRECATED: Enabled chained precompiled preambles in C++.
+   *
+   * Note: this is a *temporary* option that is available only while
+   * we are testing C++ precompiled preamble support. It is deprecated.
+   */
+	TU_CXXChainedPCH = C.CXTranslationUnit_CXXChainedPCH
+  
+  /**
+   * \brief Used to indicate that the "detailed" preprocessing record,
+   * if requested, should also contain nested macro expansions.
+   *
+   * Nested macro expansions (i.e., macro expansions that occur
+   * inside another macro expansion) can, in some code bases, require
+   * a large amount of storage to due preprocessor metaprogramming. Moreover,
+   * its fairly rare that this information is useful for libclang clients.
+   */
+	TU_NestedMacroExpansions = C.CXTranslationUnit_NestedMacroExpansions
+
+  /**
+   * \brief Legacy name to indicate that the "detailed" preprocessing record,
+   * if requested, should contain nested macro expansions.
+   *
+   * \see CXTranslationUnit_NestedMacroExpansions for the current name for this
+   * value, and its semantics. This is just an alias.
+   */
+	TU_NestedMacroInstantiations = C.CXTranslationUnit_NestedMacroInstantiations
+	
+)
+
+/**
+ * \brief Parse the given source file and the translation unit corresponding
+ * to that file.
+ *
+ * This routine is the main entry point for the Clang C API, providing the
+ * ability to parse a source file into a translation unit that can then be
+ * queried by other functions in the API. This routine accepts a set of
+ * command-line arguments so that the compilation can be configured in the same
+ * way that the compiler is configured on the command line.
+ *
+ * \param CIdx The index object with which the translation unit will be 
+ * associated.
+ *
+ * \param source_filename The name of the source file to load, or NULL if the
+ * source file is included in \p command_line_args.
+ *
+ * \param command_line_args The command-line arguments that would be
+ * passed to the \c clang executable if it were being invoked out-of-process.
+ * These command-line options will be parsed and will affect how the translation
+ * unit is parsed. Note that the following options are ignored: '-c', 
+ * '-emit-ast', '-fsyntex-only' (which is the default), and '-o <output file>'.
+ *
+ * \param num_command_line_args The number of command-line arguments in
+ * \p command_line_args.
+ *
+ * \param unsaved_files the files that have not yet been saved to disk
+ * but may be required for parsing, including the contents of
+ * those files.  The contents and name of these files (as specified by
+ * CXUnsavedFile) are copied when necessary, so the client only needs to
+ * guarantee their validity until the call to this function returns.
+ *
+ * \param num_unsaved_files the number of unsaved file entries in \p
+ * unsaved_files.
+ *
+ * \param options A bitmask of options that affects how the translation unit
+ * is managed but not its compilation. This should be a bitwise OR of the
+ * CXTranslationUnit_XXX flags.
+ *
+ * \returns A new translation unit describing the parsed code and containing
+ * any diagnostics produced by the compiler. If there is a failure from which
+ * the compiler cannot recover, returns NULL.
+ */
+//FIXME: handle unsaved-files
+func (idx Index) Parse(fname string, args []string, options TranslationUnitFlags) TranslationUnit {
+	var c_fname *C.char = nil
+	if fname != "" {
+		c_fname = C.CString(fname)
+	}
+	defer C.free(unsafe.Pointer(c_fname))
+
+	c_nargs := C.int(len(args))
+	c_cmds := make([]*C.char, len(args))
+	for i,_ := range args {
+		cstr := C.CString(args[i])
+		defer C.free(unsafe.Pointer(cstr))
+		c_cmds[i] = cstr
+	}
+	var c_args *C.char = nil
+	if len(args) > 0 {
+		c_args = c_cmds[0]
+	}
+	o := C.clang_parseTranslationUnit(
+		idx.c,
+		c_fname,
+		&c_args, c_nargs,
+		nil, C.uint(0), 
+		C.uint(options))
+	return TranslationUnit{o}
+
+}
+
 // A single translation unit, which resides in an index
 type TranslationUnit struct {
 	c C.CXTranslationUnit

pkg/clang/cursor.go

 	return LinkageKind(o)
 }
 
-//
-type AvailabilityKind uint32
-
 // Availability returns the availability of the entity that this cursor refers to
 func (c Cursor) Availability() AvailabilityKind {
 	o := C.clang_getCursorAvailability(c.c)

pkg/clang/token.go

 // CXCursor _go_clang_ocursor_at(CXCursor *c, int idx) {
 //   return c[idx];
 // }
-//
 import "C"
 
 // TokenKind describes a kind of token
 	return TokenKind(o)
 }
 
+/**
+ * \brief Determine the spelling of the given token.
+ *
+ * The spelling of a token is the textual representation of that token, e.g.,
+ * the text of an identifier or keyword.
+ */
+func (tu TranslationUnit) TokenSpelling(tok Token) string {
+	cstr := cxstring{C.clang_getTokenSpelling(tu.c, tok.c)}
+	defer cstr.Dispose()
+	return cstr.String()
+}
+
+/**
+ * \brief Retrieve the source location of the given token.
+ */
+func (tu TranslationUnit) TokenLocation(tok Token) SourceLocation {
+	o := C.clang_getTokenLocation(tu.c, tok.c)
+	return SourceLocation{o}
+}
+
+/**
+ * \brief Retrieve a source range that covers the given token.
+ */
+func (tu TranslationUnit) TokenExtent(tok Token) SourceRange {
+	o := C.clang_getTokenExtent(tu.c, tok.c)
+	return SourceRange{o}
+}
+
+/**
+ * \brief Tokenize the source code described by the given range into raw
+ * lexical tokens.
+ *
+ * \param TU the translation unit whose text is being tokenized.
+ *
+ * \param Range the source range in which text should be tokenized. All of the
+ * tokens produced by tokenization will fall within this source range,
+ *
+ * \param Tokens this pointer will be set to point to the array of tokens
+ * that occur within the given source range. The returned pointer must be
+ * freed with clang_disposeTokens() before the translation unit is destroyed.
+ *
+ * \param NumTokens will be set to the number of tokens in the \c *Tokens
+ * array.
+ *
+ */
+func Tokenize(tu TranslationUnit, src SourceRange) Tokens {
+	tokens := Tokens{}
+	tokens.tu = tu.c
+	C.clang_tokenize(tu.c, src.c, &tokens.c, &tokens.n)
+	return tokens
+}
+
+// an array of tokens
+type Tokens struct {
+	tu C.CXTranslationUnit
+	c *C.CXToken
+	n C.uint
+}
+
+/**
+ * \brief Annotate the given set of tokens by providing cursors for each token
+ * that can be mapped to a specific entity within the abstract syntax tree.
+ *
+ * This token-annotation routine is equivalent to invoking
+ * clang_getCursor() for the source locations of each of the
+ * tokens. The cursors provided are filtered, so that only those
+ * cursors that have a direct correspondence to the token are
+ * accepted. For example, given a function call \c f(x),
+ * clang_getCursor() would provide the following cursors:
+ *
+ *   * when the cursor is over the 'f', a DeclRefExpr cursor referring to 'f'.
+ *   * when the cursor is over the '(' or the ')', a CallExpr referring to 'f'.
+ *   * when the cursor is over the 'x', a DeclRefExpr cursor referring to 'x'.
+ *
+ * Only the first and last of these cursors will occur within the
+ * annotate, since the tokens "f" and "x' directly refer to a function
+ * and a variable, respectively, but the parentheses are just a small
+ * part of the full syntax of the function call expression, which is
+ * not provided as an annotation.
+ *
+ * \param TU the translation unit that owns the given tokens.
+ *
+ * \param Tokens the set of tokens to annotate.
+ *
+ * \param NumTokens the number of tokens in \p Tokens.
+ *
+ * \param Cursors an array of \p NumTokens cursors, whose contents will be
+ * replaced with the cursors corresponding to each token.
+ */
+func (t Tokens) Annotate() []Cursor {
+	c_cursors := make([]C.CXCursor, int(t.n))
+	C.clang_annotateTokens(t.tu, t.c, t.n, &c_cursors[0])
+	cursors := make([]Cursor, int(t.n))
+	for i,_ := range cursors {
+		cursors[i] = Cursor{C._go_clang_ocursor_at(&c_cursors[0], C.int(i))}
+	}
+	return cursors
+}
+
+/**
+ * \brief Free the given set of tokens.
+ */
+func (t Tokens) Dispose() {
+	C.clang_disposeTokens(t.tu, t.c, t.n)
+}
+
+
 // EOF
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.