forked from jshiffer/matterbridge
Add scripting (tengo) support for every incoming message (#731)
TengoModifyMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script. This script will receive every incoming message and can be used to modify the Username and the Text of that message. The script will have the following global variables: to modify: msgUsername and msgText to read: msgChannel and msgAccount The script is reloaded on every message, so you can modify the script on the fly. Example script can be found in https://github.com/42wim/matterbridge/tree/master/gateway/bench.tengo and https://github.com/42wim/matterbridge/tree/master/contrib/example.tengo The example below will check if the text contains blah and if so, it'll replace the text and the username of that message. text := import("text") if text.re_match("blah",msgText) { msgText="replaced by this" msgUsername="fakeuser" } More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and https://github.com/d5/tengo/blob/master/docs/stdlib.md
This commit is contained in:
parent
3190703dc8
commit
1bb39eba87
@ -282,6 +282,7 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
* xmpp - https://github.com/mattn/go-xmpp
|
* xmpp - https://github.com/mattn/go-xmpp
|
||||||
* whatsapp - https://github.com/Rhymen/go-whatsapp/
|
* whatsapp - https://github.com/Rhymen/go-whatsapp/
|
||||||
* zulip - https://github.com/ifo/gozulipbot
|
* zulip - https://github.com/ifo/gozulipbot
|
||||||
|
* tengo - https://github.com/d5/tengo
|
||||||
|
|
||||||
<!-- Links -->
|
<!-- Links -->
|
||||||
|
|
||||||
|
@ -129,6 +129,7 @@ type Protocol struct {
|
|||||||
SkipTLSVerify bool // IRC, mattermost
|
SkipTLSVerify bool // IRC, mattermost
|
||||||
StripNick bool // all protocols
|
StripNick bool // all protocols
|
||||||
SyncTopic bool // slack
|
SyncTopic bool // slack
|
||||||
|
TengoModifyMessage string // general
|
||||||
Team string // mattermost
|
Team string // mattermost
|
||||||
Token string // gitter, slack, discord, api
|
Token string // gitter, slack, discord, api
|
||||||
Topic string // zulip
|
Topic string // zulip
|
||||||
|
2
contrib/example.tengo
Normal file
2
contrib/example.tengo
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
text := import("text")
|
||||||
|
msgText=text.re_replace("matterbridge",msgText,"matterbridge (https://github.com/42wim/matterbridge)")
|
5
gateway/bench.tengo
Normal file
5
gateway/bench.tengo
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
text := import("text")
|
||||||
|
if text.re_match("blah",msgText) {
|
||||||
|
msgText="replaced by this"
|
||||||
|
msgUsername="fakeuser"
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package gateway
|
package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -8,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/d5/tengo/script"
|
||||||
"github.com/hashicorp/golang-lru"
|
"github.com/hashicorp/golang-lru"
|
||||||
"github.com/peterhellberg/emojilib"
|
"github.com/peterhellberg/emojilib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -334,6 +336,10 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gw *Gateway) modifyMessage(msg *config.Message) {
|
func (gw *Gateway) modifyMessage(msg *config.Message) {
|
||||||
|
if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil {
|
||||||
|
flog.Errorf("TengoModifyMessage failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// replace :emoji: to unicode
|
// replace :emoji: to unicode
|
||||||
msg.Text = emojilib.Replace(msg.Text)
|
msg.Text = emojilib.Replace(msg.Text)
|
||||||
|
|
||||||
@ -458,3 +464,28 @@ func getProtocol(msg *config.Message) string {
|
|||||||
p := strings.Split(msg.Account, ".")
|
p := strings.Split(msg.Account, ".")
|
||||||
return p[0]
|
return p[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func modifyMessageTengo(filename string, msg *config.Message) error {
|
||||||
|
if filename == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s := script.New(res)
|
||||||
|
_ = s.Add("msgText", msg.Text)
|
||||||
|
_ = s.Add("msgUsername", msg.Username)
|
||||||
|
_ = s.Add("msgAccount", msg.Account)
|
||||||
|
_ = s.Add("msgChannel", msg.Channel)
|
||||||
|
c, err := s.Compile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Text = c.Get("msgText").String()
|
||||||
|
msg.Username = c.Get("msgUsername").String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -499,3 +499,13 @@ func TestIgnoreNicks(t *testing.T) {
|
|||||||
assert.Equalf(t, testcase.output, output, "case '%s' failed", testname)
|
assert.Equalf(t, testcase.output, output, "case '%s' failed", testname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkTengo(b *testing.B) {
|
||||||
|
msg := &config.Message{Username: "user", Text: "blah testing", Account: "protocol.account", Channel: "mychannel"}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
err := modifyMessageTengo("bench.tengo", msg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
||||||
github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884
|
github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884
|
||||||
github.com/bwmarrin/discordgo v0.19.0
|
github.com/bwmarrin/discordgo v0.19.0
|
||||||
|
github.com/d5/tengo v1.9.2
|
||||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
||||||
|
2
go.sum
2
go.sum
@ -20,6 +20,8 @@ github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVO
|
|||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/d5/tengo v1.9.2 h1:UE/X8PYl7bLS4Ww2zGeh91nq5PTnkhe8ncgNeA5PK7k=
|
||||||
|
github.com/d5/tengo v1.9.2/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -1527,6 +1527,29 @@ MediaDownloadBlacklist=[".html$",".htm$"]
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
IgnoreFailureOnStart=false
|
IgnoreFailureOnStart=false
|
||||||
|
|
||||||
|
|
||||||
|
#TengoModifyMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
||||||
|
#This script will receive every incoming message and can be used to modify the Username and the Text of that message.
|
||||||
|
#The script will have the following global variables:
|
||||||
|
#to modify: msgUsername and msgText
|
||||||
|
#to read: msgChannel and msgAccount
|
||||||
|
#
|
||||||
|
#The script is reloaded on every message, so you can modify the script on the fly.
|
||||||
|
#
|
||||||
|
#Example script can be found in https://github.com/42wim/matterbridge/tree/master/gateway/bench.tengo
|
||||||
|
#and https://github.com/42wim/matterbridge/tree/master/contrib/example.tengo
|
||||||
|
#
|
||||||
|
#The example below will check if the text contains blah and if so, it'll replace the text and the username of that message.
|
||||||
|
#text := import("text")
|
||||||
|
#if text.re_match("blah",msgText) {
|
||||||
|
# msgText="replaced by this"
|
||||||
|
# msgUsername="fakeuser"
|
||||||
|
#}
|
||||||
|
#More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and
|
||||||
|
#https://github.com/d5/tengo/blob/master/docs/stdlib.md
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
TengoModifyMessage="example.tengo"
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#Gateway configuration
|
#Gateway configuration
|
||||||
###################################################################
|
###################################################################
|
||||||
|
21
vendor/github.com/d5/tengo/LICENSE
generated
vendored
Normal file
21
vendor/github.com/d5/tengo/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Daniel Kang
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
35
vendor/github.com/d5/tengo/compiler/ast/array_lit.go
generated
vendored
Normal file
35
vendor/github.com/d5/tengo/compiler/ast/array_lit.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ArrayLit represents an array literal.
|
||||||
|
type ArrayLit struct {
|
||||||
|
Elements []Expr
|
||||||
|
LBrack source.Pos
|
||||||
|
RBrack source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ArrayLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *ArrayLit) Pos() source.Pos {
|
||||||
|
return e.LBrack
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *ArrayLit) End() source.Pos {
|
||||||
|
return e.RBrack + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ArrayLit) String() string {
|
||||||
|
var elements []string
|
||||||
|
for _, m := range e.Elements {
|
||||||
|
elements = append(elements, m.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "[" + strings.Join(elements, ", ") + "]"
|
||||||
|
}
|
40
vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go
generated
vendored
Normal file
40
vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssignStmt represents an assignment statement.
|
||||||
|
type AssignStmt struct {
|
||||||
|
LHS []Expr
|
||||||
|
RHS []Expr
|
||||||
|
Token token.Token
|
||||||
|
TokenPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AssignStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *AssignStmt) Pos() source.Pos {
|
||||||
|
return s.LHS[0].Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *AssignStmt) End() source.Pos {
|
||||||
|
return s.RHS[len(s.RHS)-1].End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AssignStmt) String() string {
|
||||||
|
var lhs, rhs []string
|
||||||
|
for _, e := range s.LHS {
|
||||||
|
lhs = append(lhs, e.String())
|
||||||
|
}
|
||||||
|
for _, e := range s.RHS {
|
||||||
|
rhs = append(rhs, e.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(lhs, ", ") + " " + s.Token.String() + " " + strings.Join(rhs, ", ")
|
||||||
|
}
|
5
vendor/github.com/d5/tengo/compiler/ast/ast.go
generated
vendored
Normal file
5
vendor/github.com/d5/tengo/compiler/ast/ast.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
const (
|
||||||
|
nullRep = "<null>"
|
||||||
|
)
|
25
vendor/github.com/d5/tengo/compiler/ast/bad_expr.go
generated
vendored
Normal file
25
vendor/github.com/d5/tengo/compiler/ast/bad_expr.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// BadExpr represents a bad expression.
|
||||||
|
type BadExpr struct {
|
||||||
|
From source.Pos
|
||||||
|
To source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BadExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *BadExpr) Pos() source.Pos {
|
||||||
|
return e.From
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *BadExpr) End() source.Pos {
|
||||||
|
return e.To
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BadExpr) String() string {
|
||||||
|
return "<bad expression>"
|
||||||
|
}
|
25
vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go
generated
vendored
Normal file
25
vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// BadStmt represents a bad statement.
|
||||||
|
type BadStmt struct {
|
||||||
|
From source.Pos
|
||||||
|
To source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BadStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *BadStmt) Pos() source.Pos {
|
||||||
|
return s.From
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *BadStmt) End() source.Pos {
|
||||||
|
return s.To
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BadStmt) String() string {
|
||||||
|
return "<bad statement>"
|
||||||
|
}
|
30
vendor/github.com/d5/tengo/compiler/ast/binary_expr.go
generated
vendored
Normal file
30
vendor/github.com/d5/tengo/compiler/ast/binary_expr.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BinaryExpr represents a binary operator expression.
|
||||||
|
type BinaryExpr struct {
|
||||||
|
LHS Expr
|
||||||
|
RHS Expr
|
||||||
|
Token token.Token
|
||||||
|
TokenPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *BinaryExpr) Pos() source.Pos {
|
||||||
|
return e.LHS.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *BinaryExpr) End() source.Pos {
|
||||||
|
return e.RHS.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BinaryExpr) String() string {
|
||||||
|
return "(" + e.LHS.String() + " " + e.Token.String() + " " + e.RHS.String() + ")"
|
||||||
|
}
|
35
vendor/github.com/d5/tengo/compiler/ast/block_stmt.go
generated
vendored
Normal file
35
vendor/github.com/d5/tengo/compiler/ast/block_stmt.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockStmt represents a block statement.
|
||||||
|
type BlockStmt struct {
|
||||||
|
Stmts []Stmt
|
||||||
|
LBrace source.Pos
|
||||||
|
RBrace source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BlockStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *BlockStmt) Pos() source.Pos {
|
||||||
|
return s.LBrace
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *BlockStmt) End() source.Pos {
|
||||||
|
return s.RBrace + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BlockStmt) String() string {
|
||||||
|
var list []string
|
||||||
|
for _, e := range s.Stmts {
|
||||||
|
list = append(list, e.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{" + strings.Join(list, "; ") + "}"
|
||||||
|
}
|
26
vendor/github.com/d5/tengo/compiler/ast/bool_lit.go
generated
vendored
Normal file
26
vendor/github.com/d5/tengo/compiler/ast/bool_lit.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// BoolLit represents a boolean literal.
|
||||||
|
type BoolLit struct {
|
||||||
|
Value bool
|
||||||
|
ValuePos source.Pos
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BoolLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *BoolLit) Pos() source.Pos {
|
||||||
|
return e.ValuePos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *BoolLit) End() source.Pos {
|
||||||
|
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BoolLit) String() string {
|
||||||
|
return e.Literal
|
||||||
|
}
|
38
vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go
generated
vendored
Normal file
38
vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BranchStmt represents a branch statement.
|
||||||
|
type BranchStmt struct {
|
||||||
|
Token token.Token
|
||||||
|
TokenPos source.Pos
|
||||||
|
Label *Ident
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BranchStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *BranchStmt) Pos() source.Pos {
|
||||||
|
return s.TokenPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *BranchStmt) End() source.Pos {
|
||||||
|
if s.Label != nil {
|
||||||
|
return s.Label.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.Pos(int(s.TokenPos) + len(s.Token.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BranchStmt) String() string {
|
||||||
|
var label string
|
||||||
|
if s.Label != nil {
|
||||||
|
label = " " + s.Label.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Token.String() + label
|
||||||
|
}
|
36
vendor/github.com/d5/tengo/compiler/ast/call_expr.go
generated
vendored
Normal file
36
vendor/github.com/d5/tengo/compiler/ast/call_expr.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CallExpr represents a function call expression.
|
||||||
|
type CallExpr struct {
|
||||||
|
Func Expr
|
||||||
|
LParen source.Pos
|
||||||
|
Args []Expr
|
||||||
|
RParen source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CallExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *CallExpr) Pos() source.Pos {
|
||||||
|
return e.Func.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *CallExpr) End() source.Pos {
|
||||||
|
return e.RParen + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CallExpr) String() string {
|
||||||
|
var args []string
|
||||||
|
for _, e := range e.Args {
|
||||||
|
args = append(args, e.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Func.String() + "(" + strings.Join(args, ", ") + ")"
|
||||||
|
}
|
26
vendor/github.com/d5/tengo/compiler/ast/char_lit.go
generated
vendored
Normal file
26
vendor/github.com/d5/tengo/compiler/ast/char_lit.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// CharLit represents a character literal.
|
||||||
|
type CharLit struct {
|
||||||
|
Value rune
|
||||||
|
ValuePos source.Pos
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CharLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *CharLit) Pos() source.Pos {
|
||||||
|
return e.ValuePos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *CharLit) End() source.Pos {
|
||||||
|
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CharLit) String() string {
|
||||||
|
return e.Literal
|
||||||
|
}
|
30
vendor/github.com/d5/tengo/compiler/ast/cond_expr.go
generated
vendored
Normal file
30
vendor/github.com/d5/tengo/compiler/ast/cond_expr.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CondExpr represents a ternary conditional expression.
|
||||||
|
type CondExpr struct {
|
||||||
|
Cond Expr
|
||||||
|
True Expr
|
||||||
|
False Expr
|
||||||
|
QuestionPos source.Pos
|
||||||
|
ColonPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CondExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *CondExpr) Pos() source.Pos {
|
||||||
|
return e.Cond.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *CondExpr) End() source.Pos {
|
||||||
|
return e.False.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CondExpr) String() string {
|
||||||
|
return "(" + e.Cond.String() + " ? " + e.True.String() + " : " + e.False.String() + ")"
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// EmptyStmt represents an empty statement.
|
||||||
|
type EmptyStmt struct {
|
||||||
|
Semicolon source.Pos
|
||||||
|
Implicit bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EmptyStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *EmptyStmt) Pos() source.Pos {
|
||||||
|
return s.Semicolon
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *EmptyStmt) End() source.Pos {
|
||||||
|
if s.Implicit {
|
||||||
|
return s.Semicolon
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Semicolon + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EmptyStmt) String() string {
|
||||||
|
return ";"
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/error_expr.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/error_expr.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorExpr represents an error expression
|
||||||
|
type ErrorExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
ErrorPos source.Pos
|
||||||
|
LParen source.Pos
|
||||||
|
RParen source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *ErrorExpr) Pos() source.Pos {
|
||||||
|
return e.ErrorPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *ErrorExpr) End() source.Pos {
|
||||||
|
return e.RParen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorExpr) String() string {
|
||||||
|
return "error(" + e.Expr.String() + ")"
|
||||||
|
}
|
27
vendor/github.com/d5/tengo/compiler/ast/export_stmt.go
generated
vendored
Normal file
27
vendor/github.com/d5/tengo/compiler/ast/export_stmt.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExportStmt represents an export statement.
|
||||||
|
type ExportStmt struct {
|
||||||
|
ExportPos source.Pos
|
||||||
|
Result Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExportStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *ExportStmt) Pos() source.Pos {
|
||||||
|
return s.ExportPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *ExportStmt) End() source.Pos {
|
||||||
|
return s.Result.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExportStmt) String() string {
|
||||||
|
return "export " + s.Result.String()
|
||||||
|
}
|
7
vendor/github.com/d5/tengo/compiler/ast/expr.go
generated
vendored
Normal file
7
vendor/github.com/d5/tengo/compiler/ast/expr.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
// Expr represents an expression node in the AST.
|
||||||
|
type Expr interface {
|
||||||
|
Node
|
||||||
|
exprNode()
|
||||||
|
}
|
24
vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go
generated
vendored
Normal file
24
vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// ExprStmt represents an expression statement.
|
||||||
|
type ExprStmt struct {
|
||||||
|
Expr Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExprStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *ExprStmt) Pos() source.Pos {
|
||||||
|
return s.Expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *ExprStmt) End() source.Pos {
|
||||||
|
return s.Expr.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExprStmt) String() string {
|
||||||
|
return s.Expr.String()
|
||||||
|
}
|
32
vendor/github.com/d5/tengo/compiler/ast/file.go
generated
vendored
Normal file
32
vendor/github.com/d5/tengo/compiler/ast/file.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// File represents a file unit.
|
||||||
|
type File struct {
|
||||||
|
InputFile *source.File
|
||||||
|
Stmts []Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (n *File) Pos() source.Pos {
|
||||||
|
return source.Pos(n.InputFile.Base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (n *File) End() source.Pos {
|
||||||
|
return source.Pos(n.InputFile.Base + n.InputFile.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *File) String() string {
|
||||||
|
var stmts []string
|
||||||
|
for _, e := range n.Stmts {
|
||||||
|
stmts = append(stmts, e.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(stmts, "; ")
|
||||||
|
}
|
26
vendor/github.com/d5/tengo/compiler/ast/float_lit.go
generated
vendored
Normal file
26
vendor/github.com/d5/tengo/compiler/ast/float_lit.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// FloatLit represents a floating point literal.
|
||||||
|
type FloatLit struct {
|
||||||
|
Value float64
|
||||||
|
ValuePos source.Pos
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FloatLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *FloatLit) Pos() source.Pos {
|
||||||
|
return e.ValuePos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *FloatLit) End() source.Pos {
|
||||||
|
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FloatLit) String() string {
|
||||||
|
return e.Literal
|
||||||
|
}
|
32
vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go
generated
vendored
Normal file
32
vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// ForInStmt represents a for-in statement.
|
||||||
|
type ForInStmt struct {
|
||||||
|
ForPos source.Pos
|
||||||
|
Key *Ident
|
||||||
|
Value *Ident
|
||||||
|
Iterable Expr
|
||||||
|
Body *BlockStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ForInStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *ForInStmt) Pos() source.Pos {
|
||||||
|
return s.ForPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *ForInStmt) End() source.Pos {
|
||||||
|
return s.Body.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ForInStmt) String() string {
|
||||||
|
if s.Value != nil {
|
||||||
|
return "for " + s.Key.String() + ", " + s.Value.String() + " in " + s.Iterable.String() + " " + s.Body.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "for " + s.Key.String() + " in " + s.Iterable.String() + " " + s.Body.String()
|
||||||
|
}
|
43
vendor/github.com/d5/tengo/compiler/ast/for_stmt.go
generated
vendored
Normal file
43
vendor/github.com/d5/tengo/compiler/ast/for_stmt.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// ForStmt represents a for statement.
|
||||||
|
type ForStmt struct {
|
||||||
|
ForPos source.Pos
|
||||||
|
Init Stmt
|
||||||
|
Cond Expr
|
||||||
|
Post Stmt
|
||||||
|
Body *BlockStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ForStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *ForStmt) Pos() source.Pos {
|
||||||
|
return s.ForPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *ForStmt) End() source.Pos {
|
||||||
|
return s.Body.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ForStmt) String() string {
|
||||||
|
var init, cond, post string
|
||||||
|
if s.Init != nil {
|
||||||
|
init = s.Init.String()
|
||||||
|
}
|
||||||
|
if s.Cond != nil {
|
||||||
|
cond = s.Cond.String() + " "
|
||||||
|
}
|
||||||
|
if s.Post != nil {
|
||||||
|
post = s.Post.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if init != "" || post != "" {
|
||||||
|
return "for " + init + " ; " + cond + " ; " + post + s.Body.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "for " + cond + s.Body.String()
|
||||||
|
}
|
25
vendor/github.com/d5/tengo/compiler/ast/func_lit.go
generated
vendored
Normal file
25
vendor/github.com/d5/tengo/compiler/ast/func_lit.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// FuncLit represents a function literal.
|
||||||
|
type FuncLit struct {
|
||||||
|
Type *FuncType
|
||||||
|
Body *BlockStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FuncLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *FuncLit) Pos() source.Pos {
|
||||||
|
return e.Type.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *FuncLit) End() source.Pos {
|
||||||
|
return e.Body.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FuncLit) String() string {
|
||||||
|
return "func" + e.Type.Params.String() + " " + e.Body.String()
|
||||||
|
}
|
25
vendor/github.com/d5/tengo/compiler/ast/func_type.go
generated
vendored
Normal file
25
vendor/github.com/d5/tengo/compiler/ast/func_type.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// FuncType represents a function type definition.
|
||||||
|
type FuncType struct {
|
||||||
|
FuncPos source.Pos
|
||||||
|
Params *IdentList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FuncType) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *FuncType) Pos() source.Pos {
|
||||||
|
return e.FuncPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *FuncType) End() source.Pos {
|
||||||
|
return e.Params.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FuncType) String() string {
|
||||||
|
return "func" + e.Params.String()
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/ident.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/ident.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// Ident represents an identifier.
|
||||||
|
type Ident struct {
|
||||||
|
Name string
|
||||||
|
NamePos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Ident) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *Ident) Pos() source.Pos {
|
||||||
|
return e.NamePos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *Ident) End() source.Pos {
|
||||||
|
return source.Pos(int(e.NamePos) + len(e.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Ident) String() string {
|
||||||
|
if e != nil {
|
||||||
|
return e.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullRep
|
||||||
|
}
|
58
vendor/github.com/d5/tengo/compiler/ast/ident_list.go
generated
vendored
Normal file
58
vendor/github.com/d5/tengo/compiler/ast/ident_list.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IdentList represents a list of identifiers.
|
||||||
|
type IdentList struct {
|
||||||
|
LParen source.Pos
|
||||||
|
List []*Ident
|
||||||
|
RParen source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (n *IdentList) Pos() source.Pos {
|
||||||
|
if n.LParen.IsValid() {
|
||||||
|
return n.LParen
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(n.List) > 0 {
|
||||||
|
return n.List[0].Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.NoPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (n *IdentList) End() source.Pos {
|
||||||
|
if n.RParen.IsValid() {
|
||||||
|
return n.RParen + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := len(n.List); l > 0 {
|
||||||
|
return n.List[l-1].End()
|
||||||
|
}
|
||||||
|
|
||||||
|
return source.NoPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumFields returns the number of fields.
|
||||||
|
func (n *IdentList) NumFields() int {
|
||||||
|
if n == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(n.List)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *IdentList) String() string {
|
||||||
|
var list []string
|
||||||
|
for _, e := range n.List {
|
||||||
|
list = append(list, e.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "(" + strings.Join(list, ", ") + ")"
|
||||||
|
}
|
40
vendor/github.com/d5/tengo/compiler/ast/if_stmt.go
generated
vendored
Normal file
40
vendor/github.com/d5/tengo/compiler/ast/if_stmt.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// IfStmt represents an if statement.
|
||||||
|
type IfStmt struct {
|
||||||
|
IfPos source.Pos
|
||||||
|
Init Stmt
|
||||||
|
Cond Expr
|
||||||
|
Body *BlockStmt
|
||||||
|
Else Stmt // else branch; or nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IfStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *IfStmt) Pos() source.Pos {
|
||||||
|
return s.IfPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *IfStmt) End() source.Pos {
|
||||||
|
if s.Else != nil {
|
||||||
|
return s.Else.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Body.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IfStmt) String() string {
|
||||||
|
var initStmt, elseStmt string
|
||||||
|
if s.Init != nil {
|
||||||
|
initStmt = s.Init.String() + "; "
|
||||||
|
}
|
||||||
|
if s.Else != nil {
|
||||||
|
elseStmt = " else " + s.Else.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "if " + initStmt + s.Cond.String() + " " + s.Body.String() + elseStmt
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImmutableExpr represents an immutable expression
|
||||||
|
type ImmutableExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
ErrorPos source.Pos
|
||||||
|
LParen source.Pos
|
||||||
|
RParen source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ImmutableExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *ImmutableExpr) Pos() source.Pos {
|
||||||
|
return e.ErrorPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *ImmutableExpr) End() source.Pos {
|
||||||
|
return e.RParen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ImmutableExpr) String() string {
|
||||||
|
return "immutable(" + e.Expr.String() + ")"
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/import_expr.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/import_expr.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImportExpr represents an import expression
|
||||||
|
type ImportExpr struct {
|
||||||
|
ModuleName string
|
||||||
|
Token token.Token
|
||||||
|
TokenPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ImportExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *ImportExpr) Pos() source.Pos {
|
||||||
|
return e.TokenPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *ImportExpr) End() source.Pos {
|
||||||
|
return source.Pos(int(e.TokenPos) + 10 + len(e.ModuleName)) // import("moduleName")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ImportExpr) String() string {
|
||||||
|
return `import("` + e.ModuleName + `")"`
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IncDecStmt represents increment or decrement statement.
|
||||||
|
type IncDecStmt struct {
|
||||||
|
Expr Expr
|
||||||
|
Token token.Token
|
||||||
|
TokenPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IncDecStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *IncDecStmt) Pos() source.Pos {
|
||||||
|
return s.Expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *IncDecStmt) End() source.Pos {
|
||||||
|
return source.Pos(int(s.TokenPos) + 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IncDecStmt) String() string {
|
||||||
|
return s.Expr.String() + s.Token.String()
|
||||||
|
}
|
32
vendor/github.com/d5/tengo/compiler/ast/index_expr.go
generated
vendored
Normal file
32
vendor/github.com/d5/tengo/compiler/ast/index_expr.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// IndexExpr represents an index expression.
|
||||||
|
type IndexExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
LBrack source.Pos
|
||||||
|
Index Expr
|
||||||
|
RBrack source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IndexExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *IndexExpr) Pos() source.Pos {
|
||||||
|
return e.Expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *IndexExpr) End() source.Pos {
|
||||||
|
return e.RBrack + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IndexExpr) String() string {
|
||||||
|
var index string
|
||||||
|
if e.Index != nil {
|
||||||
|
index = e.Index.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Expr.String() + "[" + index + "]"
|
||||||
|
}
|
26
vendor/github.com/d5/tengo/compiler/ast/int_lit.go
generated
vendored
Normal file
26
vendor/github.com/d5/tengo/compiler/ast/int_lit.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// IntLit represents an integer literal.
|
||||||
|
type IntLit struct {
|
||||||
|
Value int64
|
||||||
|
ValuePos source.Pos
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IntLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *IntLit) Pos() source.Pos {
|
||||||
|
return e.ValuePos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *IntLit) End() source.Pos {
|
||||||
|
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IntLit) String() string {
|
||||||
|
return e.Literal
|
||||||
|
}
|
27
vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go
generated
vendored
Normal file
27
vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// MapElementLit represents a map element.
|
||||||
|
type MapElementLit struct {
|
||||||
|
Key string
|
||||||
|
KeyPos source.Pos
|
||||||
|
ColonPos source.Pos
|
||||||
|
Value Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MapElementLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *MapElementLit) Pos() source.Pos {
|
||||||
|
return e.KeyPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *MapElementLit) End() source.Pos {
|
||||||
|
return e.Value.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MapElementLit) String() string {
|
||||||
|
return e.Key + ": " + e.Value.String()
|
||||||
|
}
|
35
vendor/github.com/d5/tengo/compiler/ast/map_lit.go
generated
vendored
Normal file
35
vendor/github.com/d5/tengo/compiler/ast/map_lit.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MapLit represents a map literal.
|
||||||
|
type MapLit struct {
|
||||||
|
LBrace source.Pos
|
||||||
|
Elements []*MapElementLit
|
||||||
|
RBrace source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MapLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *MapLit) Pos() source.Pos {
|
||||||
|
return e.LBrace
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *MapLit) End() source.Pos {
|
||||||
|
return e.RBrace + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MapLit) String() string {
|
||||||
|
var elements []string
|
||||||
|
for _, m := range e.Elements {
|
||||||
|
elements = append(elements, m.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{" + strings.Join(elements, ", ") + "}"
|
||||||
|
}
|
13
vendor/github.com/d5/tengo/compiler/ast/node.go
generated
vendored
Normal file
13
vendor/github.com/d5/tengo/compiler/ast/node.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// Node represents a node in the AST.
|
||||||
|
type Node interface {
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
Pos() source.Pos
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
End() source.Pos
|
||||||
|
// String returns a string representation of the node.
|
||||||
|
String() string
|
||||||
|
}
|
26
vendor/github.com/d5/tengo/compiler/ast/paren_expr.go
generated
vendored
Normal file
26
vendor/github.com/d5/tengo/compiler/ast/paren_expr.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// ParenExpr represents a parenthesis wrapped expression.
|
||||||
|
type ParenExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
LParen source.Pos
|
||||||
|
RParen source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ParenExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *ParenExpr) Pos() source.Pos {
|
||||||
|
return e.LParen
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *ParenExpr) End() source.Pos {
|
||||||
|
return e.RParen + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ParenExpr) String() string {
|
||||||
|
return "(" + e.Expr.String() + ")"
|
||||||
|
}
|
35
vendor/github.com/d5/tengo/compiler/ast/return_stmt.go
generated
vendored
Normal file
35
vendor/github.com/d5/tengo/compiler/ast/return_stmt.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReturnStmt represents a return statement.
|
||||||
|
type ReturnStmt struct {
|
||||||
|
ReturnPos source.Pos
|
||||||
|
Result Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReturnStmt) stmtNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (s *ReturnStmt) Pos() source.Pos {
|
||||||
|
return s.ReturnPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (s *ReturnStmt) End() source.Pos {
|
||||||
|
if s.Result != nil {
|
||||||
|
return s.Result.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ReturnPos + 6
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReturnStmt) String() string {
|
||||||
|
if s.Result != nil {
|
||||||
|
return "return " + s.Result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "return"
|
||||||
|
}
|
25
vendor/github.com/d5/tengo/compiler/ast/selector_expr.go
generated
vendored
Normal file
25
vendor/github.com/d5/tengo/compiler/ast/selector_expr.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// SelectorExpr represents a selector expression.
|
||||||
|
type SelectorExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
Sel Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SelectorExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *SelectorExpr) Pos() source.Pos {
|
||||||
|
return e.Expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *SelectorExpr) End() source.Pos {
|
||||||
|
return e.Sel.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SelectorExpr) String() string {
|
||||||
|
return e.Expr.String() + "." + e.Sel.String()
|
||||||
|
}
|
36
vendor/github.com/d5/tengo/compiler/ast/slice_expr.go
generated
vendored
Normal file
36
vendor/github.com/d5/tengo/compiler/ast/slice_expr.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// SliceExpr represents a slice expression.
|
||||||
|
type SliceExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
LBrack source.Pos
|
||||||
|
Low Expr
|
||||||
|
High Expr
|
||||||
|
RBrack source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SliceExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *SliceExpr) Pos() source.Pos {
|
||||||
|
return e.Expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *SliceExpr) End() source.Pos {
|
||||||
|
return e.RBrack + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SliceExpr) String() string {
|
||||||
|
var low, high string
|
||||||
|
if e.Low != nil {
|
||||||
|
low = e.Low.String()
|
||||||
|
}
|
||||||
|
if e.High != nil {
|
||||||
|
high = e.High.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Expr.String() + "[" + low + ":" + high + "]"
|
||||||
|
}
|
7
vendor/github.com/d5/tengo/compiler/ast/stmt.go
generated
vendored
Normal file
7
vendor/github.com/d5/tengo/compiler/ast/stmt.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
// Stmt represents a statement in the AST.
|
||||||
|
type Stmt interface {
|
||||||
|
Node
|
||||||
|
stmtNode()
|
||||||
|
}
|
26
vendor/github.com/d5/tengo/compiler/ast/string_lit.go
generated
vendored
Normal file
26
vendor/github.com/d5/tengo/compiler/ast/string_lit.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// StringLit represents a string literal.
|
||||||
|
type StringLit struct {
|
||||||
|
Value string
|
||||||
|
ValuePos source.Pos
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StringLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *StringLit) Pos() source.Pos {
|
||||||
|
return e.ValuePos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *StringLit) End() source.Pos {
|
||||||
|
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StringLit) String() string {
|
||||||
|
return e.Literal
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/compiler/ast/unary_expr.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/compiler/ast/unary_expr.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnaryExpr represents an unary operator expression.
|
||||||
|
type UnaryExpr struct {
|
||||||
|
Expr Expr
|
||||||
|
Token token.Token
|
||||||
|
TokenPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryExpr) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *UnaryExpr) Pos() source.Pos {
|
||||||
|
return e.Expr.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *UnaryExpr) End() source.Pos {
|
||||||
|
return e.Expr.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnaryExpr) String() string {
|
||||||
|
return "(" + e.Token.String() + e.Expr.String() + ")"
|
||||||
|
}
|
24
vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go
generated
vendored
Normal file
24
vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// UndefinedLit represents an undefined literal.
|
||||||
|
type UndefinedLit struct {
|
||||||
|
TokenPos source.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UndefinedLit) exprNode() {}
|
||||||
|
|
||||||
|
// Pos returns the position of first character belonging to the node.
|
||||||
|
func (e *UndefinedLit) Pos() source.Pos {
|
||||||
|
return e.TokenPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the position of first character immediately after the node.
|
||||||
|
func (e *UndefinedLit) End() source.Pos {
|
||||||
|
return e.TokenPos + 9 // len(undefined) == 9
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UndefinedLit) String() string {
|
||||||
|
return "undefined"
|
||||||
|
}
|
134
vendor/github.com/d5/tengo/compiler/bytecode.go
generated
vendored
Normal file
134
vendor/github.com/d5/tengo/compiler/bytecode.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bytecode is a compiled instructions and constants.
|
||||||
|
type Bytecode struct {
|
||||||
|
FileSet *source.FileSet
|
||||||
|
MainFunction *objects.CompiledFunction
|
||||||
|
Constants []objects.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode reads Bytecode data from the reader.
|
||||||
|
func (b *Bytecode) Decode(r io.Reader) error {
|
||||||
|
dec := gob.NewDecoder(r)
|
||||||
|
|
||||||
|
if err := dec.Decode(&b.FileSet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet
|
||||||
|
// as it's private field and not serialized by gob encoder/decoder.
|
||||||
|
|
||||||
|
if err := dec.Decode(&b.MainFunction); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dec.Decode(&b.Constants); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace Bool and Undefined with known value
|
||||||
|
for i, v := range b.Constants {
|
||||||
|
b.Constants[i] = cleanupObjects(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes Bytecode data to the writer.
|
||||||
|
func (b *Bytecode) Encode(w io.Writer) error {
|
||||||
|
enc := gob.NewEncoder(w)
|
||||||
|
|
||||||
|
if err := enc.Encode(b.FileSet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := enc.Encode(b.MainFunction); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// constants
|
||||||
|
return enc.Encode(b.Constants)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatInstructions returns human readable string representations of
|
||||||
|
// compiled instructions.
|
||||||
|
func (b *Bytecode) FormatInstructions() []string {
|
||||||
|
return FormatInstructions(b.MainFunction.Instructions, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatConstants returns human readable string representations of
|
||||||
|
// compiled constants.
|
||||||
|
func (b *Bytecode) FormatConstants() (output []string) {
|
||||||
|
for cidx, cn := range b.Constants {
|
||||||
|
switch cn := cn.(type) {
|
||||||
|
case *objects.CompiledFunction:
|
||||||
|
output = append(output, fmt.Sprintf("[% 3d] (Compiled Function|%p)", cidx, &cn))
|
||||||
|
for _, l := range FormatInstructions(cn.Instructions, 0) {
|
||||||
|
output = append(output, fmt.Sprintf(" %s", l))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupObjects(o objects.Object) objects.Object {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *objects.Bool:
|
||||||
|
if o.IsFalsy() {
|
||||||
|
return objects.FalseValue
|
||||||
|
}
|
||||||
|
return objects.TrueValue
|
||||||
|
case *objects.Undefined:
|
||||||
|
return objects.UndefinedValue
|
||||||
|
case *objects.Array:
|
||||||
|
for i, v := range o.Value {
|
||||||
|
o.Value[i] = cleanupObjects(v)
|
||||||
|
}
|
||||||
|
case *objects.Map:
|
||||||
|
for k, v := range o.Value {
|
||||||
|
o.Value[k] = cleanupObjects(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gob.Register(&source.FileSet{})
|
||||||
|
gob.Register(&source.File{})
|
||||||
|
gob.Register(&objects.Array{})
|
||||||
|
gob.Register(&objects.ArrayIterator{})
|
||||||
|
gob.Register(&objects.Bool{})
|
||||||
|
gob.Register(&objects.Break{})
|
||||||
|
gob.Register(&objects.BuiltinFunction{})
|
||||||
|
gob.Register(&objects.Bytes{})
|
||||||
|
gob.Register(&objects.Char{})
|
||||||
|
gob.Register(&objects.Closure{})
|
||||||
|
gob.Register(&objects.CompiledFunction{})
|
||||||
|
gob.Register(&objects.Continue{})
|
||||||
|
gob.Register(&objects.Error{})
|
||||||
|
gob.Register(&objects.Float{})
|
||||||
|
gob.Register(&objects.ImmutableArray{})
|
||||||
|
gob.Register(&objects.ImmutableMap{})
|
||||||
|
gob.Register(&objects.Int{})
|
||||||
|
gob.Register(&objects.Map{})
|
||||||
|
gob.Register(&objects.MapIterator{})
|
||||||
|
gob.Register(&objects.ReturnValue{})
|
||||||
|
gob.Register(&objects.String{})
|
||||||
|
gob.Register(&objects.StringIterator{})
|
||||||
|
gob.Register(&objects.Time{})
|
||||||
|
gob.Register(&objects.Undefined{})
|
||||||
|
gob.Register(&objects.UserFunction{})
|
||||||
|
}
|
12
vendor/github.com/d5/tengo/compiler/compilation_scope.go
generated
vendored
Normal file
12
vendor/github.com/d5/tengo/compiler/compilation_scope.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// CompilationScope represents a compiled instructions
|
||||||
|
// and the last two instructions that were emitted.
|
||||||
|
type CompilationScope struct {
|
||||||
|
instructions []byte
|
||||||
|
lastInstructions [2]EmittedInstruction
|
||||||
|
symbolInit map[string]bool
|
||||||
|
sourceMap map[int]source.Pos
|
||||||
|
}
|
731
vendor/github.com/d5/tengo/compiler/compiler.go
generated
vendored
Normal file
731
vendor/github.com/d5/tengo/compiler/compiler.go
generated
vendored
Normal file
@ -0,0 +1,731 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
"github.com/d5/tengo/stdlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compiler compiles the AST into a bytecode.
|
||||||
|
type Compiler struct {
|
||||||
|
file *source.File
|
||||||
|
parent *Compiler
|
||||||
|
moduleName string
|
||||||
|
constants []objects.Object
|
||||||
|
symbolTable *SymbolTable
|
||||||
|
scopes []CompilationScope
|
||||||
|
scopeIndex int
|
||||||
|
moduleLoader ModuleLoader
|
||||||
|
builtinModules map[string]bool
|
||||||
|
compiledModules map[string]*objects.CompiledFunction
|
||||||
|
loops []*Loop
|
||||||
|
loopIndex int
|
||||||
|
trace io.Writer
|
||||||
|
indent int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCompiler creates a Compiler.
|
||||||
|
// User can optionally provide the symbol table if one wants to add or remove
|
||||||
|
// some global- or builtin- scope symbols. If not (nil), Compile will create
|
||||||
|
// a new symbol table and use the default builtin functions. Likewise, standard
|
||||||
|
// modules can be explicitly provided if user wants to add or remove some modules.
|
||||||
|
// By default, Compile will use all the standard modules otherwise.
|
||||||
|
func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, builtinModules map[string]bool, trace io.Writer) *Compiler {
|
||||||
|
mainScope := CompilationScope{
|
||||||
|
symbolInit: make(map[string]bool),
|
||||||
|
sourceMap: make(map[int]source.Pos),
|
||||||
|
}
|
||||||
|
|
||||||
|
// symbol table
|
||||||
|
if symbolTable == nil {
|
||||||
|
symbolTable = NewSymbolTable()
|
||||||
|
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
symbolTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// builtin modules
|
||||||
|
if builtinModules == nil {
|
||||||
|
builtinModules = make(map[string]bool)
|
||||||
|
for name := range stdlib.Modules {
|
||||||
|
builtinModules[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Compiler{
|
||||||
|
file: file,
|
||||||
|
symbolTable: symbolTable,
|
||||||
|
constants: constants,
|
||||||
|
scopes: []CompilationScope{mainScope},
|
||||||
|
scopeIndex: 0,
|
||||||
|
loopIndex: -1,
|
||||||
|
trace: trace,
|
||||||
|
builtinModules: builtinModules,
|
||||||
|
compiledModules: make(map[string]*objects.CompiledFunction),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile compiles the AST node.
|
||||||
|
func (c *Compiler) Compile(node ast.Node) error {
|
||||||
|
if c.trace != nil {
|
||||||
|
if node != nil {
|
||||||
|
defer un(trace(c, fmt.Sprintf("%s (%s)", node.String(), reflect.TypeOf(node).Elem().Name())))
|
||||||
|
} else {
|
||||||
|
defer un(trace(c, "<nil>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node := node.(type) {
|
||||||
|
case *ast.File:
|
||||||
|
for _, stmt := range node.Stmts {
|
||||||
|
if err := c.Compile(stmt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.emit(node, OpPop)
|
||||||
|
|
||||||
|
case *ast.IncDecStmt:
|
||||||
|
op := token.AddAssign
|
||||||
|
if node.Token == token.Dec {
|
||||||
|
op = token.SubAssign
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.compileAssign(node, []ast.Expr{node.Expr}, []ast.Expr{&ast.IntLit{Value: 1}}, op)
|
||||||
|
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
if node.Token == token.LAnd || node.Token == token.LOr {
|
||||||
|
return c.compileLogical(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Token == token.Less {
|
||||||
|
if err := c.Compile(node.RHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpGreaterThan)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} else if node.Token == token.LessEq {
|
||||||
|
if err := c.Compile(node.RHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpGreaterThanEqual)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Compile(node.RHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node.Token {
|
||||||
|
case token.Add:
|
||||||
|
c.emit(node, OpAdd)
|
||||||
|
case token.Sub:
|
||||||
|
c.emit(node, OpSub)
|
||||||
|
case token.Mul:
|
||||||
|
c.emit(node, OpMul)
|
||||||
|
case token.Quo:
|
||||||
|
c.emit(node, OpDiv)
|
||||||
|
case token.Rem:
|
||||||
|
c.emit(node, OpRem)
|
||||||
|
case token.Greater:
|
||||||
|
c.emit(node, OpGreaterThan)
|
||||||
|
case token.GreaterEq:
|
||||||
|
c.emit(node, OpGreaterThanEqual)
|
||||||
|
case token.Equal:
|
||||||
|
c.emit(node, OpEqual)
|
||||||
|
case token.NotEqual:
|
||||||
|
c.emit(node, OpNotEqual)
|
||||||
|
case token.And:
|
||||||
|
c.emit(node, OpBAnd)
|
||||||
|
case token.Or:
|
||||||
|
c.emit(node, OpBOr)
|
||||||
|
case token.Xor:
|
||||||
|
c.emit(node, OpBXor)
|
||||||
|
case token.AndNot:
|
||||||
|
c.emit(node, OpBAndNot)
|
||||||
|
case token.Shl:
|
||||||
|
c.emit(node, OpBShiftLeft)
|
||||||
|
case token.Shr:
|
||||||
|
c.emit(node, OpBShiftRight)
|
||||||
|
default:
|
||||||
|
return c.errorf(node, "invalid binary operator: %s", node.Token.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.IntLit:
|
||||||
|
c.emit(node, OpConstant, c.addConstant(&objects.Int{Value: node.Value}))
|
||||||
|
|
||||||
|
case *ast.FloatLit:
|
||||||
|
c.emit(node, OpConstant, c.addConstant(&objects.Float{Value: node.Value}))
|
||||||
|
|
||||||
|
case *ast.BoolLit:
|
||||||
|
if node.Value {
|
||||||
|
c.emit(node, OpTrue)
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.StringLit:
|
||||||
|
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.Value}))
|
||||||
|
|
||||||
|
case *ast.CharLit:
|
||||||
|
c.emit(node, OpConstant, c.addConstant(&objects.Char{Value: node.Value}))
|
||||||
|
|
||||||
|
case *ast.UndefinedLit:
|
||||||
|
c.emit(node, OpNull)
|
||||||
|
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node.Token {
|
||||||
|
case token.Not:
|
||||||
|
c.emit(node, OpLNot)
|
||||||
|
case token.Sub:
|
||||||
|
c.emit(node, OpMinus)
|
||||||
|
case token.Xor:
|
||||||
|
c.emit(node, OpBComplement)
|
||||||
|
case token.Add:
|
||||||
|
// do nothing?
|
||||||
|
default:
|
||||||
|
return c.errorf(node, "invalid unary operator: %s", node.Token.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
// open new symbol table for the statement
|
||||||
|
c.symbolTable = c.symbolTable.Fork(true)
|
||||||
|
defer func() {
|
||||||
|
c.symbolTable = c.symbolTable.Parent(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if node.Init != nil {
|
||||||
|
if err := c.Compile(node.Init); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.Cond); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// first jump placeholder
|
||||||
|
jumpPos1 := c.emit(node, OpJumpFalsy, 0)
|
||||||
|
|
||||||
|
if err := c.Compile(node.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Else != nil {
|
||||||
|
// second jump placeholder
|
||||||
|
jumpPos2 := c.emit(node, OpJump, 0)
|
||||||
|
|
||||||
|
// update first jump offset
|
||||||
|
curPos := len(c.currentInstructions())
|
||||||
|
c.changeOperand(jumpPos1, curPos)
|
||||||
|
|
||||||
|
if err := c.Compile(node.Else); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update second jump offset
|
||||||
|
curPos = len(c.currentInstructions())
|
||||||
|
c.changeOperand(jumpPos2, curPos)
|
||||||
|
} else {
|
||||||
|
// update first jump offset
|
||||||
|
curPos := len(c.currentInstructions())
|
||||||
|
c.changeOperand(jumpPos1, curPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ForStmt:
|
||||||
|
return c.compileForStmt(node)
|
||||||
|
|
||||||
|
case *ast.ForInStmt:
|
||||||
|
return c.compileForInStmt(node)
|
||||||
|
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if node.Token == token.Break {
|
||||||
|
curLoop := c.currentLoop()
|
||||||
|
if curLoop == nil {
|
||||||
|
return c.errorf(node, "break not allowed outside loop")
|
||||||
|
}
|
||||||
|
pos := c.emit(node, OpJump, 0)
|
||||||
|
curLoop.Breaks = append(curLoop.Breaks, pos)
|
||||||
|
} else if node.Token == token.Continue {
|
||||||
|
curLoop := c.currentLoop()
|
||||||
|
if curLoop == nil {
|
||||||
|
return c.errorf(node, "continue not allowed outside loop")
|
||||||
|
}
|
||||||
|
pos := c.emit(node, OpJump, 0)
|
||||||
|
curLoop.Continues = append(curLoop.Continues, pos)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("invalid branch statement: %s", node.Token.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
for _, stmt := range node.Stmts {
|
||||||
|
if err := c.Compile(stmt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if err := c.compileAssign(node, node.LHS, node.RHS, node.Token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.Ident:
|
||||||
|
symbol, _, ok := c.symbolTable.Resolve(node.Name)
|
||||||
|
if !ok {
|
||||||
|
return c.errorf(node, "unresolved reference '%s'", node.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch symbol.Scope {
|
||||||
|
case ScopeGlobal:
|
||||||
|
c.emit(node, OpGetGlobal, symbol.Index)
|
||||||
|
case ScopeLocal:
|
||||||
|
c.emit(node, OpGetLocal, symbol.Index)
|
||||||
|
case ScopeBuiltin:
|
||||||
|
c.emit(node, OpGetBuiltin, symbol.Index)
|
||||||
|
case ScopeFree:
|
||||||
|
c.emit(node, OpGetFree, symbol.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ArrayLit:
|
||||||
|
for _, elem := range node.Elements {
|
||||||
|
if err := c.Compile(elem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpArray, len(node.Elements))
|
||||||
|
|
||||||
|
case *ast.MapLit:
|
||||||
|
for _, elt := range node.Elements {
|
||||||
|
// key
|
||||||
|
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: elt.Key}))
|
||||||
|
|
||||||
|
// value
|
||||||
|
if err := c.Compile(elt.Value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpMap, len(node.Elements)*2)
|
||||||
|
|
||||||
|
case *ast.SelectorExpr: // selector on RHS side
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.Sel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpIndex)
|
||||||
|
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.Index); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpIndex)
|
||||||
|
|
||||||
|
case *ast.SliceExpr:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Low != nil {
|
||||||
|
if err := c.Compile(node.Low); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.High != nil {
|
||||||
|
if err := c.Compile(node.High); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpSliceIndex)
|
||||||
|
|
||||||
|
case *ast.FuncLit:
|
||||||
|
c.enterScope()
|
||||||
|
|
||||||
|
for _, p := range node.Type.Params.List {
|
||||||
|
s := c.symbolTable.Define(p.Name)
|
||||||
|
|
||||||
|
// function arguments is not assigned directly.
|
||||||
|
s.LocalAssigned = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add OpReturn if function returns nothing
|
||||||
|
if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) {
|
||||||
|
c.emit(node, OpReturn)
|
||||||
|
}
|
||||||
|
|
||||||
|
freeSymbols := c.symbolTable.FreeSymbols()
|
||||||
|
numLocals := c.symbolTable.MaxSymbols()
|
||||||
|
instructions, sourceMap := c.leaveScope()
|
||||||
|
|
||||||
|
for _, s := range freeSymbols {
|
||||||
|
switch s.Scope {
|
||||||
|
case ScopeLocal:
|
||||||
|
if !s.LocalAssigned {
|
||||||
|
// Here, the closure is capturing a local variable that's not yet assigned its value.
|
||||||
|
// One example is a local recursive function:
|
||||||
|
//
|
||||||
|
// func() {
|
||||||
|
// foo := func(x) {
|
||||||
|
// // ..
|
||||||
|
// return foo(x-1)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// which translate into
|
||||||
|
//
|
||||||
|
// 0000 GETL 0
|
||||||
|
// 0002 CLOSURE ? 1
|
||||||
|
// 0006 DEFL 0
|
||||||
|
//
|
||||||
|
// . So the local variable (0) is being captured before it's assigned the value.
|
||||||
|
//
|
||||||
|
// Solution is to transform the code into something like this:
|
||||||
|
//
|
||||||
|
// func() {
|
||||||
|
// foo := undefined
|
||||||
|
// foo = func(x) {
|
||||||
|
// // ..
|
||||||
|
// return foo(x-1)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// that is equivalent to
|
||||||
|
//
|
||||||
|
// 0000 NULL
|
||||||
|
// 0001 DEFL 0
|
||||||
|
// 0003 GETL 0
|
||||||
|
// 0005 CLOSURE ? 1
|
||||||
|
// 0009 SETL 0
|
||||||
|
//
|
||||||
|
|
||||||
|
c.emit(node, OpNull)
|
||||||
|
c.emit(node, OpDefineLocal, s.Index)
|
||||||
|
|
||||||
|
s.LocalAssigned = true
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpGetLocal, s.Index)
|
||||||
|
case ScopeFree:
|
||||||
|
c.emit(node, OpGetFree, s.Index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compiledFunction := &objects.CompiledFunction{
|
||||||
|
Instructions: instructions,
|
||||||
|
NumLocals: numLocals,
|
||||||
|
NumParameters: len(node.Type.Params.List),
|
||||||
|
SourceMap: sourceMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(freeSymbols) > 0 {
|
||||||
|
c.emit(node, OpClosure, c.addConstant(compiledFunction), len(freeSymbols))
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpConstant, c.addConstant(compiledFunction))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
if c.symbolTable.Parent(true) == nil {
|
||||||
|
// outside the function
|
||||||
|
return c.errorf(node, "return not allowed outside function")
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Result == nil {
|
||||||
|
c.emit(node, OpReturn)
|
||||||
|
} else {
|
||||||
|
if err := c.Compile(node.Result); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpReturnValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CallExpr:
|
||||||
|
if err := c.Compile(node.Func); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range node.Args {
|
||||||
|
if err := c.Compile(arg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpCall, len(node.Args))
|
||||||
|
|
||||||
|
case *ast.ImportExpr:
|
||||||
|
if c.builtinModules[node.ModuleName] {
|
||||||
|
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.ModuleName}))
|
||||||
|
c.emit(node, OpGetBuiltinModule)
|
||||||
|
} else {
|
||||||
|
userMod, err := c.compileModule(node)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpConstant, c.addConstant(userMod))
|
||||||
|
c.emit(node, OpCall, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.ExportStmt:
|
||||||
|
// export statement must be in top-level scope
|
||||||
|
if c.scopeIndex != 0 {
|
||||||
|
return c.errorf(node, "export not allowed inside function")
|
||||||
|
}
|
||||||
|
|
||||||
|
// export statement is simply ignore when compiling non-module code
|
||||||
|
if c.parent == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Compile(node.Result); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpImmutable)
|
||||||
|
c.emit(node, OpReturnValue)
|
||||||
|
|
||||||
|
case *ast.ErrorExpr:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpError)
|
||||||
|
|
||||||
|
case *ast.ImmutableExpr:
|
||||||
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(node, OpImmutable)
|
||||||
|
|
||||||
|
case *ast.CondExpr:
|
||||||
|
if err := c.Compile(node.Cond); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// first jump placeholder
|
||||||
|
jumpPos1 := c.emit(node, OpJumpFalsy, 0)
|
||||||
|
|
||||||
|
if err := c.Compile(node.True); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// second jump placeholder
|
||||||
|
jumpPos2 := c.emit(node, OpJump, 0)
|
||||||
|
|
||||||
|
// update first jump offset
|
||||||
|
curPos := len(c.currentInstructions())
|
||||||
|
c.changeOperand(jumpPos1, curPos)
|
||||||
|
|
||||||
|
if err := c.Compile(node.False); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update second jump offset
|
||||||
|
curPos = len(c.currentInstructions())
|
||||||
|
c.changeOperand(jumpPos2, curPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytecode returns a compiled bytecode.
|
||||||
|
func (c *Compiler) Bytecode() *Bytecode {
|
||||||
|
return &Bytecode{
|
||||||
|
FileSet: c.file.Set(),
|
||||||
|
MainFunction: &objects.CompiledFunction{
|
||||||
|
Instructions: c.currentInstructions(),
|
||||||
|
SourceMap: c.currentSourceMap(),
|
||||||
|
},
|
||||||
|
Constants: c.constants,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetModuleLoader sets or replaces the current module loader.
|
||||||
|
// Note that the module loader is used for user modules,
|
||||||
|
// not for the standard modules.
|
||||||
|
func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) {
|
||||||
|
c.moduleLoader = moduleLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) fork(file *source.File, moduleName string, symbolTable *SymbolTable) *Compiler {
|
||||||
|
child := NewCompiler(file, symbolTable, nil, c.builtinModules, c.trace)
|
||||||
|
child.moduleName = moduleName // name of the module to compile
|
||||||
|
child.parent = c // parent to set to current compiler
|
||||||
|
child.moduleLoader = c.moduleLoader // share module loader
|
||||||
|
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) errorf(node ast.Node, format string, args ...interface{}) error {
|
||||||
|
return &Error{
|
||||||
|
fileSet: c.file.Set(),
|
||||||
|
node: node,
|
||||||
|
error: fmt.Errorf(format, args...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) addConstant(o objects.Object) int {
|
||||||
|
if c.parent != nil {
|
||||||
|
// module compilers will use their parent's constants array
|
||||||
|
return c.parent.addConstant(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.constants = append(c.constants, o)
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace(fmt.Sprintf("CONST %04d %s", len(c.constants)-1, o))
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(c.constants) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) addInstruction(b []byte) int {
|
||||||
|
posNewIns := len(c.currentInstructions())
|
||||||
|
|
||||||
|
c.scopes[c.scopeIndex].instructions = append(c.currentInstructions(), b...)
|
||||||
|
|
||||||
|
return posNewIns
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) setLastInstruction(op Opcode, pos int) {
|
||||||
|
c.scopes[c.scopeIndex].lastInstructions[1] = c.scopes[c.scopeIndex].lastInstructions[0]
|
||||||
|
|
||||||
|
c.scopes[c.scopeIndex].lastInstructions[0].Opcode = op
|
||||||
|
c.scopes[c.scopeIndex].lastInstructions[0].Position = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) lastInstructionIs(op Opcode) bool {
|
||||||
|
if len(c.currentInstructions()) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.scopes[c.scopeIndex].lastInstructions[0].Opcode == op
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) removeLastInstruction() {
|
||||||
|
lastPos := c.scopes[c.scopeIndex].lastInstructions[0].Position
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace(fmt.Sprintf("DELET %s",
|
||||||
|
FormatInstructions(c.scopes[c.scopeIndex].instructions[lastPos:], lastPos)[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.scopes[c.scopeIndex].instructions = c.currentInstructions()[:lastPos]
|
||||||
|
c.scopes[c.scopeIndex].lastInstructions[0] = c.scopes[c.scopeIndex].lastInstructions[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) replaceInstruction(pos int, inst []byte) {
|
||||||
|
copy(c.currentInstructions()[pos:], inst)
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace(fmt.Sprintf("REPLC %s",
|
||||||
|
FormatInstructions(c.scopes[c.scopeIndex].instructions[pos:], pos)[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) changeOperand(opPos int, operand ...int) {
|
||||||
|
op := Opcode(c.currentInstructions()[opPos])
|
||||||
|
inst := MakeInstruction(op, operand...)
|
||||||
|
|
||||||
|
c.replaceInstruction(opPos, inst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
|
||||||
|
filePos := source.NoPos
|
||||||
|
if node != nil {
|
||||||
|
filePos = node.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
inst := MakeInstruction(opcode, operands...)
|
||||||
|
pos := c.addInstruction(inst)
|
||||||
|
c.scopes[c.scopeIndex].sourceMap[pos] = filePos
|
||||||
|
c.setLastInstruction(opcode, pos)
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace(fmt.Sprintf("EMIT %s",
|
||||||
|
FormatInstructions(c.scopes[c.scopeIndex].instructions[pos:], pos)[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) printTrace(a ...interface{}) {
|
||||||
|
const (
|
||||||
|
dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
|
||||||
|
n = len(dots)
|
||||||
|
)
|
||||||
|
|
||||||
|
i := 2 * c.indent
|
||||||
|
for i > n {
|
||||||
|
_, _ = fmt.Fprint(c.trace, dots)
|
||||||
|
i -= n
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprint(c.trace, dots[0:i])
|
||||||
|
_, _ = fmt.Fprintln(c.trace, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func trace(c *Compiler, msg string) *Compiler {
|
||||||
|
c.printTrace(msg, "{")
|
||||||
|
c.indent++
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func un(c *Compiler) {
|
||||||
|
c.indent--
|
||||||
|
c.printTrace("}")
|
||||||
|
}
|
133
vendor/github.com/d5/tengo/compiler/compiler_assign.go
generated
vendored
Normal file
133
vendor/github.com/d5/tengo/compiler/compiler_assign.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.Token) error {
|
||||||
|
numLHS, numRHS := len(lhs), len(rhs)
|
||||||
|
if numLHS > 1 || numRHS > 1 {
|
||||||
|
return c.errorf(node, "tuple assignment not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve and compile left-hand side
|
||||||
|
ident, selectors := resolveAssignLHS(lhs[0])
|
||||||
|
numSel := len(selectors)
|
||||||
|
|
||||||
|
if op == token.Define && numSel > 0 {
|
||||||
|
// using selector on new variable does not make sense
|
||||||
|
return c.errorf(node, "operator ':=' not allowed with selector")
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol, depth, exists := c.symbolTable.Resolve(ident)
|
||||||
|
if op == token.Define {
|
||||||
|
if depth == 0 && exists {
|
||||||
|
return c.errorf(node, "'%s' redeclared in this block", ident)
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol = c.symbolTable.Define(ident)
|
||||||
|
} else {
|
||||||
|
if !exists {
|
||||||
|
return c.errorf(node, "unresolved reference '%s'", ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// +=, -=, *=, /=
|
||||||
|
if op != token.Assign && op != token.Define {
|
||||||
|
if err := c.Compile(lhs[0]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile RHSs
|
||||||
|
for _, expr := range rhs {
|
||||||
|
if err := c.Compile(expr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case token.AddAssign:
|
||||||
|
c.emit(node, OpAdd)
|
||||||
|
case token.SubAssign:
|
||||||
|
c.emit(node, OpSub)
|
||||||
|
case token.MulAssign:
|
||||||
|
c.emit(node, OpMul)
|
||||||
|
case token.QuoAssign:
|
||||||
|
c.emit(node, OpDiv)
|
||||||
|
case token.RemAssign:
|
||||||
|
c.emit(node, OpRem)
|
||||||
|
case token.AndAssign:
|
||||||
|
c.emit(node, OpBAnd)
|
||||||
|
case token.OrAssign:
|
||||||
|
c.emit(node, OpBOr)
|
||||||
|
case token.AndNotAssign:
|
||||||
|
c.emit(node, OpBAndNot)
|
||||||
|
case token.XorAssign:
|
||||||
|
c.emit(node, OpBXor)
|
||||||
|
case token.ShlAssign:
|
||||||
|
c.emit(node, OpBShiftLeft)
|
||||||
|
case token.ShrAssign:
|
||||||
|
c.emit(node, OpBShiftRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile selector expressions (right to left)
|
||||||
|
for i := numSel - 1; i >= 0; i-- {
|
||||||
|
if err := c.Compile(selectors[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch symbol.Scope {
|
||||||
|
case ScopeGlobal:
|
||||||
|
if numSel > 0 {
|
||||||
|
c.emit(node, OpSetSelGlobal, symbol.Index, numSel)
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpSetGlobal, symbol.Index)
|
||||||
|
}
|
||||||
|
case ScopeLocal:
|
||||||
|
if numSel > 0 {
|
||||||
|
c.emit(node, OpSetSelLocal, symbol.Index, numSel)
|
||||||
|
} else {
|
||||||
|
if op == token.Define && !symbol.LocalAssigned {
|
||||||
|
c.emit(node, OpDefineLocal, symbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpSetLocal, symbol.Index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark the symbol as local-assigned
|
||||||
|
symbol.LocalAssigned = true
|
||||||
|
case ScopeFree:
|
||||||
|
if numSel > 0 {
|
||||||
|
c.emit(node, OpSetSelFree, symbol.Index, numSel)
|
||||||
|
} else {
|
||||||
|
c.emit(node, OpSetFree, symbol.Index)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr) {
|
||||||
|
switch term := expr.(type) {
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
name, selectors = resolveAssignLHS(term.Expr)
|
||||||
|
selectors = append(selectors, term.Sel)
|
||||||
|
return
|
||||||
|
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
name, selectors = resolveAssignLHS(term.Expr)
|
||||||
|
selectors = append(selectors, term.Index)
|
||||||
|
|
||||||
|
case *ast.Ident:
|
||||||
|
name = term.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
181
vendor/github.com/d5/tengo/compiler/compiler_for.go
generated
vendored
Normal file
181
vendor/github.com/d5/tengo/compiler/compiler_for.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) compileForStmt(stmt *ast.ForStmt) error {
|
||||||
|
c.symbolTable = c.symbolTable.Fork(true)
|
||||||
|
defer func() {
|
||||||
|
c.symbolTable = c.symbolTable.Parent(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// init statement
|
||||||
|
if stmt.Init != nil {
|
||||||
|
if err := c.Compile(stmt.Init); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-condition position
|
||||||
|
preCondPos := len(c.currentInstructions())
|
||||||
|
|
||||||
|
// condition expression
|
||||||
|
postCondPos := -1
|
||||||
|
if stmt.Cond != nil {
|
||||||
|
if err := c.Compile(stmt.Cond); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// condition jump position
|
||||||
|
postCondPos = c.emit(stmt, OpJumpFalsy, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enter loop
|
||||||
|
loop := c.enterLoop()
|
||||||
|
|
||||||
|
// body statement
|
||||||
|
if err := c.Compile(stmt.Body); err != nil {
|
||||||
|
c.leaveLoop()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.leaveLoop()
|
||||||
|
|
||||||
|
// post-body position
|
||||||
|
postBodyPos := len(c.currentInstructions())
|
||||||
|
|
||||||
|
// post statement
|
||||||
|
if stmt.Post != nil {
|
||||||
|
if err := c.Compile(stmt.Post); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// back to condition
|
||||||
|
c.emit(stmt, OpJump, preCondPos)
|
||||||
|
|
||||||
|
// post-statement position
|
||||||
|
postStmtPos := len(c.currentInstructions())
|
||||||
|
if postCondPos >= 0 {
|
||||||
|
c.changeOperand(postCondPos, postStmtPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update all break/continue jump positions
|
||||||
|
for _, pos := range loop.Breaks {
|
||||||
|
c.changeOperand(pos, postStmtPos)
|
||||||
|
}
|
||||||
|
for _, pos := range loop.Continues {
|
||||||
|
c.changeOperand(pos, postBodyPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
||||||
|
c.symbolTable = c.symbolTable.Fork(true)
|
||||||
|
defer func() {
|
||||||
|
c.symbolTable = c.symbolTable.Parent(false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// for-in statement is compiled like following:
|
||||||
|
//
|
||||||
|
// for :it := iterator(iterable); :it.next(); {
|
||||||
|
// k, v := :it.get() // DEFINE operator
|
||||||
|
//
|
||||||
|
// ... body ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ":it" is a local variable but will be conflict with other user variables
|
||||||
|
// because character ":" is not allowed.
|
||||||
|
|
||||||
|
// init
|
||||||
|
// :it = iterator(iterable)
|
||||||
|
itSymbol := c.symbolTable.Define(":it")
|
||||||
|
if err := c.Compile(stmt.Iterable); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.emit(stmt, OpIteratorInit)
|
||||||
|
if itSymbol.Scope == ScopeGlobal {
|
||||||
|
c.emit(stmt, OpSetGlobal, itSymbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(stmt, OpDefineLocal, itSymbol.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-condition position
|
||||||
|
preCondPos := len(c.currentInstructions())
|
||||||
|
|
||||||
|
// condition
|
||||||
|
// :it.HasMore()
|
||||||
|
if itSymbol.Scope == ScopeGlobal {
|
||||||
|
c.emit(stmt, OpGetGlobal, itSymbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(stmt, OpGetLocal, itSymbol.Index)
|
||||||
|
}
|
||||||
|
c.emit(stmt, OpIteratorNext)
|
||||||
|
|
||||||
|
// condition jump position
|
||||||
|
postCondPos := c.emit(stmt, OpJumpFalsy, 0)
|
||||||
|
|
||||||
|
// enter loop
|
||||||
|
loop := c.enterLoop()
|
||||||
|
|
||||||
|
// assign key variable
|
||||||
|
if stmt.Key.Name != "_" {
|
||||||
|
keySymbol := c.symbolTable.Define(stmt.Key.Name)
|
||||||
|
if itSymbol.Scope == ScopeGlobal {
|
||||||
|
c.emit(stmt, OpGetGlobal, itSymbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(stmt, OpGetLocal, itSymbol.Index)
|
||||||
|
}
|
||||||
|
c.emit(stmt, OpIteratorKey)
|
||||||
|
if keySymbol.Scope == ScopeGlobal {
|
||||||
|
c.emit(stmt, OpSetGlobal, keySymbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(stmt, OpDefineLocal, keySymbol.Index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign value variable
|
||||||
|
if stmt.Value.Name != "_" {
|
||||||
|
valueSymbol := c.symbolTable.Define(stmt.Value.Name)
|
||||||
|
if itSymbol.Scope == ScopeGlobal {
|
||||||
|
c.emit(stmt, OpGetGlobal, itSymbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(stmt, OpGetLocal, itSymbol.Index)
|
||||||
|
}
|
||||||
|
c.emit(stmt, OpIteratorValue)
|
||||||
|
if valueSymbol.Scope == ScopeGlobal {
|
||||||
|
c.emit(stmt, OpSetGlobal, valueSymbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(stmt, OpDefineLocal, valueSymbol.Index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// body statement
|
||||||
|
if err := c.Compile(stmt.Body); err != nil {
|
||||||
|
c.leaveLoop()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.leaveLoop()
|
||||||
|
|
||||||
|
// post-body position
|
||||||
|
postBodyPos := len(c.currentInstructions())
|
||||||
|
|
||||||
|
// back to condition
|
||||||
|
c.emit(stmt, OpJump, preCondPos)
|
||||||
|
|
||||||
|
// post-statement position
|
||||||
|
postStmtPos := len(c.currentInstructions())
|
||||||
|
c.changeOperand(postCondPos, postStmtPos)
|
||||||
|
|
||||||
|
// update all break/continue jump positions
|
||||||
|
for _, pos := range loop.Breaks {
|
||||||
|
c.changeOperand(pos, postStmtPos)
|
||||||
|
}
|
||||||
|
for _, pos := range loop.Continues {
|
||||||
|
c.changeOperand(pos, postBodyPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
30
vendor/github.com/d5/tengo/compiler/compiler_logical.go
generated
vendored
Normal file
30
vendor/github.com/d5/tengo/compiler/compiler_logical.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) compileLogical(node *ast.BinaryExpr) error {
|
||||||
|
// left side term
|
||||||
|
if err := c.Compile(node.LHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// jump position
|
||||||
|
var jumpPos int
|
||||||
|
if node.Token == token.LAnd {
|
||||||
|
jumpPos = c.emit(node, OpAndJump, 0)
|
||||||
|
} else {
|
||||||
|
jumpPos = c.emit(node, OpOrJump, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// right side term
|
||||||
|
if err := c.Compile(node.RHS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.changeOperand(jumpPos, len(c.currentInstructions()))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
31
vendor/github.com/d5/tengo/compiler/compiler_loops.go
generated
vendored
Normal file
31
vendor/github.com/d5/tengo/compiler/compiler_loops.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
func (c *Compiler) enterLoop() *Loop {
|
||||||
|
loop := &Loop{}
|
||||||
|
|
||||||
|
c.loops = append(c.loops, loop)
|
||||||
|
c.loopIndex++
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace("LOOPE", c.loopIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return loop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) leaveLoop() {
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace("LOOPL", c.loopIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.loops = c.loops[:len(c.loops)-1]
|
||||||
|
c.loopIndex--
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) currentLoop() *Loop {
|
||||||
|
if c.loopIndex >= 0 {
|
||||||
|
return c.loops[c.loopIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
123
vendor/github.com/d5/tengo/compiler/compiler_module.go
generated
vendored
Normal file
123
vendor/github.com/d5/tengo/compiler/compiler_module.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/parser"
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) compileModule(expr *ast.ImportExpr) (*objects.CompiledFunction, error) {
|
||||||
|
compiledModule, exists := c.loadCompiledModule(expr.ModuleName)
|
||||||
|
if exists {
|
||||||
|
return compiledModule, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleName := expr.ModuleName
|
||||||
|
|
||||||
|
// read module source from loader
|
||||||
|
var moduleSrc []byte
|
||||||
|
if c.moduleLoader == nil {
|
||||||
|
// default loader: read from local file
|
||||||
|
if !strings.HasSuffix(moduleName, ".tengo") {
|
||||||
|
moduleName += ".tengo"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.checkCyclicImports(expr, moduleName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
moduleSrc, err = ioutil.ReadFile(moduleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, c.errorf(expr, "module file read error: %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := c.checkCyclicImports(expr, moduleName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
moduleSrc, err = c.moduleLoader(moduleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compiledModule, err := c.doCompileModule(moduleName, moduleSrc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.storeCompiledModule(moduleName, compiledModule)
|
||||||
|
|
||||||
|
return compiledModule, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) checkCyclicImports(node ast.Node, moduleName string) error {
|
||||||
|
if c.moduleName == moduleName {
|
||||||
|
return c.errorf(node, "cyclic module import: %s", moduleName)
|
||||||
|
} else if c.parent != nil {
|
||||||
|
return c.parent.checkCyclicImports(node, moduleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.CompiledFunction, error) {
|
||||||
|
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
|
||||||
|
p := parser.NewParser(modFile, src, nil)
|
||||||
|
file, err := p.ParseFile()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
symbolTable := NewSymbolTable()
|
||||||
|
|
||||||
|
// inherit builtin functions
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
s, _, ok := c.symbolTable.Resolve(fn.Name)
|
||||||
|
if ok && s.Scope == ScopeBuiltin {
|
||||||
|
symbolTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no global scope for the module
|
||||||
|
symbolTable = symbolTable.Fork(false)
|
||||||
|
|
||||||
|
// compile module
|
||||||
|
moduleCompiler := c.fork(modFile, moduleName, symbolTable)
|
||||||
|
if err := moduleCompiler.Compile(file); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add OpReturn (== export undefined) if export is missing
|
||||||
|
if !moduleCompiler.lastInstructionIs(OpReturnValue) {
|
||||||
|
moduleCompiler.emit(nil, OpReturn)
|
||||||
|
}
|
||||||
|
|
||||||
|
compiledFunc := moduleCompiler.Bytecode().MainFunction
|
||||||
|
compiledFunc.NumLocals = symbolTable.MaxSymbols()
|
||||||
|
|
||||||
|
return compiledFunc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) loadCompiledModule(moduleName string) (mod *objects.CompiledFunction, ok bool) {
|
||||||
|
if c.parent != nil {
|
||||||
|
return c.parent.loadCompiledModule(moduleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod, ok = c.compiledModules[moduleName]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) storeCompiledModule(moduleName string, module *objects.CompiledFunction) {
|
||||||
|
if c.parent != nil {
|
||||||
|
c.parent.storeCompiledModule(moduleName, module)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.compiledModules[moduleName] = module
|
||||||
|
}
|
43
vendor/github.com/d5/tengo/compiler/compiler_scopes.go
generated
vendored
Normal file
43
vendor/github.com/d5/tengo/compiler/compiler_scopes.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
func (c *Compiler) currentInstructions() []byte {
|
||||||
|
return c.scopes[c.scopeIndex].instructions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) currentSourceMap() map[int]source.Pos {
|
||||||
|
return c.scopes[c.scopeIndex].sourceMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) enterScope() {
|
||||||
|
scope := CompilationScope{
|
||||||
|
symbolInit: make(map[string]bool),
|
||||||
|
sourceMap: make(map[int]source.Pos),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.scopes = append(c.scopes, scope)
|
||||||
|
c.scopeIndex++
|
||||||
|
|
||||||
|
c.symbolTable = c.symbolTable.Fork(false)
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace("SCOPE", c.scopeIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) leaveScope() (instructions []byte, sourceMap map[int]source.Pos) {
|
||||||
|
instructions = c.currentInstructions()
|
||||||
|
sourceMap = c.currentSourceMap()
|
||||||
|
|
||||||
|
c.scopes = c.scopes[:len(c.scopes)-1]
|
||||||
|
c.scopeIndex--
|
||||||
|
|
||||||
|
c.symbolTable = c.symbolTable.Parent(true)
|
||||||
|
|
||||||
|
if c.trace != nil {
|
||||||
|
c.printTrace("SCOPL", c.scopeIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
8
vendor/github.com/d5/tengo/compiler/emitted_instruction.go
generated
vendored
Normal file
8
vendor/github.com/d5/tengo/compiler/emitted_instruction.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// EmittedInstruction represents an opcode
|
||||||
|
// with its emitted position.
|
||||||
|
type EmittedInstruction struct {
|
||||||
|
Opcode Opcode
|
||||||
|
Position int
|
||||||
|
}
|
20
vendor/github.com/d5/tengo/compiler/error.go
generated
vendored
Normal file
20
vendor/github.com/d5/tengo/compiler/error.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error represents a compiler error.
|
||||||
|
type Error struct {
|
||||||
|
fileSet *source.FileSet
|
||||||
|
node ast.Node
|
||||||
|
error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
filePos := e.fileSet.Position(e.node.Pos())
|
||||||
|
return fmt.Sprintf("Compile Error: %s\n\tat %s", e.error.Error(), filePos)
|
||||||
|
}
|
59
vendor/github.com/d5/tengo/compiler/instructions.go
generated
vendored
Normal file
59
vendor/github.com/d5/tengo/compiler/instructions.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeInstruction returns a bytecode for an opcode and the operands.
|
||||||
|
func MakeInstruction(opcode Opcode, operands ...int) []byte {
|
||||||
|
numOperands := OpcodeOperands[opcode]
|
||||||
|
|
||||||
|
totalLen := 1
|
||||||
|
for _, w := range numOperands {
|
||||||
|
totalLen += w
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction := make([]byte, totalLen, totalLen)
|
||||||
|
instruction[0] = byte(opcode)
|
||||||
|
|
||||||
|
offset := 1
|
||||||
|
for i, o := range operands {
|
||||||
|
width := numOperands[i]
|
||||||
|
switch width {
|
||||||
|
case 1:
|
||||||
|
instruction[offset] = byte(o)
|
||||||
|
case 2:
|
||||||
|
n := uint16(o)
|
||||||
|
instruction[offset] = byte(n >> 8)
|
||||||
|
instruction[offset+1] = byte(n)
|
||||||
|
}
|
||||||
|
offset += width
|
||||||
|
}
|
||||||
|
|
||||||
|
return instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatInstructions returns string representation of
|
||||||
|
// bytecode instructions.
|
||||||
|
func FormatInstructions(b []byte, posOffset int) []string {
|
||||||
|
var out []string
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for i < len(b) {
|
||||||
|
numOperands := OpcodeOperands[Opcode(b[i])]
|
||||||
|
operands, read := ReadOperands(numOperands, b[i+1:])
|
||||||
|
|
||||||
|
switch len(numOperands) {
|
||||||
|
case 0:
|
||||||
|
out = append(out, fmt.Sprintf("%04d %-7s", posOffset+i, OpcodeNames[Opcode(b[i])]))
|
||||||
|
case 1:
|
||||||
|
out = append(out, fmt.Sprintf("%04d %-7s %-5d", posOffset+i, OpcodeNames[Opcode(b[i])], operands[0]))
|
||||||
|
case 2:
|
||||||
|
out = append(out, fmt.Sprintf("%04d %-7s %-5d %-5d", posOffset+i, OpcodeNames[Opcode(b[i])], operands[0], operands[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1 + read
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
8
vendor/github.com/d5/tengo/compiler/loop.go
generated
vendored
Normal file
8
vendor/github.com/d5/tengo/compiler/loop.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// Loop represents a loop construct that
|
||||||
|
// the compiler uses to track the current loop.
|
||||||
|
type Loop struct {
|
||||||
|
Continues []int
|
||||||
|
Breaks []int
|
||||||
|
}
|
4
vendor/github.com/d5/tengo/compiler/module_loader.go
generated
vendored
Normal file
4
vendor/github.com/d5/tengo/compiler/module_loader.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// ModuleLoader should take a module name and return the module data.
|
||||||
|
type ModuleLoader func(moduleName string) ([]byte, error)
|
191
vendor/github.com/d5/tengo/compiler/opcodes.go
generated
vendored
Normal file
191
vendor/github.com/d5/tengo/compiler/opcodes.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// Opcode represents a single byte operation code.
|
||||||
|
type Opcode = byte
|
||||||
|
|
||||||
|
// List of opcodes
|
||||||
|
const (
|
||||||
|
OpConstant Opcode = iota // Load constant
|
||||||
|
OpAdd // Add
|
||||||
|
OpSub // Sub
|
||||||
|
OpMul // Multiply
|
||||||
|
OpDiv // Divide
|
||||||
|
OpRem // Remainder
|
||||||
|
OpBAnd // bitwise AND
|
||||||
|
OpBOr // bitwise OR
|
||||||
|
OpBXor // bitwise XOR
|
||||||
|
OpBShiftLeft // bitwise shift left
|
||||||
|
OpBShiftRight // bitwise shift right
|
||||||
|
OpBAndNot // bitwise AND NOT
|
||||||
|
OpBComplement // bitwise complement
|
||||||
|
OpPop // Pop
|
||||||
|
OpTrue // Push true
|
||||||
|
OpFalse // Push false
|
||||||
|
OpEqual // Equal ==
|
||||||
|
OpNotEqual // Not equal !=
|
||||||
|
OpGreaterThan // Greater than >=
|
||||||
|
OpGreaterThanEqual // Greater than or equal to >=
|
||||||
|
OpMinus // Minus -
|
||||||
|
OpLNot // Logical not !
|
||||||
|
OpJumpFalsy // Jump if falsy
|
||||||
|
OpAndJump // Logical AND jump
|
||||||
|
OpOrJump // Logical OR jump
|
||||||
|
OpJump // Jump
|
||||||
|
OpNull // Push null
|
||||||
|
OpArray // Array object
|
||||||
|
OpMap // Map object
|
||||||
|
OpError // Error object
|
||||||
|
OpImmutable // Immutable object
|
||||||
|
OpIndex // Index operation
|
||||||
|
OpSliceIndex // Slice operation
|
||||||
|
OpCall // Call function
|
||||||
|
OpReturn // Return
|
||||||
|
OpReturnValue // Return value
|
||||||
|
OpGetGlobal // Get global variable
|
||||||
|
OpSetGlobal // Set global variable
|
||||||
|
OpSetSelGlobal // Set global variable using selectors
|
||||||
|
OpGetLocal // Get local variable
|
||||||
|
OpSetLocal // Set local variable
|
||||||
|
OpDefineLocal // Define local variable
|
||||||
|
OpSetSelLocal // Set local variable using selectors
|
||||||
|
OpGetFree // Get free variables
|
||||||
|
OpSetFree // Set free variables
|
||||||
|
OpSetSelFree // Set free variables using selectors
|
||||||
|
OpGetBuiltin // Get builtin function
|
||||||
|
OpGetBuiltinModule // Get builtin module
|
||||||
|
OpClosure // Push closure
|
||||||
|
OpIteratorInit // Iterator init
|
||||||
|
OpIteratorNext // Iterator next
|
||||||
|
OpIteratorKey // Iterator key
|
||||||
|
OpIteratorValue // Iterator value
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpcodeNames is opcode names.
|
||||||
|
var OpcodeNames = [...]string{
|
||||||
|
OpConstant: "CONST",
|
||||||
|
OpPop: "POP",
|
||||||
|
OpTrue: "TRUE",
|
||||||
|
OpFalse: "FALSE",
|
||||||
|
OpAdd: "ADD",
|
||||||
|
OpSub: "SUB",
|
||||||
|
OpMul: "MUL",
|
||||||
|
OpDiv: "DIV",
|
||||||
|
OpRem: "REM",
|
||||||
|
OpBAnd: "AND",
|
||||||
|
OpBOr: "OR",
|
||||||
|
OpBXor: "XOR",
|
||||||
|
OpBAndNot: "ANDN",
|
||||||
|
OpBShiftLeft: "SHL",
|
||||||
|
OpBShiftRight: "SHR",
|
||||||
|
OpBComplement: "NEG",
|
||||||
|
OpEqual: "EQL",
|
||||||
|
OpNotEqual: "NEQ",
|
||||||
|
OpGreaterThan: "GTR",
|
||||||
|
OpGreaterThanEqual: "GEQ",
|
||||||
|
OpMinus: "NEG",
|
||||||
|
OpLNot: "NOT",
|
||||||
|
OpJumpFalsy: "JMPF",
|
||||||
|
OpAndJump: "ANDJMP",
|
||||||
|
OpOrJump: "ORJMP",
|
||||||
|
OpJump: "JMP",
|
||||||
|
OpNull: "NULL",
|
||||||
|
OpGetGlobal: "GETG",
|
||||||
|
OpSetGlobal: "SETG",
|
||||||
|
OpSetSelGlobal: "SETSG",
|
||||||
|
OpArray: "ARR",
|
||||||
|
OpMap: "MAP",
|
||||||
|
OpError: "ERROR",
|
||||||
|
OpImmutable: "IMMUT",
|
||||||
|
OpIndex: "INDEX",
|
||||||
|
OpSliceIndex: "SLICE",
|
||||||
|
OpCall: "CALL",
|
||||||
|
OpReturn: "RET",
|
||||||
|
OpReturnValue: "RETVAL",
|
||||||
|
OpGetLocal: "GETL",
|
||||||
|
OpSetLocal: "SETL",
|
||||||
|
OpDefineLocal: "DEFL",
|
||||||
|
OpSetSelLocal: "SETSL",
|
||||||
|
OpGetBuiltin: "BUILTIN",
|
||||||
|
OpGetBuiltinModule: "BLTMOD",
|
||||||
|
OpClosure: "CLOSURE",
|
||||||
|
OpGetFree: "GETF",
|
||||||
|
OpSetFree: "SETF",
|
||||||
|
OpSetSelFree: "SETSF",
|
||||||
|
OpIteratorInit: "ITER",
|
||||||
|
OpIteratorNext: "ITNXT",
|
||||||
|
OpIteratorKey: "ITKEY",
|
||||||
|
OpIteratorValue: "ITVAL",
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpcodeOperands is the number of operands.
|
||||||
|
var OpcodeOperands = [...][]int{
|
||||||
|
OpConstant: {2},
|
||||||
|
OpPop: {},
|
||||||
|
OpTrue: {},
|
||||||
|
OpFalse: {},
|
||||||
|
OpAdd: {},
|
||||||
|
OpSub: {},
|
||||||
|
OpMul: {},
|
||||||
|
OpDiv: {},
|
||||||
|
OpRem: {},
|
||||||
|
OpBAnd: {},
|
||||||
|
OpBOr: {},
|
||||||
|
OpBXor: {},
|
||||||
|
OpBAndNot: {},
|
||||||
|
OpBShiftLeft: {},
|
||||||
|
OpBShiftRight: {},
|
||||||
|
OpBComplement: {},
|
||||||
|
OpEqual: {},
|
||||||
|
OpNotEqual: {},
|
||||||
|
OpGreaterThan: {},
|
||||||
|
OpGreaterThanEqual: {},
|
||||||
|
OpMinus: {},
|
||||||
|
OpLNot: {},
|
||||||
|
OpJumpFalsy: {2},
|
||||||
|
OpAndJump: {2},
|
||||||
|
OpOrJump: {2},
|
||||||
|
OpJump: {2},
|
||||||
|
OpNull: {},
|
||||||
|
OpGetGlobal: {2},
|
||||||
|
OpSetGlobal: {2},
|
||||||
|
OpSetSelGlobal: {2, 1},
|
||||||
|
OpArray: {2},
|
||||||
|
OpMap: {2},
|
||||||
|
OpError: {},
|
||||||
|
OpImmutable: {},
|
||||||
|
OpIndex: {},
|
||||||
|
OpSliceIndex: {},
|
||||||
|
OpCall: {1},
|
||||||
|
OpReturn: {},
|
||||||
|
OpReturnValue: {},
|
||||||
|
OpGetLocal: {1},
|
||||||
|
OpSetLocal: {1},
|
||||||
|
OpDefineLocal: {1},
|
||||||
|
OpSetSelLocal: {1, 1},
|
||||||
|
OpGetBuiltin: {1},
|
||||||
|
OpGetBuiltinModule: {},
|
||||||
|
OpClosure: {2, 1},
|
||||||
|
OpGetFree: {1},
|
||||||
|
OpSetFree: {1},
|
||||||
|
OpSetSelFree: {1, 1},
|
||||||
|
OpIteratorInit: {},
|
||||||
|
OpIteratorNext: {},
|
||||||
|
OpIteratorKey: {},
|
||||||
|
OpIteratorValue: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOperands reads operands from the bytecode.
|
||||||
|
func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) {
|
||||||
|
for _, width := range numOperands {
|
||||||
|
switch width {
|
||||||
|
case 1:
|
||||||
|
operands = append(operands, int(ins[offset]))
|
||||||
|
case 2:
|
||||||
|
operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += width
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
21
vendor/github.com/d5/tengo/compiler/parser/error.go
generated
vendored
Normal file
21
vendor/github.com/d5/tengo/compiler/parser/error.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error represents a parser error.
|
||||||
|
type Error struct {
|
||||||
|
Pos source.FilePos
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
if e.Pos.Filename != "" || e.Pos.IsValid() {
|
||||||
|
return fmt.Sprintf("Parse Error: %s\n\tat %s", e.Msg, e.Pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Parse Error: %s", e.Msg)
|
||||||
|
}
|
68
vendor/github.com/d5/tengo/compiler/parser/error_list.go
generated
vendored
Normal file
68
vendor/github.com/d5/tengo/compiler/parser/error_list.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorList is a collection of parser errors.
|
||||||
|
type ErrorList []*Error
|
||||||
|
|
||||||
|
// Add adds a new parser error to the collection.
|
||||||
|
func (p *ErrorList) Add(pos source.FilePos, msg string) {
|
||||||
|
*p = append(*p, &Error{pos, msg})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of elements in the collection.
|
||||||
|
func (p ErrorList) Len() int {
|
||||||
|
return len(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ErrorList) Swap(i, j int) {
|
||||||
|
p[i], p[j] = p[j], p[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ErrorList) Less(i, j int) bool {
|
||||||
|
e := &p[i].Pos
|
||||||
|
f := &p[j].Pos
|
||||||
|
|
||||||
|
if e.Filename != f.Filename {
|
||||||
|
return e.Filename < f.Filename
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Line != f.Line {
|
||||||
|
return e.Line < f.Line
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Column != f.Column {
|
||||||
|
return e.Column < f.Column
|
||||||
|
}
|
||||||
|
|
||||||
|
return p[i].Msg < p[j].Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts the collection.
|
||||||
|
func (p ErrorList) Sort() {
|
||||||
|
sort.Sort(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ErrorList) Error() string {
|
||||||
|
switch len(p) {
|
||||||
|
case 0:
|
||||||
|
return "no errors"
|
||||||
|
case 1:
|
||||||
|
return p[0].Error()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns an error.
|
||||||
|
func (p ErrorList) Err() error {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
28
vendor/github.com/d5/tengo/compiler/parser/parse_file.go
generated
vendored
Normal file
28
vendor/github.com/d5/tengo/compiler/parser/parse_file.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseFile parses a file with a given src.
|
||||||
|
func ParseFile(file *source.File, src []byte, trace io.Writer) (res *ast.File, err error) {
|
||||||
|
p := NewParser(file, src, trace)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
if _, ok := e.(bailout); !ok {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.errors.Sort()
|
||||||
|
err = p.errors.Err()
|
||||||
|
}()
|
||||||
|
|
||||||
|
res, err = p.ParseFile()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
16
vendor/github.com/d5/tengo/compiler/parser/parse_source.go
generated
vendored
Normal file
16
vendor/github.com/d5/tengo/compiler/parser/parse_source.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/ast"
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseSource parses source code 'src' and builds an AST.
|
||||||
|
func ParseSource(filename string, src []byte, trace io.Writer) (res *ast.File, err error) {
|
||||||
|
fileSet := source.NewFileSet()
|
||||||
|
file := fileSet.AddFile(filename, -1, len(src))
|
||||||
|
|
||||||
|
return ParseFile(file, src, trace)
|
||||||
|
}
|
1181
vendor/github.com/d5/tengo/compiler/parser/parser.go
generated
vendored
Normal file
1181
vendor/github.com/d5/tengo/compiler/parser/parser.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
vendor/github.com/d5/tengo/compiler/parser/sync.go
generated
vendored
Normal file
12
vendor/github.com/d5/tengo/compiler/parser/sync.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/token"
|
||||||
|
|
||||||
|
var stmtStart = map[token.Token]bool{
|
||||||
|
token.Break: true,
|
||||||
|
token.Continue: true,
|
||||||
|
token.For: true,
|
||||||
|
token.If: true,
|
||||||
|
token.Return: true,
|
||||||
|
token.Export: true,
|
||||||
|
}
|
6
vendor/github.com/d5/tengo/compiler/scanner/error_handler.go
generated
vendored
Normal file
6
vendor/github.com/d5/tengo/compiler/scanner/error_handler.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package scanner
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/source"
|
||||||
|
|
||||||
|
// ErrorHandler is an error handler for the scanner.
|
||||||
|
type ErrorHandler func(pos source.FilePos, msg string)
|
10
vendor/github.com/d5/tengo/compiler/scanner/mode.go
generated
vendored
Normal file
10
vendor/github.com/d5/tengo/compiler/scanner/mode.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package scanner
|
||||||
|
|
||||||
|
// Mode represents a scanner mode.
|
||||||
|
type Mode int
|
||||||
|
|
||||||
|
// List of scanner modes.
|
||||||
|
const (
|
||||||
|
ScanComments Mode = 1 << iota
|
||||||
|
DontInsertSemis
|
||||||
|
)
|
680
vendor/github.com/d5/tengo/compiler/scanner/scanner.go
generated
vendored
Normal file
680
vendor/github.com/d5/tengo/compiler/scanner/scanner.go
generated
vendored
Normal file
@ -0,0 +1,680 @@
|
|||||||
|
/*
|
||||||
|
Scanner reads the Tengo source text and tokenize them.
|
||||||
|
|
||||||
|
Scanner is a modified version of Go's scanner implementation.
|
||||||
|
|
||||||
|
Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package scanner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/source"
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// byte order mark
|
||||||
|
const bom = 0xFEFF
|
||||||
|
|
||||||
|
// Scanner reads the Tengo source text.
|
||||||
|
type Scanner struct {
|
||||||
|
file *source.File // source file handle
|
||||||
|
src []byte // source
|
||||||
|
ch rune // current character
|
||||||
|
offset int // character offset
|
||||||
|
readOffset int // reading offset (position after current character)
|
||||||
|
lineOffset int // current line offset
|
||||||
|
insertSemi bool // insert a semicolon before next newline
|
||||||
|
errorHandler ErrorHandler // error reporting; or nil
|
||||||
|
errorCount int // number of errors encountered
|
||||||
|
mode Mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScanner creates a Scanner.
|
||||||
|
func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode Mode) *Scanner {
|
||||||
|
if file.Size != len(src) {
|
||||||
|
panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size, len(src)))
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &Scanner{
|
||||||
|
file: file,
|
||||||
|
src: src,
|
||||||
|
errorHandler: errorHandler,
|
||||||
|
ch: ' ',
|
||||||
|
mode: mode,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.next()
|
||||||
|
if s.ch == bom {
|
||||||
|
s.next() // ignore BOM at file beginning
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCount returns the number of errors.
|
||||||
|
func (s *Scanner) ErrorCount() int {
|
||||||
|
return s.errorCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan returns a token, token literal and its position.
|
||||||
|
func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) {
|
||||||
|
s.skipWhitespace()
|
||||||
|
|
||||||
|
pos = s.file.FileSetPos(s.offset)
|
||||||
|
|
||||||
|
insertSemi := false
|
||||||
|
|
||||||
|
// determine token value
|
||||||
|
switch ch := s.ch; {
|
||||||
|
case isLetter(ch):
|
||||||
|
literal = s.scanIdentifier()
|
||||||
|
tok = token.Lookup(literal)
|
||||||
|
switch tok {
|
||||||
|
case token.Ident, token.Break, token.Continue, token.Return, token.Export, token.True, token.False, token.Undefined:
|
||||||
|
insertSemi = true
|
||||||
|
}
|
||||||
|
case '0' <= ch && ch <= '9':
|
||||||
|
insertSemi = true
|
||||||
|
tok, literal = s.scanNumber(false)
|
||||||
|
default:
|
||||||
|
s.next() // always make progress
|
||||||
|
|
||||||
|
switch ch {
|
||||||
|
case -1: // EOF
|
||||||
|
if s.insertSemi {
|
||||||
|
s.insertSemi = false // EOF consumed
|
||||||
|
return token.Semicolon, "\n", pos
|
||||||
|
}
|
||||||
|
tok = token.EOF
|
||||||
|
case '\n':
|
||||||
|
// we only reach here if s.insertSemi was set in the first place
|
||||||
|
s.insertSemi = false // newline consumed
|
||||||
|
return token.Semicolon, "\n", pos
|
||||||
|
case '"':
|
||||||
|
insertSemi = true
|
||||||
|
tok = token.String
|
||||||
|
literal = s.scanString()
|
||||||
|
case '\'':
|
||||||
|
insertSemi = true
|
||||||
|
tok = token.Char
|
||||||
|
literal = s.scanRune()
|
||||||
|
case '`':
|
||||||
|
insertSemi = true
|
||||||
|
tok = token.String
|
||||||
|
literal = s.scanRawString()
|
||||||
|
case ':':
|
||||||
|
tok = s.switch2(token.Colon, token.Define)
|
||||||
|
case '.':
|
||||||
|
if '0' <= s.ch && s.ch <= '9' {
|
||||||
|
insertSemi = true
|
||||||
|
tok, literal = s.scanNumber(true)
|
||||||
|
} else {
|
||||||
|
tok = token.Period
|
||||||
|
if s.ch == '.' && s.peek() == '.' {
|
||||||
|
s.next()
|
||||||
|
s.next() // consume last '.'
|
||||||
|
tok = token.Ellipsis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ',':
|
||||||
|
tok = token.Comma
|
||||||
|
case '?':
|
||||||
|
tok = token.Question
|
||||||
|
case ';':
|
||||||
|
tok = token.Semicolon
|
||||||
|
literal = ";"
|
||||||
|
case '(':
|
||||||
|
tok = token.LParen
|
||||||
|
case ')':
|
||||||
|
insertSemi = true
|
||||||
|
tok = token.RParen
|
||||||
|
case '[':
|
||||||
|
tok = token.LBrack
|
||||||
|
case ']':
|
||||||
|
insertSemi = true
|
||||||
|
tok = token.RBrack
|
||||||
|
case '{':
|
||||||
|
tok = token.LBrace
|
||||||
|
case '}':
|
||||||
|
insertSemi = true
|
||||||
|
tok = token.RBrace
|
||||||
|
case '+':
|
||||||
|
tok = s.switch3(token.Add, token.AddAssign, '+', token.Inc)
|
||||||
|
if tok == token.Inc {
|
||||||
|
insertSemi = true
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
tok = s.switch3(token.Sub, token.SubAssign, '-', token.Dec)
|
||||||
|
if tok == token.Dec {
|
||||||
|
insertSemi = true
|
||||||
|
}
|
||||||
|
case '*':
|
||||||
|
tok = s.switch2(token.Mul, token.MulAssign)
|
||||||
|
case '/':
|
||||||
|
if s.ch == '/' || s.ch == '*' {
|
||||||
|
// comment
|
||||||
|
if s.insertSemi && s.findLineEnd() {
|
||||||
|
// reset position to the beginning of the comment
|
||||||
|
s.ch = '/'
|
||||||
|
s.offset = s.file.Offset(pos)
|
||||||
|
s.readOffset = s.offset + 1
|
||||||
|
s.insertSemi = false // newline consumed
|
||||||
|
return token.Semicolon, "\n", pos
|
||||||
|
}
|
||||||
|
comment := s.scanComment()
|
||||||
|
if s.mode&ScanComments == 0 {
|
||||||
|
// skip comment
|
||||||
|
s.insertSemi = false // newline consumed
|
||||||
|
return s.Scan()
|
||||||
|
}
|
||||||
|
tok = token.Comment
|
||||||
|
literal = comment
|
||||||
|
} else {
|
||||||
|
tok = s.switch2(token.Quo, token.QuoAssign)
|
||||||
|
}
|
||||||
|
case '%':
|
||||||
|
tok = s.switch2(token.Rem, token.RemAssign)
|
||||||
|
case '^':
|
||||||
|
tok = s.switch2(token.Xor, token.XorAssign)
|
||||||
|
case '<':
|
||||||
|
tok = s.switch4(token.Less, token.LessEq, '<', token.Shl, token.ShlAssign)
|
||||||
|
case '>':
|
||||||
|
tok = s.switch4(token.Greater, token.GreaterEq, '>', token.Shr, token.ShrAssign)
|
||||||
|
case '=':
|
||||||
|
tok = s.switch2(token.Assign, token.Equal)
|
||||||
|
case '!':
|
||||||
|
tok = s.switch2(token.Not, token.NotEqual)
|
||||||
|
case '&':
|
||||||
|
if s.ch == '^' {
|
||||||
|
s.next()
|
||||||
|
tok = s.switch2(token.AndNot, token.AndNotAssign)
|
||||||
|
} else {
|
||||||
|
tok = s.switch3(token.And, token.AndAssign, '&', token.LAnd)
|
||||||
|
}
|
||||||
|
case '|':
|
||||||
|
tok = s.switch3(token.Or, token.OrAssign, '|', token.LOr)
|
||||||
|
default:
|
||||||
|
// next reports unexpected BOMs - don't repeat
|
||||||
|
if ch != bom {
|
||||||
|
s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch))
|
||||||
|
}
|
||||||
|
insertSemi = s.insertSemi // preserve insertSemi info
|
||||||
|
tok = token.Illegal
|
||||||
|
literal = string(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.mode&DontInsertSemis == 0 {
|
||||||
|
s.insertSemi = insertSemi
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) next() {
|
||||||
|
if s.readOffset < len(s.src) {
|
||||||
|
s.offset = s.readOffset
|
||||||
|
if s.ch == '\n' {
|
||||||
|
s.lineOffset = s.offset
|
||||||
|
s.file.AddLine(s.offset)
|
||||||
|
}
|
||||||
|
r, w := rune(s.src[s.readOffset]), 1
|
||||||
|
switch {
|
||||||
|
case r == 0:
|
||||||
|
s.error(s.offset, "illegal character NUL")
|
||||||
|
case r >= utf8.RuneSelf:
|
||||||
|
// not ASCII
|
||||||
|
r, w = utf8.DecodeRune(s.src[s.readOffset:])
|
||||||
|
if r == utf8.RuneError && w == 1 {
|
||||||
|
s.error(s.offset, "illegal UTF-8 encoding")
|
||||||
|
} else if r == bom && s.offset > 0 {
|
||||||
|
s.error(s.offset, "illegal byte order mark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.readOffset += w
|
||||||
|
s.ch = r
|
||||||
|
} else {
|
||||||
|
s.offset = len(s.src)
|
||||||
|
if s.ch == '\n' {
|
||||||
|
s.lineOffset = s.offset
|
||||||
|
s.file.AddLine(s.offset)
|
||||||
|
}
|
||||||
|
s.ch = -1 // eof
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) peek() byte {
|
||||||
|
if s.readOffset < len(s.src) {
|
||||||
|
return s.src[s.readOffset]
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) error(offset int, msg string) {
|
||||||
|
if s.errorHandler != nil {
|
||||||
|
s.errorHandler(s.file.Position(s.file.FileSetPos(offset)), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.errorCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanComment() string {
|
||||||
|
// initial '/' already consumed; s.ch == '/' || s.ch == '*'
|
||||||
|
offs := s.offset - 1 // position of initial '/'
|
||||||
|
var numCR int
|
||||||
|
|
||||||
|
if s.ch == '/' {
|
||||||
|
//-style comment
|
||||||
|
// (the final '\n' is not considered part of the comment)
|
||||||
|
s.next()
|
||||||
|
for s.ch != '\n' && s.ch >= 0 {
|
||||||
|
if s.ch == '\r' {
|
||||||
|
numCR++
|
||||||
|
}
|
||||||
|
s.next()
|
||||||
|
}
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-style comment */
|
||||||
|
s.next()
|
||||||
|
for s.ch >= 0 {
|
||||||
|
ch := s.ch
|
||||||
|
if ch == '\r' {
|
||||||
|
numCR++
|
||||||
|
}
|
||||||
|
s.next()
|
||||||
|
if ch == '*' && s.ch == '/' {
|
||||||
|
s.next()
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.error(offs, "comment not terminated")
|
||||||
|
|
||||||
|
exit:
|
||||||
|
lit := s.src[offs:s.offset]
|
||||||
|
|
||||||
|
// On Windows, a (//-comment) line may end in "\r\n".
|
||||||
|
// Remove the final '\r' before analyzing the text for line directives (matching the compiler).
|
||||||
|
// Remove any other '\r' afterwards (matching the pre-existing behavior of the scanner).
|
||||||
|
if numCR > 0 && len(lit) >= 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
||||||
|
lit = lit[:len(lit)-1]
|
||||||
|
numCR--
|
||||||
|
}
|
||||||
|
|
||||||
|
if numCR > 0 {
|
||||||
|
lit = StripCR(lit, lit[1] == '*')
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(lit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) findLineEnd() bool {
|
||||||
|
// initial '/' already consumed
|
||||||
|
|
||||||
|
defer func(offs int) {
|
||||||
|
// reset scanner state to where it was upon calling findLineEnd
|
||||||
|
s.ch = '/'
|
||||||
|
s.offset = offs
|
||||||
|
s.readOffset = offs + 1
|
||||||
|
s.next() // consume initial '/' again
|
||||||
|
}(s.offset - 1)
|
||||||
|
|
||||||
|
// read ahead until a newline, EOF, or non-comment tok is found
|
||||||
|
for s.ch == '/' || s.ch == '*' {
|
||||||
|
if s.ch == '/' {
|
||||||
|
//-style comment always contains a newline
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/*-style comment: look for newline */
|
||||||
|
s.next()
|
||||||
|
for s.ch >= 0 {
|
||||||
|
ch := s.ch
|
||||||
|
if ch == '\n' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s.next()
|
||||||
|
if ch == '*' && s.ch == '/' {
|
||||||
|
s.next()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.skipWhitespace() // s.insertSemi is set
|
||||||
|
if s.ch < 0 || s.ch == '\n' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s.ch != '/' {
|
||||||
|
// non-comment tok
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.next() // consume '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanIdentifier() string {
|
||||||
|
offs := s.offset
|
||||||
|
for isLetter(s.ch) || isDigit(s.ch) {
|
||||||
|
s.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(s.src[offs:s.offset])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanMantissa(base int) {
|
||||||
|
for digitVal(s.ch) < base {
|
||||||
|
s.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanNumber(seenDecimalPoint bool) (tok token.Token, lit string) {
|
||||||
|
// digitVal(s.ch) < 10
|
||||||
|
offs := s.offset
|
||||||
|
tok = token.Int
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
lit = string(s.src[offs:s.offset])
|
||||||
|
}()
|
||||||
|
|
||||||
|
if seenDecimalPoint {
|
||||||
|
offs--
|
||||||
|
tok = token.Float
|
||||||
|
s.scanMantissa(10)
|
||||||
|
goto exponent
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ch == '0' {
|
||||||
|
// int or float
|
||||||
|
offs := s.offset
|
||||||
|
s.next()
|
||||||
|
if s.ch == 'x' || s.ch == 'X' {
|
||||||
|
// hexadecimal int
|
||||||
|
s.next()
|
||||||
|
s.scanMantissa(16)
|
||||||
|
if s.offset-offs <= 2 {
|
||||||
|
// only scanned "0x" or "0X"
|
||||||
|
s.error(offs, "illegal hexadecimal number")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// octal int or float
|
||||||
|
seenDecimalDigit := false
|
||||||
|
s.scanMantissa(8)
|
||||||
|
if s.ch == '8' || s.ch == '9' {
|
||||||
|
// illegal octal int or float
|
||||||
|
seenDecimalDigit = true
|
||||||
|
s.scanMantissa(10)
|
||||||
|
}
|
||||||
|
if s.ch == '.' || s.ch == 'e' || s.ch == 'E' || s.ch == 'i' {
|
||||||
|
goto fraction
|
||||||
|
}
|
||||||
|
// octal int
|
||||||
|
if seenDecimalDigit {
|
||||||
|
s.error(offs, "illegal octal number")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decimal int or float
|
||||||
|
s.scanMantissa(10)
|
||||||
|
|
||||||
|
fraction:
|
||||||
|
if s.ch == '.' {
|
||||||
|
tok = token.Float
|
||||||
|
s.next()
|
||||||
|
s.scanMantissa(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
exponent:
|
||||||
|
if s.ch == 'e' || s.ch == 'E' {
|
||||||
|
tok = token.Float
|
||||||
|
s.next()
|
||||||
|
if s.ch == '-' || s.ch == '+' {
|
||||||
|
s.next()
|
||||||
|
}
|
||||||
|
if digitVal(s.ch) < 10 {
|
||||||
|
s.scanMantissa(10)
|
||||||
|
} else {
|
||||||
|
s.error(offs, "illegal floating-point exponent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanEscape(quote rune) bool {
|
||||||
|
offs := s.offset
|
||||||
|
|
||||||
|
var n int
|
||||||
|
var base, max uint32
|
||||||
|
switch s.ch {
|
||||||
|
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
|
||||||
|
s.next()
|
||||||
|
return true
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
n, base, max = 3, 8, 255
|
||||||
|
case 'x':
|
||||||
|
s.next()
|
||||||
|
n, base, max = 2, 16, 255
|
||||||
|
case 'u':
|
||||||
|
s.next()
|
||||||
|
n, base, max = 4, 16, unicode.MaxRune
|
||||||
|
case 'U':
|
||||||
|
s.next()
|
||||||
|
n, base, max = 8, 16, unicode.MaxRune
|
||||||
|
default:
|
||||||
|
msg := "unknown escape sequence"
|
||||||
|
if s.ch < 0 {
|
||||||
|
msg = "escape sequence not terminated"
|
||||||
|
}
|
||||||
|
s.error(offs, msg)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var x uint32
|
||||||
|
for n > 0 {
|
||||||
|
d := uint32(digitVal(s.ch))
|
||||||
|
if d >= base {
|
||||||
|
msg := fmt.Sprintf("illegal character %#U in escape sequence", s.ch)
|
||||||
|
if s.ch < 0 {
|
||||||
|
msg = "escape sequence not terminated"
|
||||||
|
}
|
||||||
|
s.error(s.offset, msg)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
x = x*base + d
|
||||||
|
s.next()
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
|
||||||
|
if x > max || 0xD800 <= x && x < 0xE000 {
|
||||||
|
s.error(offs, "escape sequence is invalid Unicode code point")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanRune() string {
|
||||||
|
offs := s.offset - 1 // '\'' opening already consumed
|
||||||
|
|
||||||
|
valid := true
|
||||||
|
n := 0
|
||||||
|
for {
|
||||||
|
ch := s.ch
|
||||||
|
if ch == '\n' || ch < 0 {
|
||||||
|
// only report error if we don't have one already
|
||||||
|
if valid {
|
||||||
|
s.error(offs, "rune literal not terminated")
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.next()
|
||||||
|
if ch == '\'' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
if ch == '\\' {
|
||||||
|
if !s.scanEscape('\'') {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
// continue to read to closing quote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if valid && n != 1 {
|
||||||
|
s.error(offs, "illegal rune literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(s.src[offs:s.offset])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanString() string {
|
||||||
|
offs := s.offset - 1 // '"' opening already consumed
|
||||||
|
|
||||||
|
for {
|
||||||
|
ch := s.ch
|
||||||
|
if ch == '\n' || ch < 0 {
|
||||||
|
s.error(offs, "string literal not terminated")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.next()
|
||||||
|
if ch == '"' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ch == '\\' {
|
||||||
|
s.scanEscape('"')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(s.src[offs:s.offset])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanRawString() string {
|
||||||
|
offs := s.offset - 1 // '`' opening already consumed
|
||||||
|
|
||||||
|
hasCR := false
|
||||||
|
for {
|
||||||
|
ch := s.ch
|
||||||
|
if ch < 0 {
|
||||||
|
s.error(offs, "raw string literal not terminated")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s.next()
|
||||||
|
|
||||||
|
if ch == '`' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch == '\r' {
|
||||||
|
hasCR = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lit := s.src[offs:s.offset]
|
||||||
|
if hasCR {
|
||||||
|
lit = StripCR(lit, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(lit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StripCR removes carriage return characters.
|
||||||
|
func StripCR(b []byte, comment bool) []byte {
|
||||||
|
c := make([]byte, len(b))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for j, ch := range b {
|
||||||
|
// In a /*-style comment, don't strip \r from *\r/ (incl. sequences of \r from *\r\r...\r/)
|
||||||
|
// since the resulting */ would terminate the comment too early unless the \r is immediately
|
||||||
|
// following the opening /* in which case it's ok because /*/ is not closed yet.
|
||||||
|
if ch != '\r' || comment && i > len("/*") && c[i-1] == '*' && j+1 < len(b) && b[j+1] == '/' {
|
||||||
|
c[i] = ch
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c[:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) skipWhitespace() {
|
||||||
|
for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' && !s.insertSemi || s.ch == '\r' {
|
||||||
|
s.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) switch2(tok0, tok1 token.Token) token.Token {
|
||||||
|
if s.ch == '=' {
|
||||||
|
s.next()
|
||||||
|
return tok1
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) switch3(tok0, tok1 token.Token, ch2 rune, tok2 token.Token) token.Token {
|
||||||
|
if s.ch == '=' {
|
||||||
|
s.next()
|
||||||
|
return tok1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ch == ch2 {
|
||||||
|
s.next()
|
||||||
|
return tok2
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Token) token.Token {
|
||||||
|
if s.ch == '=' {
|
||||||
|
s.next()
|
||||||
|
return tok1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ch == ch2 {
|
||||||
|
s.next()
|
||||||
|
if s.ch == '=' {
|
||||||
|
s.next()
|
||||||
|
return tok3
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok2
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLetter(ch rune) bool {
|
||||||
|
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDigit(ch rune) bool {
|
||||||
|
return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func digitVal(ch rune) int {
|
||||||
|
switch {
|
||||||
|
case '0' <= ch && ch <= '9':
|
||||||
|
return int(ch - '0')
|
||||||
|
case 'a' <= ch && ch <= 'f':
|
||||||
|
return int(ch - 'a' + 10)
|
||||||
|
case 'A' <= ch && ch <= 'F':
|
||||||
|
return int(ch - 'A' + 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 16 // larger than any legal digit val
|
||||||
|
}
|
110
vendor/github.com/d5/tengo/compiler/source/file.go
generated
vendored
Normal file
110
vendor/github.com/d5/tengo/compiler/source/file.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
// File represents a source file.
|
||||||
|
type File struct {
|
||||||
|
// File set for the file
|
||||||
|
set *FileSet
|
||||||
|
// File name as provided to AddFile
|
||||||
|
Name string
|
||||||
|
// Pos value range for this file is [base...base+size]
|
||||||
|
Base int
|
||||||
|
// File size as provided to AddFile
|
||||||
|
Size int
|
||||||
|
// Lines contains the offset of the first character for each line (the first entry is always 0)
|
||||||
|
Lines []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set returns FileSet.
|
||||||
|
func (f *File) Set() *FileSet {
|
||||||
|
return f.set
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineCount returns the current number of lines.
|
||||||
|
func (f *File) LineCount() int {
|
||||||
|
return len(f.Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLine adds a new line.
|
||||||
|
func (f *File) AddLine(offset int) {
|
||||||
|
if i := len(f.Lines); (i == 0 || f.Lines[i-1] < offset) && offset < f.Size {
|
||||||
|
f.Lines = append(f.Lines, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineStart returns the position of the first character in the line.
|
||||||
|
func (f *File) LineStart(line int) Pos {
|
||||||
|
if line < 1 {
|
||||||
|
panic("illegal line number (line numbering starts at 1)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if line > len(f.Lines) {
|
||||||
|
panic("illegal line number")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pos(f.Base + f.Lines[line-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileSetPos returns the position in the file set.
|
||||||
|
func (f *File) FileSetPos(offset int) Pos {
|
||||||
|
if offset > f.Size {
|
||||||
|
panic("illegal file offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pos(f.Base + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset translates the file set position into the file offset.
|
||||||
|
func (f *File) Offset(p Pos) int {
|
||||||
|
if int(p) < f.Base || int(p) > f.Base+f.Size {
|
||||||
|
panic("illegal Pos value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(p) - f.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position translates the file set position into the file position.
|
||||||
|
func (f *File) Position(p Pos) (pos FilePos) {
|
||||||
|
if p != NoPos {
|
||||||
|
if int(p) < f.Base || int(p) > f.Base+f.Size {
|
||||||
|
panic("illegal Pos value")
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = f.position(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) position(p Pos) (pos FilePos) {
|
||||||
|
offset := int(p) - f.Base
|
||||||
|
pos.Offset = offset
|
||||||
|
pos.Filename, pos.Line, pos.Column = f.unpack(offset)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) unpack(offset int) (filename string, line, column int) {
|
||||||
|
filename = f.Name
|
||||||
|
if i := searchInts(f.Lines, offset); i >= 0 {
|
||||||
|
line, column = i+1, offset-f.Lines[i]+1
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchInts(a []int, x int) int {
|
||||||
|
// This function body is a manually inlined version of:
|
||||||
|
// return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
|
||||||
|
i, j := 0, len(a)
|
||||||
|
for i < j {
|
||||||
|
h := i + (j-i)/2 // avoid overflow when computing h
|
||||||
|
// i ≤ h < j
|
||||||
|
if a[h] <= x {
|
||||||
|
i = h + 1
|
||||||
|
} else {
|
||||||
|
j = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i - 1
|
||||||
|
}
|
47
vendor/github.com/d5/tengo/compiler/source/file_pos.go
generated
vendored
Normal file
47
vendor/github.com/d5/tengo/compiler/source/file_pos.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// FilePos represents a position information in the file.
|
||||||
|
type FilePos struct {
|
||||||
|
Filename string // filename, if any
|
||||||
|
Offset int // offset, starting at 0
|
||||||
|
Line int // line number, starting at 1
|
||||||
|
Column int // column number, starting at 1 (byte count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if the position is valid.
|
||||||
|
func (p FilePos) IsValid() bool {
|
||||||
|
return p.Line > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string in one of several forms:
|
||||||
|
//
|
||||||
|
// file:line:column valid position with file name
|
||||||
|
// file:line valid position with file name but no column (column == 0)
|
||||||
|
// line:column valid position without file name
|
||||||
|
// line valid position without file name and no column (column == 0)
|
||||||
|
// file invalid position with file name
|
||||||
|
// - invalid position without file name
|
||||||
|
//
|
||||||
|
func (p FilePos) String() string {
|
||||||
|
s := p.Filename
|
||||||
|
|
||||||
|
if p.IsValid() {
|
||||||
|
if s != "" {
|
||||||
|
s += ":"
|
||||||
|
}
|
||||||
|
|
||||||
|
s += fmt.Sprintf("%d", p.Line)
|
||||||
|
|
||||||
|
if p.Column != 0 {
|
||||||
|
s += fmt.Sprintf(":%d", p.Column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
s = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
96
vendor/github.com/d5/tengo/compiler/source/file_set.go
generated
vendored
Normal file
96
vendor/github.com/d5/tengo/compiler/source/file_set.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileSet represents a set of source files.
|
||||||
|
type FileSet struct {
|
||||||
|
Base int // base offset for the next file
|
||||||
|
Files []*File // list of files in the order added to the set
|
||||||
|
LastFile *File // cache of last file looked up
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileSet creates a new file set.
|
||||||
|
func NewFileSet() *FileSet {
|
||||||
|
return &FileSet{
|
||||||
|
Base: 1, // 0 == NoPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFile adds a new file in the file set.
|
||||||
|
func (s *FileSet) AddFile(filename string, base, size int) *File {
|
||||||
|
if base < 0 {
|
||||||
|
base = s.Base
|
||||||
|
}
|
||||||
|
if base < s.Base || size < 0 {
|
||||||
|
panic("illegal base or size")
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &File{
|
||||||
|
set: s,
|
||||||
|
Name: filename,
|
||||||
|
Base: base,
|
||||||
|
Size: size,
|
||||||
|
Lines: []int{0},
|
||||||
|
}
|
||||||
|
|
||||||
|
base += size + 1 // +1 because EOF also has a position
|
||||||
|
if base < 0 {
|
||||||
|
panic("offset overflow (> 2G of source code in file set)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the file to the file set
|
||||||
|
s.Base = base
|
||||||
|
s.Files = append(s.Files, f)
|
||||||
|
s.LastFile = f
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// File returns the file that contains the position p.
|
||||||
|
// If no such file is found (for instance for p == NoPos),
|
||||||
|
// the result is nil.
|
||||||
|
//
|
||||||
|
func (s *FileSet) File(p Pos) (f *File) {
|
||||||
|
if p != NoPos {
|
||||||
|
f = s.file(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position converts a Pos p in the fileset into a FilePos value.
|
||||||
|
func (s *FileSet) Position(p Pos) (pos FilePos) {
|
||||||
|
if p != NoPos {
|
||||||
|
if f := s.file(p); f != nil {
|
||||||
|
return f.position(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSet) file(p Pos) *File {
|
||||||
|
// common case: p is in last file
|
||||||
|
if f := s.LastFile; f != nil && f.Base <= int(p) && int(p) <= f.Base+f.Size {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// p is not in last file - search all files
|
||||||
|
if i := searchFiles(s.Files, int(p)); i >= 0 {
|
||||||
|
f := s.Files[i]
|
||||||
|
|
||||||
|
// f.base <= int(p) by definition of searchFiles
|
||||||
|
if int(p) <= f.Base+f.Size {
|
||||||
|
s.LastFile = f // race is ok - s.last is only a cache
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchFiles(a []*File, x int) int {
|
||||||
|
return sort.Search(len(a), func(i int) bool { return a[i].Base > x }) - 1
|
||||||
|
}
|
12
vendor/github.com/d5/tengo/compiler/source/pos.go
generated
vendored
Normal file
12
vendor/github.com/d5/tengo/compiler/source/pos.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
// Pos represents a position in the file set.
|
||||||
|
type Pos int
|
||||||
|
|
||||||
|
// NoPos represents an invalid position.
|
||||||
|
const NoPos Pos = 0
|
||||||
|
|
||||||
|
// IsValid returns true if the position is valid.
|
||||||
|
func (p Pos) IsValid() bool {
|
||||||
|
return p != NoPos
|
||||||
|
}
|
9
vendor/github.com/d5/tengo/compiler/symbol.go
generated
vendored
Normal file
9
vendor/github.com/d5/tengo/compiler/symbol.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// Symbol represents a symbol in the symbol table.
|
||||||
|
type Symbol struct {
|
||||||
|
Name string
|
||||||
|
Scope SymbolScope
|
||||||
|
Index int
|
||||||
|
LocalAssigned bool // if the local symbol is assigned at least once
|
||||||
|
}
|
12
vendor/github.com/d5/tengo/compiler/symbol_scopes.go
generated
vendored
Normal file
12
vendor/github.com/d5/tengo/compiler/symbol_scopes.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// SymbolScope represents a symbol scope.
|
||||||
|
type SymbolScope string
|
||||||
|
|
||||||
|
// List of symbol scopes
|
||||||
|
const (
|
||||||
|
ScopeGlobal SymbolScope = "GLOBAL"
|
||||||
|
ScopeLocal = "LOCAL"
|
||||||
|
ScopeBuiltin = "BUILTIN"
|
||||||
|
ScopeFree = "FREE"
|
||||||
|
)
|
145
vendor/github.com/d5/tengo/compiler/symbol_table.go
generated
vendored
Normal file
145
vendor/github.com/d5/tengo/compiler/symbol_table.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// SymbolTable represents a symbol table.
|
||||||
|
type SymbolTable struct {
|
||||||
|
parent *SymbolTable
|
||||||
|
block bool
|
||||||
|
store map[string]*Symbol
|
||||||
|
numDefinition int
|
||||||
|
maxDefinition int
|
||||||
|
freeSymbols []*Symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSymbolTable creates a SymbolTable.
|
||||||
|
func NewSymbolTable() *SymbolTable {
|
||||||
|
return &SymbolTable{
|
||||||
|
store: make(map[string]*Symbol),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define adds a new symbol in the current scope.
|
||||||
|
func (t *SymbolTable) Define(name string) *Symbol {
|
||||||
|
symbol := &Symbol{Name: name, Index: t.nextIndex()}
|
||||||
|
t.numDefinition++
|
||||||
|
|
||||||
|
if t.Parent(true) == nil {
|
||||||
|
symbol.Scope = ScopeGlobal
|
||||||
|
} else {
|
||||||
|
symbol.Scope = ScopeLocal
|
||||||
|
}
|
||||||
|
|
||||||
|
t.store[name] = symbol
|
||||||
|
|
||||||
|
t.updateMaxDefs(symbol.Index + 1)
|
||||||
|
|
||||||
|
return symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefineBuiltin adds a symbol for builtin function.
|
||||||
|
func (t *SymbolTable) DefineBuiltin(index int, name string) *Symbol {
|
||||||
|
symbol := &Symbol{
|
||||||
|
Name: name,
|
||||||
|
Index: index,
|
||||||
|
Scope: ScopeBuiltin,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.store[name] = symbol
|
||||||
|
|
||||||
|
return symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve resolves a symbol with a given name.
|
||||||
|
func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool) {
|
||||||
|
symbol, ok = t.store[name]
|
||||||
|
if !ok && t.parent != nil {
|
||||||
|
symbol, depth, ok = t.parent.Resolve(name)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t.block {
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
|
||||||
|
// if symbol is defined in parent table and if it's not global/builtin
|
||||||
|
// then it's free variable.
|
||||||
|
if !t.block && depth > 0 && symbol.Scope != ScopeGlobal && symbol.Scope != ScopeBuiltin {
|
||||||
|
return t.defineFree(symbol), depth, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fork creates a new symbol table for a new scope.
|
||||||
|
func (t *SymbolTable) Fork(block bool) *SymbolTable {
|
||||||
|
return &SymbolTable{
|
||||||
|
store: make(map[string]*Symbol),
|
||||||
|
parent: t,
|
||||||
|
block: block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent returns the outer scope of the current symbol table.
|
||||||
|
func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
|
||||||
|
if skipBlock && t.block {
|
||||||
|
return t.parent.Parent(skipBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxSymbols returns the total number of symbols defined in the scope.
|
||||||
|
func (t *SymbolTable) MaxSymbols() int {
|
||||||
|
return t.maxDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeSymbols returns free symbols for the scope.
|
||||||
|
func (t *SymbolTable) FreeSymbols() []*Symbol {
|
||||||
|
return t.freeSymbols
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns the name of all the symbols.
|
||||||
|
func (t *SymbolTable) Names() []string {
|
||||||
|
var names []string
|
||||||
|
for name := range t.store {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SymbolTable) nextIndex() int {
|
||||||
|
if t.block {
|
||||||
|
return t.parent.nextIndex() + t.numDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.numDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SymbolTable) updateMaxDefs(numDefs int) {
|
||||||
|
if numDefs > t.maxDefinition {
|
||||||
|
t.maxDefinition = numDefs
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.block {
|
||||||
|
t.parent.updateMaxDefs(numDefs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SymbolTable) defineFree(original *Symbol) *Symbol {
|
||||||
|
// TODO: should we check duplicates?
|
||||||
|
|
||||||
|
t.freeSymbols = append(t.freeSymbols, original)
|
||||||
|
|
||||||
|
symbol := &Symbol{
|
||||||
|
Name: original.Name,
|
||||||
|
Index: len(t.freeSymbols) - 1,
|
||||||
|
Scope: ScopeFree,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.store[original.Name] = symbol
|
||||||
|
|
||||||
|
return symbol
|
||||||
|
}
|
19
vendor/github.com/d5/tengo/compiler/token/keywords.go
generated
vendored
Normal file
19
vendor/github.com/d5/tengo/compiler/token/keywords.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
var keywords map[string]Token
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
keywords = make(map[string]Token)
|
||||||
|
for i := _keywordBeg + 1; i < _keywordEnd; i++ {
|
||||||
|
keywords[tokens[i]] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup returns corresponding keyword if ident is a keyword.
|
||||||
|
func Lookup(ident string) Token {
|
||||||
|
if tok, isKeyword := keywords[ident]; isKeyword {
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ident
|
||||||
|
}
|
208
vendor/github.com/d5/tengo/compiler/token/tokens.go
generated
vendored
Normal file
208
vendor/github.com/d5/tengo/compiler/token/tokens.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// Token represents a token.
|
||||||
|
type Token int
|
||||||
|
|
||||||
|
// List of tokens
|
||||||
|
const (
|
||||||
|
Illegal Token = iota
|
||||||
|
EOF
|
||||||
|
Comment
|
||||||
|
_literalBeg
|
||||||
|
Ident
|
||||||
|
Int
|
||||||
|
Float
|
||||||
|
Char
|
||||||
|
String
|
||||||
|
_literalEnd
|
||||||
|
_operatorBeg
|
||||||
|
Add // +
|
||||||
|
Sub // -
|
||||||
|
Mul // *
|
||||||
|
Quo // /
|
||||||
|
Rem // %
|
||||||
|
And // &
|
||||||
|
Or // |
|
||||||
|
Xor // ^
|
||||||
|
Shl // <<
|
||||||
|
Shr // >>
|
||||||
|
AndNot // &^
|
||||||
|
AddAssign // +=
|
||||||
|
SubAssign // -=
|
||||||
|
MulAssign // *=
|
||||||
|
QuoAssign // /=
|
||||||
|
RemAssign // %=
|
||||||
|
AndAssign // &=
|
||||||
|
OrAssign // |=
|
||||||
|
XorAssign // ^=
|
||||||
|
ShlAssign // <<=
|
||||||
|
ShrAssign // >>=
|
||||||
|
AndNotAssign // &^=
|
||||||
|
LAnd // &&
|
||||||
|
LOr // ||
|
||||||
|
Inc // ++
|
||||||
|
Dec // --
|
||||||
|
Equal // ==
|
||||||
|
Less // <
|
||||||
|
Greater // >
|
||||||
|
Assign // =
|
||||||
|
Not // !
|
||||||
|
NotEqual // !=
|
||||||
|
LessEq // <=
|
||||||
|
GreaterEq // >=
|
||||||
|
Define // :=
|
||||||
|
Ellipsis // ...
|
||||||
|
LParen // (
|
||||||
|
LBrack // [
|
||||||
|
LBrace // {
|
||||||
|
Comma // ,
|
||||||
|
Period // .
|
||||||
|
RParen // )
|
||||||
|
RBrack // ]
|
||||||
|
RBrace // }
|
||||||
|
Semicolon // ;
|
||||||
|
Colon // :
|
||||||
|
Question // ?
|
||||||
|
_operatorEnd
|
||||||
|
_keywordBeg
|
||||||
|
Break
|
||||||
|
Continue
|
||||||
|
Else
|
||||||
|
For
|
||||||
|
Func
|
||||||
|
Error
|
||||||
|
Immutable
|
||||||
|
If
|
||||||
|
Return
|
||||||
|
Export
|
||||||
|
True
|
||||||
|
False
|
||||||
|
In
|
||||||
|
Undefined
|
||||||
|
Import
|
||||||
|
_keywordEnd
|
||||||
|
)
|
||||||
|
|
||||||
|
var tokens = [...]string{
|
||||||
|
Illegal: "ILLEGAL",
|
||||||
|
EOF: "EOF",
|
||||||
|
Comment: "COMMENT",
|
||||||
|
Ident: "IDENT",
|
||||||
|
Int: "INT",
|
||||||
|
Float: "FLOAT",
|
||||||
|
Char: "CHAR",
|
||||||
|
String: "STRING",
|
||||||
|
Add: "+",
|
||||||
|
Sub: "-",
|
||||||
|
Mul: "*",
|
||||||
|
Quo: "/",
|
||||||
|
Rem: "%",
|
||||||
|
And: "&",
|
||||||
|
Or: "|",
|
||||||
|
Xor: "^",
|
||||||
|
Shl: "<<",
|
||||||
|
Shr: ">>",
|
||||||
|
AndNot: "&^",
|
||||||
|
AddAssign: "+=",
|
||||||
|
SubAssign: "-=",
|
||||||
|
MulAssign: "*=",
|
||||||
|
QuoAssign: "/=",
|
||||||
|
RemAssign: "%=",
|
||||||
|
AndAssign: "&=",
|
||||||
|
OrAssign: "|=",
|
||||||
|
XorAssign: "^=",
|
||||||
|
ShlAssign: "<<=",
|
||||||
|
ShrAssign: ">>=",
|
||||||
|
AndNotAssign: "&^=",
|
||||||
|
LAnd: "&&",
|
||||||
|
LOr: "||",
|
||||||
|
Inc: "++",
|
||||||
|
Dec: "--",
|
||||||
|
Equal: "==",
|
||||||
|
Less: "<",
|
||||||
|
Greater: ">",
|
||||||
|
Assign: "=",
|
||||||
|
Not: "!",
|
||||||
|
NotEqual: "!=",
|
||||||
|
LessEq: "<=",
|
||||||
|
GreaterEq: ">=",
|
||||||
|
Define: ":=",
|
||||||
|
Ellipsis: "...",
|
||||||
|
LParen: "(",
|
||||||
|
LBrack: "[",
|
||||||
|
LBrace: "{",
|
||||||
|
Comma: ",",
|
||||||
|
Period: ".",
|
||||||
|
RParen: ")",
|
||||||
|
RBrack: "]",
|
||||||
|
RBrace: "}",
|
||||||
|
Semicolon: ";",
|
||||||
|
Colon: ":",
|
||||||
|
Question: "?",
|
||||||
|
Break: "break",
|
||||||
|
Continue: "continue",
|
||||||
|
Else: "else",
|
||||||
|
For: "for",
|
||||||
|
Func: "func",
|
||||||
|
Error: "error",
|
||||||
|
Immutable: "immutable",
|
||||||
|
If: "if",
|
||||||
|
Return: "return",
|
||||||
|
Export: "export",
|
||||||
|
True: "true",
|
||||||
|
False: "false",
|
||||||
|
In: "in",
|
||||||
|
Undefined: "undefined",
|
||||||
|
Import: "import",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tok Token) String() string {
|
||||||
|
s := ""
|
||||||
|
|
||||||
|
if 0 <= tok && tok < Token(len(tokens)) {
|
||||||
|
s = tokens[tok]
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
s = "token(" + strconv.Itoa(int(tok)) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// LowestPrec represents lowest operator precedence.
|
||||||
|
const LowestPrec = 0
|
||||||
|
|
||||||
|
// Precedence returns the precedence for the operator token.
|
||||||
|
func (tok Token) Precedence() int {
|
||||||
|
switch tok {
|
||||||
|
case LOr:
|
||||||
|
return 1
|
||||||
|
case LAnd:
|
||||||
|
return 2
|
||||||
|
case Equal, NotEqual, Less, LessEq, Greater, GreaterEq:
|
||||||
|
return 3
|
||||||
|
case Add, Sub, Or, Xor:
|
||||||
|
return 4
|
||||||
|
case Mul, Quo, Rem, Shl, Shr, And, AndNot:
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
return LowestPrec
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLiteral returns true if the token is a literal.
|
||||||
|
func (tok Token) IsLiteral() bool {
|
||||||
|
return _literalBeg < tok && tok < _literalEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOperator returns true if the token is an operator.
|
||||||
|
func (tok Token) IsOperator() bool {
|
||||||
|
return _operatorBeg < tok && tok < _operatorEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsKeyword returns true if the token is a keyword.
|
||||||
|
func (tok Token) IsKeyword() bool {
|
||||||
|
return _keywordBeg < tok && tok < _keywordEnd
|
||||||
|
}
|
130
vendor/github.com/d5/tengo/objects/array.go
generated
vendored
Normal file
130
vendor/github.com/d5/tengo/objects/array.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Array represents an array of objects.
|
||||||
|
type Array struct {
|
||||||
|
Value []Object
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *Array) TypeName() string {
|
||||||
|
return "array"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Array) String() string {
|
||||||
|
var elements []string
|
||||||
|
for _, e := range o.Value {
|
||||||
|
elements = append(elements, e.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
if rhs, ok := rhs.(*Array); ok {
|
||||||
|
switch op {
|
||||||
|
case token.Add:
|
||||||
|
if len(rhs.Value) == 0 {
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
return &Array{Value: append(o.Value, rhs.Value...)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *Array) Copy() Object {
|
||||||
|
var c []Object
|
||||||
|
for _, elem := range o.Value {
|
||||||
|
c = append(c, elem.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Array{Value: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *Array) IsFalsy() bool {
|
||||||
|
return len(o.Value) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *Array) Equals(x Object) bool {
|
||||||
|
var xVal []Object
|
||||||
|
switch x := x.(type) {
|
||||||
|
case *Array:
|
||||||
|
xVal = x.Value
|
||||||
|
case *ImmutableArray:
|
||||||
|
xVal = x.Value
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(o.Value) != len(xVal) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, e := range o.Value {
|
||||||
|
if !e.Equals(xVal[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexGet returns an element at a given index.
|
||||||
|
func (o *Array) IndexGet(index Object) (res Object, err error) {
|
||||||
|
intIdx, ok := index.(*Int)
|
||||||
|
if !ok {
|
||||||
|
err = ErrInvalidIndexType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
idxVal := int(intIdx.Value)
|
||||||
|
|
||||||
|
if idxVal < 0 || idxVal >= len(o.Value) {
|
||||||
|
res = UndefinedValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = o.Value[idxVal]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexSet sets an element at a given index.
|
||||||
|
func (o *Array) IndexSet(index, value Object) (err error) {
|
||||||
|
intIdx, ok := ToInt(index)
|
||||||
|
if !ok {
|
||||||
|
err = ErrInvalidIndexType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if intIdx < 0 || intIdx >= len(o.Value) {
|
||||||
|
err = ErrIndexOutOfBounds
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Value[intIdx] = value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate creates an array iterator.
|
||||||
|
func (o *Array) Iterate() Iterator {
|
||||||
|
return &ArrayIterator{
|
||||||
|
v: o.Value,
|
||||||
|
l: len(o.Value),
|
||||||
|
}
|
||||||
|
}
|
57
vendor/github.com/d5/tengo/objects/array_iterator.go
generated
vendored
Normal file
57
vendor/github.com/d5/tengo/objects/array_iterator.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/token"
|
||||||
|
|
||||||
|
// ArrayIterator is an iterator for an array.
|
||||||
|
type ArrayIterator struct {
|
||||||
|
v []Object
|
||||||
|
i int
|
||||||
|
l int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (i *ArrayIterator) TypeName() string {
|
||||||
|
return "array-iterator"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ArrayIterator) String() string {
|
||||||
|
return "<array-iterator>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (i *ArrayIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (i *ArrayIterator) IsFalsy() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (i *ArrayIterator) Equals(Object) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (i *ArrayIterator) Copy() Object {
|
||||||
|
return &ArrayIterator{v: i.v, i: i.i, l: i.l}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns true if there are more elements to iterate.
|
||||||
|
func (i *ArrayIterator) Next() bool {
|
||||||
|
i.i++
|
||||||
|
return i.i <= i.l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the key or index value of the current element.
|
||||||
|
func (i *ArrayIterator) Key() Object {
|
||||||
|
return &Int{Value: int64(i.i - 1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value of the current element.
|
||||||
|
func (i *ArrayIterator) Value() Object {
|
||||||
|
return i.v[i.i-1]
|
||||||
|
}
|
64
vendor/github.com/d5/tengo/objects/bool.go
generated
vendored
Normal file
64
vendor/github.com/d5/tengo/objects/bool.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bool represents a boolean value.
|
||||||
|
type Bool struct {
|
||||||
|
// this is intentionally non-public to force using objects.TrueValue and FalseValue always
|
||||||
|
value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Bool) String() string {
|
||||||
|
if o.value {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *Bool) TypeName() string {
|
||||||
|
return "bool"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *Bool) Copy() Object {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *Bool) IsFalsy() bool {
|
||||||
|
return !o.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *Bool) Equals(x Object) bool {
|
||||||
|
return o == x
|
||||||
|
}
|
||||||
|
|
||||||
|
// GobDecode decodes bool value from input bytes.
|
||||||
|
func (o *Bool) GobDecode(b []byte) (err error) {
|
||||||
|
o.value = b[0] == 1
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GobEncode encodes bool values into bytes.
|
||||||
|
func (o *Bool) GobEncode() (b []byte, err error) {
|
||||||
|
if o.value {
|
||||||
|
b = []byte{1}
|
||||||
|
} else {
|
||||||
|
b = []byte{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
37
vendor/github.com/d5/tengo/objects/break.go
generated
vendored
Normal file
37
vendor/github.com/d5/tengo/objects/break.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/token"
|
||||||
|
|
||||||
|
// Break represents a break statement.
|
||||||
|
type Break struct{}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *Break) TypeName() string {
|
||||||
|
return "break"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Break) String() string {
|
||||||
|
return "<break>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *Break) Copy() Object {
|
||||||
|
return &Break{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *Break) IsFalsy() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *Break) Equals(x Object) bool {
|
||||||
|
return false
|
||||||
|
}
|
21
vendor/github.com/d5/tengo/objects/builtin_append.go
generated
vendored
Normal file
21
vendor/github.com/d5/tengo/objects/builtin_append.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// append(arr, items...)
|
||||||
|
func builtinAppend(args ...Object) (Object, error) {
|
||||||
|
if len(args) < 2 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
switch arg := args[0].(type) {
|
||||||
|
case *Array:
|
||||||
|
return &Array{Value: append(arg.Value, args[1:]...)}, nil
|
||||||
|
case *ImmutableArray:
|
||||||
|
return &Array{Value: append(arg.Value, args[1:]...)}, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "array",
|
||||||
|
Found: arg.TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
vendor/github.com/d5/tengo/objects/builtin_convert.go
generated
vendored
Normal file
155
vendor/github.com/d5/tengo/objects/builtin_convert.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
func builtinString(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if !(argsLen == 1 || argsLen == 2) {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*String); ok {
|
||||||
|
return args[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToString(args[0])
|
||||||
|
if ok {
|
||||||
|
return &String{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsLen == 2 {
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinInt(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if !(argsLen == 1 || argsLen == 2) {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Int); ok {
|
||||||
|
return args[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToInt64(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Int{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsLen == 2 {
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinFloat(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if !(argsLen == 1 || argsLen == 2) {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Float); ok {
|
||||||
|
return args[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToFloat64(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Float{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsLen == 2 {
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinBool(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Bool); ok {
|
||||||
|
return args[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToBool(args[0])
|
||||||
|
if ok {
|
||||||
|
if v {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinChar(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if !(argsLen == 1 || argsLen == 2) {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Char); ok {
|
||||||
|
return args[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToRune(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Char{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsLen == 2 {
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinBytes(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if !(argsLen == 1 || argsLen == 2) {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytes(N) => create a new bytes with given size N
|
||||||
|
if n, ok := args[0].(*Int); ok {
|
||||||
|
return &Bytes{Value: make([]byte, int(n.Value))}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToByteSlice(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Bytes{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsLen == 2 {
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinTime(args ...Object) (Object, error) {
|
||||||
|
argsLen := len(args)
|
||||||
|
if !(argsLen == 1 || argsLen == 2) {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Time); ok {
|
||||||
|
return args[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToTime(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Time{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsLen == 2 {
|
||||||
|
return args[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
|
}
|
9
vendor/github.com/d5/tengo/objects/builtin_copy.go
generated
vendored
Normal file
9
vendor/github.com/d5/tengo/objects/builtin_copy.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
func builtinCopy(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return args[0].Copy(), nil
|
||||||
|
}
|
47
vendor/github.com/d5/tengo/objects/builtin_function.go
generated
vendored
Normal file
47
vendor/github.com/d5/tengo/objects/builtin_function.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuiltinFunction represents a builtin function.
|
||||||
|
type BuiltinFunction struct {
|
||||||
|
Name string
|
||||||
|
Value CallableFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *BuiltinFunction) TypeName() string {
|
||||||
|
return "builtin-function:" + o.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *BuiltinFunction) String() string {
|
||||||
|
return "<builtin-function>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *BuiltinFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *BuiltinFunction) Copy() Object {
|
||||||
|
return &BuiltinFunction{Value: o.Value}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *BuiltinFunction) IsFalsy() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *BuiltinFunction) Equals(x Object) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call executes a builtin function.
|
||||||
|
func (o *BuiltinFunction) Call(args ...Object) (Object, error) {
|
||||||
|
return o.Value(args...)
|
||||||
|
}
|
54
vendor/github.com/d5/tengo/objects/builtin_json.go
generated
vendored
Normal file
54
vendor/github.com/d5/tengo/objects/builtin_json.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// to_json(v object) => bytes
|
||||||
|
func builtinToJSON(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := json.Marshal(objectToInterface(args[0]))
|
||||||
|
if err != nil {
|
||||||
|
return &Error{Value: &String{Value: err.Error()}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Bytes{Value: res}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// from_json(data string/bytes) => object
|
||||||
|
func builtinFromJSON(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
var target interface{}
|
||||||
|
|
||||||
|
switch o := args[0].(type) {
|
||||||
|
case *Bytes:
|
||||||
|
err := json.Unmarshal(o.Value, &target)
|
||||||
|
if err != nil {
|
||||||
|
return &Error{Value: &String{Value: err.Error()}}, nil
|
||||||
|
}
|
||||||
|
case *String:
|
||||||
|
err := json.Unmarshal([]byte(o.Value), &target)
|
||||||
|
if err != nil {
|
||||||
|
return &Error{Value: &String{Value: err.Error()}}, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "bytes/string",
|
||||||
|
Found: args[0].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := FromInterface(target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
29
vendor/github.com/d5/tengo/objects/builtin_len.go
generated
vendored
Normal file
29
vendor/github.com/d5/tengo/objects/builtin_len.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// len(obj object) => int
|
||||||
|
func builtinLen(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
switch arg := args[0].(type) {
|
||||||
|
case *Array:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *ImmutableArray:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *String:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *Bytes:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *Map:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *ImmutableMap:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "array/string/bytes/map",
|
||||||
|
Found: arg.TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
vendor/github.com/d5/tengo/objects/builtin_print.go
generated
vendored
Normal file
75
vendor/github.com/d5/tengo/objects/builtin_print.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// print(args...)
|
||||||
|
func builtinPrint(args ...Object) (Object, error) {
|
||||||
|
for _, arg := range args {
|
||||||
|
if str, ok := arg.(*String); ok {
|
||||||
|
fmt.Println(str.Value)
|
||||||
|
} else {
|
||||||
|
fmt.Println(arg.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("format", args...)
|
||||||
|
func builtinPrintf(args ...Object) (Object, error) {
|
||||||
|
numArgs := len(args)
|
||||||
|
if numArgs == 0 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
format, ok := args[0].(*String)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "format",
|
||||||
|
Expected: "string",
|
||||||
|
Found: args[0].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if numArgs == 1 {
|
||||||
|
fmt.Print(format)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
formatArgs := make([]interface{}, numArgs-1, numArgs-1)
|
||||||
|
for idx, arg := range args[1:] {
|
||||||
|
formatArgs[idx] = objectToInterface(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(format.Value, formatArgs...)
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sprintf("format", args...)
|
||||||
|
func builtinSprintf(args ...Object) (Object, error) {
|
||||||
|
numArgs := len(args)
|
||||||
|
if numArgs == 0 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
format, ok := args[0].(*String)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInvalidArgumentType{
|
||||||
|
Name: "format",
|
||||||
|
Expected: "string",
|
||||||
|
Found: args[0].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if numArgs == 1 {
|
||||||
|
return format, nil // okay to return 'format' directly as String is immutable
|
||||||
|
}
|
||||||
|
|
||||||
|
formatArgs := make([]interface{}, numArgs-1, numArgs-1)
|
||||||
|
for idx, arg := range args[1:] {
|
||||||
|
formatArgs[idx] = objectToInterface(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &String{Value: fmt.Sprintf(format.Value, formatArgs...)}, nil
|
||||||
|
}
|
9
vendor/github.com/d5/tengo/objects/builtin_type.go
generated
vendored
Normal file
9
vendor/github.com/d5/tengo/objects/builtin_type.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
func builtinTypeName(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return &String{Value: args[0].TypeName()}, nil
|
||||||
|
}
|
183
vendor/github.com/d5/tengo/objects/builtin_type_checks.go
generated
vendored
Normal file
183
vendor/github.com/d5/tengo/objects/builtin_type_checks.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
func builtinIsString(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*String); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsInt(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Int); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsFloat(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Float); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsBool(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Bool); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsChar(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Char); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsBytes(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Bytes); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsArray(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Array); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsImmutableArray(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*ImmutableArray); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsMap(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Map); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsImmutableMap(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*ImmutableMap); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsTime(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Time); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsError(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Error); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsUndefined(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0] == UndefinedValue {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsFunction(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0].(type) {
|
||||||
|
case *CompiledFunction, *Closure:
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsCallable(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0].(type) {
|
||||||
|
case *CompiledFunction, *Closure, Callable: // BuiltinFunction is Callable
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
135
vendor/github.com/d5/tengo/objects/builtins.go
generated
vendored
Normal file
135
vendor/github.com/d5/tengo/objects/builtins.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// NamedBuiltinFunc is a named builtin function.
|
||||||
|
type NamedBuiltinFunc struct {
|
||||||
|
Name string
|
||||||
|
Func CallableFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builtins contains all default builtin functions.
|
||||||
|
var Builtins = []NamedBuiltinFunc{
|
||||||
|
{
|
||||||
|
Name: "print",
|
||||||
|
Func: builtinPrint,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "printf",
|
||||||
|
Func: builtinPrintf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "sprintf",
|
||||||
|
Func: builtinSprintf,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "len",
|
||||||
|
Func: builtinLen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "copy",
|
||||||
|
Func: builtinCopy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "append",
|
||||||
|
Func: builtinAppend,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "string",
|
||||||
|
Func: builtinString,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "int",
|
||||||
|
Func: builtinInt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bool",
|
||||||
|
Func: builtinBool,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "float",
|
||||||
|
Func: builtinFloat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "char",
|
||||||
|
Func: builtinChar,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bytes",
|
||||||
|
Func: builtinBytes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Func: builtinTime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_int",
|
||||||
|
Func: builtinIsInt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_float",
|
||||||
|
Func: builtinIsFloat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_string",
|
||||||
|
Func: builtinIsString,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_bool",
|
||||||
|
Func: builtinIsBool,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_char",
|
||||||
|
Func: builtinIsChar,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_bytes",
|
||||||
|
Func: builtinIsBytes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_array",
|
||||||
|
Func: builtinIsArray,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_immutable_array",
|
||||||
|
Func: builtinIsImmutableArray,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_map",
|
||||||
|
Func: builtinIsMap,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_immutable_map",
|
||||||
|
Func: builtinIsImmutableMap,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_time",
|
||||||
|
Func: builtinIsTime,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_error",
|
||||||
|
Func: builtinIsError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_undefined",
|
||||||
|
Func: builtinIsUndefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_function",
|
||||||
|
Func: builtinIsFunction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_callable",
|
||||||
|
Func: builtinIsCallable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "to_json",
|
||||||
|
Func: builtinToJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "from_json",
|
||||||
|
Func: builtinFromJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "type_name",
|
||||||
|
Func: builtinTypeName,
|
||||||
|
},
|
||||||
|
}
|
76
vendor/github.com/d5/tengo/objects/bytes.go
generated
vendored
Normal file
76
vendor/github.com/d5/tengo/objects/bytes.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bytes represents a byte array.
|
||||||
|
type Bytes struct {
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Bytes) String() string {
|
||||||
|
return string(o.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *Bytes) TypeName() string {
|
||||||
|
return "bytes"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
switch op {
|
||||||
|
case token.Add:
|
||||||
|
switch rhs := rhs.(type) {
|
||||||
|
case *Bytes:
|
||||||
|
return &Bytes{Value: append(o.Value, rhs.Value...)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *Bytes) Copy() Object {
|
||||||
|
return &Bytes{Value: append([]byte{}, o.Value...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *Bytes) IsFalsy() bool {
|
||||||
|
return len(o.Value) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *Bytes) Equals(x Object) bool {
|
||||||
|
t, ok := x.(*Bytes)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.Compare(o.Value, t.Value) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexGet returns an element (as Int) at a given index.
|
||||||
|
func (o *Bytes) IndexGet(index Object) (res Object, err error) {
|
||||||
|
intIdx, ok := index.(*Int)
|
||||||
|
if !ok {
|
||||||
|
err = ErrInvalidIndexType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
idxVal := int(intIdx.Value)
|
||||||
|
|
||||||
|
if idxVal < 0 || idxVal >= len(o.Value) {
|
||||||
|
res = UndefinedValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = &Int{Value: int64(o.Value[idxVal])}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user