Skip to content

Commit

Permalink
core!: 1) improve buffer helper functions: obstart, obend and `fl…
Browse files Browse the repository at this point in the history
…ush`; 2) improve mixed `Text` type as safe string value; 3) improve custom non `Text` object write handler.
  • Loading branch information
moisespsena committed Nov 21, 2023
1 parent f2d9400 commit d2c4b3e
Show file tree
Hide file tree
Showing 14 changed files with 444 additions and 98 deletions.
2 changes: 2 additions & 0 deletions builtin_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var (
TFloat,
TDecimal,
TChar,
TText,
TString,
TBytes,
TBuffer,
Expand Down Expand Up @@ -133,6 +134,7 @@ func init() {
TFloat = RegisterBuiltinType(BuiltinFloat, "float", Float(0), funcPf64RO(builtinFloatFunc))
TDecimal = RegisterBuiltinType(BuiltinDecimal, "decimal", Decimal{}, funcPOROe(builtinDecimalFunc))
TChar = RegisterBuiltinType(BuiltinChar, "char", Char(0), funcPOROe(builtinCharFunc))
TText = RegisterBuiltinType(BuiltinText, "text", Text(""), builtinTextFunc)
TString = RegisterBuiltinType(BuiltinString, "string", String(""), builtinStringFunc)
TBytes = RegisterBuiltinType(BuiltinBytes, "bytes", Bytes{}, builtinBytesFunc)
TBuffer = RegisterBuiltinType(BuiltinBuffer, "buffer", Buffer{}, builtinBufferFunc)
Expand Down
151 changes: 126 additions & 25 deletions builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
BuiltinFloat
BuiltinDecimal
BuiltinChar
BuiltinText
BuiltinString
BuiltinBytes
BuiltinArray
Expand Down Expand Up @@ -81,6 +82,9 @@ const (
BuiltinItems
BuiltinVMPushWriter
BuiltinVMPopWriter
BuiltinOBStart
BuiltinOBEnd
BuiltinFlush

BuiltinIs
BuiltinIsError
Expand Down Expand Up @@ -186,6 +190,9 @@ var BuiltinsMap = map[string]BuiltinType{

"vmPushWriter": BuiltinVMPushWriter,
"vmPopWriter": BuiltinVMPopWriter,
"obstart": BuiltinOBStart,
"obend": BuiltinOBEnd,
"flush": BuiltinFlush,
"DISCARD_WRITER": BuiltinDiscardWriter,
}

Expand Down Expand Up @@ -248,10 +255,6 @@ var BuiltinObjects = map[BuiltinType]Object{
Name: "typeName",
Value: funcPORO(builtinTypeNameFunc),
},
BuiltinWrite: &BuiltinFunction{
Name: "write",
Value: builtinWriteFunc,
},
BuiltinPrint: &BuiltinFunction{
Name: "print",
Value: builtinPrintFunc,
Expand Down Expand Up @@ -372,6 +375,18 @@ var BuiltinObjects = map[BuiltinType]Object{
Name: "vmPopWriter",
Value: builtinVMPopWriterFunc,
},
BuiltinOBStart: &BuiltinFunction{
Name: "obstart",
Value: builtinOBStartFunc,
},
BuiltinOBEnd: &BuiltinFunction{
Name: "obend",
Value: builtinOBEndFunc,
},
BuiltinFlush: &BuiltinFunction{
Name: "flush",
Value: builtinFlushFunc,
},

BuiltinWrongNumArgumentsError: ErrWrongNumArguments,
BuiltinInvalidOperatorError: ErrInvalidOperator,
Expand All @@ -388,6 +403,10 @@ var BuiltinObjects = map[BuiltinType]Object{
}

func init() {
BuiltinObjects[BuiltinWrite] = &BuiltinFunction{
Name: "write",
Value: builtinWriteFunc,
}
BuiltinObjects[BuiltinMap] = &BuiltinFunction{
Name: "map",
Value: builtinMapFunc,
Expand Down Expand Up @@ -945,6 +964,23 @@ func builtinCharFunc(arg Object) (Object, error) {
)
}

func builtinTextFunc(c Call) (ret Object, err error) {
if err := c.Args.CheckLen(1); err != nil {
return Nil, err
}

o := c.Args.Get(0)

if ts, _ := o.(ToStringer); ts != nil {
var s String
s, err = ts.Stringer(c)
ret = Text(s)
} else {
ret = Text(o.ToString())
}
return
}

func builtinStringFunc(c Call) (ret Object, err error) {
if err := c.Args.CheckLen(1); err != nil {
return Nil, err
Expand Down Expand Up @@ -1067,6 +1103,31 @@ func builtinWriteFunc(c Call) (ret Object, err error) {
w io.Writer = c.VM.StdOut
total Int
n int
write = func(w io.Writer, obj Object) (total Int, err error) {
var n int
switch t := obj.(type) {
case Text:
n, err = w.Write([]byte(t))
case String:
n, err = w.Write([]byte(t))
case Bytes:
n, err = w.Write(t)
case BytesConverter:
var b Bytes
if b, err = t.ToBytes(); err == nil {
n, err = w.Write(b)
}
case io.WriterTo:
var i64 int64
i64, err = t.WriteTo(w)
total = Int(i64)
default:
n, err = fmt.Fprint(w, t)
}
total += Int(n)
return
}
convert CallerObject
)

if err = c.Args.CheckMinLen(1); err != nil {
Expand All @@ -1079,27 +1140,56 @@ func builtinWriteFunc(c Call) (ret Object, err error) {
c.Args.Shift()
}

c.Args.Walk(func(i int, arg Object) (continueLoop bool) {
switch t := arg.(type) {
case String:
n, err = w.Write([]byte(t))
case Bytes:
n, err = w.Write(t)
case BytesConverter:
var b Bytes
if b, err = t.ToBytes(); err == nil {
n, err = w.Write(b)
if convertValue := c.NamedArgs.GetValueOrNil("convert"); convertValue != nil {
convert = convertValue.(CallerObject)
}

if convert == nil {
c.Args.Walk(func(i int, arg Object) (continueLoop bool) {
switch t := arg.(type) {
case Text:
n, err = w.Write([]byte(t))
total += Int(n)
default:
total, err = write(w, arg)
}
case io.WriterTo:
var i64 int64
i64, err = t.WriteTo(w)
total = Int(i64)
default:
n, err = fmt.Fprint(w, arg)
return err == nil
})
} else {
var (
convertCallArgs = Array{
NewWriter(w),
&Function{
Value: func(c Call) (_ Object, err error) {
var i Int
i, err = write(c.Args.MustGet(0).(Writer), c.Args.MustGet(1))
return i, err
},
},
nil,
}
caller VMCaller
)
if caller, err = NewInvoker(c.VM, convert).Caller(Args{convertCallArgs}, nil); err != nil {
return
}
total += Int(n)
return err == nil
})

c.Args.Walk(func(i int, arg Object) (continueLoop bool) {
switch t := arg.(type) {
case Text:
n, err = w.Write([]byte(t))
total += Int(n)
default:
var iO Object
convertCallArgs[2] = t
iO, err = caller.Call()
if i, ok := iO.(Int); ok {
total += i
}
}
return err == nil
})
}

return total, err
}
Expand Down Expand Up @@ -1591,8 +1681,19 @@ func builtinVMPushWriterFunc(c Call) (ret Object, err error) {
}

func builtinVMPopWriterFunc(c Call) (ret Object, err error) {
c.VM.StdOut.Pop()
return Nil, nil
return c.VM.StdOut.Pop(), nil
}

func builtinOBStartFunc(c Call) (ret Object, err error) {
return builtinVMPushWriterFunc(Call{VM: c.VM, Args: Args{Array{&Buffer{}}}})
}

func builtinOBEndFunc(c Call) (ret Object, err error) {
return c.VM.StdOut.Pop(), nil
}

func builtinFlushFunc(c Call) (Object, error) {
return c.VM.StdOut.Flush()
}

func builtinWrapFunc(c Call) (ret Object, err error) {
Expand Down
6 changes: 6 additions & 0 deletions call.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ func (o Args) Get(n int) (v Object) {
return
}

// MustGet returns the nth argument. If n is greater than the number of arguments,
// it returns the nth variadic argument or Nil.
func (o Args) MustGet(n int) Object {
return o.GetDefault(n, Nil)
}

func (o Args) GetIJ(n int) (i, j int, ok bool) {
var (
at int
Expand Down
57 changes: 36 additions & 21 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,22 @@ type (

// CompilerOptions represents customizable options for Compile().
CompilerOptions struct {
ModuleMap *ModuleMap
ModulePath string
Constants []Object
SymbolTable *SymbolTable
Trace io.Writer
TraceParser bool
TraceCompiler bool
TraceOptimizer bool
OptimizerMaxCycle int
OptimizeConst bool
OptimizeExpr bool
Mixed bool
MixedWriteFunction node.Expr
moduleStore *moduleStore
constsCache map[Object]int
ModuleMap *ModuleMap
ModulePath string
Constants []Object
SymbolTable *SymbolTable
Trace io.Writer
TraceParser bool
TraceCompiler bool
TraceOptimizer bool
OptimizerMaxCycle int
OptimizeConst bool
OptimizeExpr bool
Mixed bool
MixedWriteFunction node.Expr
MixedExprToTextFunc node.Expr
moduleStore *moduleStore
constsCache map[Object]int
}

// CompilerError represents a compiler error.
Expand Down Expand Up @@ -322,19 +323,28 @@ stmts:
for z, s := range stmt[i:j] {
switch t := s.(type) {
case *node.TextStmt:
exprs[z] = &node.StringLit{Value: t.Literal}
exprs[z] = &node.TextLit{StringLit: node.StringLit{Value: t.Literal}}
case *node.ExprToTextStmt:
exprs[z] = t.Expr
}
}

wf := c.opts.MixedWriteFunction
var (
wf = c.opts.MixedWriteFunction
na node.CallExprNamedArgs
)
if wf == nil {
wf = &node.Ident{Name: "write"}
}
if c.opts.MixedExprToTextFunc != nil {
na = *new(node.CallExprNamedArgs).AppendS("convert", c.opts.MixedExprToTextFunc)
}
err = c.compileCallExpr(&node.CallExpr{
Func: wf,
CallArgs: node.CallArgs{Args: node.CallExprArgs{Values: exprs}},
Func: wf,
CallArgs: node.CallArgs{
Args: node.CallExprArgs{Values: exprs},
NamedArgs: na,
},
})
if err != nil {
return
Expand Down Expand Up @@ -413,6 +423,8 @@ func (c *Compiler) Compile(nd ast.Node) error {
}
case *node.StringLit:
c.emit(nt, OpConstant, c.addConstant(String(nt.Value)))
case *node.TextLit:
c.emit(nt, OpConstant, c.addConstant(Text(nt.Value)))
case *node.CharLit:
c.emit(nt, OpConstant, c.addConstant(Char(nt.Value)))
case *node.NilLit:
Expand Down Expand Up @@ -483,12 +495,15 @@ func (c *Compiler) Compile(nd ast.Node) error {
case *node.CondExpr:
return c.compileCondExpr(nt)
case *node.TextStmt:
return c.Compile(&node.StringLit{Value: nt.Literal})
return c.Compile(&node.TextLit{StringLit: node.StringLit{Value: nt.Literal}})
case *node.EmptyStmt:
case *node.ConfigStmt:
if nt.Options.WriteFunc != nil {
c.opts.MixedWriteFunction = nt.Options.WriteFunc
}
if nt.Options.ExprToTextFunc != nil {
c.opts.MixedExprToTextFunc = nt.Options.ExprToTextFunc
}
case nil:
default:
return c.errorf(nt, `%[1]T "%[1]v" not implemented`, nt)
Expand Down Expand Up @@ -530,7 +545,7 @@ func (c *Compiler) addConstant(obj Object) (index int) {
}()

switch obj.(type) {
case Int, Uint, String, Bool, Float, Char, *NilType:
case Int, Uint, String, Text, Bool, Float, Char, *NilType:
i, ok := c.constsCache[obj]
if ok {
index = i
Expand Down
Loading

0 comments on commit d2c4b3e

Please sign in to comment.