I'm creating a JS module that serves as a 'programming language' This programming language interprets human words and is supposed to translate it into Javascript.
The programming language is going to be strict on grammar, similarly to other programming languages, and that's what the question here is here to accomplish.
In my programming language, you can write your program many different ways, but to keep mines clean, I like to split it off in chunks for readability. This block of code shown here:
...
add 2 and 2
multiply by 5
...
Would spew out:
20
It's working, but if we are trying to be strict on grammar as well, we would have to ask this question:
- Multiply what by 5? The result of the previous math equation, or is the user initiating another equation?
And if we were using the result of the previous math equation, the code would need to look something like this:
...
add 2 and 2
then multiply the result by 5
...
Still spewing the same result of:
20
How can I accomplish this goal?
Full Source Code
source.js:
Array.prototype.remove = function(value) {
for (var i = this.length; i--; )
{
if (this[i] === value) {
this.splice(i, 1);
}
}
}
// from https://stackoverflow.com/questions/175739/how-can-i-check-if-a-string-is-a-valid-number
function isNumeric(str) {
if (typeof str != "string") return false
return !isNaN(str) && !isNaN(parseFloat(str))
}
function isOperand(token) {
const ops = ["add", "multiply", "subtract", "divide"]
if (ops.includes(token)) {
return true
}
return false
}
function interpret(input) {
const tokens = input.split(' ') // in fancy programming language terms,
// this is a lexical analysis step
// note that we are not supporting things like
// double spaces, something to think about!
tokens.remove('\n')
tokens.remove('')
console.log(tokens)
let state = 0 // we are keeping the results from our operation here
for (i = 0; i < tokens.length; i++) {
const t = tokens[i] // to keep things shorter
if (!isOperand(t)) {
throw new Error(`expected operand token, got: ${t}`)
}
// all operators are binary, so these variables will hold the operands
// they may be two numbers, or a number and the internal state
let a, b;
const next = tokens[i + 1]
if (next == "by") {
// we should add the next token (hopefully a number!) to the state
a = state
b = parseFloat(tokens[i + 2])
i += 2 // very important! the two tokens we read should be skipped
// by the loop. they were "consumed".
}
else if (isNumeric(next)) {
const and = tokens[i + 2] // this should be the "and"
if (and != "and") {
throw new Error(`expected "and" token, got: ${and}`)
}
a = parseFloat(next)
b = parseFloat(tokens[i + 3])
i += 3 // in this case, we are consuming more tokens
} else {
throw new Error(`unexpected token: ${next}`)
}
switch (t) {
case "add":
state = a + b
break;
case "multiply":
state = a * b
break;
case "subtract":
state = a - b
break;
case "divide":
state = a / b
break;
}
}
return state
}
function out(main) {
console.log(interpret(main))
}
module.exports = {out}
index.js:
const cjs = require('./source.js')
var main = `
add 2 and 2
multiply by 5
`
cjs.out(main)