Create own plurals subpackage
fix Typo
This commit is contained in:
8
Gopkg.lock
generated
8
Gopkg.lock
generated
@@ -1,15 +1,9 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/mattn/kinako"
|
|
||||||
packages = ["ast","parser","vm"]
|
|
||||||
revision = "332c0a7e205a29536e672337a4bea6c7a96b04c1"
|
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "d3069fabe2d6f79fe33ad88133e861db84aef0400f6b949c4e64395913b3ae97"
|
inputs-digest = "ab4fef131ee828e96ba67d31a7d690bd5f2f42040c6766b1b12fe856f87e0ff7"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|||||||
14
Gopkg.toml
14
Gopkg.toml
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
# Gopkg.toml example
|
# Gopkg.toml example
|
||||||
#
|
#
|
||||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||||
# for detailed Gopkg.toml documentation.
|
# for detailed Gopkg.toml documentation.
|
||||||
#
|
#
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
@@ -19,8 +18,13 @@
|
|||||||
# [[override]]
|
# [[override]]
|
||||||
# name = "github.com/x/y"
|
# name = "github.com/x/y"
|
||||||
# version = "2.4.0"
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
[[constraint]]
|
[prune]
|
||||||
branch = "master"
|
go-tests = true
|
||||||
name = "github.com/mattn/kinako"
|
unused-packages = true
|
||||||
|
|||||||
431
plurals/compiler.go
Normal file
431
plurals/compiler.go
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
*/
|
||||||
|
package plurals
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type match struct {
|
||||||
|
openPos int
|
||||||
|
closePos int
|
||||||
|
}
|
||||||
|
|
||||||
|
var pat = regexp.MustCompile(`(\?|:|\|\||&&|==|!=|>=|>|<=|<|%|\d+|n)`)
|
||||||
|
|
||||||
|
type exprToken interface {
|
||||||
|
compile(tokens []string) (expr Expression, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type testToken interface {
|
||||||
|
compile(tokens []string) (test test, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmpTestBuilder func(val uint32, flipped bool) test
|
||||||
|
type logicTestBuild func(left test, right test) test
|
||||||
|
|
||||||
|
var ternaryToken ternaryStruct
|
||||||
|
|
||||||
|
type ternaryStruct struct{}
|
||||||
|
|
||||||
|
func (ternaryStruct) compile(tokens []string) (expr Expression, err error) {
|
||||||
|
main, err := splitTokens(tokens, "?")
|
||||||
|
if err != nil {
|
||||||
|
return expr, err
|
||||||
|
}
|
||||||
|
test, err := compileTest(strings.Join(main.Left, ""))
|
||||||
|
if err != nil {
|
||||||
|
return expr, err
|
||||||
|
}
|
||||||
|
actions, err := splitTokens(main.Right, ":")
|
||||||
|
if err != nil {
|
||||||
|
return expr, err
|
||||||
|
}
|
||||||
|
true_action, err := compileExpression(strings.Join(actions.Left, ""))
|
||||||
|
if err != nil {
|
||||||
|
return expr, err
|
||||||
|
}
|
||||||
|
false_action, err := compileExpression(strings.Join(actions.Right, ""))
|
||||||
|
if err != nil {
|
||||||
|
return expr, nil
|
||||||
|
}
|
||||||
|
return ternary{
|
||||||
|
test: test,
|
||||||
|
trueExpr: true_action,
|
||||||
|
falseExpr: false_action,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var constToken constValStruct
|
||||||
|
|
||||||
|
type constValStruct struct{}
|
||||||
|
|
||||||
|
func (constValStruct) compile(tokens []string) (expr Expression, err error) {
|
||||||
|
if len(tokens) == 0 {
|
||||||
|
return expr, errors.New("got nothing instead of constant")
|
||||||
|
}
|
||||||
|
if len(tokens) != 1 {
|
||||||
|
return expr, fmt.Errorf("invalid constant: %s", strings.Join(tokens, ""))
|
||||||
|
}
|
||||||
|
i, err := strconv.Atoi(tokens[0])
|
||||||
|
if err != nil {
|
||||||
|
return expr, err
|
||||||
|
}
|
||||||
|
return constValue{value: i}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileLogicTest(tokens []string, sep string, builder logicTestBuild) (test test, err error) {
|
||||||
|
split, err := splitTokens(tokens, sep)
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
left, err := compileTest(strings.Join(split.Left, ""))
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
right, err := compileTest(strings.Join(split.Right, ""))
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
return builder(left, right), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var orToken orStruct
|
||||||
|
|
||||||
|
type orStruct struct{}
|
||||||
|
|
||||||
|
func (orStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileLogicTest(tokens, "||", buildOr)
|
||||||
|
}
|
||||||
|
func buildOr(left test, right test) test {
|
||||||
|
return or{left: left, right: right}
|
||||||
|
}
|
||||||
|
|
||||||
|
var andToken andStruct
|
||||||
|
|
||||||
|
type andStruct struct{}
|
||||||
|
|
||||||
|
func (andStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileLogicTest(tokens, "&&", buildAnd)
|
||||||
|
}
|
||||||
|
func buildAnd(left test, right test) test {
|
||||||
|
return and{left: left, right: right}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileMod(tokens []string) (math math, err error) {
|
||||||
|
split, err := splitTokens(tokens, "%")
|
||||||
|
if err != nil {
|
||||||
|
return math, err
|
||||||
|
}
|
||||||
|
if len(split.Left) != 1 || split.Left[0] != "n" {
|
||||||
|
return math, errors.New("Modulus operation requires 'n' as left operand")
|
||||||
|
}
|
||||||
|
if len(split.Right) != 1 {
|
||||||
|
return math, errors.New("Modulus operation requires simple integer as right operand")
|
||||||
|
}
|
||||||
|
i, err := parseUint32(split.Right[0])
|
||||||
|
if err != nil {
|
||||||
|
return math, err
|
||||||
|
}
|
||||||
|
return mod{value: uint32(i)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func subPipe(modTokens []string, actionTokens []string, builder cmpTestBuilder, flipped bool) (test test, err error) {
|
||||||
|
modifier, err := compileMod(modTokens)
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
if len(actionTokens) != 1 {
|
||||||
|
return test, errors.New("can only get modulus of integer")
|
||||||
|
}
|
||||||
|
i, err := parseUint32(actionTokens[0])
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
action := builder(uint32(i), flipped)
|
||||||
|
return pipe{
|
||||||
|
modifier: modifier,
|
||||||
|
action: action,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileEquality(tokens []string, sep string, builder cmpTestBuilder) (test test, err error) {
|
||||||
|
split, err := splitTokens(tokens, sep)
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
if len(split.Left) == 1 && split.Left[0] == "n" {
|
||||||
|
if len(split.Right) != 1 {
|
||||||
|
return test, errors.New("test can only compare n to integers")
|
||||||
|
}
|
||||||
|
i, err := parseUint32(split.Right[0])
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
return builder(i, false), nil
|
||||||
|
} else if len(split.Right) == 1 && split.Right[0] == "n" {
|
||||||
|
if len(split.Left) != 1 {
|
||||||
|
return test, errors.New("test can only compare n to integers")
|
||||||
|
}
|
||||||
|
i, err := parseUint32(split.Left[0])
|
||||||
|
if err != nil {
|
||||||
|
return test, err
|
||||||
|
}
|
||||||
|
return builder(i, true), nil
|
||||||
|
} else if contains(split.Left, "n") && contains(split.Left, "%") {
|
||||||
|
return subPipe(split.Left, split.Right, builder, false)
|
||||||
|
} else {
|
||||||
|
return test, errors.New("equality test must have 'n' as one of the two tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var eqToken eqStruct
|
||||||
|
|
||||||
|
type eqStruct struct{}
|
||||||
|
|
||||||
|
func (eqStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileEquality(tokens, "==", buildEq)
|
||||||
|
}
|
||||||
|
func buildEq(val uint32, flipped bool) test {
|
||||||
|
return equal{value: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
var neqToken neqStruct
|
||||||
|
|
||||||
|
type neqStruct struct{}
|
||||||
|
|
||||||
|
func (neqStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileEquality(tokens, "!=", buildNeq)
|
||||||
|
}
|
||||||
|
func buildNeq(val uint32, flipped bool) test {
|
||||||
|
return notequal{value: val}
|
||||||
|
}
|
||||||
|
|
||||||
|
var gtToken gtStruct
|
||||||
|
|
||||||
|
type gtStruct struct{}
|
||||||
|
|
||||||
|
func (gtStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileEquality(tokens, ">", buildGt)
|
||||||
|
}
|
||||||
|
func buildGt(val uint32, flipped bool) test {
|
||||||
|
return gt{value: val, flipped: flipped}
|
||||||
|
}
|
||||||
|
|
||||||
|
var gteToken gteStruct
|
||||||
|
|
||||||
|
type gteStruct struct{}
|
||||||
|
|
||||||
|
func (gteStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileEquality(tokens, ">=", buildGte)
|
||||||
|
}
|
||||||
|
func buildGte(val uint32, flipped bool) test {
|
||||||
|
return gte{value: val, flipped: flipped}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ltToken ltStruct
|
||||||
|
|
||||||
|
type ltStruct struct{}
|
||||||
|
|
||||||
|
func (ltStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileEquality(tokens, "<", buildLt)
|
||||||
|
}
|
||||||
|
func buildLt(val uint32, flipped bool) test {
|
||||||
|
return lt{value: val, flipped: flipped}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lteToken lteStruct
|
||||||
|
|
||||||
|
type lteStruct struct{}
|
||||||
|
|
||||||
|
func (lteStruct) compile(tokens []string) (test test, err error) {
|
||||||
|
return compileEquality(tokens, "<=", buildLte)
|
||||||
|
}
|
||||||
|
func buildLte(val uint32, flipped bool) test {
|
||||||
|
return lte{value: val, flipped: flipped}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testTokenDef struct {
|
||||||
|
op string
|
||||||
|
token testToken
|
||||||
|
}
|
||||||
|
|
||||||
|
var precedence = []testTokenDef{
|
||||||
|
{op: "||", token: orToken},
|
||||||
|
{op: "&&", token: andToken},
|
||||||
|
{op: "==", token: eqToken},
|
||||||
|
{op: "!=", token: neqToken},
|
||||||
|
{op: ">=", token: gteToken},
|
||||||
|
{op: ">", token: gtToken},
|
||||||
|
{op: "<=", token: lteToken},
|
||||||
|
{op: "<", token: ltToken},
|
||||||
|
}
|
||||||
|
|
||||||
|
type splitted struct {
|
||||||
|
Left []string
|
||||||
|
Right []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find index of token in list of tokens
|
||||||
|
func index(tokens []string, sep string) int {
|
||||||
|
for index, token := range tokens {
|
||||||
|
if token == sep {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split a list of tokens by a token into a splitted struct holding the tokens
|
||||||
|
// before and after the token to be split by.
|
||||||
|
func splitTokens(tokens []string, sep string) (s splitted, err error) {
|
||||||
|
index := index(tokens, sep)
|
||||||
|
if index == -1 {
|
||||||
|
return s, fmt.Errorf("'%s' not found in ['%s']", sep, strings.Join(tokens, "','"))
|
||||||
|
}
|
||||||
|
return splitted{
|
||||||
|
Left: tokens[:index],
|
||||||
|
Right: tokens[index+1:],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan a string for parenthesis
|
||||||
|
func scan(s string) <-chan match {
|
||||||
|
ch := make(chan match)
|
||||||
|
go func() {
|
||||||
|
depth := 0
|
||||||
|
opener := 0
|
||||||
|
for index, char := range s {
|
||||||
|
switch char {
|
||||||
|
case '(':
|
||||||
|
if depth == 0 {
|
||||||
|
opener = index
|
||||||
|
}
|
||||||
|
depth++
|
||||||
|
case ')':
|
||||||
|
depth--
|
||||||
|
if depth == 0 {
|
||||||
|
ch <- match{
|
||||||
|
openPos: opener,
|
||||||
|
closePos: index + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the string into tokens
|
||||||
|
func split(s string) <- chan string {
|
||||||
|
ch := make(chan string)
|
||||||
|
go func() {
|
||||||
|
s = strings.Replace(s, " ", "", -1)
|
||||||
|
if !strings.Contains(s, "(") {
|
||||||
|
ch <- s
|
||||||
|
} else {
|
||||||
|
last := 0
|
||||||
|
end := len(s)
|
||||||
|
for info := range scan(s) {
|
||||||
|
if last != info.openPos {
|
||||||
|
ch <- s[last:info.openPos]
|
||||||
|
}
|
||||||
|
ch <- s[info.openPos:info.closePos]
|
||||||
|
last = info.closePos
|
||||||
|
}
|
||||||
|
if last != end {
|
||||||
|
ch <- s[last:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tokenizes a string into a list of strings, tokens grouped by parenthesis are
|
||||||
|
// not split! If the string starts with ( and ends in ), those are stripped.
|
||||||
|
func tokenize(s string) []string {
|
||||||
|
/*
|
||||||
|
TODO: Properly detect if the string starts with a ( and ends with a )
|
||||||
|
and that those two form a matching pair.
|
||||||
|
|
||||||
|
Eg: (foo) -> true; (foo)(bar) -> false;
|
||||||
|
*/
|
||||||
|
if s[0] == '(' && s[len(s)-1] == ')' {
|
||||||
|
s = s[1 : len(s)-1]
|
||||||
|
}
|
||||||
|
ret := []string{}
|
||||||
|
for chunk := range split(s) {
|
||||||
|
if len(chunk) != 0 {
|
||||||
|
if chunk[0] == '(' && chunk[len(chunk)-1] == ')' {
|
||||||
|
ret = append(ret, chunk)
|
||||||
|
} else {
|
||||||
|
for _, token := range pat.FindAllStringSubmatch(chunk, -1) {
|
||||||
|
ret = append(ret, token[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Empty chunk in string '%s'\n", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile a string containing a plural form expression to a Expression object.
|
||||||
|
func Compile(s string) (expr Expression, err error) {
|
||||||
|
if s == "0" {
|
||||||
|
return constValue{value: 0}, nil
|
||||||
|
}
|
||||||
|
if !strings.Contains(s, "?") {
|
||||||
|
s += "?1:0"
|
||||||
|
}
|
||||||
|
return compileExpression(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a token is in a slice of strings
|
||||||
|
func contains(haystack []string, needle string) bool {
|
||||||
|
for _, s := range haystack {
|
||||||
|
if s == needle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles an expression (ternary or constant)
|
||||||
|
func compileExpression(s string) (expr Expression, err error) {
|
||||||
|
tokens := tokenize(s)
|
||||||
|
if contains(tokens, "?") {
|
||||||
|
return ternaryToken.compile(tokens)
|
||||||
|
} else {
|
||||||
|
return constToken.compile(tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles a test (comparison)
|
||||||
|
func compileTest(s string) (test test, err error) {
|
||||||
|
tokens := tokenize(s)
|
||||||
|
for _, tokenDef := range precedence {
|
||||||
|
if contains(tokens, tokenDef.op) {
|
||||||
|
return tokenDef.token.compile(tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return test, errors.New("cannot compile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint32(s string) (ui uint32, err error) {
|
||||||
|
i, err := strconv.ParseUint(s, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return ui, err
|
||||||
|
} else {
|
||||||
|
return uint32(i), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
50
plurals/compiler_test.go
Normal file
50
plurals/compiler_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package plurals
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fixture struct {
|
||||||
|
PluralForm string
|
||||||
|
Fixture []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompiler(t *testing.T) {
|
||||||
|
f, err := os.Open("testdata/pluralforms.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dec := json.NewDecoder(f)
|
||||||
|
var fixtures []fixture
|
||||||
|
err = dec.Decode(&fixtures)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, data := range fixtures {
|
||||||
|
expr, err := Compile(data.PluralForm)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("'%s' triggered error: %s", data.PluralForm, err)
|
||||||
|
} else if expr == nil {
|
||||||
|
t.Logf("'%s' compiled to nil", data.PluralForm)
|
||||||
|
t.Fail()
|
||||||
|
} else {
|
||||||
|
for n, e := range data.Fixture {
|
||||||
|
i := expr.Eval(uint32(n))
|
||||||
|
if i != e {
|
||||||
|
t.Logf("'%s' with n = %d, expected %d, got %d, compiled to %s", data.PluralForm, n, e, i, expr)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if i == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
plurals/expression.go
Normal file
44
plurals/expression.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package plurals
|
||||||
|
|
||||||
|
// Expression is a plurals expression. Eval evaluates the expression for
|
||||||
|
// a given n value. Use plurals.Compile to generate Expression instances.
|
||||||
|
type Expression interface {
|
||||||
|
Eval(n uint32) int
|
||||||
|
}
|
||||||
|
|
||||||
|
type constValue struct {
|
||||||
|
value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c constValue) Eval(n uint32) int {
|
||||||
|
return c.value
|
||||||
|
}
|
||||||
|
|
||||||
|
type test interface {
|
||||||
|
test(n uint32) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ternary struct {
|
||||||
|
test test
|
||||||
|
trueExpr Expression
|
||||||
|
falseExpr Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t ternary) Eval(n uint32) int {
|
||||||
|
if t.test.test(n) {
|
||||||
|
if t.trueExpr == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return t.trueExpr.Eval(n)
|
||||||
|
} else {
|
||||||
|
if t.falseExpr == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return t.falseExpr.Eval(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
18
plurals/math.go
Normal file
18
plurals/math.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package plurals
|
||||||
|
|
||||||
|
type math interface {
|
||||||
|
calc(n uint32) uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type mod struct {
|
||||||
|
value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mod) calc(n uint32) uint32 {
|
||||||
|
return n % m.value
|
||||||
|
}
|
||||||
1
plurals/testdata/pluralforms.json
vendored
Normal file
1
plurals/testdata/pluralforms.json
vendored
Normal file
File diff suppressed because one or more lines are too long
109
plurals/tests.go
Normal file
109
plurals/tests.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 DeineAgentur UG https://www.deineagentur.com. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package plurals
|
||||||
|
|
||||||
|
type equal struct {
|
||||||
|
value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e equal) test(n uint32) bool {
|
||||||
|
return n == e.value
|
||||||
|
}
|
||||||
|
|
||||||
|
type notequal struct {
|
||||||
|
value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e notequal) test(n uint32) bool {
|
||||||
|
return n != e.value
|
||||||
|
}
|
||||||
|
|
||||||
|
type gt struct {
|
||||||
|
value uint32
|
||||||
|
flipped bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e gt) test(n uint32) bool {
|
||||||
|
if e.flipped {
|
||||||
|
return e.value > n
|
||||||
|
} else {
|
||||||
|
return n > e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lt struct {
|
||||||
|
value uint32
|
||||||
|
flipped bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e lt) test(n uint32) bool {
|
||||||
|
if e.flipped {
|
||||||
|
return e.value < n
|
||||||
|
} else {
|
||||||
|
return n < e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gte struct {
|
||||||
|
value uint32
|
||||||
|
flipped bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e gte) test(n uint32) bool {
|
||||||
|
if e.flipped {
|
||||||
|
return e.value >= n
|
||||||
|
} else {
|
||||||
|
return n >= e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lte struct {
|
||||||
|
value uint32
|
||||||
|
flipped bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e lte) test(n uint32) bool {
|
||||||
|
if e.flipped {
|
||||||
|
return e.value <= n
|
||||||
|
} else {
|
||||||
|
return n <= e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type and struct {
|
||||||
|
left test
|
||||||
|
right test
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e and) test(n uint32) bool {
|
||||||
|
if !e.left.test(n) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return e.right.test(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type or struct {
|
||||||
|
left test
|
||||||
|
right test
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e or) test(n uint32) bool {
|
||||||
|
if e.left.test(n) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return e.right.test(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipe struct {
|
||||||
|
modifier math
|
||||||
|
action test
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e pipe) test(n uint32) bool {
|
||||||
|
return e.action.test(e.modifier.calc(n))
|
||||||
|
}
|
||||||
43
po.go
43
po.go
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/mattn/kinako/vm"
|
"github.com/leonelquinteros/gotext/plurals"
|
||||||
)
|
)
|
||||||
|
|
||||||
type translation struct {
|
type translation struct {
|
||||||
@@ -33,7 +33,7 @@ func (t *translation) get() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return unstranlated id by default
|
// Return untranslated id by default
|
||||||
return t.id
|
return t.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ func (t *translation) getN(n int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return unstranlated singular if corresponding
|
// Return untranslated singular if corresponding
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return t.id
|
return t.id
|
||||||
}
|
}
|
||||||
@@ -91,6 +91,7 @@ type Po struct {
|
|||||||
// Parsed Plural-Forms header values
|
// Parsed Plural-Forms header values
|
||||||
nplurals int
|
nplurals int
|
||||||
plural string
|
plural string
|
||||||
|
pluralforms plurals.Expression
|
||||||
|
|
||||||
// Storage
|
// Storage
|
||||||
translations map[string]*translation
|
translations map[string]*translation
|
||||||
@@ -377,6 +378,11 @@ func (po *Po) parseHeaders() {
|
|||||||
|
|
||||||
case "plural":
|
case "plural":
|
||||||
po.plural = vs[1]
|
po.plural = vs[1]
|
||||||
|
|
||||||
|
if expr, err := plurals.Compile(po.plural); err == nil {
|
||||||
|
po.pluralforms = expr
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,35 +393,16 @@ func (po *Po) pluralForm(n int) int {
|
|||||||
po.RLock()
|
po.RLock()
|
||||||
defer po.RUnlock()
|
defer po.RUnlock()
|
||||||
|
|
||||||
// Failsafe
|
// Failure fallback
|
||||||
if po.nplurals < 1 {
|
if po.pluralforms == nil {
|
||||||
|
/* Use the Germanic plural rule. */
|
||||||
|
if n == 1 {
|
||||||
return 0
|
return 0
|
||||||
}
|
} else {
|
||||||
if po.plural == "" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init compiler
|
|
||||||
env := vm.NewEnv()
|
|
||||||
env.Define("n", n)
|
|
||||||
|
|
||||||
plural, err := env.Execute(po.plural)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if plural.Type().Name() == "bool" {
|
|
||||||
if plural.Bool() {
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
// Else
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
return po.pluralforms.Eval(uint32(n))
|
||||||
if int(plural.Int()) > po.nplurals {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(plural.Int())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves the corresponding translation for the given string.
|
// Get retrieves the corresponding translation for the given string.
|
||||||
|
|||||||
33
po_test.go
33
po_test.go
@@ -136,7 +136,7 @@ msgstr "More translation"
|
|||||||
t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr)
|
t.Errorf("Expected 'This one is the plural: Variable' but got '%s'", tr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test inexistent translations
|
// Test not existent translations
|
||||||
tr = po.Get("This is a test")
|
tr = po.Get("This is a test")
|
||||||
if tr != "This is a test" {
|
if tr != "This is a test" {
|
||||||
t.Errorf("Expected 'This is a test' but got '%s'", tr)
|
t.Errorf("Expected 'This is a test' but got '%s'", tr)
|
||||||
@@ -212,6 +212,37 @@ msgstr[1] "TR Plural: %s"
|
|||||||
msgstr[2] "TR Plural 2: %s"
|
msgstr[2] "TR Plural 2: %s"
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
// Create po object
|
||||||
|
po := new(Po)
|
||||||
|
po.Parse(str)
|
||||||
|
|
||||||
|
v := "Var"
|
||||||
|
tr := po.GetN("Singular: %s", "Plural: %s", 2, v)
|
||||||
|
if tr != "TR Plural: Var" {
|
||||||
|
t.Errorf("Expected 'TR Plural: Var' but got '%s'", tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
tr = po.GetN("Singular: %s", "Plural: %s", 1, v)
|
||||||
|
if tr != "TR Singular: Var" {
|
||||||
|
t.Errorf("Expected 'TR Singular: Var' but got '%s'", tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestPluralNoHeaderInformation(t *testing.T) {
|
||||||
|
// Set PO content
|
||||||
|
str := `
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Singular: %s"
|
||||||
|
msgid_plural "Plural: %s"
|
||||||
|
msgstr[0] "TR Singular: %s"
|
||||||
|
msgstr[1] "TR Plural: %s"
|
||||||
|
msgstr[2] "TR Plural 2: %s"
|
||||||
|
|
||||||
|
|
||||||
`
|
`
|
||||||
// Create po object
|
// Create po object
|
||||||
po := new(Po)
|
po := new(Po)
|
||||||
|
|||||||
37
vendor/github.com/mattn/kinako/README.md
generated
vendored
37
vendor/github.com/mattn/kinako/README.md
generated
vendored
@@ -1,37 +0,0 @@
|
|||||||
# kinako
|
|
||||||
|
|
||||||
Kinako is small VM written in Go.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
(Picture licensed under CC BY-SA 3.0 by wikipedia)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
Requires Go.
|
|
||||||
```
|
|
||||||
$ go get -u github.com/mattn/kinako
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Embedding the interpreter into your own program:
|
|
||||||
|
|
||||||
```Go
|
|
||||||
var env = vm.NewEnv()
|
|
||||||
|
|
||||||
env.Define("foo", 1)
|
|
||||||
val, err := env.Execute(`foo + 3`)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(val)
|
|
||||||
```
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
||||||
# Author
|
|
||||||
|
|
||||||
Yasuhiro Matsumoto (a.k.a mattn)
|
|
||||||
18
vendor/github.com/mattn/kinako/_example/main.go
generated
vendored
18
vendor/github.com/mattn/kinako/_example/main.go
generated
vendored
@@ -1,18 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/mattn/kinako/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
env := vm.NewEnv()
|
|
||||||
v, err := env.Execute(`foo=1; foo+3`)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(v)
|
|
||||||
}
|
|
||||||
112
vendor/github.com/mattn/kinako/ast/expr.go
generated
vendored
112
vendor/github.com/mattn/kinako/ast/expr.go
generated
vendored
@@ -1,112 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
type Token struct {
|
|
||||||
Tok int
|
|
||||||
Lit string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position provides interface to store code locations.
|
|
||||||
type Position struct {
|
|
||||||
Line int
|
|
||||||
Column int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expr provides all of interfaces for expression.
|
|
||||||
type Expr interface {
|
|
||||||
expr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExprImpl provide commonly implementations for Expr.
|
|
||||||
type ExprImpl struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// expr provide restraint interface.
|
|
||||||
func (x *ExprImpl) expr() {}
|
|
||||||
|
|
||||||
// NumberExpr provide Number expression.
|
|
||||||
type NumberExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Lit string
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnaryExpr provide unary minus expression. ex: -1, ^1, ~1.
|
|
||||||
type UnaryExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Operator string
|
|
||||||
Expr Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdentExpr provide identity expression.
|
|
||||||
type IdentExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Lit string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stmt provides all of interfaces for statement.
|
|
||||||
type Stmt interface {
|
|
||||||
stmt()
|
|
||||||
}
|
|
||||||
|
|
||||||
// StmtImpl provide commonly implementations for Stmt..
|
|
||||||
type StmtImpl struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// stmt provide restraint interface.
|
|
||||||
func (x *StmtImpl) stmt() {}
|
|
||||||
|
|
||||||
// LetsStmt provide multiple statement of let.
|
|
||||||
type LetsStmt struct {
|
|
||||||
StmtImpl
|
|
||||||
Lhss []Expr
|
|
||||||
Operator string
|
|
||||||
Rhss []Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringExpr provide String expression.
|
|
||||||
type StringExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Lit string
|
|
||||||
}
|
|
||||||
|
|
||||||
type TernaryOpExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Expr Expr
|
|
||||||
Lhs Expr
|
|
||||||
Rhs Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallExpr provide calling expression.
|
|
||||||
type CallExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Func interface{}
|
|
||||||
Name string
|
|
||||||
SubExprs []Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParenExpr provide parent block expression.
|
|
||||||
type ParenExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
SubExpr Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinOpExpr provide binary operator expression.
|
|
||||||
type BinOpExpr struct {
|
|
||||||
ExprImpl
|
|
||||||
Lhs Expr
|
|
||||||
Operator string
|
|
||||||
Rhs Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExprStmt provide expression statement.
|
|
||||||
type ExprStmt struct {
|
|
||||||
StmtImpl
|
|
||||||
Expr Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// LetStmt provide statement of let.
|
|
||||||
type LetStmt struct {
|
|
||||||
StmtImpl
|
|
||||||
Lhs Expr
|
|
||||||
Operator string
|
|
||||||
Rhs Expr
|
|
||||||
}
|
|
||||||
BIN
vendor/github.com/mattn/kinako/kinako.png
generated
vendored
BIN
vendor/github.com/mattn/kinako/kinako.png
generated
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 450 KiB |
4
vendor/github.com/mattn/kinako/parser/Makefile
generated
vendored
4
vendor/github.com/mattn/kinako/parser/Makefile
generated
vendored
@@ -1,4 +0,0 @@
|
|||||||
all : parser.go
|
|
||||||
|
|
||||||
parser.go : parser.go.y
|
|
||||||
goyacc -o $@ parser.go.y
|
|
||||||
427
vendor/github.com/mattn/kinako/parser/lexer.go
generated
vendored
427
vendor/github.com/mattn/kinako/parser/lexer.go
generated
vendored
@@ -1,427 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/mattn/kinako/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
EOF = -1 // End of file.
|
|
||||||
EOL = '\n' // End of line.
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error provides a convenient interface for handling runtime error.
|
|
||||||
// It can be Error inteface with type cast which can call Pos().
|
|
||||||
type Error struct {
|
|
||||||
Message string
|
|
||||||
Filename string
|
|
||||||
Fatal bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the error message.
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scanner stores informations for lexer.
|
|
||||||
type Scanner struct {
|
|
||||||
src []rune
|
|
||||||
offset int
|
|
||||||
lineHead int
|
|
||||||
line int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init resets code to scan.
|
|
||||||
func (s *Scanner) Init(src string) {
|
|
||||||
s.src = []rune(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan analyses token, and decide identify or literals.
|
|
||||||
func (s *Scanner) Scan() (tok int, lit string, pos ast.Position, err error) {
|
|
||||||
retry:
|
|
||||||
s.skipBlank()
|
|
||||||
pos = s.pos()
|
|
||||||
switch ch := s.peek(); {
|
|
||||||
case isLetter(ch):
|
|
||||||
tok = IDENT
|
|
||||||
lit, err = s.scanIdentifier()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case isDigit(ch):
|
|
||||||
tok = NUMBER
|
|
||||||
lit, err = s.scanNumber()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case ch == '"':
|
|
||||||
tok = STRING
|
|
||||||
lit, err = s.scanString('"')
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case ch == '\'':
|
|
||||||
tok = STRING
|
|
||||||
lit, err = s.scanString('\'')
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case ch == '`':
|
|
||||||
tok = STRING
|
|
||||||
lit, err = s.scanRawString()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
switch ch {
|
|
||||||
case EOF:
|
|
||||||
tok = EOF
|
|
||||||
case '#':
|
|
||||||
for !isEOL(s.peek()) {
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
goto retry
|
|
||||||
case '!':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case '=':
|
|
||||||
tok = NEQ
|
|
||||||
lit = "!="
|
|
||||||
default:
|
|
||||||
s.back()
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
}
|
|
||||||
case '=':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case '=':
|
|
||||||
tok = EQEQ
|
|
||||||
lit = "=="
|
|
||||||
default:
|
|
||||||
s.back()
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
}
|
|
||||||
case '+':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
case '-':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
case '*':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
case '/':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
case '>':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case '=':
|
|
||||||
tok = GE
|
|
||||||
lit = ">="
|
|
||||||
case '>':
|
|
||||||
tok = SHIFTRIGHT
|
|
||||||
lit = ">>"
|
|
||||||
default:
|
|
||||||
s.back()
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
}
|
|
||||||
case '<':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case '=':
|
|
||||||
tok = LE
|
|
||||||
lit = "<="
|
|
||||||
case '<':
|
|
||||||
tok = SHIFTLEFT
|
|
||||||
lit = "<<"
|
|
||||||
default:
|
|
||||||
s.back()
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
}
|
|
||||||
case '|':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case '|':
|
|
||||||
tok = OROR
|
|
||||||
lit = "||"
|
|
||||||
default:
|
|
||||||
s.back()
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
}
|
|
||||||
case '&':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case '&':
|
|
||||||
tok = ANDAND
|
|
||||||
lit = "&&"
|
|
||||||
default:
|
|
||||||
s.back()
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
}
|
|
||||||
case '.':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
case '\n':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
case '(', ')', ':', ';', '%', '?', '{', '}', ',', '[', ']', '^':
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf(`syntax error "%s"`, string(ch))
|
|
||||||
tok = int(ch)
|
|
||||||
lit = string(ch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// isLetter returns true if the rune is a letter for identity.
|
|
||||||
func isLetter(ch rune) bool {
|
|
||||||
return unicode.IsLetter(ch) || ch == '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDigit returns true if the rune is a number.
|
|
||||||
func isDigit(ch rune) bool {
|
|
||||||
return '0' <= ch && ch <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
// isHex returns true if the rune is a hex digits.
|
|
||||||
func isHex(ch rune) bool {
|
|
||||||
return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEOL returns true if the rune is at end-of-line or end-of-file.
|
|
||||||
func isEOL(ch rune) bool {
|
|
||||||
return ch == '\n' || ch == -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// isBlank returns true if the rune is empty character..
|
|
||||||
func isBlank(ch rune) bool {
|
|
||||||
return ch == ' ' || ch == '\t' || ch == '\r'
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns current rune in the code.
|
|
||||||
func (s *Scanner) peek() rune {
|
|
||||||
if s.reachEOF() {
|
|
||||||
return EOF
|
|
||||||
}
|
|
||||||
return s.src[s.offset]
|
|
||||||
}
|
|
||||||
|
|
||||||
// next moves offset to next.
|
|
||||||
func (s *Scanner) next() {
|
|
||||||
if !s.reachEOF() {
|
|
||||||
if s.peek() == '\n' {
|
|
||||||
s.lineHead = s.offset + 1
|
|
||||||
s.line++
|
|
||||||
}
|
|
||||||
s.offset++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// current returns the current offset.
|
|
||||||
func (s *Scanner) current() int {
|
|
||||||
return s.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset sets the offset value.
|
|
||||||
func (s *Scanner) set(o int) {
|
|
||||||
s.offset = o
|
|
||||||
}
|
|
||||||
|
|
||||||
// back moves back offset once to top.
|
|
||||||
func (s *Scanner) back() {
|
|
||||||
s.offset--
|
|
||||||
}
|
|
||||||
|
|
||||||
// reachEOF returns true if offset is at end-of-file.
|
|
||||||
func (s *Scanner) reachEOF() bool {
|
|
||||||
return len(s.src) <= s.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// pos returns the position of current.
|
|
||||||
func (s *Scanner) pos() ast.Position {
|
|
||||||
return ast.Position{Line: s.line + 1, Column: s.offset - s.lineHead + 1}
|
|
||||||
}
|
|
||||||
|
|
||||||
// skipBlank moves position into non-black character.
|
|
||||||
func (s *Scanner) skipBlank() {
|
|
||||||
for isBlank(s.peek()) {
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanIdentifier returns identifier begining at current position.
|
|
||||||
func (s *Scanner) scanIdentifier() (string, error) {
|
|
||||||
var ret []rune
|
|
||||||
for {
|
|
||||||
if !isLetter(s.peek()) && !isDigit(s.peek()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
return string(ret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanNumber returns number begining at current position.
|
|
||||||
func (s *Scanner) scanNumber() (string, error) {
|
|
||||||
var ret []rune
|
|
||||||
ch := s.peek()
|
|
||||||
ret = append(ret, ch)
|
|
||||||
s.next()
|
|
||||||
if ch == '0' && s.peek() == 'x' {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
for isHex(s.peek()) {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for isDigit(s.peek()) || s.peek() == '.' {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
if s.peek() == 'e' {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
if isDigit(s.peek()) || s.peek() == '+' || s.peek() == '-' {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
for isDigit(s.peek()) || s.peek() == '.' {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for isDigit(s.peek()) || s.peek() == '.' {
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
s.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isLetter(s.peek()) {
|
|
||||||
return "", errors.New("identifier starts immediately after numeric literal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(ret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanRawString returns raw-string starting at current position.
|
|
||||||
func (s *Scanner) scanRawString() (string, error) {
|
|
||||||
var ret []rune
|
|
||||||
for {
|
|
||||||
s.next()
|
|
||||||
if s.peek() == EOF {
|
|
||||||
return "", errors.New("unexpected EOF")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if s.peek() == '`' {
|
|
||||||
s.next()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
}
|
|
||||||
return string(ret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanString returns string starting at current position.
|
|
||||||
// This handles backslash escaping.
|
|
||||||
func (s *Scanner) scanString(l rune) (string, error) {
|
|
||||||
var ret []rune
|
|
||||||
eos:
|
|
||||||
for {
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case EOL:
|
|
||||||
return "", errors.New("unexpected EOL")
|
|
||||||
case EOF:
|
|
||||||
return "", errors.New("unexpected EOF")
|
|
||||||
case l:
|
|
||||||
s.next()
|
|
||||||
break eos
|
|
||||||
case '\\':
|
|
||||||
s.next()
|
|
||||||
switch s.peek() {
|
|
||||||
case 'b':
|
|
||||||
ret = append(ret, '\b')
|
|
||||||
continue
|
|
||||||
case 'f':
|
|
||||||
ret = append(ret, '\f')
|
|
||||||
continue
|
|
||||||
case 'r':
|
|
||||||
ret = append(ret, '\r')
|
|
||||||
continue
|
|
||||||
case 'n':
|
|
||||||
ret = append(ret, '\n')
|
|
||||||
continue
|
|
||||||
case 't':
|
|
||||||
ret = append(ret, '\t')
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
ret = append(ret, s.peek())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(ret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lexer provides inteface to parse codes.
|
|
||||||
type Lexer struct {
|
|
||||||
s *Scanner
|
|
||||||
lit string
|
|
||||||
pos ast.Position
|
|
||||||
e error
|
|
||||||
stmts []ast.Stmt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lex scans the token and literals.
|
|
||||||
func (l *Lexer) Lex(lval *yySymType) int {
|
|
||||||
tok, lit, pos, err := l.s.Scan()
|
|
||||||
if err != nil {
|
|
||||||
l.e = &Error{Message: fmt.Sprintf("%s", err.Error()), Fatal: true}
|
|
||||||
}
|
|
||||||
lval.tok = ast.Token{Tok: tok, Lit: lit}
|
|
||||||
l.lit = lit
|
|
||||||
l.pos = pos
|
|
||||||
return tok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error sets parse error.
|
|
||||||
func (l *Lexer) Error(msg string) {
|
|
||||||
l.e = &Error{Message: msg, Fatal: false}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser provides way to parse the code using Scanner.
|
|
||||||
func Parse(s *Scanner) ([]ast.Stmt, error) {
|
|
||||||
l := Lexer{s: s}
|
|
||||||
if yyParse(&l) != 0 {
|
|
||||||
return nil, l.e
|
|
||||||
}
|
|
||||||
return l.stmts, l.e
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnableErrorVerbose() {
|
|
||||||
yyErrorVerbose = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParserSrc provides way to parse the code from source.
|
|
||||||
func ParseSrc(src string) ([]ast.Stmt, error) {
|
|
||||||
scanner := &Scanner{
|
|
||||||
src: []rune(src),
|
|
||||||
}
|
|
||||||
return Parse(scanner)
|
|
||||||
}
|
|
||||||
778
vendor/github.com/mattn/kinako/parser/parser.go
generated
vendored
778
vendor/github.com/mattn/kinako/parser/parser.go
generated
vendored
@@ -1,778 +0,0 @@
|
|||||||
//line parser.go.y:2
|
|
||||||
package parser
|
|
||||||
|
|
||||||
import __yyfmt__ "fmt"
|
|
||||||
|
|
||||||
//line parser.go.y:2
|
|
||||||
import (
|
|
||||||
"github.com/mattn/kinako/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
//line parser.go.y:16
|
|
||||||
type yySymType struct {
|
|
||||||
yys int
|
|
||||||
compstmt []ast.Stmt
|
|
||||||
stmts []ast.Stmt
|
|
||||||
stmt ast.Stmt
|
|
||||||
expr ast.Expr
|
|
||||||
exprs []ast.Expr
|
|
||||||
tok ast.Token
|
|
||||||
term ast.Token
|
|
||||||
terms ast.Token
|
|
||||||
opt_terms ast.Token
|
|
||||||
}
|
|
||||||
|
|
||||||
const IDENT = 57346
|
|
||||||
const NUMBER = 57347
|
|
||||||
const STRING = 57348
|
|
||||||
const EQEQ = 57349
|
|
||||||
const NEQ = 57350
|
|
||||||
const GE = 57351
|
|
||||||
const LE = 57352
|
|
||||||
const OROR = 57353
|
|
||||||
const ANDAND = 57354
|
|
||||||
const POW = 57355
|
|
||||||
const SHIFTLEFT = 57356
|
|
||||||
const SHIFTRIGHT = 57357
|
|
||||||
const PLUSPLUS = 57358
|
|
||||||
const MINUSMINUS = 57359
|
|
||||||
const UNARY = 57360
|
|
||||||
|
|
||||||
var yyToknames = [...]string{
|
|
||||||
"$end",
|
|
||||||
"error",
|
|
||||||
"$unk",
|
|
||||||
"IDENT",
|
|
||||||
"NUMBER",
|
|
||||||
"STRING",
|
|
||||||
"EQEQ",
|
|
||||||
"NEQ",
|
|
||||||
"GE",
|
|
||||||
"LE",
|
|
||||||
"OROR",
|
|
||||||
"ANDAND",
|
|
||||||
"POW",
|
|
||||||
"'='",
|
|
||||||
"'?'",
|
|
||||||
"':'",
|
|
||||||
"','",
|
|
||||||
"'>'",
|
|
||||||
"'<'",
|
|
||||||
"SHIFTLEFT",
|
|
||||||
"SHIFTRIGHT",
|
|
||||||
"'+'",
|
|
||||||
"'-'",
|
|
||||||
"PLUSPLUS",
|
|
||||||
"MINUSMINUS",
|
|
||||||
"'*'",
|
|
||||||
"'/'",
|
|
||||||
"'%'",
|
|
||||||
"UNARY",
|
|
||||||
"'!'",
|
|
||||||
"'^'",
|
|
||||||
"'('",
|
|
||||||
"')'",
|
|
||||||
"'|'",
|
|
||||||
"'&'",
|
|
||||||
"';'",
|
|
||||||
"'\\n'",
|
|
||||||
}
|
|
||||||
var yyStatenames = [...]string{}
|
|
||||||
|
|
||||||
const yyEofCode = 1
|
|
||||||
const yyErrCode = 2
|
|
||||||
const yyInitialStackSize = 16
|
|
||||||
|
|
||||||
//line parser.go.y:213
|
|
||||||
|
|
||||||
//line yacctab:1
|
|
||||||
var yyExca = [...]int{
|
|
||||||
-1, 1,
|
|
||||||
1, -1,
|
|
||||||
-2, 0,
|
|
||||||
-1, 50,
|
|
||||||
7, 0,
|
|
||||||
8, 0,
|
|
||||||
-2, 20,
|
|
||||||
-1, 51,
|
|
||||||
7, 0,
|
|
||||||
8, 0,
|
|
||||||
-2, 21,
|
|
||||||
}
|
|
||||||
|
|
||||||
const yyNprod = 40
|
|
||||||
const yyPrivate = 57344
|
|
||||||
|
|
||||||
var yyTokenNames []string
|
|
||||||
var yyStates []string
|
|
||||||
|
|
||||||
const yyLast = 251
|
|
||||||
|
|
||||||
var yyAct = [...]int{
|
|
||||||
|
|
||||||
9, 6, 7, 33, 35, 37, 22, 23, 60, 3,
|
|
||||||
24, 25, 26, 38, 39, 40, 1, 41, 33, 35,
|
|
||||||
8, 43, 44, 45, 46, 47, 48, 49, 50, 51,
|
|
||||||
52, 53, 54, 55, 56, 57, 58, 59, 61, 42,
|
|
||||||
27, 28, 30, 32, 34, 36, 65, 0, 21, 63,
|
|
||||||
4, 29, 31, 2, 18, 22, 23, 17, 0, 24,
|
|
||||||
25, 26, 64, 0, 66, 0, 67, 33, 35, 27,
|
|
||||||
28, 30, 32, 34, 36, 0, 0, 21, 0, 0,
|
|
||||||
29, 31, 0, 0, 22, 23, 0, 0, 24, 25,
|
|
||||||
26, 0, 0, 0, 0, 62, 33, 35, 27, 28,
|
|
||||||
30, 32, 34, 36, 0, 20, 21, 0, 0, 29,
|
|
||||||
31, 0, 0, 22, 23, 5, 0, 24, 25, 26,
|
|
||||||
19, 0, 0, 0, 0, 33, 35, 27, 28, 30,
|
|
||||||
32, 34, 36, 0, 19, 21, 0, 0, 29, 31,
|
|
||||||
0, 0, 22, 23, 0, 0, 24, 25, 26, 0,
|
|
||||||
0, 0, 0, 0, 33, 35, 27, 28, 30, 32,
|
|
||||||
0, 36, 0, 0, 0, 0, 0, 29, 31, 0,
|
|
||||||
0, 22, 23, 0, 0, 24, 25, 26, 27, 28,
|
|
||||||
30, 32, 0, 33, 35, 0, 0, 0, 0, 29,
|
|
||||||
31, 0, 0, 22, 23, 0, 0, 24, 25, 26,
|
|
||||||
30, 32, 10, 11, 15, 33, 35, 0, 0, 29,
|
|
||||||
31, 0, 0, 22, 23, 0, 0, 24, 25, 26,
|
|
||||||
0, 12, 10, 11, 15, 33, 35, 0, 13, 14,
|
|
||||||
16, 24, 25, 26, 6, 7, 0, 0, 0, 33,
|
|
||||||
35, 12, 0, 0, 0, 0, 0, 0, 13, 14,
|
|
||||||
16,
|
|
||||||
}
|
|
||||||
var yyPact = [...]int{
|
|
||||||
|
|
||||||
-35, -1000, 218, -35, -35, -1000, -1000, -1000, -1000, 91,
|
|
||||||
-27, -1000, 218, 218, 218, -1000, 218, -1000, 198, -1000,
|
|
||||||
218, 218, 218, 218, 218, 218, 218, 218, 218, 218,
|
|
||||||
218, 218, 218, 218, 218, 218, 218, 218, -31, -31,
|
|
||||||
-31, 62, -1000, 120, 33, 205, 205, -31, -31, -31,
|
|
||||||
191, 191, -16, -16, -16, -16, 120, 149, 120, 171,
|
|
||||||
29, 120, -1000, 218, -1000, 218, 120, 120,
|
|
||||||
}
|
|
||||||
var yyPgo = [...]int{
|
|
||||||
|
|
||||||
0, 16, 9, 20, 0, 8, 53, 50, 115,
|
|
||||||
}
|
|
||||||
var yyR1 = [...]int{
|
|
||||||
|
|
||||||
0, 1, 1, 2, 2, 3, 3, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 5, 5, 5, 6, 6, 7, 7, 8, 8,
|
|
||||||
}
|
|
||||||
var yyR2 = [...]int{
|
|
||||||
|
|
||||||
0, 1, 2, 2, 3, 3, 1, 1, 1, 2,
|
|
||||||
2, 2, 1, 5, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
4, 0, 1, 3, 0, 1, 1, 2, 1, 1,
|
|
||||||
}
|
|
||||||
var yyChk = [...]int{
|
|
||||||
|
|
||||||
-1000, -1, -6, -2, -7, -8, 36, 37, -3, -4,
|
|
||||||
4, 5, 23, 30, 31, 6, 32, -6, -7, -8,
|
|
||||||
14, 15, 22, 23, 26, 27, 28, 7, 8, 18,
|
|
||||||
9, 19, 10, 34, 11, 35, 12, 32, -4, -4,
|
|
||||||
-4, -4, -3, -4, -4, -4, -4, -4, -4, -4,
|
|
||||||
-4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
|
|
||||||
-5, -4, 33, 16, 33, 17, -4, -4,
|
|
||||||
}
|
|
||||||
var yyDef = [...]int{
|
|
||||||
|
|
||||||
34, -2, 1, 34, 35, 36, 38, 39, 3, 6,
|
|
||||||
7, 8, 0, 0, 0, 12, 0, 2, 35, 37,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 31, 9, 10,
|
|
||||||
11, 0, 4, 5, 0, 15, 16, 17, 18, 19,
|
|
||||||
-2, -2, 22, 23, 24, 25, 26, 27, 28, 29,
|
|
||||||
0, 32, 14, 0, 30, 0, 13, 33,
|
|
||||||
}
|
|
||||||
var yyTok1 = [...]int{
|
|
||||||
|
|
||||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
37, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 30, 3, 3, 3, 28, 35, 3,
|
|
||||||
32, 33, 26, 22, 17, 23, 3, 27, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 16, 36,
|
|
||||||
19, 14, 18, 15, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 31, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 34,
|
|
||||||
}
|
|
||||||
var yyTok2 = [...]int{
|
|
||||||
|
|
||||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
|
||||||
12, 13, 20, 21, 24, 25, 29,
|
|
||||||
}
|
|
||||||
var yyTok3 = [...]int{
|
|
||||||
0,
|
|
||||||
}
|
|
||||||
|
|
||||||
var yyErrorMessages = [...]struct {
|
|
||||||
state int
|
|
||||||
token int
|
|
||||||
msg string
|
|
||||||
}{}
|
|
||||||
|
|
||||||
//line yaccpar:1
|
|
||||||
|
|
||||||
/* parser for yacc output */
|
|
||||||
|
|
||||||
var (
|
|
||||||
yyDebug = 0
|
|
||||||
yyErrorVerbose = false
|
|
||||||
)
|
|
||||||
|
|
||||||
type yyLexer interface {
|
|
||||||
Lex(lval *yySymType) int
|
|
||||||
Error(s string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type yyParser interface {
|
|
||||||
Parse(yyLexer) int
|
|
||||||
Lookahead() int
|
|
||||||
}
|
|
||||||
|
|
||||||
type yyParserImpl struct {
|
|
||||||
lval yySymType
|
|
||||||
stack [yyInitialStackSize]yySymType
|
|
||||||
char int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *yyParserImpl) Lookahead() int {
|
|
||||||
return p.char
|
|
||||||
}
|
|
||||||
|
|
||||||
func yyNewParser() yyParser {
|
|
||||||
return &yyParserImpl{}
|
|
||||||
}
|
|
||||||
|
|
||||||
const yyFlag = -1000
|
|
||||||
|
|
||||||
func yyTokname(c int) string {
|
|
||||||
if c >= 1 && c-1 < len(yyToknames) {
|
|
||||||
if yyToknames[c-1] != "" {
|
|
||||||
return yyToknames[c-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return __yyfmt__.Sprintf("tok-%v", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func yyStatname(s int) string {
|
|
||||||
if s >= 0 && s < len(yyStatenames) {
|
|
||||||
if yyStatenames[s] != "" {
|
|
||||||
return yyStatenames[s]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return __yyfmt__.Sprintf("state-%v", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func yyErrorMessage(state, lookAhead int) string {
|
|
||||||
const TOKSTART = 4
|
|
||||||
|
|
||||||
if !yyErrorVerbose {
|
|
||||||
return "syntax error"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range yyErrorMessages {
|
|
||||||
if e.state == state && e.token == lookAhead {
|
|
||||||
return "syntax error: " + e.msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res := "syntax error: unexpected " + yyTokname(lookAhead)
|
|
||||||
|
|
||||||
// To match Bison, suggest at most four expected tokens.
|
|
||||||
expected := make([]int, 0, 4)
|
|
||||||
|
|
||||||
// Look for shiftable tokens.
|
|
||||||
base := yyPact[state]
|
|
||||||
for tok := TOKSTART; tok-1 < len(yyToknames); tok++ {
|
|
||||||
if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok {
|
|
||||||
if len(expected) == cap(expected) {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
expected = append(expected, tok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if yyDef[state] == -2 {
|
|
||||||
i := 0
|
|
||||||
for yyExca[i] != -1 || yyExca[i+1] != state {
|
|
||||||
i += 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for tokens that we accept or reduce.
|
|
||||||
for i += 2; yyExca[i] >= 0; i += 2 {
|
|
||||||
tok := yyExca[i]
|
|
||||||
if tok < TOKSTART || yyExca[i+1] == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(expected) == cap(expected) {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
expected = append(expected, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the default action is to accept or reduce, give up.
|
|
||||||
if yyExca[i+1] != 0 {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tok := range expected {
|
|
||||||
if i == 0 {
|
|
||||||
res += ", expecting "
|
|
||||||
} else {
|
|
||||||
res += " or "
|
|
||||||
}
|
|
||||||
res += yyTokname(tok)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func yylex1(lex yyLexer, lval *yySymType) (char, token int) {
|
|
||||||
token = 0
|
|
||||||
char = lex.Lex(lval)
|
|
||||||
if char <= 0 {
|
|
||||||
token = yyTok1[0]
|
|
||||||
goto out
|
|
||||||
}
|
|
||||||
if char < len(yyTok1) {
|
|
||||||
token = yyTok1[char]
|
|
||||||
goto out
|
|
||||||
}
|
|
||||||
if char >= yyPrivate {
|
|
||||||
if char < yyPrivate+len(yyTok2) {
|
|
||||||
token = yyTok2[char-yyPrivate]
|
|
||||||
goto out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < len(yyTok3); i += 2 {
|
|
||||||
token = yyTok3[i+0]
|
|
||||||
if token == char {
|
|
||||||
token = yyTok3[i+1]
|
|
||||||
goto out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if token == 0 {
|
|
||||||
token = yyTok2[1] /* unknown char */
|
|
||||||
}
|
|
||||||
if yyDebug >= 3 {
|
|
||||||
__yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char))
|
|
||||||
}
|
|
||||||
return char, token
|
|
||||||
}
|
|
||||||
|
|
||||||
func yyParse(yylex yyLexer) int {
|
|
||||||
return yyNewParser().Parse(yylex)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int {
|
|
||||||
var yyn int
|
|
||||||
var yyVAL yySymType
|
|
||||||
var yyDollar []yySymType
|
|
||||||
_ = yyDollar // silence set and not used
|
|
||||||
yyS := yyrcvr.stack[:]
|
|
||||||
|
|
||||||
Nerrs := 0 /* number of errors */
|
|
||||||
Errflag := 0 /* error recovery flag */
|
|
||||||
yystate := 0
|
|
||||||
yyrcvr.char = -1
|
|
||||||
yytoken := -1 // yyrcvr.char translated into internal numbering
|
|
||||||
defer func() {
|
|
||||||
// Make sure we report no lookahead when not parsing.
|
|
||||||
yystate = -1
|
|
||||||
yyrcvr.char = -1
|
|
||||||
yytoken = -1
|
|
||||||
}()
|
|
||||||
yyp := -1
|
|
||||||
goto yystack
|
|
||||||
|
|
||||||
ret0:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
ret1:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
yystack:
|
|
||||||
/* put a state and value onto the stack */
|
|
||||||
if yyDebug >= 4 {
|
|
||||||
__yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate))
|
|
||||||
}
|
|
||||||
|
|
||||||
yyp++
|
|
||||||
if yyp >= len(yyS) {
|
|
||||||
nyys := make([]yySymType, len(yyS)*2)
|
|
||||||
copy(nyys, yyS)
|
|
||||||
yyS = nyys
|
|
||||||
}
|
|
||||||
yyS[yyp] = yyVAL
|
|
||||||
yyS[yyp].yys = yystate
|
|
||||||
|
|
||||||
yynewstate:
|
|
||||||
yyn = yyPact[yystate]
|
|
||||||
if yyn <= yyFlag {
|
|
||||||
goto yydefault /* simple state */
|
|
||||||
}
|
|
||||||
if yyrcvr.char < 0 {
|
|
||||||
yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval)
|
|
||||||
}
|
|
||||||
yyn += yytoken
|
|
||||||
if yyn < 0 || yyn >= yyLast {
|
|
||||||
goto yydefault
|
|
||||||
}
|
|
||||||
yyn = yyAct[yyn]
|
|
||||||
if yyChk[yyn] == yytoken { /* valid shift */
|
|
||||||
yyrcvr.char = -1
|
|
||||||
yytoken = -1
|
|
||||||
yyVAL = yyrcvr.lval
|
|
||||||
yystate = yyn
|
|
||||||
if Errflag > 0 {
|
|
||||||
Errflag--
|
|
||||||
}
|
|
||||||
goto yystack
|
|
||||||
}
|
|
||||||
|
|
||||||
yydefault:
|
|
||||||
/* default state action */
|
|
||||||
yyn = yyDef[yystate]
|
|
||||||
if yyn == -2 {
|
|
||||||
if yyrcvr.char < 0 {
|
|
||||||
yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* look through exception table */
|
|
||||||
xi := 0
|
|
||||||
for {
|
|
||||||
if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
xi += 2
|
|
||||||
}
|
|
||||||
for xi += 2; ; xi += 2 {
|
|
||||||
yyn = yyExca[xi+0]
|
|
||||||
if yyn < 0 || yyn == yytoken {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yyn = yyExca[xi+1]
|
|
||||||
if yyn < 0 {
|
|
||||||
goto ret0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if yyn == 0 {
|
|
||||||
/* error ... attempt to resume parsing */
|
|
||||||
switch Errflag {
|
|
||||||
case 0: /* brand new error */
|
|
||||||
yylex.Error(yyErrorMessage(yystate, yytoken))
|
|
||||||
Nerrs++
|
|
||||||
if yyDebug >= 1 {
|
|
||||||
__yyfmt__.Printf("%s", yyStatname(yystate))
|
|
||||||
__yyfmt__.Printf(" saw %s\n", yyTokname(yytoken))
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case 1, 2: /* incompletely recovered error ... try again */
|
|
||||||
Errflag = 3
|
|
||||||
|
|
||||||
/* find a state where "error" is a legal shift action */
|
|
||||||
for yyp >= 0 {
|
|
||||||
yyn = yyPact[yyS[yyp].yys] + yyErrCode
|
|
||||||
if yyn >= 0 && yyn < yyLast {
|
|
||||||
yystate = yyAct[yyn] /* simulate a shift of "error" */
|
|
||||||
if yyChk[yystate] == yyErrCode {
|
|
||||||
goto yystack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the current p has no shift on "error", pop stack */
|
|
||||||
if yyDebug >= 2 {
|
|
||||||
__yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys)
|
|
||||||
}
|
|
||||||
yyp--
|
|
||||||
}
|
|
||||||
/* there is no state on the stack with an error shift ... abort */
|
|
||||||
goto ret1
|
|
||||||
|
|
||||||
case 3: /* no shift yet; clobber input char */
|
|
||||||
if yyDebug >= 2 {
|
|
||||||
__yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken))
|
|
||||||
}
|
|
||||||
if yytoken == yyEofCode {
|
|
||||||
goto ret1
|
|
||||||
}
|
|
||||||
yyrcvr.char = -1
|
|
||||||
yytoken = -1
|
|
||||||
goto yynewstate /* try again in the same state */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reduction by production yyn */
|
|
||||||
if yyDebug >= 2 {
|
|
||||||
__yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate))
|
|
||||||
}
|
|
||||||
|
|
||||||
yynt := yyn
|
|
||||||
yypt := yyp
|
|
||||||
_ = yypt // guard against "declared and not used"
|
|
||||||
|
|
||||||
yyp -= yyR2[yyn]
|
|
||||||
// yyp is now the index of $0. Perform the default action. Iff the
|
|
||||||
// reduced production is ε, $1 is possibly out of range.
|
|
||||||
if yyp+1 >= len(yyS) {
|
|
||||||
nyys := make([]yySymType, len(yyS)*2)
|
|
||||||
copy(nyys, yyS)
|
|
||||||
yyS = nyys
|
|
||||||
}
|
|
||||||
yyVAL = yyS[yyp+1]
|
|
||||||
|
|
||||||
/* consult goto table to find next state */
|
|
||||||
yyn = yyR1[yyn]
|
|
||||||
yyg := yyPgo[yyn]
|
|
||||||
yyj := yyg + yyS[yyp].yys + 1
|
|
||||||
|
|
||||||
if yyj >= yyLast {
|
|
||||||
yystate = yyAct[yyg]
|
|
||||||
} else {
|
|
||||||
yystate = yyAct[yyj]
|
|
||||||
if yyChk[yystate] != -yyn {
|
|
||||||
yystate = yyAct[yyg]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// dummy call; replaced with literal code
|
|
||||||
switch yynt {
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:45
|
|
||||||
{
|
|
||||||
yyVAL.compstmt = nil
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
yyDollar = yyS[yypt-2 : yypt+1]
|
|
||||||
//line parser.go.y:49
|
|
||||||
{
|
|
||||||
yyVAL.compstmt = yyDollar[1].stmts
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
yyDollar = yyS[yypt-2 : yypt+1]
|
|
||||||
//line parser.go.y:55
|
|
||||||
{
|
|
||||||
yyVAL.stmts = []ast.Stmt{yyDollar[2].stmt}
|
|
||||||
if l, ok := yylex.(*Lexer); ok {
|
|
||||||
l.stmts = yyVAL.stmts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 4:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:62
|
|
||||||
{
|
|
||||||
if yyDollar[3].stmt != nil {
|
|
||||||
yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[3].stmt)
|
|
||||||
if l, ok := yylex.(*Lexer); ok {
|
|
||||||
l.stmts = yyVAL.stmts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 5:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:73
|
|
||||||
{
|
|
||||||
yyVAL.stmt = &ast.LetStmt{Lhs: yyDollar[1].expr, Operator: "=", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 6:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:77
|
|
||||||
{
|
|
||||||
yyVAL.stmt = &ast.ExprStmt{Expr: yyDollar[1].expr}
|
|
||||||
}
|
|
||||||
case 7:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:83
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.IdentExpr{Lit: yyDollar[1].tok.Lit}
|
|
||||||
}
|
|
||||||
case 8:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:87
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.NumberExpr{Lit: yyDollar[1].tok.Lit}
|
|
||||||
}
|
|
||||||
case 9:
|
|
||||||
yyDollar = yyS[yypt-2 : yypt+1]
|
|
||||||
//line parser.go.y:91
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.UnaryExpr{Operator: "-", Expr: yyDollar[2].expr}
|
|
||||||
}
|
|
||||||
case 10:
|
|
||||||
yyDollar = yyS[yypt-2 : yypt+1]
|
|
||||||
//line parser.go.y:95
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.UnaryExpr{Operator: "!", Expr: yyDollar[2].expr}
|
|
||||||
}
|
|
||||||
case 11:
|
|
||||||
yyDollar = yyS[yypt-2 : yypt+1]
|
|
||||||
//line parser.go.y:99
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.UnaryExpr{Operator: "^", Expr: yyDollar[2].expr}
|
|
||||||
}
|
|
||||||
case 12:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:103
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.StringExpr{Lit: yyDollar[1].tok.Lit}
|
|
||||||
}
|
|
||||||
case 13:
|
|
||||||
yyDollar = yyS[yypt-5 : yypt+1]
|
|
||||||
//line parser.go.y:107
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.TernaryOpExpr{Expr: yyDollar[1].expr, Lhs: yyDollar[3].expr, Rhs: yyDollar[5].expr}
|
|
||||||
}
|
|
||||||
case 14:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:111
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.ParenExpr{SubExpr: yyDollar[2].expr}
|
|
||||||
}
|
|
||||||
case 15:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:115
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "+", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 16:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:119
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "-", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 17:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:123
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "*", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 18:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:127
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "/", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 19:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:131
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "%", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 20:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:135
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "==", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 21:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:139
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "!=", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 22:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:143
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: ">", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 23:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:147
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: ">=", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 24:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:151
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "<", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 25:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:155
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "<=", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 26:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:159
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "|", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 27:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:163
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "||", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 28:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:167
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "&", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 29:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:171
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "&&", Rhs: yyDollar[3].expr}
|
|
||||||
}
|
|
||||||
case 30:
|
|
||||||
yyDollar = yyS[yypt-4 : yypt+1]
|
|
||||||
//line parser.go.y:175
|
|
||||||
{
|
|
||||||
yyVAL.expr = &ast.CallExpr{Name: yyDollar[1].tok.Lit, SubExprs: yyDollar[3].exprs}
|
|
||||||
}
|
|
||||||
case 31:
|
|
||||||
yyDollar = yyS[yypt-0 : yypt+1]
|
|
||||||
//line parser.go.y:180
|
|
||||||
{
|
|
||||||
yyVAL.exprs = nil
|
|
||||||
}
|
|
||||||
case 32:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:184
|
|
||||||
{
|
|
||||||
yyVAL.exprs = []ast.Expr{yyDollar[1].expr}
|
|
||||||
}
|
|
||||||
case 33:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
//line parser.go.y:188
|
|
||||||
{
|
|
||||||
yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr)
|
|
||||||
}
|
|
||||||
case 36:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:198
|
|
||||||
{
|
|
||||||
}
|
|
||||||
case 37:
|
|
||||||
yyDollar = yyS[yypt-2 : yypt+1]
|
|
||||||
//line parser.go.y:201
|
|
||||||
{
|
|
||||||
}
|
|
||||||
case 38:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:206
|
|
||||||
{
|
|
||||||
}
|
|
||||||
case 39:
|
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
|
||||||
//line parser.go.y:209
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
goto yystack /* stack new state and value */
|
|
||||||
}
|
|
||||||
214
vendor/github.com/mattn/kinako/parser/parser.go.y
generated
vendored
214
vendor/github.com/mattn/kinako/parser/parser.go.y
generated
vendored
@@ -1,214 +0,0 @@
|
|||||||
%{
|
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/mattn/kinako/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
%type<compstmt> compstmt
|
|
||||||
%type<stmts> stmts
|
|
||||||
%type<stmt> stmt
|
|
||||||
%type<expr> expr
|
|
||||||
%type<exprs> exprs
|
|
||||||
|
|
||||||
%union{
|
|
||||||
compstmt []ast.Stmt
|
|
||||||
stmts []ast.Stmt
|
|
||||||
stmt ast.Stmt
|
|
||||||
expr ast.Expr
|
|
||||||
exprs []ast.Expr
|
|
||||||
tok ast.Token
|
|
||||||
term ast.Token
|
|
||||||
terms ast.Token
|
|
||||||
opt_terms ast.Token
|
|
||||||
}
|
|
||||||
|
|
||||||
%token<tok> IDENT NUMBER STRING EQEQ NEQ GE LE OROR ANDAND POW
|
|
||||||
|
|
||||||
%right '='
|
|
||||||
%right '?' ':'
|
|
||||||
%left OROR
|
|
||||||
%left ANDAND
|
|
||||||
%left IDENT
|
|
||||||
%nonassoc EQEQ NEQ ','
|
|
||||||
%left '>' GE '<' LE SHIFTLEFT SHIFTRIGHT
|
|
||||||
|
|
||||||
%left '+' '-' PLUSPLUS MINUSMINUS
|
|
||||||
%left '*' '/' '%'
|
|
||||||
%right UNARY
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
compstmt : opt_terms
|
|
||||||
{
|
|
||||||
$$ = nil
|
|
||||||
}
|
|
||||||
| stmts opt_terms
|
|
||||||
{
|
|
||||||
$$ = $1
|
|
||||||
}
|
|
||||||
|
|
||||||
stmts :
|
|
||||||
opt_terms stmt
|
|
||||||
{
|
|
||||||
$$ = []ast.Stmt{$2}
|
|
||||||
if l, ok := yylex.(*Lexer); ok {
|
|
||||||
l.stmts = $$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| stmts terms stmt
|
|
||||||
{
|
|
||||||
if $3 != nil {
|
|
||||||
$$ = append($1, $3)
|
|
||||||
if l, ok := yylex.(*Lexer); ok {
|
|
||||||
l.stmts = $$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt :
|
|
||||||
expr '=' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.LetStmt{Lhs: $1, Operator: "=", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr
|
|
||||||
{
|
|
||||||
$$ = &ast.ExprStmt{Expr: $1}
|
|
||||||
}
|
|
||||||
|
|
||||||
expr :
|
|
||||||
IDENT
|
|
||||||
{
|
|
||||||
$$ = &ast.IdentExpr{Lit: $1.Lit}
|
|
||||||
}
|
|
||||||
| NUMBER
|
|
||||||
{
|
|
||||||
$$ = &ast.NumberExpr{Lit: $1.Lit}
|
|
||||||
}
|
|
||||||
| '-' expr %prec UNARY
|
|
||||||
{
|
|
||||||
$$ = &ast.UnaryExpr{Operator: "-", Expr: $2}
|
|
||||||
}
|
|
||||||
| '!' expr %prec UNARY
|
|
||||||
{
|
|
||||||
$$ = &ast.UnaryExpr{Operator: "!", Expr: $2}
|
|
||||||
}
|
|
||||||
| '^' expr %prec UNARY
|
|
||||||
{
|
|
||||||
$$ = &ast.UnaryExpr{Operator: "^", Expr: $2}
|
|
||||||
}
|
|
||||||
| STRING
|
|
||||||
{
|
|
||||||
$$ = &ast.StringExpr{Lit: $1.Lit}
|
|
||||||
}
|
|
||||||
| expr '?' expr ':' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.TernaryOpExpr{Expr: $1, Lhs: $3, Rhs: $5}
|
|
||||||
}
|
|
||||||
| '(' expr ')'
|
|
||||||
{
|
|
||||||
$$ = &ast.ParenExpr{SubExpr: $2}
|
|
||||||
}
|
|
||||||
| expr '+' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "+", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '-' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "-", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '*' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "*", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '/' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "/", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '%' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "%", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr EQEQ expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "==", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr NEQ expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "!=", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '>' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: ">", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr GE expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: ">=", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '<' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "<", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr LE expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "<=", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '|' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "|", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr OROR expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "||", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr '&' expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "&", Rhs: $3}
|
|
||||||
}
|
|
||||||
| expr ANDAND expr
|
|
||||||
{
|
|
||||||
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "&&", Rhs: $3}
|
|
||||||
}
|
|
||||||
| IDENT '(' exprs ')'
|
|
||||||
{
|
|
||||||
$$ = &ast.CallExpr{Name: $1.Lit, SubExprs: $3}
|
|
||||||
}
|
|
||||||
|
|
||||||
exprs :
|
|
||||||
{
|
|
||||||
$$ = nil
|
|
||||||
}
|
|
||||||
| expr
|
|
||||||
{
|
|
||||||
$$ = []ast.Expr{$1}
|
|
||||||
}
|
|
||||||
| exprs ',' expr
|
|
||||||
{
|
|
||||||
$$ = append($1, $3)
|
|
||||||
}
|
|
||||||
|
|
||||||
opt_terms : /* none */
|
|
||||||
| terms
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
terms : term
|
|
||||||
{
|
|
||||||
}
|
|
||||||
| terms term
|
|
||||||
{
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
term : ';'
|
|
||||||
{
|
|
||||||
}
|
|
||||||
| '\n'
|
|
||||||
{
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
1381
vendor/github.com/mattn/kinako/parser/y.output
generated
vendored
1381
vendor/github.com/mattn/kinako/parser/y.output
generated
vendored
File diff suppressed because it is too large
Load Diff
258
vendor/github.com/mattn/kinako/vm/env.go
generated
vendored
258
vendor/github.com/mattn/kinako/vm/env.go
generated
vendored
@@ -1,258 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/mattn/kinako/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Env provides interface to run VM. This mean function scope and blocked-scope.
|
|
||||||
// If stack goes to blocked-scope, it will make new Env.
|
|
||||||
type Env struct {
|
|
||||||
name string
|
|
||||||
env map[string]reflect.Value
|
|
||||||
typ map[string]reflect.Type
|
|
||||||
parent *Env
|
|
||||||
interrupt *bool
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEnv creates new global scope.
|
|
||||||
func NewEnv() *Env {
|
|
||||||
b := false
|
|
||||||
|
|
||||||
return &Env{
|
|
||||||
env: make(map[string]reflect.Value),
|
|
||||||
typ: make(map[string]reflect.Type),
|
|
||||||
parent: nil,
|
|
||||||
interrupt: &b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEnv creates new child scope.
|
|
||||||
func (e *Env) NewEnv() *Env {
|
|
||||||
return &Env{
|
|
||||||
env: make(map[string]reflect.Value),
|
|
||||||
typ: make(map[string]reflect.Type),
|
|
||||||
parent: e,
|
|
||||||
name: e.name,
|
|
||||||
interrupt: e.interrupt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPackage(n string) *Env {
|
|
||||||
b := false
|
|
||||||
|
|
||||||
return &Env{
|
|
||||||
env: make(map[string]reflect.Value),
|
|
||||||
typ: make(map[string]reflect.Type),
|
|
||||||
parent: nil,
|
|
||||||
name: n,
|
|
||||||
interrupt: &b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Env) NewPackage(n string) *Env {
|
|
||||||
return &Env{
|
|
||||||
env: make(map[string]reflect.Value),
|
|
||||||
typ: make(map[string]reflect.Type),
|
|
||||||
parent: e,
|
|
||||||
name: n,
|
|
||||||
interrupt: e.interrupt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy deletes current scope.
|
|
||||||
func (e *Env) Destroy() {
|
|
||||||
e.Lock()
|
|
||||||
defer e.Unlock()
|
|
||||||
|
|
||||||
if e.parent == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for k, v := range e.parent.env {
|
|
||||||
if v.IsValid() && v.Interface() == e {
|
|
||||||
delete(e.parent.env, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.parent = nil
|
|
||||||
e.env = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewModule creates new module scope as global.
|
|
||||||
func (e *Env) NewModule(n string) *Env {
|
|
||||||
m := &Env{
|
|
||||||
env: make(map[string]reflect.Value),
|
|
||||||
parent: e,
|
|
||||||
name: n,
|
|
||||||
}
|
|
||||||
e.Define(n, m)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetName sets a name of the scope. This means that the scope is module.
|
|
||||||
func (e *Env) SetName(n string) {
|
|
||||||
e.Lock()
|
|
||||||
e.name = n
|
|
||||||
e.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns module name.
|
|
||||||
func (e *Env) GetName() string {
|
|
||||||
e.RLock()
|
|
||||||
defer e.RUnlock()
|
|
||||||
|
|
||||||
return e.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addr returns pointer value which specified symbol. It goes to upper scope until
|
|
||||||
// found or returns error.
|
|
||||||
func (e *Env) Addr(k string) (reflect.Value, error) {
|
|
||||||
e.RLock()
|
|
||||||
defer e.RUnlock()
|
|
||||||
|
|
||||||
if v, ok := e.env[k]; ok {
|
|
||||||
return v.Addr(), nil
|
|
||||||
}
|
|
||||||
if e.parent == nil {
|
|
||||||
return NilValue, fmt.Errorf("Undefined symbol '%s'", k)
|
|
||||||
}
|
|
||||||
return e.parent.Addr(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns type which specified symbol. It goes to upper scope until
|
|
||||||
// found or returns error.
|
|
||||||
func (e *Env) Type(k string) (reflect.Type, error) {
|
|
||||||
e.RLock()
|
|
||||||
defer e.RUnlock()
|
|
||||||
|
|
||||||
if v, ok := e.typ[k]; ok {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
if e.parent == nil {
|
|
||||||
return NilType, fmt.Errorf("Undefined type '%s'", k)
|
|
||||||
}
|
|
||||||
return e.parent.Type(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns value which specified symbol. It goes to upper scope until
|
|
||||||
// found or returns error.
|
|
||||||
func (e *Env) Get(k string) (reflect.Value, error) {
|
|
||||||
e.RLock()
|
|
||||||
defer e.RUnlock()
|
|
||||||
|
|
||||||
if v, ok := e.env[k]; ok {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
if e.parent == nil {
|
|
||||||
return NilValue, fmt.Errorf("Undefined symbol '%s'", k)
|
|
||||||
}
|
|
||||||
return e.parent.Get(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set modifies value which specified as symbol. It goes to upper scope until
|
|
||||||
// found or returns error.
|
|
||||||
func (e *Env) Set(k string, v interface{}) error {
|
|
||||||
e.Lock()
|
|
||||||
defer e.Unlock()
|
|
||||||
|
|
||||||
if _, ok := e.env[k]; ok {
|
|
||||||
val, ok := v.(reflect.Value)
|
|
||||||
if !ok {
|
|
||||||
val = reflect.ValueOf(v)
|
|
||||||
}
|
|
||||||
e.env[k] = val
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if e.parent == nil {
|
|
||||||
return fmt.Errorf("Unknown symbol '%s'", k)
|
|
||||||
}
|
|
||||||
return e.parent.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefineGlobal defines symbol in global scope.
|
|
||||||
func (e *Env) DefineGlobal(k string, v interface{}) error {
|
|
||||||
if e.parent == nil {
|
|
||||||
return e.Define(k, v)
|
|
||||||
}
|
|
||||||
return e.parent.DefineGlobal(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefineType defines type which specifis symbol in global scope.
|
|
||||||
func (e *Env) DefineType(k string, t interface{}) error {
|
|
||||||
if strings.Contains(k, ".") {
|
|
||||||
return fmt.Errorf("Unknown symbol '%s'", k)
|
|
||||||
}
|
|
||||||
global := e
|
|
||||||
keys := []string{k}
|
|
||||||
|
|
||||||
e.RLock()
|
|
||||||
for global.parent != nil {
|
|
||||||
if global.name != "" {
|
|
||||||
keys = append(keys, global.name)
|
|
||||||
}
|
|
||||||
global = global.parent
|
|
||||||
}
|
|
||||||
e.RUnlock()
|
|
||||||
|
|
||||||
for i, j := 0, len(keys)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
keys[i], keys[j] = keys[j], keys[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, ok := t.(reflect.Type)
|
|
||||||
if !ok {
|
|
||||||
typ = reflect.TypeOf(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Lock()
|
|
||||||
global.typ[strings.Join(keys, ".")] = typ
|
|
||||||
global.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define defines symbol in current scope.
|
|
||||||
func (e *Env) Define(k string, v interface{}) error {
|
|
||||||
if strings.Contains(k, ".") {
|
|
||||||
return fmt.Errorf("Unknown symbol '%s'", k)
|
|
||||||
}
|
|
||||||
val, ok := v.(reflect.Value)
|
|
||||||
if !ok {
|
|
||||||
val = reflect.ValueOf(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Lock()
|
|
||||||
e.env[k] = val
|
|
||||||
e.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String return the name of current scope.
|
|
||||||
func (e *Env) String() string {
|
|
||||||
e.RLock()
|
|
||||||
defer e.RUnlock()
|
|
||||||
|
|
||||||
return e.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump show symbol values in the scope.
|
|
||||||
func (e *Env) Dump() {
|
|
||||||
e.RLock()
|
|
||||||
for k, v := range e.env {
|
|
||||||
fmt.Printf("%v = %#v\n", k, v)
|
|
||||||
}
|
|
||||||
e.RUnlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute parses and runs source in current scope.
|
|
||||||
func (e *Env) Execute(src string) (reflect.Value, error) {
|
|
||||||
stmts, err := parser.ParseSrc(src)
|
|
||||||
if err != nil {
|
|
||||||
return NilValue, err
|
|
||||||
}
|
|
||||||
return Run(stmts, e)
|
|
||||||
}
|
|
||||||
476
vendor/github.com/mattn/kinako/vm/vm.go
generated
vendored
476
vendor/github.com/mattn/kinako/vm/vm.go
generated
vendored
@@ -1,476 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mattn/kinako/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
NilValue = reflect.ValueOf((*interface{})(nil))
|
|
||||||
NilType = reflect.TypeOf((*interface{})(nil))
|
|
||||||
TrueValue = reflect.ValueOf(true)
|
|
||||||
FalseValue = reflect.ValueOf(false)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error provides a convenient interface for handling runtime error.
|
|
||||||
// It can be Error interface with type cast which can call Pos().
|
|
||||||
type Error struct {
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
BreakError = errors.New("Unexpected break statement")
|
|
||||||
ContinueError = errors.New("Unexpected continue statement")
|
|
||||||
ReturnError = errors.New("Unexpected return statement")
|
|
||||||
InterruptError = errors.New("Execution interrupted")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error returns the error message.
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Func is function interface to reflect functions internaly.
|
|
||||||
type Func func(args ...reflect.Value) (reflect.Value, error)
|
|
||||||
|
|
||||||
// Run executes statements in the specified environment.
|
|
||||||
func Run(stmts []ast.Stmt, env *Env) (reflect.Value, error) {
|
|
||||||
rv := NilValue
|
|
||||||
var err error
|
|
||||||
for _, stmt := range stmts {
|
|
||||||
rv, err = RunSingleStmt(stmt, env)
|
|
||||||
if err != nil {
|
|
||||||
return rv, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interrupts the execution of any running statements in the specified environment.
|
|
||||||
//
|
|
||||||
// Note that the execution is not instantly aborted: after a call to Interrupt,
|
|
||||||
// the current running statement will finish, but the next statement will not run,
|
|
||||||
// and instead will return a NilValue and an InterruptError.
|
|
||||||
func Interrupt(env *Env) {
|
|
||||||
env.Lock()
|
|
||||||
*(env.interrupt) = true
|
|
||||||
env.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunSingleStmt executes one statement in the specified environment.
|
|
||||||
func RunSingleStmt(stmt ast.Stmt, env *Env) (reflect.Value, error) {
|
|
||||||
env.Lock()
|
|
||||||
if *(env.interrupt) {
|
|
||||||
*(env.interrupt) = false
|
|
||||||
env.Unlock()
|
|
||||||
|
|
||||||
return NilValue, InterruptError
|
|
||||||
}
|
|
||||||
env.Unlock()
|
|
||||||
|
|
||||||
switch stmt := stmt.(type) {
|
|
||||||
case *ast.ExprStmt:
|
|
||||||
rv, err := invokeExpr(stmt.Expr, env)
|
|
||||||
if err != nil {
|
|
||||||
return rv, err
|
|
||||||
}
|
|
||||||
return rv, nil
|
|
||||||
case *ast.LetStmt:
|
|
||||||
rv := NilValue
|
|
||||||
var err error
|
|
||||||
rv, err = invokeExpr(stmt.Rhs, env)
|
|
||||||
if err != nil {
|
|
||||||
return rv, err
|
|
||||||
}
|
|
||||||
_, err = invokeLetExpr(stmt.Lhs, rv, env)
|
|
||||||
if err != nil {
|
|
||||||
return rv, err
|
|
||||||
}
|
|
||||||
return rv, nil
|
|
||||||
default:
|
|
||||||
return NilValue, errors.New("unknown statement")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// toString converts all reflect.Value-s into string.
|
|
||||||
func toString(v reflect.Value) string {
|
|
||||||
if v.Kind() == reflect.Interface {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
if v.Kind() == reflect.String {
|
|
||||||
return v.String()
|
|
||||||
}
|
|
||||||
if !v.IsValid() {
|
|
||||||
return "nil"
|
|
||||||
}
|
|
||||||
return fmt.Sprint(v.Interface())
|
|
||||||
}
|
|
||||||
|
|
||||||
// toBool converts all reflect.Value-s into bool.
|
|
||||||
func toBool(v reflect.Value) bool {
|
|
||||||
if v.Kind() == reflect.Interface {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float() != 0.0
|
|
||||||
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
||||||
return v.Int() != 0
|
|
||||||
case reflect.Bool:
|
|
||||||
return v.Bool()
|
|
||||||
case reflect.String:
|
|
||||||
if v.String() == "true" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if toInt64(v) != 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// toFloat64 converts all reflect.Value-s into float64.
|
|
||||||
func toFloat64(v reflect.Value) float64 {
|
|
||||||
if v.Kind() == reflect.Interface {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float()
|
|
||||||
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
||||||
return float64(v.Int())
|
|
||||||
}
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNil(v reflect.Value) bool {
|
|
||||||
if !v.IsValid() || v.Kind().String() == "unsafe.Pointer" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr) && v.IsNil() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNum(v reflect.Value) bool {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// equal returns true when lhsV and rhsV is same value.
|
|
||||||
func equal(lhsV, rhsV reflect.Value) bool {
|
|
||||||
lhsIsNil, rhsIsNil := isNil(lhsV), isNil(rhsV)
|
|
||||||
if lhsIsNil && rhsIsNil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (!lhsIsNil && rhsIsNil) || (lhsIsNil && !rhsIsNil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhsV.Kind() == reflect.Interface || lhsV.Kind() == reflect.Ptr {
|
|
||||||
lhsV = lhsV.Elem()
|
|
||||||
}
|
|
||||||
if rhsV.Kind() == reflect.Interface || rhsV.Kind() == reflect.Ptr {
|
|
||||||
rhsV = rhsV.Elem()
|
|
||||||
}
|
|
||||||
if !lhsV.IsValid() || !rhsV.IsValid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if isNum(lhsV) && isNum(rhsV) {
|
|
||||||
if rhsV.Type().ConvertibleTo(lhsV.Type()) {
|
|
||||||
rhsV = rhsV.Convert(lhsV.Type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lhsV.CanInterface() && rhsV.CanInterface() {
|
|
||||||
return reflect.DeepEqual(lhsV.Interface(), rhsV.Interface())
|
|
||||||
}
|
|
||||||
return reflect.DeepEqual(lhsV, rhsV)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toInt64 converts all reflect.Value-s into int64.
|
|
||||||
func toInt64(v reflect.Value) int64 {
|
|
||||||
if v.Kind() == reflect.Interface {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return int64(v.Float())
|
|
||||||
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
||||||
return v.Int()
|
|
||||||
case reflect.String:
|
|
||||||
s := v.String()
|
|
||||||
var i int64
|
|
||||||
var err error
|
|
||||||
if strings.HasPrefix(s, "0x") {
|
|
||||||
i, err = strconv.ParseInt(s, 16, 64)
|
|
||||||
} else {
|
|
||||||
i, err = strconv.ParseInt(s, 10, 64)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
return int64(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func invokeLetExpr(expr ast.Expr, rv reflect.Value, env *Env) (reflect.Value, error) {
|
|
||||||
switch lhs := expr.(type) {
|
|
||||||
case *ast.IdentExpr:
|
|
||||||
if env.Set(lhs.Lit, rv) != nil {
|
|
||||||
if strings.Contains(lhs.Lit, ".") {
|
|
||||||
return NilValue, fmt.Errorf("Undefined symbol '%s'", lhs.Lit)
|
|
||||||
}
|
|
||||||
env.Define(lhs.Lit, rv)
|
|
||||||
}
|
|
||||||
return rv, nil
|
|
||||||
}
|
|
||||||
return NilValue, errors.New("Invalid operation")
|
|
||||||
}
|
|
||||||
|
|
||||||
// invokeExpr evaluates one expression.
|
|
||||||
func invokeExpr(expr ast.Expr, env *Env) (reflect.Value, error) {
|
|
||||||
switch e := expr.(type) {
|
|
||||||
case *ast.NumberExpr:
|
|
||||||
if strings.Contains(e.Lit, ".") || strings.Contains(e.Lit, "e") {
|
|
||||||
v, err := strconv.ParseFloat(e.Lit, 64)
|
|
||||||
if err != nil {
|
|
||||||
return NilValue, err
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(float64(v)), nil
|
|
||||||
}
|
|
||||||
var i int64
|
|
||||||
var err error
|
|
||||||
if strings.HasPrefix(e.Lit, "0x") {
|
|
||||||
i, err = strconv.ParseInt(e.Lit[2:], 16, 64)
|
|
||||||
} else {
|
|
||||||
i, err = strconv.ParseInt(e.Lit, 10, 64)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return NilValue, err
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(i), nil
|
|
||||||
case *ast.IdentExpr:
|
|
||||||
return env.Get(e.Lit)
|
|
||||||
case *ast.StringExpr:
|
|
||||||
return reflect.ValueOf(e.Lit), nil
|
|
||||||
case *ast.UnaryExpr:
|
|
||||||
v, err := invokeExpr(e.Expr, env)
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
switch e.Operator {
|
|
||||||
case "-":
|
|
||||||
if v.Kind() == reflect.Float64 {
|
|
||||||
return reflect.ValueOf(-v.Float()), nil
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(-v.Int()), nil
|
|
||||||
case "^":
|
|
||||||
return reflect.ValueOf(^toInt64(v)), nil
|
|
||||||
case "!":
|
|
||||||
return reflect.ValueOf(!toBool(v)), nil
|
|
||||||
default:
|
|
||||||
return NilValue, errors.New("Unknown operator ''")
|
|
||||||
}
|
|
||||||
case *ast.ParenExpr:
|
|
||||||
v, err := invokeExpr(e.SubExpr, env)
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
case *ast.BinOpExpr:
|
|
||||||
lhsV := NilValue
|
|
||||||
rhsV := NilValue
|
|
||||||
var err error
|
|
||||||
|
|
||||||
lhsV, err = invokeExpr(e.Lhs, env)
|
|
||||||
if err != nil {
|
|
||||||
return lhsV, err
|
|
||||||
}
|
|
||||||
if lhsV.Kind() == reflect.Interface {
|
|
||||||
lhsV = lhsV.Elem()
|
|
||||||
}
|
|
||||||
if e.Rhs != nil {
|
|
||||||
rhsV, err = invokeExpr(e.Rhs, env)
|
|
||||||
if err != nil {
|
|
||||||
return rhsV, err
|
|
||||||
}
|
|
||||||
if rhsV.Kind() == reflect.Interface {
|
|
||||||
rhsV = rhsV.Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch e.Operator {
|
|
||||||
case "+":
|
|
||||||
if lhsV.Kind() == reflect.String || rhsV.Kind() == reflect.String {
|
|
||||||
return reflect.ValueOf(toString(lhsV) + toString(rhsV)), nil
|
|
||||||
}
|
|
||||||
if (lhsV.Kind() == reflect.Array || lhsV.Kind() == reflect.Slice) && (rhsV.Kind() != reflect.Array && rhsV.Kind() != reflect.Slice) {
|
|
||||||
return reflect.Append(lhsV, rhsV), nil
|
|
||||||
}
|
|
||||||
if (lhsV.Kind() == reflect.Array || lhsV.Kind() == reflect.Slice) && (rhsV.Kind() == reflect.Array || rhsV.Kind() == reflect.Slice) {
|
|
||||||
return reflect.AppendSlice(lhsV, rhsV), nil
|
|
||||||
}
|
|
||||||
if lhsV.Kind() == reflect.Float64 || rhsV.Kind() == reflect.Float64 {
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) + toFloat64(rhsV)), nil
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) + toInt64(rhsV)), nil
|
|
||||||
case "-":
|
|
||||||
if lhsV.Kind() == reflect.Float64 || rhsV.Kind() == reflect.Float64 {
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) - toFloat64(rhsV)), nil
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) - toInt64(rhsV)), nil
|
|
||||||
case "*":
|
|
||||||
if lhsV.Kind() == reflect.String && (rhsV.Kind() == reflect.Int || rhsV.Kind() == reflect.Int32 || rhsV.Kind() == reflect.Int64) {
|
|
||||||
return reflect.ValueOf(strings.Repeat(toString(lhsV), int(toInt64(rhsV)))), nil
|
|
||||||
}
|
|
||||||
if lhsV.Kind() == reflect.Float64 || rhsV.Kind() == reflect.Float64 {
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) * toFloat64(rhsV)), nil
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) * toInt64(rhsV)), nil
|
|
||||||
case "/":
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) / toFloat64(rhsV)), nil
|
|
||||||
case "%":
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) % toInt64(rhsV)), nil
|
|
||||||
case "==":
|
|
||||||
return reflect.ValueOf(equal(lhsV, rhsV)), nil
|
|
||||||
case "!=":
|
|
||||||
return reflect.ValueOf(equal(lhsV, rhsV) == false), nil
|
|
||||||
case ">":
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) > toFloat64(rhsV)), nil
|
|
||||||
case ">=":
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) >= toFloat64(rhsV)), nil
|
|
||||||
case "<":
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) < toFloat64(rhsV)), nil
|
|
||||||
case "<=":
|
|
||||||
return reflect.ValueOf(toFloat64(lhsV) <= toFloat64(rhsV)), nil
|
|
||||||
case "|":
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) | toInt64(rhsV)), nil
|
|
||||||
case "||":
|
|
||||||
if toBool(lhsV) {
|
|
||||||
return lhsV, nil
|
|
||||||
}
|
|
||||||
return rhsV, nil
|
|
||||||
case "&":
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) & toInt64(rhsV)), nil
|
|
||||||
case "&&":
|
|
||||||
if toBool(lhsV) {
|
|
||||||
return rhsV, nil
|
|
||||||
}
|
|
||||||
return lhsV, nil
|
|
||||||
case "**":
|
|
||||||
if lhsV.Kind() == reflect.Float64 {
|
|
||||||
return reflect.ValueOf(math.Pow(toFloat64(lhsV), toFloat64(rhsV))), nil
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(int64(math.Pow(toFloat64(lhsV), toFloat64(rhsV)))), nil
|
|
||||||
case ">>":
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) >> uint64(toInt64(rhsV))), nil
|
|
||||||
case "<<":
|
|
||||||
return reflect.ValueOf(toInt64(lhsV) << uint64(toInt64(rhsV))), nil
|
|
||||||
default:
|
|
||||||
return NilValue, errors.New("Unknown operator")
|
|
||||||
}
|
|
||||||
case *ast.CallExpr:
|
|
||||||
f, err := env.Get(e.Name)
|
|
||||||
if err != nil {
|
|
||||||
return f, err
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []reflect.Value{}
|
|
||||||
for i, expr := range e.SubExprs {
|
|
||||||
arg, err := invokeExpr(expr, env)
|
|
||||||
if err != nil {
|
|
||||||
return arg, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < f.Type().NumIn() {
|
|
||||||
if !f.Type().IsVariadic() {
|
|
||||||
it := f.Type().In(i)
|
|
||||||
if arg.Kind().String() == "unsafe.Pointer" {
|
|
||||||
arg = reflect.New(it).Elem()
|
|
||||||
}
|
|
||||||
if arg.Kind() != it.Kind() && arg.IsValid() && arg.Type().ConvertibleTo(it) {
|
|
||||||
arg = arg.Convert(it)
|
|
||||||
} else if arg.Kind() == reflect.Func {
|
|
||||||
if _, isFunc := arg.Interface().(Func); isFunc {
|
|
||||||
rfunc := arg
|
|
||||||
arg = reflect.MakeFunc(it, func(args []reflect.Value) []reflect.Value {
|
|
||||||
for i := range args {
|
|
||||||
args[i] = reflect.ValueOf(args[i])
|
|
||||||
}
|
|
||||||
return rfunc.Call(args)[:it.NumOut()]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if !arg.IsValid() {
|
|
||||||
arg = reflect.Zero(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !arg.IsValid() {
|
|
||||||
arg = NilValue
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, arg)
|
|
||||||
}
|
|
||||||
ret := NilValue
|
|
||||||
fnc := func() {
|
|
||||||
defer func() {
|
|
||||||
if os.Getenv("KINAKO_DEBUG") == "" {
|
|
||||||
if ex := recover(); ex != nil {
|
|
||||||
if e, ok := ex.(error); ok {
|
|
||||||
err = e
|
|
||||||
} else {
|
|
||||||
err = errors.New(fmt.Sprint(ex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if f.Kind() == reflect.Interface {
|
|
||||||
f = f.Elem()
|
|
||||||
}
|
|
||||||
rets := f.Call(args)
|
|
||||||
if f.Type().NumOut() == 1 {
|
|
||||||
ret = rets[0]
|
|
||||||
} else {
|
|
||||||
var result []interface{}
|
|
||||||
for _, r := range rets {
|
|
||||||
result = append(result, r.Interface())
|
|
||||||
}
|
|
||||||
ret = reflect.ValueOf(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fnc()
|
|
||||||
if err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
case *ast.TernaryOpExpr:
|
|
||||||
rv, err := invokeExpr(e.Expr, env)
|
|
||||||
if err != nil {
|
|
||||||
return rv, err
|
|
||||||
}
|
|
||||||
if toBool(rv) {
|
|
||||||
lhsV, err := invokeExpr(e.Lhs, env)
|
|
||||||
if err != nil {
|
|
||||||
return lhsV, err
|
|
||||||
}
|
|
||||||
return lhsV, nil
|
|
||||||
}
|
|
||||||
rhsV, err := invokeExpr(e.Rhs, env)
|
|
||||||
if err != nil {
|
|
||||||
return rhsV, err
|
|
||||||
}
|
|
||||||
return rhsV, nil
|
|
||||||
default:
|
|
||||||
return NilValue, errors.New("Unknown expression")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
54
vendor/github.com/mattn/kinako/vm/vm_test.go
generated
vendored
54
vendor/github.com/mattn/kinako/vm/vm_test.go
generated
vendored
@@ -1,54 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExecute(t *testing.T) {
|
|
||||||
e := NewEnv()
|
|
||||||
e.Define("foo", int64(1))
|
|
||||||
e.Define("bar", int64(2))
|
|
||||||
e.Define("baz", int64(3))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
want interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: "foo+bar",
|
|
||||||
want: int64(3),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "foo-bar",
|
|
||||||
want: int64(-1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "foo*bar",
|
|
||||||
want: int64(2),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "foo/bar",
|
|
||||||
want: float64(0.5),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "baz*(foo+bar)",
|
|
||||||
want: int64(9),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "baz > 2 ? foo : bar",
|
|
||||||
want: int64(1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
r, err := e.Execute(tt.input)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
got := r.Interface()
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Fatalf("want %v, but %v:", tt.want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user