-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunfold.go
127 lines (118 loc) · 3.36 KB
/
unfold.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package unfoldcpp
import (
"bufio"
"errors"
"os"
"path/filepath"
"regexp"
"strings"
)
func Unfold(path string, optional_libraries ...*[]string) (string, error) {
// adding user provided directories, optional argument
libraries := make([]string, 0)
if len(optional_libraries) > 0 {
libraries = *optional_libraries[0]
}
// trace dependencies recursively
current_dependencies := make([]string, 0)
current_dependencies = append(current_dependencies, path)
current_code := "#define UNFOLDED\n"
err := unfold_recursively(¤t_dependencies, ¤t_code, &libraries)
if err != nil {
return "", err
}
return current_code, nil
}
func unfold_recursively(current_dependencies *[]string, current_code *string, libraries *[]string) error {
// open file
path := (*current_dependencies)[len(*current_dependencies)-1]
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
// setup regex
r, err := regexp.Compile(`^( |\t)*#include[\s]+\"(.*)\"[\s]*$*`)
if err != nil {
return err
}
// read lines to check for #include
// if #include found, add to dependencies and call unfold_recursively
// if #include not found, add to code
scanner := bufio.NewScanner(file)
for scanner.Scan() {
if r.MatchString(scanner.Text()) {
line := scanner.Text()
start_index := strings.Index(line, "\"")
end_index, err := findNextQuote(line, start_index+1)
if err != nil {
return err
}
relative_path := line[start_index+1 : end_index]
// matching the relative path to provided directories
// and current directory
absolute_path, err := findPathToFile(relative_path, filepath.Dir(path), libraries)
if err != nil {
return err
}
// check if already in dependencies
already_in_dependencies := false
for _, dependency := range *current_dependencies {
if dependency == absolute_path {
already_in_dependencies = true
break
}
}
if !already_in_dependencies {
*current_dependencies = append(*current_dependencies, absolute_path)
err = unfold_recursively(current_dependencies, current_code, libraries)
if err != nil {
return err
}
} else {
// call cyclic dependency error
return errors.New("Cyclic dependency detected")
}
} else {
*current_code += scanner.Text() + "\n"
}
}
// delete here from dependencies
*current_dependencies = (*current_dependencies)[:len(*current_dependencies)-1]
return nil
}
func findNextQuote(s string, startIndex int) (int, error) {
if startIndex < 0 || startIndex >= len(s) {
return -1, errors.New("startIndex out of range")
}
for i := startIndex; i < len(s); i++ {
if s[i] == '"' {
if i == 0 || s[i-1] != '\\' {
return i, nil
}
}
}
return -1, errors.New("no unescaped quote found")
}
// Looks for the file in current directory and in specified folders
func findPathToFile(relative_path string, current_dir string, libraries *[]string) (string, error) {
absolute_path, err := filepath.Abs(current_dir + "/" + relative_path)
if err != nil {
return "", err
}
_, err = os.Stat(absolute_path)
if err == nil {
return absolute_path, nil
}
for _, dir := range *libraries {
absolute_path, err := filepath.Abs(dir + "/" + relative_path)
if err != nil {
return absolute_path, err
}
_, err = os.Stat(absolute_path)
if err == nil {
return absolute_path, nil
}
}
return "", errors.New("no match for dependecy: " + relative_path)
}