Skip to content

Instantly share code, notes, and snippets.

@ikasoba
Created February 8, 2022 05:40
Show Gist options
  • Save ikasoba/b95e6b87bbdaf22e61f73d7c07cc0a31 to your computer and use it in GitHub Desktop.
Save ikasoba/b95e6b87bbdaf22e61f73d7c07cc0a31 to your computer and use it in GitHub Desktop.
順序あり(多分)、正負の数値(実数あり)の計算式を計算するJavaScriptの関数
const calc=((raw)=>{
const sw = (addIndex,v,f)=>new class {
cases=new Map();
def=null;
c(v,f){
this.cases.set(v,f)
return this
}
d(f){
this.def=f
return this
}
eval(_f=()=>{}){
_f(this,v)
for (const [k,f] of this.cases.entries()){
let r;
if ((typeof k == "string" && v.startsWith(k)) || (k instanceof RegExp && (r=v.match(k)))){
addIndex(k.length!=null ? k.length : r[0]?.length)
return f(r)
}
}
if (this.def)return this.def(v)
}
}().eval(f);
class Group extends Array {
constructor(...iterable){
super(...iterable)
}
setPriority(priority=0){
this.priority=priority
return this
}
}
let tkns=[]
let bracketLev=0
for (let i=0;i<raw.length;){
const t=sw((v)=>i+=v,raw.slice(i),(s)=>
s.c(/^-?(?:[1-9][0-9]+|[0-9])(?:\.[0-9]+)?/,(v)=>parseFloat(v[0]))
.c(/^[+\-*/^%]/,(v)=>v[0])
.c("(",()=>{bracketLev++;return "("})
.c(")",()=>{bracketLev--;return ")"})
.d((v)=>{throw new Error(`invalid character ${v[0]}`)})
)
if (t)tkns.push(t)
}
function generateGroupFromBrackets(tkns){
let currentBracketLev=null
let currentBracketStart=null
let bracketLev=0
for (let i=0;i<tkns.length;i++){
if (tkns[i]=="("){
if (currentBracketLev==null){
currentBracketLev=bracketLev
currentBracketStart=i
}
bracketLev++
}else if (tkns[i]==")"){
bracketLev--
if (currentBracketLev!=null && currentBracketLev==bracketLev){
return tkns.slice(0,currentBracketStart)
.concat(
[new Group(...generateGroupFromBrackets(tkns.slice(currentBracketStart+1,i))).setPriority(2)],
)
.concat(
tkns.slice(i+1)
)
}
}
}
return tkns
}
function generateGroupFromBinOps(tkns,nested=0){
for (let i=0;i<tkns.length;i++){
if (tkns[i] instanceof Array){
tkns[i]=generateGroupFromBinOps(tkns[i])
continue
}
}
if (nested)return tkns;
for (const o of [["^"],["*","/","%"],["+","-"]]){
for (let i=0;i<tkns.length;i++){
if (tkns[i] instanceof Array)continue;
if (o.some(o=>tkns[i]==o)){
console.log(tkns.slice(i-1,i+2))
return tkns.slice(0,i-1)
.concat(
[new Group(...generateGroupFromBinOps(tkns.slice(i-1,i+2),1)).setPriority(0+(o=="^"))]
)
.concat(
tkns.slice(i+2)
)
}
}
}
return tkns
}
function generateGroups(tkns){
return generateGroupFromBinOps(generateGroupFromBrackets(tkns))
}
const tree=generateGroups(tkns)
if (bracketLev)throw new Error("Parentheses are not closed")
const stack=[]
const findGroup=()=>{}
const evaluate = (tree)=>{
if (typeof tree == "number")return tree;
for (let i=0;i<tree.length;i++){
if (tree[i] instanceof Array){
stack.push(evaluate(tree[i]))
continue
}
if (typeof tree[i] == "number")stack.push(tree[i]);
else if (tree[i]=="^")stack.push(stack.pop()**evaluate(tree[i+=1]));
else if (tree[i]=="*")stack.push(stack.pop()*evaluate(tree[i+=1]));
else if (tree[i]=="/")stack.push(stack.pop()/evaluate(tree[i+=1]));
else if (tree[i]=="%")stack.push(stack.pop()%evaluate(tree[i+=1]));
else if (tree[i]=="+")stack.push(stack.pop()+evaluate(tree[i+=1]));
else if (tree[i]=="-")stack.push(stack.pop()-evaluate(tree[i+=1]));
}
return stack.pop()
}
return evaluate(tree)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment