Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 81 additions & 24 deletions preprocessor/preprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,22 @@ type FilePP struct {
pp []byte
comments []Comment
includes []IncludeHeader
defines map[string]string
}

// NewFilePP create a struct FilePP with results of analyzing
// preprocessor C code
func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
f FilePP, err error) {
func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (f FilePP, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("preprocess error : %v", err)
}
}()

var allItems []entity
f.defines = make(map[string]string) // Initialize defines map

allItems, err = analyzeFiles(inputFiles, clangFlags, cppCode)
var allItems []entity
allItems, err = analyzeFiles(inputFiles, clangFlags, cppCode, &f) // Pass FilePP to store defines
if err != nil {
return
}
Expand All @@ -152,12 +153,9 @@ func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
userSource[us[j]] = true
}

// Merge the entities
// Merge the entities and generate preprocessor output
var lines []string
for i := range allItems {
// If found same part of preprocess code, then
// don't include in result buffer for transpiling
// for avoid duplicate of code
var found bool
for j := 0; j < i; j++ {
if allItems[i].isSame(&allItems[j]) {
Expand All @@ -168,27 +166,20 @@ func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
if found {
continue
}

// Parse comments only for user sources
var isUserSource bool
if userSource[allItems[i].include] {
isUserSource = true
}
if allItems[i].include[0] == '.' &&
allItems[i].include[1] == '/' &&
userSource[allItems[i].include[2:]] {
if allItems[i].include[0] == '.' && allItems[i].include[1] == '/' && userSource[allItems[i].include[2:]] {
isUserSource = true
}
if isUserSource {
allItems[i].parseComments(&f.comments)
}

// Parameter "other" is not included for avoid like:
// ./tests/multi/head.h:4:28: error: invalid line marker flag '2': \
// cannot pop empty include stack
// # 2 "./tests/multi/main.c" 2
// ^
header := fmt.Sprintf("# %d \"%s\"",
allItems[i].positionInSource, allItems[i].include)
header := fmt.Sprintf("# %d \"%s\"", allItems[i].positionInSource, allItems[i].include)
lines = append(lines, header)
if len(allItems[i].lines) > 0 {
for ii, l := range allItems[i].lines {
Expand All @@ -200,17 +191,30 @@ func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
}
f.entities = append(f.entities, allItems[i])
}
f.pp = ([]byte)(strings.Join(lines, "\n"))

// Add constants to the output
if len(f.defines) > 0 {
var constLines []string
for name, value := range f.defines {
constLines = append(constLines, fmt.Sprintf("%s = %s", name, value))
}
lines = append([]string{"const (", "\t" + strings.Join(constLines, "\n\t"), ")"}, lines...)
}

f.pp = []byte(strings.Join(lines, "\n"))

// Correct include names for external includes (restored original logic)
{
for i := range f.includes {
f.includes[i].BaseHeaderName = f.includes[i].HeaderName
}
// correct include names only for external Includes
var ier []string
ier, err = GetIeraphyIncludeList(inputFiles, clangFlags, cppCode)
if err != nil {
return
}

// cut lines without pattern ". "
// Cut lines without pattern ". "
again:
for i := range ier {
remove := false
Expand Down Expand Up @@ -242,7 +246,6 @@ func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
if f.includes[i].IsUserSource {
continue
}
// find position in Include ierarphy
var pos int = -1
for j := range ier {
if strings.Contains(ier[j], f.includes[i].BaseHeaderName) {
Expand All @@ -254,9 +257,7 @@ func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
continue
}

// find level of line
level, _ := separator(ier[pos])

for j := pos; j >= 0; j-- {
levelJ, nameJ := separator(ier[j])
if levelJ >= level {
Expand All @@ -270,6 +271,62 @@ func NewFilePP(inputFiles, clangFlags []string, cppCode bool) (
}
}
}

return
}

// analyzeFiles - analyze single file and separation preprocessor code to parts
func analyzeFiles(inputFiles, clangFlags []string, cppCode bool, f *FilePP) (items []entity, err error) {
var out bytes.Buffer
out, err = getPreprocessSources(inputFiles, clangFlags, cppCode)
if err != nil {
return
}

r := bytes.NewReader(out.Bytes())
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)

var counter int
var item *entity
reg := util.GetRegex("# (\\d+) \".*\".*")
defineReg := util.GetRegex(`#define\s+(\w+)\s+([^\s]+)`)

for scanner.Scan() {
line := scanner.Text()
if reg.MatchString(line) {
if item != nil {
items = append(items, *item)
}
item, err = parseIncludePreprocessorLine(line)
if err != nil {
err = fmt.Errorf("cannot parse line : %s with error: %s", line, err)
return
}
if item.positionInSource == 0 {
item.positionInSource = 1
}
item.lines = make([]*string, 0)
} else if defineReg.MatchString(line) {
// Handle #define directives
matches := defineReg.FindStringSubmatch(line)
if len(matches) == 3 {
name := matches[1]
value := matches[2]
switch name {
case "T_EOF", "T_SPACE", "T_TAB":
f.defines[name] = value
}
}
}
counter++
if item != nil {
item.lines = append(item.lines, &line)
}
}
if item != nil {
items = append(items, *item)
}
return
}

Expand Down
65 changes: 55 additions & 10 deletions preprocessor/preprocessor_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,62 @@
// preprocessor_test.go
package preprocessor

import "testing"
import (
"io/ioutil"
"os"
"strings"
"testing"
)

func TestNewFilePPFail(t *testing.T) {
_, err := NewFilePP([]string{""}, []string{""}, false)
if err == nil {
t.Fatalf("Haven`t error")
func TestDefineConstants(t *testing.T) {
tests := []struct {
name string
input []string
expected string
}{
{
name: "Basic defines",
input: []string{
"#define T_EOF 0x0000",
"#define T_SPACE 0x0001",
"#define T_TAB 0x0002",
"# 1 \"test.c\"",
"int main() { return 0; }",
},
expected: "const (\n\tT_EOF = 0x0000\n\tT_SPACE = 0x0001\n\tT_TAB = 0x0002\n)\n# 1 \"test.c\"\nint main() { return 0; }",
},
{
name: "No defines",
input: []string{
"# 1 \"test.c\"",
"int main() { return 0; }",
},
expected: "# 1 \"test.c\"\nint main() { return 0; }",
},
}
}

func TestGetIncludeListFail(t *testing.T) {
_, err := getIncludeList([]string{"@sdf s"}, []string{"wqq4 `?p"}, []string{"w3 fdws", "sdfsr 4"}, false)
if err == nil {
t.Fatalf("Haven`t error")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "test*.c")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.WriteString(strings.Join(tt.input, "\n"))
if err != nil {
t.Fatal(err)
}
tmpFile.Close()

f, err := NewFilePP([]string{tmpFile.Name()}, []string{}, false)
if err != nil {
t.Errorf("NewFilePP() error = %v", err)
return
}
got := string(f.GetSource())
if got != tt.expected {
t.Errorf("GetSource() = %q, want %q", got, tt.expected)
}
})
}
}