package cards import ( "fmt" "html/template" "regexp" "strings" ) const calcExtraHead = ` ` const solveRenderScript = `
` 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, ",") + ")" }