This repository was archived by the owner on Jun 8, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
151 lines (127 loc) · 4.26 KB
/
Copy pathmain.go
File metadata and controls
151 lines (127 loc) · 4.26 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main
import (
"fmt"
"grua/src"
"grua/src/lexer"
"grua/src/parser"
"grua/src/runtime"
"os"
"path/filepath"
"strings"
)
var (
parsedFiles = make(map[string]bool)
globalSymbolTable = make(map[string]parser.Symbol)
stdlibDir string
)
func init() {
// Figure out where the compiler is running from to establish the stdlib path
ex, err := os.Executable()
if err != nil {
panic(err)
}
compilerDir := filepath.Dir(ex)
stdlibDir = filepath.Join(compilerDir, "../stdlib/")
}
func main() {
args := os.Args[1:]
filepathArg := src.GetFilePath(args)
shouldRun := src.HasRunFlag(args)
if filepathArg == "" {
fmt.Println("Usage: grua --file <yourfile.grua> [--run]")
os.Exit(1)
}
// Enforce that the entry file is strictly a .grua file
if !strings.HasSuffix(filepathArg, ".grua") {
fmt.Printf("Build Error: Invalid file extension. The compiler only accepts '.grua' files.\nProvided: %s\n", filepathArg)
os.Exit(1)
}
fmt.Printf("Building Project starting from %s...\n", filepathArg)
// Process the main entry file (which will recursively process imports)
mainAST := processModule(filepathArg)
// In an interpreted language, we execute the AST directly instead of building binaries
if shouldRun {
fmt.Println("========================================")
fmt.Printf("Executing: %s\n", filepathArg)
fmt.Println("----------------------------------------")
// 1. Create the Global Environment
globalEnv := runtime.NewEnvironment(nil)
// 2. Evaluate the AST
result, err := runtime.Evaluate(mainAST, globalEnv)
if err != nil {
fmt.Printf("Execution Failed:\n%v\n", err)
os.Exit(1)
}
// 3. Print the final evaluation result (temporary, just to see it work!)
if result != nil && result.Type() != runtime.NullValueType {
fmt.Printf("Program exited with value: %s\n", result.Inspect())
}
fmt.Println("========================================")
} else {
fmt.Println("Parse and Borrow-Check completed successfully. (AST generated)")
}
}
func processModule(currentFilepath string) *parser.Program {
// Enforce that all imported/processed modules are strictly .grua files
if !strings.HasSuffix(currentFilepath, ".grua") {
fmt.Printf("Build Error: Invalid module extension. Only '.grua' files can be imported.\nTarget: %s\n", currentFilepath)
os.Exit(1)
}
// Prevent circular imports and duplicate parsing
absPath, err := filepath.Abs(currentFilepath)
if err != nil {
absPath = currentFilepath
}
if parsedFiles[absPath] {
return nil
}
parsedFiles[absPath] = true
if !src.FileExists(currentFilepath) {
fmt.Printf("Build Error: Module file not found -> %s\n", currentFilepath)
os.Exit(1)
}
fmt.Printf("Compiling: %s...\n", currentFilepath)
lines := src.LinesFrom(currentFilepath)
sourceCode := strings.Join(lines, "\n")
currentDir := filepath.Dir(currentFilepath)
// 1. Lexical Analysis
l := &lexer.Lexer{}
err = l.Tokenize(sourceCode)
if err != nil {
fmt.Printf("Lexer Error in %s: %v\n", currentFilepath, err)
os.Exit(1)
}
// 2. Pre-scan tokens for imports to resolve dependencies before parsing
// This ensures the global symbol table is populated with imported types/functions
for i := 0; i < len(l.Tokens); i++ {
if l.Tokens[i].Type == lexer.TokenImport {
if i+1 < len(l.Tokens) && l.Tokens[i+1].Type == lexer.TokenString {
rawPath := l.Tokens[i+1].Literal
localPath := filepath.Join(currentDir, rawPath+".grua")
stdlibPath := filepath.Join(stdlibDir, rawPath+".grua")
targetPath := ""
if src.FileExists(localPath) {
targetPath = localPath
} else if src.FileExists(stdlibPath) {
targetPath = stdlibPath
} else {
fmt.Printf("Build Error: Cannot resolve import '%s'. Checked local (%s) and stdlib (%s).\n", rawPath, localPath, stdlibPath)
os.Exit(1)
}
// Recursively parse the imported module
processModule(targetPath)
}
}
}
// 3. Parsing (Using the shared globalSymbolTable)
p := parser.NewParser(l.Tokens, globalSymbolTable)
ast := p.Parse()
// 4. Borrow Checking
fmt.Printf("Running Borrow Checker for %s...\n", currentFilepath)
bc := runtime.NewBorrowChecker(globalSymbolTable)
if err := bc.Verify(ast); err != nil {
fmt.Printf("Compilation halted due to Borrow Checker violations in %s:\n%v\n", currentFilepath, err)
os.Exit(1)
}
return ast
}