-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
123 lines (117 loc) · 5.24 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
// Settings
const INDENT_SIZE = 4
const CAPITALIZATION = null // `LOWERCASE`, `UPPERCASE`, `CAPITALIZE`, or null
// End settings
const fs = require(`fs`)
const esprima = require('esprima')
const program = fs.readFileSync('program.js').toString()
const pseudocode = javaScriptToPseudoCode(program)
console.log(`------------------------`)
console.log(`Here is your pseudocode!`)
console.log(`------------------------`)
console.log(pseudocode)
function javaScriptToPseudoCode (javaScript) {
const parsed = esprima.parseScript(program)
return `BEGIN\n${parsed.body.map(item => indentLines(objToString(item), 1)).join(`\n`)}\nEND`
}
function objToString (obj) {
const type = obj.type
let operator, temp
switch (type) {
case `Literal`:
if (obj.value === false) return `FALSE`
if (obj.value === true) return `TRUE`
return obj.raw
case `Identifier`:
switch (CAPITALIZATION) {
case `LOWERCASE`: return obj.name.toLowerCase()
case `UPPERCASE`: return obj.name.toUpperCase()
case `CAPITALIZE`: return obj.name[0].toUpperCase() + obj.name.slice(1).toLowerCase()
default: return obj.name
}
case `LogicalExpression`:
operator = obj.operator
switch (operator) {
case `&&`: operator = `AND`; break
case `||`: operator = `OR`; break
}
return `${objToString(obj.left)} ${operator} ${objToString(obj.right)}`
case `BinaryExpression`:
operator = obj.operator
switch (operator) {
case `==`: case `===`: operator = `=`; break
case `!=`: case `!==`: operator = `<>`; break
}
return `${objToString(obj.left)} ${operator} ${objToString(obj.right)}`
case `WhileStatement`:
return `WHILE ${objToString(obj.test)}\n${objToString(obj.body)}\nENDWHILE`
case `IfStatement`:
case `ElseIfStatement`:
// I made this type up — see the IfStatement case code
temp = `${obj.type === `ElseIfStatement` ? `\nELSE ` : ``}IF ${objToString(obj.test)} THEN\n`
temp += `${objToString(obj.consequent)}`
if (obj.alternate) {
if (obj.alternate.type == `IfStatement`) {
obj.alternate.type = `ElseIfStatement`
} else {
temp += `\nELSE\n`
}
temp += `${objToString(obj.alternate)}`
}
if (obj.type === `IfStatement`) {
temp += `\nENDIF`
}
return temp
case `BlockStatement`:
return obj.body.map(item => indentLines(objToString(item), 1)).join(`\n`)
case `ExpressionStatement`:
return objToString(obj.expression)
case `UpdateExpression`:
operator = obj.operator
if (operator === `++`) return `INCREMENT ${objToString(obj.argument)}`
if (operator === `--`) return `DECREMENT ${objToString(obj.argument)}`
console.log(`Unknown update operator`, operator)
return `?`
case `AssignmentExpression`:
if (obj.left.type === `Identifier`
&& obj.right.type === `CallExpression`
&& obj.right.callee.name === `prompt`) {
return `get ${objToString(obj.left)}`
}
return `${objToString(obj.left)} ${obj.operator} ${objToString(obj.right)}`
case `VariableDeclaration`:
return obj.declarations.map(declaration => {
if (declaration.init.type === `CallExpression` && declaration.init.callee.name === `prompt`) {
return `get ${objToString(declaration.id)}`
}
return `${objToString(declaration.id)} = ${objToString(declaration.init)}`
}).join(`\n`)
case `CallExpression`:
const calleeAsString = objToString(obj.callee).toLowerCase()
if (calleeAsString === `alert`
|| calleeAsString === `console.log`
|| calleeAsString === `console.info`
|| calleeAsString === `console.error`
|| calleeAsString === `console.warning`) {
return `Display ${obj.arguments.map(arg => objToString(arg)).join(`; `)}`
}
return `${objToString(obj.callee)}(${obj.arguments.map(arg => objToString(arg)).join(`, `)})`
case `MemberExpression`:
return `${objToString(obj.object)}.${objToString(obj.property)}`
case `TemplateLiteral`:
temp = []
for (let i = 0; i < Math.max(obj.quasis.length, obj.expressions.length); i++) {
if (i < obj.quasis.length) temp.push(objToString(obj.quasis[i]))
if (i < obj.expressions.length) temp.push(objToString(obj.expressions[i]))
}
return temp.join(` + `)
case `TemplateElement`:
return `'${obj.value.raw}'`
default:
console.log(`Unknown obj type`, obj.type, JSON.stringify(obj, null, 2))
return `\x1b[31m${obj.type} not implemented\x1b[0m`
}
}
function indentLines (str, n) {
return str.split(`\n`).map(line => ` `.repeat(n * INDENT_SIZE) + line).join(`\n`)
}