-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
140 lines (129 loc) · 4.02 KB
/
index.js
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
// Import the interpreter files
const preprocess = require("./interpreter/preprocessor.js")
const lexical = require("./interpreter/lexer.js")
const syntax = require("./interpreter/syntax.js")
const execute = require("./interpreter/executor.js")
const generate = require("./interpreter/generator.js")
module.exports = {
/**
* Interpret a peice of code
* @param {String} input The string to interpret
* @param {Object} options Object containing config options
*/
"run": (input, options) => {
/**
* Get the current time in microseconds
* @return {Int} Timestamp
*/
function getTime() {
// Get the time in nano seconds
let hrTime = 0
if (process.hrtime) hrTime = process.hrtime()
// Get a rounded microsecond time
return Math.round((hrTime[0] * 1000000 + hrTime[1]) / 10000)
}
return new Promise((resolve, reject) => {
// The default config
let config = {
// Expose extra debug info
"debug": false,
// Filename of the script being run
"filename": "inline",
// Filesystem location of the script without the filename
"location": "/",
// The maximum number of files including each other in a loop
"recursionLimit": 3,
// Preset variables in the interpreter
"variables": {}
}
// Go though every set config option to copy it
if (typeof options == "object") {
for (let option in options) {
// Check that the option already has a default value
if (option in config) {
// Check that the type of the set option matches the default
if (typeof config[option] === typeof options[option]) {
// Set the config option
config[option] = options[option]
}
// Reject it if it doesn't
else {
reject({
type: "InvalidConfigOption",
error: `Type "${typeof options[option]}" is invalid for "${option}" config option`
})
}
}
// If it doesn't it's not a valid option
else {
reject({
type: "InvalidConfigOption",
error: `Invalid config option "${option}"`
})
}
}
}
// Save the reject function in the config
config.reject = reject
/**
* Abort execution and raise a critcal error
* @param {String} type A short name for the error thrown
* @param {String} errorMsg The description of that went wrong
* @param {Object} meta An object with all debug info available
*/
config.crit = (type, errorMsg, meta) => {
// Build the error object
let errorObj = {
type: type
}
// Add an error message if we have one
if (typeof errorMsg == "string") errorObj.error = errorMsg
// Do the same for the meta object
if (typeof meta == "object") errorObj.meta = meta
// Fail the promise
reject(errorObj)
}
// Start an empty array for non-critical warnings
config.warns = []
/**
* Add a non-critical warning to the list
* @param {String} warning The warning raised
* @param {Object} meta All possible info of the location of the warning
*/
config.warn = (warning, meta) => {
config.warns.push({
warning: warning,
meta: meta
})
}
// The stages to run through
const stages = [preprocess, lexical, syntax, execute, generate]
// The last returned output, is the script input at first
let lastResult = input
// Collection of timings
let timings = []
// The last time recorded
let lastTime = getTime()
// Go though each stage
for (let stage of stages) {
// Note the time before we start
lastTime = getTime()
// Start the active stage
lastResult = stage(lastResult, config)
// Push the time it took
timings.push((getTime() - lastTime) / 100)
}
// Generate an object with the script output and more
resolve({
// The raw output as a string
output: lastResult,
// Generated warnings as an array
warnings: config.warns,
// Array of the time the 5 components took to run in ms
timings: timings,
// The state of the variables at the end if the run
variables: config.variables
})
})
}
}