190 lines
5.3 KiB
Go
190 lines
5.3 KiB
Go
/*
|
|
* Scope - A simple and minimal metasearch engine
|
|
* Copyright (C) 2021 Arsen Musayelyan
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package cards
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
const calcExtraHead = `
|
|
<!-- Import KaTeX for math rendering -->
|
|
<link rel="stylesheet" href="/static/ext/katex.min.css">
|
|
<script defer src="/static/ext/katex.min.js"></script>
|
|
|
|
<!-- Import Nerdamer for equation evaluator -->
|
|
<script src="/static/ext/nerdamer.core.js"></script>
|
|
<script src="/static/ext/Algebra.js"></script>
|
|
<script src="/static/ext/Calculus.js"></script>
|
|
<script src="/static/ext/Solve.js"></script>`
|
|
|
|
const solveRenderScript = `
|
|
<div id="calc-content" class="subtitle mx-2 my-0"></div>
|
|
<script>
|
|
window.onload = () => {
|
|
// Execute input and get output as LaTeX
|
|
latex = nerdamer('%s').latex()
|
|
// Use KaTeX to render output to #calc-content div
|
|
katex.render(latex, document.getElementById('calc-content'))
|
|
}
|
|
</script>`
|
|
|
|
func init() {
|
|
// Register calc card
|
|
Register("calc", 0, NewCalcCard)
|
|
}
|
|
|
|
// CalcCard represents a calculation card
|
|
type CalcCard struct {
|
|
query string
|
|
useFunc bool
|
|
function string
|
|
args []string
|
|
expression string
|
|
}
|
|
|
|
// NewCalcCard is a NewCardFunc that creates a new CalcCard
|
|
func NewCalcCard(query, _ string) Card {
|
|
return &CalcCard{query: query, useFunc: true}
|
|
}
|
|
|
|
// Matches determines whether a query matches the requirements
|
|
// fot CalcCard and determines which function to run with what arguments
|
|
func (cc *CalcCard) Matches() bool {
|
|
// If query has solve prefix
|
|
if strings.HasPrefix(cc.query, "solve") {
|
|
// Set function to solve
|
|
cc.function = "solve"
|
|
|
|
// Compile regex for specifying veriable to solve for
|
|
forRgx := regexp.MustCompile(`solve (.+) for (.+)`)
|
|
// If query matches regex
|
|
if forRgx.MatchString(cc.query) {
|
|
// Find furst regex match
|
|
matches := forRgx.FindStringSubmatch(cc.query)
|
|
// Append function aeguments. First is equation, second variable
|
|
cc.args = append(cc.args, matches[1], matches[2])
|
|
} else {
|
|
// Append function arguments assuming variable is x
|
|
cc.args = append(cc.args, cc.StripKey(), "x")
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// If query has integrate prefix
|
|
if strings.HasPrefix(cc.query, "integrate") ||
|
|
strings.HasPrefix(cc.query, "integral of") ||
|
|
strings.HasPrefix(cc.query, "integral") {
|
|
|
|
// Set function to integrate
|
|
cc.function = "integrate"
|
|
// Append function arguments
|
|
cc.args = append(cc.args, cc.StripKey())
|
|
|
|
return true
|
|
}
|
|
|
|
// If query has derivative prefix
|
|
if strings.HasPrefix(cc.query, "diff") ||
|
|
strings.HasPrefix(cc.query, "derive") ||
|
|
strings.HasPrefix(cc.query, "differentiate") ||
|
|
strings.HasPrefix(cc.query, "derivative of") ||
|
|
strings.HasPrefix(cc.query, "derivative") {
|
|
|
|
// Set function to diff
|
|
cc.function = "diff"
|
|
// Append function arguments
|
|
cc.args = append(cc.args, cc.StripKey())
|
|
|
|
return true
|
|
}
|
|
|
|
// If query has calculate prefix
|
|
if strings.HasPrefix(cc.query, "calculate") {
|
|
// This is an expression, so no function needed
|
|
cc.useFunc = false
|
|
// Set expression to query stripped of keys
|
|
cc.expression = cc.StripKey()
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// StripKey removes all key words related to CalcCard
|
|
func (cc *CalcCard) StripKey() string {
|
|
// Compile regex for words to be removed
|
|
trimRgx := regexp.MustCompile(`^(.*?)solve|integrate|integral(?: of)?|diff|differentiate|derivative(?: of)?|derive|calculate(.*)$`)
|
|
// Return string with words removed
|
|
return trimRgx.ReplaceAllString(cc.query, "${1}${2}")
|
|
}
|
|
|
|
// Content returns the solveRenderScript with the given input
|
|
func (cc *CalcCard) Content() template.HTML {
|
|
var input string
|
|
// If function is being used
|
|
if cc.useFunc {
|
|
// Set input to formatted function
|
|
input = formatFunc(cc.function, cc.args)
|
|
} else {
|
|
// Set input to expression
|
|
input = cc.expression
|
|
}
|
|
// Return script with given input
|
|
return template.HTML(fmt.Sprintf(
|
|
solveRenderScript,
|
|
input,
|
|
))
|
|
}
|
|
|
|
// Footer returns empty string because CalcCard has no footer
|
|
func (cc *CalcCard) Footer() template.HTML {
|
|
return ""
|
|
}
|
|
|
|
// Returned always returns true since CalcCard is a frontend
|
|
// card and it is thus impossible to determine whether
|
|
// the query gets an answer
|
|
func (cc *CalcCard) Returned() bool {
|
|
return true
|
|
}
|
|
|
|
// RunQuery returns nil as CalcCard is a frontend card
|
|
func (cc *CalcCard) RunQuery() error {
|
|
return nil
|
|
}
|
|
|
|
// Title for CalcCard is "Calculation"
|
|
func (cc *CalcCard) Title() string {
|
|
return "Calculation"
|
|
}
|
|
|
|
// Head returns the extra head tags required for CalcCard
|
|
func (cc *CalcCard) Head() template.HTML {
|
|
return calcExtraHead
|
|
}
|
|
|
|
func formatFunc(function string, args []string) string {
|
|
// Format as function(arg1,arg2...)
|
|
return function + "(" + strings.Join(args, ",") + ")"
|
|
}
|