From 34899e8d3632b6d8dbf32b25e3476d7b8297b187 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 8 Aug 2024 11:49:55 +0800 Subject: [PATCH] llcppg design --- chore/llcppg/ast/ast.go | 8 +- chore/llcppg/design.md | 266 +++------------------------------------- 2 files changed, 19 insertions(+), 255 deletions(-) diff --git a/chore/llcppg/ast/ast.go b/chore/llcppg/ast/ast.go index 25d8ec07..a2f5fa16 100644 --- a/chore/llcppg/ast/ast.go +++ b/chore/llcppg/ast/ast.go @@ -269,7 +269,7 @@ func (*TypeDecl) declNode() {} // AST File type Include struct { - Path string + Path string `json:"path"` } func (*Include) ppdNode() {} @@ -284,9 +284,9 @@ func (*Macro) ppdNode() {} // ------------------------------------------------ type File struct { - Decls []Decl - Includes []*Include - Macros []*Macro + Decls []Decl `json:"decls"` + Includes []*Include `json:"includes,omitempty"` + Macros []*Macro `json:"macros,omitempty"` } // ============================================================================= diff --git a/chore/llcppg/design.md b/chore/llcppg/design.md index 9c6eda54..2a4acdce 100644 --- a/chore/llcppg/design.md +++ b/chore/llcppg/design.md @@ -59,8 +59,21 @@ llcppsigfetch - # read config from stdin It fetches information of C/C++ symbols and print to stdout. Its format is as follows: -``` -TODO: see llgo/xtool/clang/ast +```json +[ + { + "path": "/path/to/file.h", + "doc": { + "decls": [], + "macros": [], + "includes": [ + { + "path": "incfile.h" + } + ] + } + } +] ``` ### gogensig @@ -69,252 +82,3 @@ TODO: see llgo/xtool/clang/ast gogensig ast-file gogensig - # read AST from stdin ``` - -## Overall - -### Process - -1. The Parsing Module reads `llcppg.cfg` to obtain dynamic libraries, header files, and the package name. After parsing, it writes the generated `llcppg.symb.json` path into `llcppg.cfg`. -2. The Function Declaration Generation Module reads `llcppg.cfg` to get the package name, header files, and the previously generated `llcppg.symb.json`. After parsing, it generates the function prototype `llcppg.function.json`. -3. Reads the previously generated `llcppg.information.json`, stores it as a structure, and uses gogen to generate code based on the structure. - -## Parsing Module - -### Input - -Obtains the paths to header files and dynamic library files by reading the JSON file `llcppg.cfg`. - -```json -{ - "name": "inih", - "cflags": "$(pkg-config --cflags INIReader)", - "include": [ - "INIReader.h", - "AnotherHeaderFile.h" - ], - "libs": "$(pkg-config --libs INIReader)", - "trimPrefixes": ["Ini", "INI"] -} -``` - -```bash -llcppsymg config-file -``` - -### Implementation Steps - -1. Parse dylib and store: - -```go -// types.go -type CPPSymbol struct { - Symbol string `json:"symbol"` - Type string `json:"type"` - Name string `json:"name"` -} - -// parser_dylib.go -func parseDylibSymbols(lib string) ([]common.CPPSymbol, error) -``` - -2. Parse header files and store: - -```go -// common.go -type ASTInformation struct { - Namespace string `json:"namespace"` - Class string `json:"class"` - Name string `json:"name"` - BaseClasses []string `json:"baseClasses"` - ReturnType string `json:"returnType"` - Location string `json:"location"` - Parameters []Parameter `json:"parameters"` - Symbol string `json:"symbol"` -} - -type Parameter struct { - Name string `json:"name"` - Type string `json:"type"` -} - -// parser_ast.go -func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) -``` - -3. Cross-reference data from the first two steps to get the final output - -```go -// common.go -type SymbolInfo struct { - Mangle string `json:"mangle"` // C++ Symbol - CPP string `json:"c++"` // C++ function name - Go string `json:"go"` // Go function name -} - -// common_symbols.go -func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.SymbolInfo { -``` - -4. Generate `llcppg.symb.json` file and store the JSON file path into `llcppg.cfg` - -```go -func generateJSON([]CommonSymbolInfo) -``` - -5. Example `llcppg.symb.json` file - -```json -{ - "FunctionName": "A::B::C", - "Symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE", - "Location": "a.h", - "UserFunctionName": "CFromA" -} -``` - -## Function Declaration Generation Module - -### Input - -No input required, directly reads the `llcppg.cfg` file - -### Implementation Steps - -1. Execute the executable - -```bash -llcppsigfetch config-file -``` - -2. Parse header files - -```go -// common.go -type ASTInformation struct { - Namespace string `json:"namespace"` - Class string `json:"class"` - Name string `json:"name"` - BaseClasses []string `json:"baseClasses"` - ReturnType string `json:"returnType"` - Location string `json:"location"` - Parameters []Parameter `json:"parameters"` - Symbol string `json:"symbol"` -} - -type Parameter struct { - Name string `json:"name"` - Type string `json:"type"` -} - -// parser_ast.go -func ParseHeaderFile(filePath string) ([]common.ASTInformation, error) -``` - -3. Generate the final JSON mapping file `llcppg.information.json` - -```go - func GenerateJSONFile(info []common.ASTInformation) -``` - -```json -{ - "functionName": "A::B::C", - "symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE", - "location": "a.h", - "returnType": "int", - "userFunctionName": "CFromA", - "parameters": [ - { - "arg1": "int" - }, - { - "arg2": "*char" - } - ] -} -``` - -## Code Generation Module - -### Input - -No input required, directly reads `llcppg.information.json` file - -### Implementation Steps - -1. Execute the executable - -```bash -gogensig ast-file -``` - -2. Parse JSON file - -```go -// common.go -type HeaderFileInfo struct { - FunctionName string `json:"functionName"` - Symbol string `json:"symbol"` - Location string `json:"location"` - UserFunctionName string `json:"userFunctionName"` - Parameters map[string]string `json:"parameters"` -} - -// parse_json.go -func ParseJSON(jsonFilePath string) ([]common.HeaderFileInfo, error) -``` - -3. Generate code using the parsed structure with gogen - -```go -// generator.go -func GenerateCode(info []common.HeaderFileInfo) { - pkg := gogen.NewPackage("", PackageName, nil) - cm := comment(fmt.Sprintf("llgo:link %s %s", funcName1, symbol1)) - pkg.NewFunc(recv, funcName, params, results, variadic).SetComments(pkg, cm).BodyStart(pkg).End() -} -``` - -### Output - -1. Directory structure - -```bash -package_name/ -├── _demo - ├── demo1.go -└── llgo_link.go -└── a.go -└── b.go -└── c.go -``` - -Note that `demo1.go` file needs to be written by the user - -2. `llgo_link.go` is responsible for linking configuration - -```go -package inih -const ( - LLGoFiles = "$(pkg-config --cflags INIReader): _wrap/reader.cpp" - LLGoPackage = "link: $(pkg-config --libs inih INIReader); -linih -lINIReader" -) -``` - -3. Example content for `a.go` - -```go -package inih -import ( - _ "unsafe" - "github.com/goplus/llgo/c" -) -//go:linkname Parse C.ini_parse -func Parse(filename *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int - -//go:linkname ParseFile C.ini_parse_file -func ParseFile(file c.FilePtr, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int - -//go:linkname ParseString C.ini_parse_string -func ParseString(str *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int -``` \ No newline at end of file