feat: added modules

This commit is contained in:
Marvin Zhang
2024-06-14 16:37:48 +08:00
parent dc21bce11f
commit 6a60433d25
157 changed files with 5887 additions and 305 deletions

View File

@@ -0,0 +1,208 @@
package parser
import (
"encoding/json"
"errors"
"fmt"
"github.com/robertkrimen/otto"
"regexp"
"strings"
)
type GeneralParser struct {
tagPattern string
tagRegexp *regexp.Regexp
mathPattern string
mathRegexp *regexp.Regexp
template string
indexes [][]int
matches [][]string
placeholders []string
variables []Variable
vm *otto.Otto
}
const VariableNameResult = "result"
const ValueNameNA = "N/A"
func (p *GeneralParser) Parse(template string) (err error) {
p.template = template
p.indexes = p.tagRegexp.FindAllStringIndex(template, -1)
p.matches = p.tagRegexp.FindAllStringSubmatch(template, -1)
for _, arr := range p.matches {
p.placeholders = append(p.placeholders, arr[1])
}
return nil
}
func (p *GeneralParser) Render(args ...interface{}) (content string, err error) {
// render tag content
content, err = p.renderTagContent(args...)
if err != nil {
return content, err
}
// render math content
content, err = p.renderMathContent(content)
if err != nil {
return content, err
}
return content, nil
}
func (p *GeneralParser) renderTagContent(args ...interface{}) (content string, err error) {
// validate
if len(args) == 0 {
return "", errors.New("no arguments")
}
// first argument
arg := args[0]
// content
content = p.template
// old strings
var oldStrList []string
for _, index := range p.indexes {
// old string
oldStr := content[index[0]:index[1]]
oldStrList = append(oldStrList, oldStr)
}
// iterate placeholders
for i, placeholder := range p.placeholders {
// variable
v, err := NewVariable(arg, placeholder)
if err != nil {
return "", err
}
// value
value, err := v.GetValue()
if err != nil || value == nil {
value = ValueNameNA
}
// old string
oldStr := oldStrList[i]
// new string
var newStr string
switch value.(type) {
case string:
newStr = value.(string)
default:
newStrBytes, err := json.Marshal(value)
if err != nil {
return "", err
}
newStr = string(newStrBytes)
}
// replace old string with new string
content = strings.Replace(content, oldStr, newStr, 1)
}
return content, nil
}
func (p *GeneralParser) renderMathContent(inputContent string) (content string, err error) {
content = inputContent
indexes := p.mathRegexp.FindAllStringIndex(inputContent, -1)
matches := p.mathRegexp.FindAllStringSubmatch(inputContent, -1)
// old strings
var oldStrList []string
for _, index := range indexes {
// old string
oldStr := content[index[0]:index[1]]
oldStrList = append(oldStrList, oldStr)
}
// iterate matches
for i, m := range matches {
// js script to run to get evaluate result
script := fmt.Sprintf("%s = %s; %s", VariableNameResult, m[1], VariableNameResult)
// replace NA
script = strings.ReplaceAll(script, ValueNameNA, "NaN")
// value
value, err := p.vm.Run(script)
if err != nil {
return "", err
}
// old string
oldStr := oldStrList[i]
// new string
newStr := value.String()
// replace old string with new string
content = strings.Replace(content, oldStr, newStr, 1)
}
return content, nil
}
func (p *GeneralParser) GetPlaceholders() (placeholders []string) {
return p.placeholders
}
func NewGeneralParser() (p Parser, err error) {
// tag regexp
tagPrefix := "\\{\\{"
tagSuffix := "\\}\\}"
tagBasicChars := "\\$\\.\\w_"
tagAssociateChars := "\\[\\]:"
tagPattern := fmt.Sprintf(
"%s *([%s%s]+) *%s",
tagPrefix,
tagBasicChars,
tagAssociateChars,
tagSuffix,
)
tagRegexp, err := regexp.Compile(tagPattern)
if err != nil {
return nil, err
}
// math regexp
mathPrefix := "\\{#"
mathSuffix := "#\\}"
mathBasicChars := " \\(\\)"
mathOpChars := "\\+\\-\\*/%"
mathNumChars := "\\d\\."
mathSpecialChars := "(?:NaN|null)"
mathPattern := fmt.Sprintf(
"%s *([%s%s%s%s]+) *%s",
mathPrefix,
mathBasicChars,
mathOpChars,
mathNumChars,
mathSpecialChars,
mathSuffix,
)
mathRegexp, err := regexp.Compile(mathPattern)
if err != nil {
return nil, err
}
// math vm
vm := otto.New()
// parser
p = &GeneralParser{
tagPattern: tagPattern,
tagRegexp: tagRegexp,
mathPattern: mathPattern,
mathRegexp: mathRegexp,
vm: vm,
}
return p, nil
}