-
Notifications
You must be signed in to change notification settings - Fork 0
/
project.mjs
151 lines (131 loc) · 4 KB
/
project.mjs
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
import { promises } from "fs"
import { parse as parseGrammar } from "./generated/grammar.out.mjs"
import * as tree from "./tree.mjs"
import { transpile } from "./transpiler.mjs"
import path from "path"
import { dump, pretty } from "./debug.mjs"
class Project {
constructor() {
this.modules = []
}
}
class Module {
constructor(filename, codeTree) {
if (!tree.is(codeTree, tree.MODULE))
throw new Error("Expecting module")
this.filename = filename
this.name = path.parse(filename).name
this.codeTree = codeTree
this.dependOn = []
}
}
class Dependency {
constructor(moduleName) {
this.moduleName = moduleName
this.module = null
}
}
export async function readProject(mainFilename, autoUse) {
var project = new Project
var filesToRead = [mainFilename]
var useAdded = new Set
var error = false
while (filesToRead.length > 0) {
const filename = filesToRead.shift()
console.log("Reading " + filename)
let sourceCode
try {
sourceCode = await promises.readFile(filename, 'utf8')
const codeTree = parseModule(sourceCode)
const module = new Module(filename, codeTree)
var useList = new Set(autoUse)
codeTree.statements
.filter(m => tree.is(m, tree.USE))
.forEach(use =>
use.modules.forEach(n => useList.add(n))
)
useList.forEach(u => module.dependOn.push(new Dependency(u)))
for (const m of module.dependOn) {
if (!useAdded.has(m.moduleName)) {
useAdded.add(m.moduleName)
filesToRead.push(path.join(path.dirname(filename), m.moduleName + ".ap"))
}
}
project.modules.push(module)
}
catch (e) {
error = true
displayError(e, sourceCode)
}
}
if (error)
return null
for (const m of project.modules) {
for (const d of m.dependOn) {
const { moduleName } = d
for (const m2 of project.modules) {
if (m2.name == moduleName) {
d.module = m2
}
}
}
}
dump(project.modules[0])
for (const m of project.modules)
{
console.log(m.filename + " => JS")
transpile(m.codeTree)
}
return project
}
function parseModule(sourceCode)
{
try {
return parseGrammar(sourceCode)
}
catch (e) {
displayError(e, sourceCode)
}
}
function displayMessageWithLocation(message, location, sourceCode) {
const lines = sourceCode.split(/\r?\n/);
const {start, end} = location
console.log(lines[start.line - 1])
if (end.line > start.line)
{
if (end.line - start.line > 2)
console.log("...")
else if (end.line - start.line === 2)
console.log(lines[start.line])
console.log(lines[end.line - 1])
if (end.column === 1)
console.log("^")
else
console.log("~".repeat(end.column - 1))
}
else if (end.column > start.column + 1)
{
console.log(" ".repeat(start.column - 1) + "~".repeat(end.column - start.column))
}
else
console.log(" ".repeat(start.column - 1) + "^")
console.log(`line ${end.line}: `+ message
.replace("[A-Za-z]", "identifier")
.replace("[A-Za-z0-9_]", "more letters")
.replace("[0-9]", "number")
.replace("[ \\t\\r\\n]", "end of line")
.replace("[ \\t]", "space")
.replace("\"\\n\"", "end of line")
.replace("\"/*\", ", "")
.replace("\"//\", ", ""))
}
function displayError(e, sourceCode) {
if (e.location)
{
displayMessageWithLocation(e.message, e.location, sourceCode)
if (e.otherLocation)
displayMessageWithLocation("Other declaration", e.otherLocation, sourceCode)
}
else
console.log(e)
}