Skip to content

Commit

Permalink
imp: 明确并改进了计算过程标注规则
Browse files Browse the repository at this point in the history
  • Loading branch information
fy0 committed Jun 22, 2024
1 parent 4708cf7 commit fad9003
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 49 deletions.
97 changes: 75 additions & 22 deletions rollvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package dicescript

import (
"bytes"
"errors"
"fmt"
"sort"
Expand Down Expand Up @@ -166,6 +167,35 @@ func (a spanByEnd) Less(i, j int) bool { return a[i].End < a[j].End }
// return errors.New("E5: 超出单指令允许算力,不予计算")
// }

/*
计算过程标注
基础格式1: 算式=结果
基础格式2: 算式=多个过程=结果
过程: 值[小算式=值=文本标注, 子过程] - 即 span.Ret[expr=span.Ret=span.Text, subDetails]
多个过程: 过程的集合,如 a + b,如果过程只有一个,那么多个过程与过程相同
文本标注区分: 目前骰点式有文本标注,如2d1,1d2,f2,a10等,加减乘除等算式没有,计算类型和变量读取有,函数调用有
补丁: 过程进行调整,取消"=值"这部分输出
分为三种情况再进行细分
1. 直接相等,一级,单项,无子句
d10=10[d10=10=10]=10 首先进行一次省略,即文本标注如果等于值,那么=10=10 可以省略为=10
基于d10=10[d10=10]=10二次省略,如果单项过程的文本标注=值,进行省略,那么 d10=10[d10]=10
基于d10=10[d10]=10三次省略,如果只有一项过程,且没有子项,且大算式=小算式,过程可全部省略
最终: d10=10
疑点: 2d10=12[2d10=12=2+10]=12 这种三项都不满足,但是2d10=12=2+10是否过于繁琐?
2.一级,单项,有子句
(2d1)d1=2[(2d1)d1=2=1+1,2d1=2]=2
(2d1+((2d1)d1)d1)d1=4[(2d1+((2d1)d1)d1)d1=4=1+1+1+1,2d1=2,2d1=2,(2d1)d1=2,((2d1)d1)d1=2]=4
一下不知道怎么弄了
3.组合算式
种类1和种类2使用加减乘除等符号相连
也包括 [] {} 等数据组合形式
值得注意的是,目前 [2d1,2]kl 这种形式,2d1和2都属于一级,未来可能会修改。
同理还有 [2d1,2].kl() 这个与上面等价,只是写法不同
*/
func (ctx *Context) makeDetailStr(details []BufferSpan) string {
if ctx.Config.CustomMakeDetailFunc != nil {
return ctx.Config.CustomMakeDetailFunc(ctx, details, ctx.parser.data)
Expand All @@ -175,21 +205,20 @@ func (ctx *Context) makeDetailStr(details []BufferSpan) string {
curPoint := IntType(-1) // nolint
lastEnd := IntType(-1) // nolint

var m []struct {
type Group struct {
begin IntType
end IntType
tag string
spans []BufferSpan
val *VMValue
}

var m []Group
for _, i := range details {
// fmt.Println("?", i, lastEnd)
if i.Begin > lastEnd {
curPoint = i.Begin
m = append(m, struct {
begin IntType
end IntType
spans []BufferSpan
}{begin: curPoint, end: i.End, spans: []BufferSpan{i}})
m = append(m, Group{begin: curPoint, end: i.End, tag: i.Tag, spans: []BufferSpan{i}, val: i.Ret})
} else {
m[len(m)-1].spans = append(m[len(m)-1].spans, i)
if i.End > m[len(m)-1].end {
Expand All @@ -203,7 +232,14 @@ func (ctx *Context) makeDetailStr(details []BufferSpan) string {
}

for i := len(m) - 1; i >= 0; i-- {
// for i := 0; i < len(m); i++ {
buf := bytes.Buffer{}
writeBuf := func(p []byte) {
buf.Write(p)
}
writeBufStr := func(s string) {
buf.WriteString(s)
}

item := m[i]
size := len(item.spans)
sort.Sort(spanByEnd(item.spans))
Expand All @@ -220,20 +256,31 @@ func (ctx *Context) makeDetailStr(details []BufferSpan) string {
}

exprText := string(detailResult[item.begin:item.end])
writeBuf(detailResult[:item.begin])

var r []byte
r = append(r, detailResult[:item.begin]...)
// 主体结果部分,如 (10d3)d5=63[(10d3)d5=2+2+2+5+2+5+5+4+1+3+4+1+4+5+4+3+4+5+2,10d3=19]
partRet := last.Ret.ToString()

// 主体结果部分,如 (10d3)d5=63[(10d3)d5=63=2+2+2+5+2+5+5+4+1+3+4+1+4+5+4+3+4+5+2,10d3=19]
detail := "[" + exprText + "=" + last.Ret.ToString()
if last.Text != "" {
detail := "[" + exprText
if last.Text != "" && partRet != last.Text { // 规则1.1
detail += "=" + last.Text
}
detail += subDetailsText + "]"

r = append(r, ([]byte)(last.Ret.ToString()+detail)...)
r = append(r, detailResult[item.end:]...)
detailResult = r
switch item.tag {
case "load.computed":
detail += "=" + partRet
}

detail += subDetailsText + "]"
if len(m) == 1 && detail == "["+exprText+"]" {
detail = "" // 规则1.3
}
if len(detail) > 400 {
detail = "[略]"
}
writeBufStr(partRet + detail)
writeBuf(detailResult[item.end:])
detailResult = buf.Bytes()
}

return string(detailResult)
Expand Down Expand Up @@ -620,15 +667,21 @@ func (ctx *Context) evaluate() {
e.top++
case typeLoadName, typeLoadNameRaw, typeLoadNameWithDetail:
name := code.Value.(string)
val := ctx.LoadName(name, typeLoadNameRaw == code.T, true)
var val *VMValue
if typeLoadNameWithDetail == code.T {
detail := &details[len(details)-1]
detail.Tag = "load"
detail.Text = ""

val = ctx.LoadNameWithDetail(name, typeLoadNameRaw == code.T, true, detail)
detail.Ret = val
} else {
val = ctx.LoadName(name, typeLoadNameRaw == code.T, true)
}
if ctx.Error != nil {
return
}
if typeLoadNameWithDetail == code.T {
details[len(details)-1].Tag = "load"
details[len(details)-1].Ret = val
details[len(details)-1].Text = ""
}

if ctx.Config.HookFuncValueLoadOverwrite != nil {
oldRet := details[len(details)-1].Ret
val = ctx.Config.HookFuncValueLoadOverwrite(name, val, &details[len(details)-1])
Expand Down
54 changes: 52 additions & 2 deletions rollvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1456,7 +1456,7 @@ func TestIsDiceCalculateExists3(t *testing.T) {
func TestDiceExprIndexBug(t *testing.T) {
// 12.1 于言诺发现,如 2d(3d1) 会被错误计算为 9[2d(3d1)=9=3+3+3,3d1=3]
// 经查原因为Dice字节指令执行时,并未将骰子栈正确出栈
reResult := regexp.MustCompile(`2d\(3d1\)=(\d+)=(\d+)\+(\d+),`)
reResult := regexp.MustCompile(`2d\(3d1\)=(\d+)\+(\d+),`)

vm := NewVM()
err := vm.Run("2d(3d1)")
Expand Down Expand Up @@ -1543,7 +1543,7 @@ func TestNameDetailBug(t *testing.T) {
err := vm.Run(`a = 1;a `)
if assert.NoError(t, err) {
// TODO: 后面的空格
assert.Equal(t, "a = 1;1[a=1] ", vm.GetDetailText())
assert.Equal(t, "a = 1;1 ", vm.GetDetailText())
}
}

Expand Down Expand Up @@ -1730,3 +1730,53 @@ func TestDicePushDefaultExpr(t *testing.T) {
assert.True(t, vm.Ret.MustReadInt() >= 2)
}
}

func TestDetailTextComputed(t *testing.T) {
vm := NewVM()
vm.Attrs.Store("a", NewComputedVal("4d1"))
err := vm.Run("a")
if assert.NoError(t, err) {
assert.Equal(t, "4[a=4d1=4]", vm.GetDetailText())
}
}

func TestDetailTextComputed2(t *testing.T) {
vm := NewVM()
vm.Attrs.Store("a", NewComputedVal("4d(1d1)"))
err := vm.Run("a")
if assert.NoError(t, err) {
assert.Equal(t, "4[a=4d(1d1)=4]", vm.GetDetailText())
}
}

func TestDetailText1(t *testing.T) {
vm := NewVM()
err := vm.Run("(6d1)d1")
if assert.NoError(t, err) {
assert.Equal(t, "6[(6d1)d1=1+1+1+1+1+1,6d1=6]", vm.GetDetailText())
}
}

func TestDetailTextRule13(t *testing.T) {
vm := NewVM()
err := vm.Run("d1")
if assert.NoError(t, err) {
assert.Equal(t, "1", vm.GetDetailText())
}
}

func TestDetailText2(t *testing.T) {
vm := NewVM()
err := vm.Run("2d1")
if assert.NoError(t, err) {
assert.Equal(t, "2[2d1=1+1]", vm.GetDetailText())
}
}

func TestDetailText3(t *testing.T) {
vm := NewVM()
err := vm.Run("(2d1)d1")
if assert.NoError(t, err) {
assert.Equal(t, "2[(2d1)d1=1+1,2d1=2]", vm.GetDetailText())
}
}
51 changes: 26 additions & 25 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (ctx *Context) loadInnerVar(name string) *VMValue {
return builtinValues[name]
}

func (ctx *Context) LoadNameGlobal(name string, isRaw bool) *VMValue {
func (ctx *Context) LoadNameGlobalWithDetail(name string, isRaw bool, detail *BufferSpan) *VMValue {
var loadFunc func(name string) *VMValue
if loadFunc == nil {
loadFunc = ctx.GlobalValueLoadFunc
Expand All @@ -231,7 +231,7 @@ func (ctx *Context) LoadNameGlobal(name string, isRaw bool) *VMValue {
val := loadFunc(name)
if val != nil {
if !isRaw && val.TypeId == VMTypeComputedValue {
val = val.ComputedExecute(ctx)
val = val.ComputedExecute(ctx, detail)
if ctx.Error != nil {
return nil
}
Expand All @@ -253,15 +253,19 @@ func (ctx *Context) LoadNameGlobal(name string, isRaw bool) *VMValue {
val = NewNullVal()
}
if !isRaw && val.TypeId == VMTypeComputedValue {
val = val.ComputedExecute(ctx)
val = val.ComputedExecute(ctx, detail)
if ctx.Error != nil {
return nil
}
}
return val
}

func (ctx *Context) LoadNameLocal(name string, isRaw bool) *VMValue {
func (ctx *Context) LoadNameGlobal(name string, isRaw bool) *VMValue {
return ctx.LoadNameGlobalWithDetail(name, isRaw, nil)
}

func (ctx *Context) LoadNameLocalWithDetail(name string, isRaw bool, detail *BufferSpan) *VMValue {
// if ctx.currentThis != nil {
// return ctx.currentThis.AttrGet(ctx, name)
// } else {
Expand All @@ -271,7 +275,7 @@ func (ctx *Context) LoadNameLocal(name string, isRaw bool) *VMValue {
ret = NewNullVal()
}
if !isRaw && ret.TypeId == VMTypeComputedValue {
ret = ret.ComputedExecute(ctx)
ret = ret.ComputedExecute(ctx, detail)
if ctx.Error != nil {
return nil
}
Expand All @@ -281,7 +285,11 @@ func (ctx *Context) LoadNameLocal(name string, isRaw bool) *VMValue {
// }
}

func (ctx *Context) LoadName(name string, isRaw bool, useHook bool) *VMValue {
func (ctx *Context) LoadNameLocal(name string, isRaw bool) *VMValue {
return ctx.LoadNameLocalWithDetail(name, isRaw, nil)
}

func (ctx *Context) LoadNameWithDetail(name string, isRaw bool, useHook bool, detail *BufferSpan) *VMValue {
if useHook && ctx.Config.HookFuncValueLoad != nil {
var overwrite *VMValue
name, overwrite = ctx.Config.HookFuncValueLoad(name)
Expand All @@ -295,7 +303,7 @@ func (ctx *Context) LoadName(name string, isRaw bool, useHook bool) *VMValue {
// 先local再global
curCtx := ctx
for {
ret := curCtx.LoadNameLocal(name, isRaw)
ret := curCtx.LoadNameLocalWithDetail(name, isRaw, detail)

if curCtx.Error != nil {
ctx.Error = curCtx.Error
Expand All @@ -311,23 +319,11 @@ func (ctx *Context) LoadName(name string, isRaw bool, useHook bool) *VMValue {
}
}

return ctx.LoadNameGlobal(name, isRaw)
// if ctx.GlobalValueLoadFunc != nil {
// Ret := ctx.GlobalValueLoadFunc(name)
// if ctx.Error != nil {
// return nil
// }
// if Ret != nil {
// if !isRaw && Ret.TypeId == VMTypeComputedValue {
// Ret = Ret.ComputedExecute(ctx)
// if ctx.Error != nil {
// return nil
// }
// }
// return Ret
// }
// }
// return VMValueNewUndefined()
return ctx.LoadNameGlobalWithDetail(name, isRaw, detail)
}

func (ctx *Context) LoadName(name string, isRaw bool, useHook bool) *VMValue {
return ctx.LoadNameWithDetail(name, isRaw, useHook, nil)
}

// StoreName 储存变量
Expand Down Expand Up @@ -1396,7 +1392,7 @@ func (v *VMValue) GetTypeName() string {
return "unknown"
}

func (v *VMValue) ComputedExecute(ctx *Context) *VMValue {
func (v *VMValue) ComputedExecute(ctx *Context, detail *BufferSpan) *VMValue {
cd, _ := v.ReadComputed()

vm := NewVM()
Expand Down Expand Up @@ -1444,6 +1440,11 @@ func (v *VMValue) ComputedExecute(ctx *Context) *VMValue {

ctx.NumOpCount = vm.NumOpCount
ctx.IsComputedLoaded = true

if detail != nil {
detail.Tag = "load.computed"
detail.Text = cd.Expr
}
return ret
}

Expand Down

0 comments on commit fad9003

Please sign in to comment.