Just replacing the interpreter doesn’t really look like a text
First, fix the simple interpreter traverse = =
It is estimated that these types are the only ones left, and orZ will be filled if missing
A gadget function ~
Then adjust the parameters of the visitor a little bit
Oh yeah, from the looks of it, it’s done!
None of the declared arguments are used because there is no output
Yeah, that’s why I don’t want to explain water alone
So is it difficult to add a result output
Actually, this shouldn’t be too difficult
However, the happiness of interpreting the AST depends entirely on the state of health at the time the AST was designed, at least in my case
After intense and unremitting efforts, the sapling finally showed the slightest sign of life, but it did not seem to last much longer
… At least for now (manual squint)
Then modify the CallExpression part of the visitor. In fact, this is the only place where changes are made. We need to deal with both literals and variables
Calling functions has also been slightly modified, adding a simpleAST step orz in the middle of the English language
Ok ~ next, output four variables ~
Perfect scatter flower ~
Well, it doesn’t look like it’s adding much, is that the end of it
Print 4 variables on the same line: print 4 variables on the same line: print 4 variables on the same line: print 4 variables on the same line: print 4 variables on the same line: print 4 variables on the same line
Well, yes, this time is that short, again scatter flowers ~
PS: To summarize, at least for now, this level of “programming language” is still “business”, which means that I’ve received a need to deal with text and strings. In the short term, this is basically the pace of building a “programming language” with strings in mind. At present, there is not much need to refer to the compilation of related books
PS2: After all, for the purpose of learning, it would be better to look for information after encountering problems or while writing. Books should be regarded as a tool for solving problems rather than learning, at least… When you don’t have a difficult problem to solve, you can put the book aside (purely personal opinion, but sooner or later will open the book
Attach the source.jpg
traverser.js
const fs = require('fs')
const path = require('path')
function executeAST(ast, execPath) {
const vars = {}
const types = {}
traverser(ast, {
ImportDeclaration(node, parent) {
const p = path.resolve(execPath, node.source.value)
const js = fs.readFileSync(p, { encoding: 'utf-8' })
// Use the syntax of js to request variable space in memory
vars[node.property.value] = eval(js)
},
CallExpression(node, parent) {
const key = parent.object.value.value
const obj = vars[key]
function printValue(val, type) {
// Float special conversion before output = =
returntype ! = ='float' ? val : val.toFixed(1)}const params = node.arguments.map(arg= > {
const type = types[arg.value].value
if (arg.type === 'Identifier') {
return printValue(vars[arg.value], type)
}
if (arg.type.endsWith('Literal')) {
return printValue(arg.value, type)
}
})
// The chain call problem will have to be solved sooner or later = =obj[node.property.value](... params) },VariableDeclaration(node, parent) {
vars[node.property.value] = node.value.value
types[node.property.value] = node.datatype
},
IntegerLiteral(node, parent) {
node.value = parseInt(node.value)
},
FloatLiteral(node, parent) {
node.value = parseFloat(node.value)
},
BooleanLiteral(node, parent) {
node.value = node.value === 'true' ? true : false}})}function traverser(ast, visitor) {
function traverseArray(array, parent) {
array.forEach(child= > {
traverseNode(child, parent)
})
}
function traverseNodePropertys(node, props) {
props.forEach(p= > {
traverseNode(node[p], node)
})
}
function traverseNode(node, parent) {
if(! node) {return
}
const method = visitor[node.type]
// I have a hunch that the switch will be huge
switch (node.type) {
case 'Program':
traverseArray(node.body, node)
break
case 'ImportDeclaration':
traverseNodePropertys(node, ['source'.'property'])
break
case 'VariableDeclaration':
traverseNodePropertys(node, ['property'.'value'.'datatype'])
break
case 'ExpressionStatement':
traverseNodePropertys(node, ['object'.'callee'])
break
case 'MemberExpression':
traverseNodePropertys(node, ['value'])
break
case 'CallExpression':
traverseArray(node.arguments, node)
traverseNodePropertys(node, ['property'])
break
case 'Identifier':
case 'StringLiteral':
case 'IntegerLiteral':
case 'FloatLiteral':
case 'BooleanLiteral':
traverseNode(node.value, node)
break
}
// For now, orz is enough
if (method) {
method(node, parent)
}
}
traverseNode(ast, null)}module.exports = { traverser, executeAST }
Copy the code
Ast.js // Only modified functions
ExpressionStatement(node, parent) {
const parenStart = node.params.findIndex(p= >
p.type === 'ParenIdentifier' && p.value === '('
)
const parenEnd = node.params.findIndex(p= >
p.type === 'ParenIdentifier' && p.value === ') '
)
if (parenStart > -1 && parenEnd > -1) {
node.callee = { type: 'CallExpression' }
node.callee.arguments = node.params.slice(parenStart + 1, parenEnd)
node.callee.property = node.params[0]
delete node.params
return
}
// Function calls are also cumbersome things, anyway, this time mainly the data type orz
const objIdx = node.params.findIndex(p= >
p.type === 'OperatorIdentifier' && p.value === '. '
)
const calleeIdx = node.params.findIndex(p= >
p.type === 'ExpressionStatement' && p.callee
)
// Temporarily handle the parenthesis depth problem T T
if (parenEnd > -1) {
node.callee = { type: 'CallExpression' }
node.callee.arguments = [node.params[0]]
delete node.params
return
}
if (parenStart > -1 && calleeIdx > -1) {
node.callee = node.params[calleeIdx].callee
node.callee.property = node.params[0]
delete node.params
return
}
if (objIdx > -1 && calleeIdx > -1) {
node.object = { type: 'MemberExpression'.value: node.params[objIdx -1 ]}
node.callee = node.params[calleeIdx].callee
delete node.params
return}},Copy the code