7 Commits

Author SHA1 Message Date
Leonel Quinteros
1fc8dec04d Rewrite PO headers parsing and handling. Implement correct GNU gettext headers format. Fix tests. Fixes #10 2017-09-08 18:08:56 -03:00
Leonel Quinteros
1bb93891f4 Fix README examples 2017-09-01 17:15:49 -03:00
Leonel Quinteros
1e28907f7a Replace Godeps with dep 2017-09-01 17:05:40 -03:00
Leonel Quinteros
4b94e83723 Properly handle singular vs plural defaults for untranslated strings. Fixes #9 2017-09-01 13:28:51 -03:00
Leonel Quinteros
756045ab5e Handle empty translation strings as untranslated as defined in https://www.gnu.org/software/gettext/manual/html_node/Untranslated-Entries.html. Fixes #9 2017-08-30 10:53:39 -03:00
Leonel Quinteros
2bb9254f26 Edit badges 2017-07-05 19:06:45 -03:00
Leonel Quinteros
88952938dc Update readme 2017-07-05 19:03:09 -03:00
20 changed files with 994 additions and 459 deletions

18
Godeps/Godeps.json generated
View File

@@ -1,18 +0,0 @@
{
"ImportPath": "github.com/leonelquinteros/gotext",
"GoVersion": "devel +be9d7f6d87 Fri Jun 16 00:31:25 2017 +0000",
"Deps": [
{
"ImportPath": "github.com/mattn/kinako/ast",
"Rev": "fbc18625ec69b28ae03b4eea4c349f215840ee09"
},
{
"ImportPath": "github.com/mattn/kinako/parser",
"Rev": "fbc18625ec69b28ae03b4eea4c349f215840ee09"
},
{
"ImportPath": "github.com/mattn/kinako/vm",
"Rev": "fbc18625ec69b28ae03b4eea4c349f215840ee09"
}
]
}

5
Godeps/Readme generated
View File

@@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

15
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,15 @@
# 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]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d3069fabe2d6f79fe33ad88133e861db84aef0400f6b949c4e64395913b3ae97"
solver-name = "gps-cdcl"
solver-version = 1

26
Gopkg.toml Normal file
View File

@@ -0,0 +1,26 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
branch = "master"
name = "github.com/mattn/kinako"

View File

@@ -1,5 +1,5 @@
[![GitHub release](https://img.shields.io/github/release/leonelquinteros/gotext.svg)](https://github.com/leonelquinteros/gotext)
[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![GitHub version](https://badge.fury.io/gh/leonelquinteros%2Fgotext.svg)](https://badge.fury.io/gh/leonelquinteros%2Fgotext)
[![GoDoc](https://godoc.org/github.com/leonelquinteros/gotext?status.svg)](https://godoc.org/github.com/leonelquinteros/gotext) [![GoDoc](https://godoc.org/github.com/leonelquinteros/gotext?status.svg)](https://godoc.org/github.com/leonelquinteros/gotext)
[![Build Status](https://travis-ci.org/leonelquinteros/gotext.svg?branch=master)](https://travis-ci.org/leonelquinteros/gotext) [![Build Status](https://travis-ci.org/leonelquinteros/gotext.svg?branch=master)](https://travis-ci.org/leonelquinteros/gotext)
[![codecov](https://codecov.io/gh/leonelquinteros/gotext/branch/master/graph/badge.svg)](https://codecov.io/gh/leonelquinteros/gotext) [![codecov](https://codecov.io/gh/leonelquinteros/gotext/branch/master/graph/badge.svg)](https://codecov.io/gh/leonelquinteros/gotext)
@@ -9,8 +9,6 @@
[GNU gettext utilities](https://www.gnu.org/software/gettext) for Go. [GNU gettext utilities](https://www.gnu.org/software/gettext) for Go.
Version: [v1.1.1](https://github.com/leonelquinteros/gotext/releases/tag/v1.1.1)
# Features # Features
@@ -118,39 +116,6 @@ A library directory structure can look like:
And so on... And so on...
# About translation function names
The standard GNU gettext defines helper functions that maps to the `gettext()` function and it's widely adopted by most implementations.
The basic translation function is usually `_()` in the form:
```
_("Translate this")
```
In Go, this can't be implemented by a reusable package as the function name has to start with a capital letter in order to be exported.
Each implementation of this package can declare this helper functions inside their own packages if this function naming are desired/needed:
```go
package main
import "github.com/leonelquinteros/gotext"
func _(str string, vars ...interface{}) string {
return gotext.Get(str, vars...)
}
```
This is valid and can be used within a package.
In normal conditions the Go compiler will optimize the calls to `_()` by replacing its content in place of the function call to reduce the function calling overhead.
This is a normal Go compiler behavior.
# Usage examples # Usage examples
## Using package for single language/domain settings ## Using package for single language/domain settings
@@ -158,17 +123,20 @@ This is a normal Go compiler behavior.
For quick/simple translations you can use the package level functions directly. For quick/simple translations you can use the package level functions directly.
```go ```go
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Configure package // Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name") gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
// Translate text from default domain // Translate text from default domain
println(gotext.Get("My text on 'domain-name' domain")) fmt.Println(gotext.Get("My text on 'domain-name' domain"))
// Translate text from a different domain without reconfigure // Translate text from a different domain without reconfigure
println(gotext.GetD("domain2", "Another text on a different domain")) fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
} }
``` ```
@@ -179,7 +147,10 @@ All translation strings support dynamic variables to be inserted without transla
Use the fmt.Printf syntax (from Go's "fmt" package) to specify how to print the non-translated variable inside the translation string. Use the fmt.Printf syntax (from Go's "fmt" package) to specify how to print the non-translated variable inside the translation string.
```go ```go
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Configure package // Configure package
@@ -189,7 +160,7 @@ func main() {
name := "John" name := "John"
// Translate text with variables // Translate text with variables
println(gotext.Get("Hi, my name is %s", name)) fmt.Println(gotext.Get("Hi, my name is %s", name))
} }
``` ```
@@ -201,7 +172,10 @@ When having multiple languages/domains/libraries at the same time, you can creat
so you can handle each settings on their own. so you can handle each settings on their own.
```go ```go
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Create Locale with library path and language code // Create Locale with library path and language code
@@ -211,13 +185,13 @@ func main() {
l.AddDomain("default") l.AddDomain("default")
// Translate text from default domain // Translate text from default domain
println(l.Get("Translate this")) fmt.Println(l.Get("Translate this"))
// Load different domain // Load different domain
l.AddDomain("translations") l.AddDomain("translations")
// Translate text from domain // Translate text from domain
println(l.GetD("translations", "Translate this")) fmt.Println(l.GetD("translations", "Translate this"))
} }
``` ```
@@ -235,7 +209,10 @@ For when you need to work with PO files and strings,
you can directly use the Po object to parse it and access the translations in there in the same way. you can directly use the Po object to parse it and access the translations in there in the same way.
```go ```go
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Set PO content // Set PO content
@@ -251,10 +228,10 @@ msgstr "This one sets the var: %s"
` `
// Create Po object // Create Po object
po := new(Po) po := new(gotext.Po)
po.Parse(str) po.Parse(str)
println(po.Get("Translate this")) fmt.Println(po.Get("Translate this"))
} }
``` ```
@@ -268,7 +245,10 @@ as defined in (https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_no
Plural formulas are parsed and evaluated using [Kinako](https://github.com/mattn/kinako) Plural formulas are parsed and evaluated using [Kinako](https://github.com/mattn/kinako)
```go ```go
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Set PO content // Set PO content
@@ -292,10 +272,10 @@ msgstr[1] "This one is the plural: %s"
` `
// Create Po object // Create Po object
po := new(Po) po := new(gotext.Po)
po.Parse(str) po.Parse(str)
println(po.GetN("One with var: %s", "Several with vars: %s", 54, v)) fmt.Println(po.GetN("One with var: %s", "Several with vars: %s", 54, v))
// "This one is the plural: Variable" // "This one is the plural: Variable"
} }
``` ```

View File

@@ -3,17 +3,20 @@ Package gotext implements GNU gettext utilities.
For quick/simple translations you can use the package level functions directly. For quick/simple translations you can use the package level functions directly.
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Configure package // Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name") gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
// Translate text from default domain // Translate text from default domain
println(gotext.Get("My text on 'domain-name' domain")) fmt.Println(gotext.Get("My text on 'domain-name' domain"))
// Translate text from a different domain without reconfigure // Translate text from a different domain without reconfigure
println(gotext.GetD("domain2", "Another text on a different domain")) fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
} }
*/ */

View File

@@ -3,6 +3,7 @@ package gotext
import ( import (
"os" "os"
"path" "path"
"sync"
"testing" "testing"
) )
@@ -32,10 +33,12 @@ func TestGettersSetters(t *testing.T) {
func TestPackageFunctions(t *testing.T) { func TestPackageFunctions(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
# msgid "" msgid ""
# msgstr "" msgstr "Project-Id-Version: %s\n"
"Report-Msgid-Bugs-To: %s\n"
# Initial comment # Initial comment
# Headers below # More Headers below
"Language: en\n" "Language: en\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@@ -55,14 +58,6 @@ msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s" msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s" msgstr[2] "And this is the second plural form: %s"
msgid "This one has invalid syntax translations"
msgid_plural "Plural index"
msgstr[abc] "Wrong index"
msgstr[1 "Forgot to close brackets"
msgstr[0] "Badly formatted string'
msgid "Invalid formatted id[] with no translations
msgctxt "Ctx" msgctxt "Ctx"
msgid "One with var: %s" msgid "One with var: %s"
msgid_plural "Several with vars: %s" msgid_plural "Several with vars: %s"
@@ -79,6 +74,11 @@ msgstr "Some random translation in a context"
msgid "More" msgid "More"
msgstr "More translation" msgstr "More translation"
msgid "Untranslated"
msgid_plural "Several untranslated"
msgstr[0] ""
msgstr[1] ""
` `
// Create Locales directory on default location // Create Locales directory on default location
@@ -141,6 +141,79 @@ msgstr "More translation"
} }
} }
func TestUntranslated(t *testing.T) {
// Set PO content
str := `
msgid ""
msgstr ""
# Initial comment
# Headers below
"Language: en\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Untranslated"
msgid_plural "Several untranslated"
msgstr[0] ""
msgstr[1] ""
`
// Create Locales directory on default location
dirname := path.Clean("/tmp" + string(os.PathSeparator) + "en_US")
err := os.MkdirAll(dirname, os.ModePerm)
if err != nil {
t.Fatalf("Can't create test directory: %s", err.Error())
}
// Write PO content to default domain file
filename := path.Clean(dirname + string(os.PathSeparator) + "default.po")
f, err := os.Create(filename)
if err != nil {
t.Fatalf("Can't create test file: %s", err.Error())
}
defer f.Close()
_, err = f.WriteString(str)
if err != nil {
t.Fatalf("Can't write to test file: %s", err.Error())
}
// Set package configuration
Configure("/tmp", "en_US", "default")
// Test untranslated
tr := Get("Untranslated")
if tr != "Untranslated" {
t.Errorf("Expected 'Untranslated' but got '%s'", tr)
}
tr = GetN("Untranslated", "Several untranslated", 1)
if tr != "Untranslated" {
t.Errorf("Expected 'Untranslated' but got '%s'", tr)
}
tr = GetN("Untranslated", "Several untranslated", 2)
if tr != "Several untranslated" {
t.Errorf("Expected 'Several untranslated' but got '%s'", tr)
}
tr = GetD("default", "Untranslated")
if tr != "Untranslated" {
t.Errorf("Expected 'Untranslated' but got '%s'", tr)
}
tr = GetND("default", "Untranslated", "Several untranslated", 1)
if tr != "Untranslated" {
t.Errorf("Expected 'Untranslated' but got '%s'", tr)
}
tr = GetND("default", "Untranslated", "Several untranslated", 2)
if tr != "Several untranslated" {
t.Errorf("Expected 'Several untranslated' but got '%s'", tr)
}
}
func TestPackageRace(t *testing.T) { func TestPackageRace(t *testing.T) {
// Set PO content // Set PO content
str := `# Some comment str := `# Some comment
@@ -180,20 +253,29 @@ msgstr[2] "And this is the second plural form: %s"
t.Fatalf("Can't write to test file: %s", err.Error()) t.Fatalf("Can't write to test file: %s", err.Error())
} }
// Init sync channels var wg sync.WaitGroup
c1 := make(chan bool)
c2 := make(chan bool) for i := 0; i < 100; i++ {
wg.Add(1)
// Test translations
go func() {
defer wg.Done()
Get("My text")
GetN("One with var: %s", "Several with vars: %s", 0, "test")
}()
wg.Add(1)
go func() {
defer wg.Done()
Get("My text")
GetN("One with var: %s", "Several with vars: %s", 1, "test")
}()
// Test translations
go func(done chan bool) {
Get("My text") Get("My text")
done <- true GetN("One with var: %s", "Several with vars: %s", 2, "test")
}(c1) }
go func(done chan bool) { wg.Wait()
Get("My text")
done <- true
}(c2)
Get("My text")
} }

View File

@@ -14,7 +14,10 @@ multiple languages at the same time by working with this object.
Example: Example:
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Create Locale with library path and language code // Create Locale with library path and language code
@@ -24,13 +27,13 @@ Example:
l.AddDomain("default") l.AddDomain("default")
// Translate text from default domain // Translate text from default domain
println(l.Get("Translate this")) fmt.Println(l.Get("Translate this"))
// Load different domain ('/path/to/i18n/dir/en_US/LC_MESSAGES/extras.po') // Load different domain ('/path/to/i18n/dir/en_US/LC_MESSAGES/extras.po')
l.AddDomain("extras") l.AddDomain("extras")
// Translate text from domain // Translate text from domain
println(l.GetD("extras", "Translate this")) fmt.Println(l.GetD("extras", "Translate this"))
} }
*/ */

View File

@@ -9,8 +9,8 @@ import (
func TestLocale(t *testing.T) { func TestLocale(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
# msgid "" msgid ""
# msgstr "" msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -38,8 +38,6 @@ msgstr[abc] "Wrong index"
msgstr[1 "Forgot to close brackets" msgstr[1 "Forgot to close brackets"
msgstr[0] "Badly formatted string' msgstr[0] "Badly formatted string'
msgid "Invalid formatted id[] with no translations
msgctxt "Ctx" msgctxt "Ctx"
msgid "One with var: %s" msgid "One with var: %s"
msgid_plural "Several with vars: %s" msgid_plural "Several with vars: %s"

159
po.go
View File

@@ -3,14 +3,13 @@ package gotext
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"github.com/mattn/kinako/vm"
"io/ioutil" "io/ioutil"
"net/textproto" "net/textproto"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"github.com/mattn/kinako/vm"
) )
type translation struct { type translation struct {
@@ -29,7 +28,9 @@ func newTranslation() *translation {
func (t *translation) get() string { func (t *translation) get() string {
// Look for translation index 0 // Look for translation index 0
if _, ok := t.trs[0]; ok { if _, ok := t.trs[0]; ok {
return t.trs[0] if t.trs[0] != "" {
return t.trs[0]
}
} }
// Return unstranlated id by default // Return unstranlated id by default
@@ -39,10 +40,17 @@ func (t *translation) get() string {
func (t *translation) getN(n int) string { func (t *translation) getN(n int) string {
// Look for translation index // Look for translation index
if _, ok := t.trs[n]; ok { if _, ok := t.trs[n]; ok {
return t.trs[n] if t.trs[n] != "" {
return t.trs[n]
}
} }
// Return unstranlated plural by default // Return unstranlated singular if corresponding
if n == 0 {
return t.id
}
// Return untranslated plural by default
return t.pluralID return t.pluralID
} }
@@ -53,7 +61,10 @@ And it's safe for concurrent use by multiple goroutines by using the sync packag
Example: Example:
import "github.com/leonelquinteros/gotext" import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() { func main() {
// Create po object // Create po object
@@ -63,13 +74,13 @@ Example:
po.ParseFile("/path/to/po/file/translations.po") po.ParseFile("/path/to/po/file/translations.po")
// Get translation // Get translation
println(po.Get("Translate this")) fmt.Println(po.Get("Translate this"))
} }
*/ */
type Po struct { type Po struct {
// Headers // Headers storage
RawHeaders string Headers textproto.MIMEHeader
// Language header // Language header
Language string Language string
@@ -129,7 +140,6 @@ func (po *Po) ParseFile(f string) {
func (po *Po) Parse(str string) { func (po *Po) Parse(str string) {
// Lock while parsing // Lock while parsing
po.Lock() po.Lock()
defer po.Unlock()
// Init storage // Init storage
if po.translations == nil { if po.translations == nil {
@@ -184,7 +194,7 @@ func (po *Po) Parse(str string) {
// Multi line strings and headers // Multi line strings and headers
if strings.HasPrefix(l, "\"") && strings.HasSuffix(l, "\"") { if strings.HasPrefix(l, "\"") && strings.HasSuffix(l, "\"") {
state = po.parseString(l, state) po.parseString(l, state)
continue continue
} }
} }
@@ -192,6 +202,9 @@ func (po *Po) Parse(str string) {
// Save last translation buffer. // Save last translation buffer.
po.saveBuffer() po.saveBuffer()
// Unlock to parse headers
po.Unlock()
// Parse headers // Parse headers
po.parseHeaders() po.parseHeaders()
} }
@@ -199,23 +212,24 @@ func (po *Po) Parse(str string) {
// saveBuffer takes the context and translation buffers // saveBuffer takes the context and translation buffers
// and saves it on the translations collection // and saves it on the translations collection
func (po *Po) saveBuffer() { func (po *Po) saveBuffer() {
// If we have something to save... // With no context...
if po.trBuffer.id != "" { if po.ctxBuffer == "" {
// With no context... po.translations[po.trBuffer.id] = po.trBuffer
if po.ctxBuffer == "" { } else {
po.translations[po.trBuffer.id] = po.trBuffer // With context...
} else { if _, ok := po.contexts[po.ctxBuffer]; !ok {
// With context... po.contexts[po.ctxBuffer] = make(map[string]*translation)
if _, ok := po.contexts[po.ctxBuffer]; !ok {
po.contexts[po.ctxBuffer] = make(map[string]*translation)
}
po.contexts[po.ctxBuffer][po.trBuffer.id] = po.trBuffer
} }
po.contexts[po.ctxBuffer][po.trBuffer.id] = po.trBuffer
// Flush buffer // Cleanup current context buffer if needed
po.trBuffer = newTranslation() if po.trBuffer.id != "" {
po.ctxBuffer = "" po.ctxBuffer = ""
}
} }
// Flush translation buffer
po.trBuffer = newTranslation()
} }
// parseContext takes a line starting with "msgctxt", // parseContext takes a line starting with "msgctxt",
@@ -275,70 +289,72 @@ func (po *Po) parseMessage(l string) {
// parseString takes a well formatted string without prefix // parseString takes a well formatted string without prefix
// and creates headers or attach multi-line strings when corresponding // and creates headers or attach multi-line strings when corresponding
func (po *Po) parseString(l string, state parseState) parseState { func (po *Po) parseString(l string, state parseState) {
clean, _ := strconv.Unquote(l)
switch state { switch state {
case msgStr: case msgStr:
// Check for multiline from previously set msgid // Append to last translation found
if po.trBuffer.id != "" { po.trBuffer.trs[len(po.trBuffer.trs)-1] += clean
// Append to last translation found
uq, _ := strconv.Unquote(l)
po.trBuffer.trs[len(po.trBuffer.trs)-1] += uq
}
case msgID: case msgID:
// Multiline msgid - Append to current id // Multiline msgid - Append to current id
uq, _ := strconv.Unquote(l) po.trBuffer.id += clean
po.trBuffer.id += uq
case msgIDPlural: case msgIDPlural:
// Multiline msgid - Append to current id // Multiline msgid - Append to current id
uq, _ := strconv.Unquote(l) po.trBuffer.pluralID += clean
po.trBuffer.pluralID += uq
case msgCtxt: case msgCtxt:
// Multiline context - Append to current context // Multiline context - Append to current context
ctxt, _ := strconv.Unquote(l) po.ctxBuffer += clean
po.ctxBuffer += ctxt
default:
// Otherwise is a header
h, _ := strconv.Unquote(strings.TrimSpace(l))
po.RawHeaders += h
return head
}
return state }
} }
// isValidLine checks for line prefixes to detect valid syntax. // isValidLine checks for line prefixes to detect valid syntax.
func (po *Po) isValidLine(l string) bool { func (po *Po) isValidLine(l string) bool {
// Skip empty lines
if l == "" {
return false
}
// Check prefix // Check prefix
if !strings.HasPrefix(l, "\"") && !strings.HasPrefix(l, "msgctxt") && !strings.HasPrefix(l, "msgid") && !strings.HasPrefix(l, "msgid_plural") && !strings.HasPrefix(l, "msgstr") { valid := []string{
return false "\"",
"msgctxt",
"msgid",
"msgid_plural",
"msgstr",
} }
return true for _, v := range valid {
if strings.HasPrefix(l, v) {
return true
}
}
return false
} }
// parseHeaders retrieves data from previously parsed headers // parseHeaders retrieves data from previously parsed headers
func (po *Po) parseHeaders() { func (po *Po) parseHeaders() {
// Make sure we end with 2 carriage returns. // Make sure we end with 2 carriage returns.
po.RawHeaders += "\n\n" raw := po.Get("") + "\n\n"
// Read // Read
reader := bufio.NewReader(strings.NewReader(po.RawHeaders)) reader := bufio.NewReader(strings.NewReader(raw))
tp := textproto.NewReader(reader) tp := textproto.NewReader(reader)
mimeHeader, err := tp.ReadMIMEHeader() var err error
// Sync Headers write.
po.Lock()
defer po.Unlock()
po.Headers, err = tp.ReadMIMEHeader()
if err != nil { if err != nil {
return return
} }
// Get/save needed headers // Get/save needed headers
po.Language = mimeHeader.Get("Language") po.Language = po.Headers.Get("Language")
po.PluralForms = mimeHeader.Get("Plural-Forms") po.PluralForms = po.Headers.Get("Plural-Forms")
// Parse Plural-Forms formula // Parse Plural-Forms formula
if po.PluralForms == "" { if po.PluralForms == "" {
@@ -411,12 +427,12 @@ func (po *Po) Get(str string, vars ...interface{}) string {
if po.translations != nil { if po.translations != nil {
if _, ok := po.translations[str]; ok { if _, ok := po.translations[str]; ok {
return fmt.Sprintf(po.translations[str].get(), vars...) return po.printf(po.translations[str].get(), vars...)
} }
} }
// Return the same we received by default // Return the same we received by default
return fmt.Sprintf(str, vars...) return po.printf(str, vars...)
} }
// GetN retrieves the (N)th plural form of translation for the given string. // GetN retrieves the (N)th plural form of translation for the given string.
@@ -428,14 +444,14 @@ func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string {
if po.translations != nil { if po.translations != nil {
if _, ok := po.translations[str]; ok { if _, ok := po.translations[str]; ok {
return fmt.Sprintf(po.translations[str].getN(po.pluralForm(n)), vars...) return po.printf(po.translations[str].getN(po.pluralForm(n)), vars...)
} }
} }
if n == 1 { if n == 1 {
return fmt.Sprintf(str, vars...) return po.printf(str, vars...)
} }
return fmt.Sprintf(plural, vars...) return po.printf(plural, vars...)
} }
// GetC retrieves the corresponding translation for a given string in the given context. // GetC retrieves the corresponding translation for a given string in the given context.
@@ -449,14 +465,14 @@ func (po *Po) GetC(str, ctx string, vars ...interface{}) string {
if _, ok := po.contexts[ctx]; ok { if _, ok := po.contexts[ctx]; ok {
if po.contexts[ctx] != nil { if po.contexts[ctx] != nil {
if _, ok := po.contexts[ctx][str]; ok { if _, ok := po.contexts[ctx][str]; ok {
return fmt.Sprintf(po.contexts[ctx][str].get(), vars...) return po.printf(po.contexts[ctx][str].get(), vars...)
} }
} }
} }
} }
// Return the string we received by default // Return the string we received by default
return fmt.Sprintf(str, vars...) return po.printf(str, vars...)
} }
// GetNC retrieves the (N)th plural form of translation for the given string in the given context. // GetNC retrieves the (N)th plural form of translation for the given string in the given context.
@@ -470,14 +486,23 @@ func (po *Po) GetNC(str, plural string, n int, ctx string, vars ...interface{})
if _, ok := po.contexts[ctx]; ok { if _, ok := po.contexts[ctx]; ok {
if po.contexts[ctx] != nil { if po.contexts[ctx] != nil {
if _, ok := po.contexts[ctx][str]; ok { if _, ok := po.contexts[ctx][str]; ok {
return fmt.Sprintf(po.contexts[ctx][str].getN(po.pluralForm(n)), vars...) return po.printf(po.contexts[ctx][str].getN(po.pluralForm(n)), vars...)
} }
} }
} }
} }
if n == 1 { if n == 1 {
return po.printf(str, vars...)
}
return po.printf(plural, vars...)
}
// printf applies text formatting only when needed to parse variables.
func (po *Po) printf(str string, vars ...interface{}) string {
if len(vars) > 0 {
return fmt.Sprintf(str, vars...) return fmt.Sprintf(str, vars...)
} }
return fmt.Sprintf(plural, vars...)
return str
} }

View File

@@ -9,6 +9,9 @@ import (
func TestPo(t *testing.T) { func TestPo(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
msgid ""
msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -48,14 +51,6 @@ msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %s" msgstr[1] "This one is the plural: %s"
msgstr[2] "And this is the second plural form: %s" msgstr[2] "And this is the second plural form: %s"
msgid "This one has invalid syntax translations"
msgid_plural "Plural index"
msgstr[abc] "Wrong index"
msgstr[1 "Forgot to close brackets"
msgstr[0] "Badly formatted string'
msgid "Invalid formatted id[] with no translations
msgctxt "Ctx" msgctxt "Ctx"
msgid "One with var: %s" msgid "One with var: %s"
msgid_plural "Several with vars: %s" msgid_plural "Several with vars: %s"
@@ -69,8 +64,17 @@ msgctxt "Ctx"
msgid "Some random in a context" msgid "Some random in a context"
msgstr "Some random translation in a context" msgstr "Some random translation in a context"
msgid "Empty translation"
msgstr ""
msgid "Empty plural form singular"
msgid_plural "Empty plural form"
msgstr[0] "Singular translated"
msgstr[1] ""
msgid "More" msgid "More"
msgstr "More translation" msgstr "More translation"
` `
// Write PO content to file // Write PO content to file
@@ -143,17 +147,6 @@ msgstr "More translation"
t.Errorf("Expected 'This are tests' but got '%s'", tr) t.Errorf("Expected 'This are tests' but got '%s'", tr)
} }
// Test syntax error parsed translations
tr = po.Get("This one has invalid syntax translations")
if tr != "" {
t.Errorf("Expected '' but got '%s'", tr)
}
tr = po.GetN("This one has invalid syntax translations", "This are tests", 4)
if tr != "Plural index" {
t.Errorf("Expected 'Plural index' but got '%s'", tr)
}
// Test context translations // Test context translations
v = "Test" v = "Test"
tr = po.GetC("One with var: %s", "Ctx", v) tr = po.GetC("One with var: %s", "Ctx", v)
@@ -177,6 +170,27 @@ msgstr "More translation"
t.Errorf("Expected 'Original' but got '%s'", tr) t.Errorf("Expected 'Original' but got '%s'", tr)
} }
// Test empty translation strings
tr = po.Get("Empty translation")
if tr != "Empty translation" {
t.Errorf("Expected 'Empty translation' but got '%s'", tr)
}
tr = po.Get("Empty plural form singular")
if tr != "Singular translated" {
t.Errorf("Expected 'Singular translated' but got '%s'", tr)
}
tr = po.GetN("Empty plural form singular", "Empty plural form", 1)
if tr != "Singular translated" {
t.Errorf("Expected 'Singular translated' but got '%s'", tr)
}
tr = po.GetN("Empty plural form singular", "Empty plural form", 2)
if tr != "Empty plural form" {
t.Errorf("Expected 'Empty plural form' but got '%s'", tr)
}
// Test last translation // Test last translation
tr = po.Get("More") tr = po.Get("More")
if tr != "More translation" { if tr != "More translation" {
@@ -184,9 +198,42 @@ msgstr "More translation"
} }
} }
func TestPlural(t *testing.T) {
// Set PO content
str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
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
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 TestPoHeaders(t *testing.T) { func TestPoHeaders(t *testing.T) {
// Set PO content // Set PO content
str := ` str := `
msgid ""
msgstr ""
# Initial comment # Initial comment
# Headers below # Headers below
"Language: en\n" "Language: en\n"
@@ -216,9 +263,30 @@ msgstr "Translated example"
} }
} }
func TestMissingPoHeadersSupport(t *testing.T) {
// Set PO content
str := `
msgid "Example"
msgstr "Translated example"
`
// Create po object
po := new(Po)
// Parse
po.Parse(str)
// Check translation expected
if po.Get("Example") != "Translated example" {
t.Errorf("Expected 'Translated example' but got '%s'", po.Get("Example"))
}
}
func TestPluralFormsSingle(t *testing.T) { func TestPluralFormsSingle(t *testing.T) {
// Single form // Single form
str := ` str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=1; plural=0;" "Plural-Forms: nplurals=1; plural=0;"
# Some comment # Some comment
@@ -262,6 +330,8 @@ msgstr[3] "Plural form 3"
func TestPluralForms2(t *testing.T) { func TestPluralForms2(t *testing.T) {
// 2 forms // 2 forms
str := ` str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;" "Plural-Forms: nplurals=2; plural=n != 1;"
# Some comment # Some comment
@@ -301,6 +371,8 @@ msgstr[3] "Plural form 3"
func TestPluralForms3(t *testing.T) { func TestPluralForms3(t *testing.T) {
// 3 forms // 3 forms
str := ` str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;"
# Some comment # Some comment
@@ -348,6 +420,8 @@ msgstr[3] "Plural form 3"
func TestPluralFormsSpecial(t *testing.T) { func TestPluralFormsSpecial(t *testing.T) {
// 3 forms special // 3 forms special
str := ` str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=3;" "Plural-Forms: nplurals=3;"
"plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" "plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"

37
vendor/github.com/mattn/kinako/README.md generated vendored Normal file
View File

@@ -0,0 +1,37 @@
# kinako
Kinako is small VM written in Go.
![](https://raw.githubusercontent.com/mattn/kinako/master/kinako.png)
(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 Normal file
View File

@@ -0,0 +1,18 @@
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)
}

View File

@@ -75,6 +75,14 @@ type TernaryOpExpr struct {
Rhs Expr Rhs Expr
} }
// CallExpr provide calling expression.
type CallExpr struct {
ExprImpl
Func interface{}
Name string
SubExprs []Expr
}
// ParenExpr provide parent block expression. // ParenExpr provide parent block expression.
type ParenExpr struct { type ParenExpr struct {
ExprImpl ExprImpl

BIN
vendor/github.com/mattn/kinako/kinako.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

View File

@@ -8,13 +8,14 @@ import (
"github.com/mattn/kinako/ast" "github.com/mattn/kinako/ast"
) )
//line parser.go.y:15 //line parser.go.y:16
type yySymType struct { type yySymType struct {
yys int yys int
compstmt []ast.Stmt compstmt []ast.Stmt
stmts []ast.Stmt stmts []ast.Stmt
stmt ast.Stmt stmt ast.Stmt
expr ast.Expr expr ast.Expr
exprs []ast.Expr
tok ast.Token tok ast.Token
term ast.Token term ast.Token
terms ast.Token terms ast.Token
@@ -82,106 +83,107 @@ const yyEofCode = 1
const yyErrCode = 2 const yyErrCode = 2
const yyInitialStackSize = 16 const yyInitialStackSize = 16
//line parser.go.y:194 //line parser.go.y:213
//line yacctab:1 //line yacctab:1
var yyExca = [...]int{ var yyExca = [...]int{
-1, 1, -1, 1,
1, -1, 1, -1,
-2, 0, -2, 0,
-1, 49, -1, 50,
7, 0, 7, 0,
8, 0, 8, 0,
-2, 20, -2, 20,
-1, 50, -1, 51,
7, 0, 7, 0,
8, 0, 8, 0,
-2, 21, -2, 21,
} }
const yyNprod = 36 const yyNprod = 40
const yyPrivate = 57344 const yyPrivate = 57344
var yyTokenNames []string var yyTokenNames []string
var yyStates []string var yyStates []string
const yyLast = 249 const yyLast = 251
var yyAct = [...]int{ var yyAct = [...]int{
9, 6, 7, 33, 35, 4, 22, 23, 3, 18, 9, 6, 7, 33, 35, 37, 22, 23, 60, 3,
24, 25, 26, 37, 38, 39, 2, 40, 33, 35, 24, 25, 26, 38, 39, 40, 1, 41, 33, 35,
17, 42, 43, 44, 45, 46, 47, 48, 49, 50, 8, 43, 44, 45, 46, 47, 48, 49, 50, 51,
51, 52, 53, 54, 55, 56, 57, 58, 27, 28, 52, 53, 54, 55, 56, 57, 58, 59, 61, 42,
30, 32, 34, 36, 8, 1, 21, 60, 0, 29, 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, 31, 0, 0, 22, 23, 5, 0, 24, 25, 26,
19, 61, 0, 41, 0, 33, 35, 27, 28, 30, 19, 0, 0, 0, 0, 33, 35, 27, 28, 30,
32, 34, 36, 0, 19, 21, 0, 0, 29, 31, 32, 34, 36, 0, 19, 21, 0, 0, 29, 31,
0, 0, 22, 23, 0, 0, 24, 25, 26, 0, 0, 0, 22, 23, 0, 0, 24, 25, 26, 0,
0, 0, 0, 59, 33, 35, 27, 28, 30, 32, 0, 0, 0, 0, 33, 35, 27, 28, 30, 32,
34, 36, 0, 20, 21, 0, 0, 29, 31, 0, 0, 36, 0, 0, 0, 0, 0, 29, 31, 0,
0, 22, 23, 0, 0, 24, 25, 26, 0, 0, 0, 22, 23, 0, 0, 24, 25, 26, 27, 28,
0, 0, 0, 33, 35, 27, 28, 30, 32, 34, 30, 32, 0, 33, 35, 0, 0, 0, 0, 29,
36, 0, 0, 21, 0, 0, 29, 31, 0, 0, 31, 0, 0, 22, 23, 0, 0, 24, 25, 26,
22, 23, 0, 0, 24, 25, 26, 0, 0, 0, 30, 32, 10, 11, 15, 33, 35, 0, 0, 29,
0, 0, 33, 35, 27, 28, 30, 32, 0, 36, 31, 0, 0, 22, 23, 0, 0, 24, 25, 26,
0, 0, 0, 0, 0, 29, 31, 0, 0, 22, 0, 12, 10, 11, 15, 33, 35, 0, 13, 14,
23, 0, 0, 24, 25, 26, 27, 28, 30, 32, 16, 24, 25, 26, 6, 7, 0, 0, 0, 33,
0, 33, 35, 0, 0, 0, 0, 29, 31, 0, 35, 12, 0, 0, 0, 0, 0, 0, 13, 14,
0, 22, 23, 0, 0, 24, 25, 26, 30, 32, 16,
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{ var yyPact = [...]int{
-35, -1000, 216, -35, -35, -1000, -1000, -1000, -1000, 89, -35, -1000, 218, -35, -35, -1000, -1000, -1000, -1000, 91,
-1000, -1000, 216, 216, 216, -1000, 216, -1000, 196, -1000, -27, -1000, 218, 218, 218, -1000, 218, -1000, 198, -1000,
216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218,
216, 216, 216, 216, 216, 216, 216, -31, -31, -31, 218, 218, 218, 218, 218, 218, 218, 218, -31, -31,
60, -1000, 118, 31, 203, 203, -31, -31, -31, 189, -31, 62, -1000, 120, 33, 205, 205, -31, -31, -31,
189, -16, -16, -16, -16, 118, 147, 118, 169, -1000, 191, 191, -16, -16, -16, -16, 120, 149, 120, 171,
216, 118, 29, 120, -1000, 218, -1000, 218, 120, 120,
} }
var yyPgo = [...]int{ var yyPgo = [...]int{
0, 45, 8, 44, 0, 16, 5, 55, 0, 16, 9, 20, 0, 8, 53, 50, 115,
} }
var yyR1 = [...]int{ var yyR1 = [...]int{
0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 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, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 6, 6, 7, 7, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8,
} }
var yyR2 = [...]int{ var yyR2 = [...]int{
0, 1, 2, 2, 3, 3, 1, 1, 1, 2, 0, 1, 2, 2, 3, 3, 1, 1, 1, 2,
2, 2, 1, 5, 3, 3, 3, 3, 3, 3, 2, 2, 1, 5, 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,
0, 1, 1, 2, 1, 1, 4, 0, 1, 3, 0, 1, 1, 2, 1, 1,
} }
var yyChk = [...]int{ var yyChk = [...]int{
-1000, -1, -5, -2, -6, -7, 36, 37, -3, -4, -1000, -1, -6, -2, -7, -8, 36, 37, -3, -4,
4, 5, 23, 30, 31, 6, 32, -5, -6, -7, 4, 5, 23, 30, 31, 6, 32, -6, -7, -8,
14, 15, 22, 23, 26, 27, 28, 7, 8, 18, 14, 15, 22, 23, 26, 27, 28, 7, 8, 18,
9, 19, 10, 34, 11, 35, 12, -4, -4, -4, 9, 19, 10, 34, 11, 35, 12, 32, -4, -4,
-4, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -4, -4, -4, -4, -4, -4, -4,
-4, -4, -4, -4, -4, -4, -4, -4, -4, 33, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
16, -4, -5, -4, 33, 16, 33, 17, -4, -4,
} }
var yyDef = [...]int{ var yyDef = [...]int{
30, -2, 1, 30, 31, 32, 34, 35, 3, 6, 34, -2, 1, 34, 35, 36, 38, 39, 3, 6,
7, 8, 0, 0, 0, 12, 0, 2, 31, 33, 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, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 0, 0, 0, 0, 0, 0, 0, 31, 9, 10,
0, 4, 5, 0, 15, 16, 17, 18, 19, -2, 11, 0, 4, 5, 0, 15, 16, 17, 18, 19,
-2, 22, 23, 24, 25, 26, 27, 28, 29, 14, -2, -2, 22, 23, 24, 25, 26, 27, 28, 29,
0, 13, 0, 32, 14, 0, 30, 0, 13, 33,
} }
var yyTok1 = [...]int{ var yyTok1 = [...]int{
@@ -547,19 +549,19 @@ yydefault:
case 1: case 1:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:43 //line parser.go.y:45
{ {
yyVAL.compstmt = nil yyVAL.compstmt = nil
} }
case 2: case 2:
yyDollar = yyS[yypt-2 : yypt+1] yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:47 //line parser.go.y:49
{ {
yyVAL.compstmt = yyDollar[1].stmts yyVAL.compstmt = yyDollar[1].stmts
} }
case 3: case 3:
yyDollar = yyS[yypt-2 : yypt+1] yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:53 //line parser.go.y:55
{ {
yyVAL.stmts = []ast.Stmt{yyDollar[2].stmt} yyVAL.stmts = []ast.Stmt{yyDollar[2].stmt}
if l, ok := yylex.(*Lexer); ok { if l, ok := yylex.(*Lexer); ok {
@@ -568,7 +570,7 @@ yydefault:
} }
case 4: case 4:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:60 //line parser.go.y:62
{ {
if yyDollar[3].stmt != nil { if yyDollar[3].stmt != nil {
yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[3].stmt) yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[3].stmt)
@@ -579,172 +581,196 @@ yydefault:
} }
case 5: case 5:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:71 //line parser.go.y:73
{ {
yyVAL.stmt = &ast.LetStmt{Lhs: yyDollar[1].expr, Operator: "=", Rhs: yyDollar[3].expr} yyVAL.stmt = &ast.LetStmt{Lhs: yyDollar[1].expr, Operator: "=", Rhs: yyDollar[3].expr}
} }
case 6: case 6:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:75 //line parser.go.y:77
{ {
yyVAL.stmt = &ast.ExprStmt{Expr: yyDollar[1].expr} yyVAL.stmt = &ast.ExprStmt{Expr: yyDollar[1].expr}
} }
case 7: case 7:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:81 //line parser.go.y:83
{ {
yyVAL.expr = &ast.IdentExpr{Lit: yyDollar[1].tok.Lit} yyVAL.expr = &ast.IdentExpr{Lit: yyDollar[1].tok.Lit}
} }
case 8: case 8:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:85 //line parser.go.y:87
{ {
yyVAL.expr = &ast.NumberExpr{Lit: yyDollar[1].tok.Lit} yyVAL.expr = &ast.NumberExpr{Lit: yyDollar[1].tok.Lit}
} }
case 9: case 9:
yyDollar = yyS[yypt-2 : yypt+1] yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:89 //line parser.go.y:91
{ {
yyVAL.expr = &ast.UnaryExpr{Operator: "-", Expr: yyDollar[2].expr} yyVAL.expr = &ast.UnaryExpr{Operator: "-", Expr: yyDollar[2].expr}
} }
case 10: case 10:
yyDollar = yyS[yypt-2 : yypt+1] yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:93 //line parser.go.y:95
{ {
yyVAL.expr = &ast.UnaryExpr{Operator: "!", Expr: yyDollar[2].expr} yyVAL.expr = &ast.UnaryExpr{Operator: "!", Expr: yyDollar[2].expr}
} }
case 11: case 11:
yyDollar = yyS[yypt-2 : yypt+1] yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:97 //line parser.go.y:99
{ {
yyVAL.expr = &ast.UnaryExpr{Operator: "^", Expr: yyDollar[2].expr} yyVAL.expr = &ast.UnaryExpr{Operator: "^", Expr: yyDollar[2].expr}
} }
case 12: case 12:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:101 //line parser.go.y:103
{ {
yyVAL.expr = &ast.StringExpr{Lit: yyDollar[1].tok.Lit} yyVAL.expr = &ast.StringExpr{Lit: yyDollar[1].tok.Lit}
} }
case 13: case 13:
yyDollar = yyS[yypt-5 : yypt+1] yyDollar = yyS[yypt-5 : yypt+1]
//line parser.go.y:105 //line parser.go.y:107
{ {
yyVAL.expr = &ast.TernaryOpExpr{Expr: yyDollar[1].expr, Lhs: yyDollar[3].expr, Rhs: yyDollar[5].expr} yyVAL.expr = &ast.TernaryOpExpr{Expr: yyDollar[1].expr, Lhs: yyDollar[3].expr, Rhs: yyDollar[5].expr}
} }
case 14: case 14:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:109 //line parser.go.y:111
{ {
yyVAL.expr = &ast.ParenExpr{SubExpr: yyDollar[2].expr} yyVAL.expr = &ast.ParenExpr{SubExpr: yyDollar[2].expr}
} }
case 15: case 15:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:113 //line parser.go.y:115
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "+", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "+", Rhs: yyDollar[3].expr}
} }
case 16: case 16:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:117 //line parser.go.y:119
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "-", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "-", Rhs: yyDollar[3].expr}
} }
case 17: case 17:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:121 //line parser.go.y:123
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "*", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "*", Rhs: yyDollar[3].expr}
} }
case 18: case 18:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:125 //line parser.go.y:127
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "/", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "/", Rhs: yyDollar[3].expr}
} }
case 19: case 19:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:129 //line parser.go.y:131
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "%", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "%", Rhs: yyDollar[3].expr}
} }
case 20: case 20:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:133 //line parser.go.y:135
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "==", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "==", Rhs: yyDollar[3].expr}
} }
case 21: case 21:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:137 //line parser.go.y:139
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "!=", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "!=", Rhs: yyDollar[3].expr}
} }
case 22: case 22:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:141 //line parser.go.y:143
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: ">", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: ">", Rhs: yyDollar[3].expr}
} }
case 23: case 23:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:145 //line parser.go.y:147
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: ">=", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: ">=", Rhs: yyDollar[3].expr}
} }
case 24: case 24:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:149 //line parser.go.y:151
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "<", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "<", Rhs: yyDollar[3].expr}
} }
case 25: case 25:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:153 //line parser.go.y:155
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "<=", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "<=", Rhs: yyDollar[3].expr}
} }
case 26: case 26:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:157 //line parser.go.y:159
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "|", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "|", Rhs: yyDollar[3].expr}
} }
case 27: case 27:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:161 //line parser.go.y:163
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "||", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "||", Rhs: yyDollar[3].expr}
} }
case 28: case 28:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:165 //line parser.go.y:167
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "&", Rhs: yyDollar[3].expr} yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "&", Rhs: yyDollar[3].expr}
} }
case 29: case 29:
yyDollar = yyS[yypt-3 : yypt+1] yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:169 //line parser.go.y:171
{ {
yyVAL.expr = &ast.BinOpExpr{Lhs: yyDollar[1].expr, Operator: "&&", Rhs: yyDollar[3].expr} 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: case 32:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:179 //line parser.go.y:184
{ {
yyVAL.exprs = []ast.Expr{yyDollar[1].expr}
} }
case 33: 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] yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:182 //line parser.go.y:201
{ {
} }
case 34: case 38:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:187 //line parser.go.y:206
{ {
} }
case 35: case 39:
yyDollar = yyS[yypt-1 : yypt+1] yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:190 //line parser.go.y:209
{ {
} }
} }

View File

@@ -11,12 +11,14 @@ import (
%type<stmts> stmts %type<stmts> stmts
%type<stmt> stmt %type<stmt> stmt
%type<expr> expr %type<expr> expr
%type<exprs> exprs
%union{ %union{
compstmt []ast.Stmt compstmt []ast.Stmt
stmts []ast.Stmt stmts []ast.Stmt
stmt ast.Stmt stmt ast.Stmt
expr ast.Expr expr ast.Expr
exprs []ast.Expr
tok ast.Token tok ast.Token
term ast.Token term ast.Token
terms ast.Token terms ast.Token
@@ -169,6 +171,23 @@ expr :
{ {
$$ = &ast.BinOpExpr{Lhs: $1, Operator: "&&", Rhs: $3} $$ = &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 */ opt_terms : /* none */
| terms | terms

View File

@@ -1,11 +1,11 @@
state 0 state 0
$accept: .compstmt $end $accept: .compstmt $end
opt_terms: . (30) opt_terms: . (34)
';' shift 6 ';' shift 6
'\n' shift 7 '\n' shift 7
. reduce 30 (src line 173) . reduce 34 (src line 192)
compstmt goto 1 compstmt goto 1
stmts goto 3 stmts goto 3
@@ -31,7 +31,7 @@ state 2
'!' shift 13 '!' shift 13
'^' shift 14 '^' shift 14
'(' shift 16 '(' shift 16
. reduce 1 (src line 42) . reduce 1 (src line 44)
stmt goto 8 stmt goto 8
expr goto 9 expr goto 9
@@ -39,48 +39,48 @@ state 2
state 3 state 3
compstmt: stmts.opt_terms compstmt: stmts.opt_terms
stmts: stmts.terms stmt stmts: stmts.terms stmt
opt_terms: . (30) opt_terms: . (34)
';' shift 6 ';' shift 6
'\n' shift 7 '\n' shift 7
. reduce 30 (src line 173) . reduce 34 (src line 192)
opt_terms goto 17 opt_terms goto 17
terms goto 18 terms goto 18
term goto 5 term goto 5
state 4 state 4
opt_terms: terms. (31) opt_terms: terms. (35)
terms: terms.term terms: terms.term
';' shift 6 ';' shift 6
'\n' shift 7 '\n' shift 7
. reduce 31 (src line 174) . reduce 35 (src line 193)
term goto 19 term goto 19
state 5 state 5
terms: term. (32) terms: term. (36)
. reduce 32 (src line 178) . reduce 36 (src line 197)
state 6 state 6
term: ';'. (34) term: ';'. (38)
. reduce 34 (src line 186) . reduce 38 (src line 205)
state 7 state 7
term: '\n'. (35) term: '\n'. (39)
. reduce 35 (src line 189) . reduce 39 (src line 208)
state 8 state 8
stmts: opt_terms stmt. (3) stmts: opt_terms stmt. (3)
. reduce 3 (src line 51) . reduce 3 (src line 53)
state 9 state 9
@@ -120,19 +120,21 @@ state 9
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 6 (src line 74) . reduce 6 (src line 76)
state 10 state 10
expr: IDENT. (7) expr: IDENT. (7)
expr: IDENT.'(' exprs ')'
. reduce 7 (src line 79) '(' shift 37
. reduce 7 (src line 81)
state 11 state 11
expr: NUMBER. (8) expr: NUMBER. (8)
. reduce 8 (src line 84) . reduce 8 (src line 86)
state 12 state 12
@@ -147,7 +149,7 @@ state 12
'(' shift 16 '(' shift 16
. error . error
expr goto 37 expr goto 38
state 13 state 13
expr: '!'.expr expr: '!'.expr
@@ -161,7 +163,7 @@ state 13
'(' shift 16 '(' shift 16
. error . error
expr goto 38 expr goto 39
state 14 state 14
expr: '^'.expr expr: '^'.expr
@@ -175,12 +177,12 @@ state 14
'(' shift 16 '(' shift 16
. error . error
expr goto 39 expr goto 40
state 15 state 15
expr: STRING. (12) expr: STRING. (12)
. reduce 12 (src line 100) . reduce 12 (src line 102)
state 16 state 16
@@ -195,17 +197,17 @@ state 16
'(' shift 16 '(' shift 16
. error . error
expr goto 40 expr goto 41
state 17 state 17
compstmt: stmts opt_terms. (2) compstmt: stmts opt_terms. (2)
. reduce 2 (src line 46) . reduce 2 (src line 48)
state 18 state 18
stmts: stmts terms.stmt stmts: stmts terms.stmt
opt_terms: terms. (31) opt_terms: terms. (35)
terms: terms.term terms: terms.term
IDENT shift 10 IDENT shift 10
@@ -217,16 +219,16 @@ state 18
'(' shift 16 '(' shift 16
';' shift 6 ';' shift 6
'\n' shift 7 '\n' shift 7
. reduce 31 (src line 174) . reduce 35 (src line 193)
stmt goto 41 stmt goto 42
expr goto 9 expr goto 9
term goto 19 term goto 19
state 19 state 19
terms: terms term. (33) terms: terms term. (37)
. reduce 33 (src line 181) . reduce 37 (src line 200)
state 20 state 20
@@ -241,7 +243,7 @@ state 20
'(' shift 16 '(' shift 16
. error . error
expr goto 42 expr goto 43
state 21 state 21
expr: expr '?'.expr ':' expr expr: expr '?'.expr ':' expr
@@ -255,7 +257,7 @@ state 21
'(' shift 16 '(' shift 16
. error . error
expr goto 43 expr goto 44
state 22 state 22
expr: expr '+'.expr expr: expr '+'.expr
@@ -269,7 +271,7 @@ state 22
'(' shift 16 '(' shift 16
. error . error
expr goto 44 expr goto 45
state 23 state 23
expr: expr '-'.expr expr: expr '-'.expr
@@ -283,7 +285,7 @@ state 23
'(' shift 16 '(' shift 16
. error . error
expr goto 45 expr goto 46
state 24 state 24
expr: expr '*'.expr expr: expr '*'.expr
@@ -297,7 +299,7 @@ state 24
'(' shift 16 '(' shift 16
. error . error
expr goto 46 expr goto 47
state 25 state 25
expr: expr '/'.expr expr: expr '/'.expr
@@ -311,7 +313,7 @@ state 25
'(' shift 16 '(' shift 16
. error . error
expr goto 47 expr goto 48
state 26 state 26
expr: expr '%'.expr expr: expr '%'.expr
@@ -325,7 +327,7 @@ state 26
'(' shift 16 '(' shift 16
. error . error
expr goto 48 expr goto 49
state 27 state 27
expr: expr EQEQ.expr expr: expr EQEQ.expr
@@ -339,7 +341,7 @@ state 27
'(' shift 16 '(' shift 16
. error . error
expr goto 49 expr goto 50
state 28 state 28
expr: expr NEQ.expr expr: expr NEQ.expr
@@ -353,7 +355,7 @@ state 28
'(' shift 16 '(' shift 16
. error . error
expr goto 50 expr goto 51
state 29 state 29
expr: expr '>'.expr expr: expr '>'.expr
@@ -367,7 +369,7 @@ state 29
'(' shift 16 '(' shift 16
. error . error
expr goto 51 expr goto 52
state 30 state 30
expr: expr GE.expr expr: expr GE.expr
@@ -381,7 +383,7 @@ state 30
'(' shift 16 '(' shift 16
. error . error
expr goto 52 expr goto 53
state 31 state 31
expr: expr '<'.expr expr: expr '<'.expr
@@ -395,7 +397,7 @@ state 31
'(' shift 16 '(' shift 16
. error . error
expr goto 53 expr goto 54
state 32 state 32
expr: expr LE.expr expr: expr LE.expr
@@ -409,7 +411,7 @@ state 32
'(' shift 16 '(' shift 16
. error . error
expr goto 54 expr goto 55
state 33 state 33
expr: expr '|'.expr expr: expr '|'.expr
@@ -423,7 +425,7 @@ state 33
'(' shift 16 '(' shift 16
. error . error
expr goto 55 expr goto 56
state 34 state 34
expr: expr OROR.expr expr: expr OROR.expr
@@ -437,7 +439,7 @@ state 34
'(' shift 16 '(' shift 16
. error . error
expr goto 56 expr goto 57
state 35 state 35
expr: expr '&'.expr expr: expr '&'.expr
@@ -451,7 +453,7 @@ state 35
'(' shift 16 '(' shift 16
. error . error
expr goto 57 expr goto 58
state 36 state 36
expr: expr ANDAND.expr expr: expr ANDAND.expr
@@ -465,11 +467,27 @@ state 36
'(' shift 16 '(' shift 16
. error . error
expr goto 58 expr goto 59
37: shift/reduce conflict (shift 33(0), red'n 9(10)) on '|'
37: shift/reduce conflict (shift 35(0), red'n 9(10)) on '&'
state 37 state 37
expr: IDENT '('.exprs ')'
exprs: . (31)
IDENT shift 10
NUMBER shift 11
STRING shift 15
'-' shift 12
'!' shift 13
'^' shift 14
'(' shift 16
. reduce 31 (src line 179)
expr goto 61
exprs goto 60
38: shift/reduce conflict (shift 33(0), red'n 9(10)) on '|'
38: shift/reduce conflict (shift 35(0), red'n 9(10)) on '&'
state 38
expr: '-' expr. (9) expr: '-' expr. (9)
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
@@ -490,12 +508,12 @@ state 37
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 9 (src line 88) . reduce 9 (src line 90)
38: shift/reduce conflict (shift 33(0), red'n 10(10)) on '|' 39: shift/reduce conflict (shift 33(0), red'n 10(10)) on '|'
38: shift/reduce conflict (shift 35(0), red'n 10(10)) on '&' 39: shift/reduce conflict (shift 35(0), red'n 10(10)) on '&'
state 38 state 39
expr: '!' expr. (10) expr: '!' expr. (10)
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
@@ -516,12 +534,12 @@ state 38
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 10 (src line 92) . reduce 10 (src line 94)
39: shift/reduce conflict (shift 33(0), red'n 11(10)) on '|' 40: shift/reduce conflict (shift 33(0), red'n 11(10)) on '|'
39: shift/reduce conflict (shift 35(0), red'n 11(10)) on '&' 40: shift/reduce conflict (shift 35(0), red'n 11(10)) on '&'
state 39 state 40
expr: '^' expr. (11) expr: '^' expr. (11)
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
@@ -542,10 +560,10 @@ state 39
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 11 (src line 96) . reduce 11 (src line 98)
state 40 state 41
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: '(' expr.')' expr: '(' expr.')'
expr: expr.'+' expr expr: expr.'+' expr
@@ -578,19 +596,19 @@ state 40
'*' shift 24 '*' shift 24
'/' shift 25 '/' shift 25
'%' shift 26 '%' shift 26
')' shift 59 ')' shift 62
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. error . error
state 41 state 42
stmts: stmts terms stmt. (4) stmts: stmts terms stmt. (4)
. reduce 4 (src line 59) . reduce 4 (src line 61)
state 42 state 43
stmt: expr '=' expr. (5) stmt: expr '=' expr. (5)
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
@@ -625,10 +643,10 @@ state 42
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 5 (src line 69) . reduce 5 (src line 71)
state 43 state 44
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr '?' expr.':' expr expr: expr '?' expr.':' expr
expr: expr.'+' expr expr: expr.'+' expr
@@ -654,7 +672,7 @@ state 43
OROR shift 34 OROR shift 34
ANDAND shift 36 ANDAND shift 36
'?' shift 21 '?' shift 21
':' shift 60 ':' shift 63
'>' shift 29 '>' shift 29
'<' shift 31 '<' shift 31
'+' shift 22 '+' shift 22
@@ -667,9 +685,9 @@ state 43
. error . error
44: shift/reduce conflict (shift 33(0), red'n 15(8)) on '|' 45: shift/reduce conflict (shift 33(0), red'n 15(8)) on '|'
44: shift/reduce conflict (shift 35(0), red'n 15(8)) on '&' 45: shift/reduce conflict (shift 35(0), red'n 15(8)) on '&'
state 44 state 45
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr '+' expr. (15) expr: expr '+' expr. (15)
@@ -693,12 +711,12 @@ state 44
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 15 (src line 112) . reduce 15 (src line 114)
45: shift/reduce conflict (shift 33(0), red'n 16(8)) on '|' 46: shift/reduce conflict (shift 33(0), red'n 16(8)) on '|'
45: shift/reduce conflict (shift 35(0), red'n 16(8)) on '&' 46: shift/reduce conflict (shift 35(0), red'n 16(8)) on '&'
state 45 state 46
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -722,12 +740,12 @@ state 45
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 16 (src line 116) . reduce 16 (src line 118)
46: shift/reduce conflict (shift 33(0), red'n 17(9)) on '|' 47: shift/reduce conflict (shift 33(0), red'n 17(9)) on '|'
46: shift/reduce conflict (shift 35(0), red'n 17(9)) on '&' 47: shift/reduce conflict (shift 35(0), red'n 17(9)) on '&'
state 46 state 47
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -748,12 +766,12 @@ state 46
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 17 (src line 120) . reduce 17 (src line 122)
47: shift/reduce conflict (shift 33(0), red'n 18(9)) on '|' 48: shift/reduce conflict (shift 33(0), red'n 18(9)) on '|'
47: shift/reduce conflict (shift 35(0), red'n 18(9)) on '&' 48: shift/reduce conflict (shift 35(0), red'n 18(9)) on '&'
state 47 state 48
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -774,12 +792,12 @@ state 47
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 18 (src line 124) . reduce 18 (src line 126)
48: shift/reduce conflict (shift 33(0), red'n 19(9)) on '|' 49: shift/reduce conflict (shift 33(0), red'n 19(9)) on '|'
48: shift/reduce conflict (shift 35(0), red'n 19(9)) on '&' 49: shift/reduce conflict (shift 35(0), red'n 19(9)) on '&'
state 48 state 49
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -800,12 +818,12 @@ state 48
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 19 (src line 128) . reduce 19 (src line 130)
49: shift/reduce conflict (shift 33(0), red'n 20(6)) on '|' 50: shift/reduce conflict (shift 33(0), red'n 20(6)) on '|'
49: shift/reduce conflict (shift 35(0), red'n 20(6)) on '&' 50: shift/reduce conflict (shift 35(0), red'n 20(6)) on '&'
state 49 state 50
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -837,12 +855,12 @@ state 49
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 20 (src line 132) . reduce 20 (src line 134)
50: shift/reduce conflict (shift 33(0), red'n 21(6)) on '|' 51: shift/reduce conflict (shift 33(0), red'n 21(6)) on '|'
50: shift/reduce conflict (shift 35(0), red'n 21(6)) on '&' 51: shift/reduce conflict (shift 35(0), red'n 21(6)) on '&'
state 50 state 51
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -874,12 +892,12 @@ state 50
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 21 (src line 136) . reduce 21 (src line 138)
51: shift/reduce conflict (shift 33(0), red'n 22(7)) on '|' 52: shift/reduce conflict (shift 33(0), red'n 22(7)) on '|'
51: shift/reduce conflict (shift 35(0), red'n 22(7)) on '&' 52: shift/reduce conflict (shift 35(0), red'n 22(7)) on '&'
state 51 state 52
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -905,12 +923,12 @@ state 51
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 22 (src line 140) . reduce 22 (src line 142)
52: shift/reduce conflict (shift 33(0), red'n 23(7)) on '|' 53: shift/reduce conflict (shift 33(0), red'n 23(7)) on '|'
52: shift/reduce conflict (shift 35(0), red'n 23(7)) on '&' 53: shift/reduce conflict (shift 35(0), red'n 23(7)) on '&'
state 52 state 53
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -936,12 +954,12 @@ state 52
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 23 (src line 144) . reduce 23 (src line 146)
53: shift/reduce conflict (shift 33(0), red'n 24(7)) on '|' 54: shift/reduce conflict (shift 33(0), red'n 24(7)) on '|'
53: shift/reduce conflict (shift 35(0), red'n 24(7)) on '&' 54: shift/reduce conflict (shift 35(0), red'n 24(7)) on '&'
state 53 state 54
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -967,12 +985,12 @@ state 53
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 24 (src line 148) . reduce 24 (src line 150)
54: shift/reduce conflict (shift 33(0), red'n 25(7)) on '|' 55: shift/reduce conflict (shift 33(0), red'n 25(7)) on '|'
54: shift/reduce conflict (shift 35(0), red'n 25(7)) on '&' 55: shift/reduce conflict (shift 35(0), red'n 25(7)) on '&'
state 54 state 55
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -998,26 +1016,26 @@ state 54
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 25 (src line 152) . reduce 25 (src line 154)
55: shift/reduce conflict (shift 27(6), red'n 26(0)) on EQEQ 56: shift/reduce conflict (shift 27(6), red'n 26(0)) on EQEQ
55: shift/reduce conflict (shift 28(6), red'n 26(0)) on NEQ 56: shift/reduce conflict (shift 28(6), red'n 26(0)) on NEQ
55: shift/reduce conflict (shift 30(7), red'n 26(0)) on GE 56: shift/reduce conflict (shift 30(7), red'n 26(0)) on GE
55: shift/reduce conflict (shift 32(7), red'n 26(0)) on LE 56: shift/reduce conflict (shift 32(7), red'n 26(0)) on LE
55: shift/reduce conflict (shift 34(3), red'n 26(0)) on OROR 56: shift/reduce conflict (shift 34(3), red'n 26(0)) on OROR
55: shift/reduce conflict (shift 36(4), red'n 26(0)) on ANDAND 56: shift/reduce conflict (shift 36(4), red'n 26(0)) on ANDAND
55: shift/reduce conflict (shift 21(2), red'n 26(0)) on '?' 56: shift/reduce conflict (shift 21(2), red'n 26(0)) on '?'
55: shift/reduce conflict (shift 29(7), red'n 26(0)) on '>' 56: shift/reduce conflict (shift 29(7), red'n 26(0)) on '>'
55: shift/reduce conflict (shift 31(7), red'n 26(0)) on '<' 56: shift/reduce conflict (shift 31(7), red'n 26(0)) on '<'
55: shift/reduce conflict (shift 22(8), red'n 26(0)) on '+' 56: shift/reduce conflict (shift 22(8), red'n 26(0)) on '+'
55: shift/reduce conflict (shift 23(8), red'n 26(0)) on '-' 56: shift/reduce conflict (shift 23(8), red'n 26(0)) on '-'
55: shift/reduce conflict (shift 24(9), red'n 26(0)) on '*' 56: shift/reduce conflict (shift 24(9), red'n 26(0)) on '*'
55: shift/reduce conflict (shift 25(9), red'n 26(0)) on '/' 56: shift/reduce conflict (shift 25(9), red'n 26(0)) on '/'
55: shift/reduce conflict (shift 26(9), red'n 26(0)) on '%' 56: shift/reduce conflict (shift 26(9), red'n 26(0)) on '%'
55: shift/reduce conflict (shift 33(0), red'n 26(0)) on '|' 56: shift/reduce conflict (shift 33(0), red'n 26(0)) on '|'
55: shift/reduce conflict (shift 35(0), red'n 26(0)) on '&' 56: shift/reduce conflict (shift 35(0), red'n 26(0)) on '&'
state 55 state 56
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -1052,12 +1070,12 @@ state 55
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 26 (src line 156) . reduce 26 (src line 158)
56: shift/reduce conflict (shift 33(0), red'n 27(3)) on '|' 57: shift/reduce conflict (shift 33(0), red'n 27(3)) on '|'
56: shift/reduce conflict (shift 35(0), red'n 27(3)) on '&' 57: shift/reduce conflict (shift 35(0), red'n 27(3)) on '&'
state 56 state 57
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -1090,26 +1108,26 @@ state 56
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 27 (src line 160) . reduce 27 (src line 162)
57: shift/reduce conflict (shift 27(6), red'n 28(0)) on EQEQ 58: shift/reduce conflict (shift 27(6), red'n 28(0)) on EQEQ
57: shift/reduce conflict (shift 28(6), red'n 28(0)) on NEQ 58: shift/reduce conflict (shift 28(6), red'n 28(0)) on NEQ
57: shift/reduce conflict (shift 30(7), red'n 28(0)) on GE 58: shift/reduce conflict (shift 30(7), red'n 28(0)) on GE
57: shift/reduce conflict (shift 32(7), red'n 28(0)) on LE 58: shift/reduce conflict (shift 32(7), red'n 28(0)) on LE
57: shift/reduce conflict (shift 34(3), red'n 28(0)) on OROR 58: shift/reduce conflict (shift 34(3), red'n 28(0)) on OROR
57: shift/reduce conflict (shift 36(4), red'n 28(0)) on ANDAND 58: shift/reduce conflict (shift 36(4), red'n 28(0)) on ANDAND
57: shift/reduce conflict (shift 21(2), red'n 28(0)) on '?' 58: shift/reduce conflict (shift 21(2), red'n 28(0)) on '?'
57: shift/reduce conflict (shift 29(7), red'n 28(0)) on '>' 58: shift/reduce conflict (shift 29(7), red'n 28(0)) on '>'
57: shift/reduce conflict (shift 31(7), red'n 28(0)) on '<' 58: shift/reduce conflict (shift 31(7), red'n 28(0)) on '<'
57: shift/reduce conflict (shift 22(8), red'n 28(0)) on '+' 58: shift/reduce conflict (shift 22(8), red'n 28(0)) on '+'
57: shift/reduce conflict (shift 23(8), red'n 28(0)) on '-' 58: shift/reduce conflict (shift 23(8), red'n 28(0)) on '-'
57: shift/reduce conflict (shift 24(9), red'n 28(0)) on '*' 58: shift/reduce conflict (shift 24(9), red'n 28(0)) on '*'
57: shift/reduce conflict (shift 25(9), red'n 28(0)) on '/' 58: shift/reduce conflict (shift 25(9), red'n 28(0)) on '/'
57: shift/reduce conflict (shift 26(9), red'n 28(0)) on '%' 58: shift/reduce conflict (shift 26(9), red'n 28(0)) on '%'
57: shift/reduce conflict (shift 33(0), red'n 28(0)) on '|' 58: shift/reduce conflict (shift 33(0), red'n 28(0)) on '|'
57: shift/reduce conflict (shift 35(0), red'n 28(0)) on '&' 58: shift/reduce conflict (shift 35(0), red'n 28(0)) on '&'
state 57 state 58
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -1144,12 +1162,12 @@ state 57
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 28 (src line 164) . reduce 28 (src line 166)
58: shift/reduce conflict (shift 33(0), red'n 29(4)) on '|' 59: shift/reduce conflict (shift 33(0), red'n 29(4)) on '|'
58: shift/reduce conflict (shift 35(0), red'n 29(4)) on '&' 59: shift/reduce conflict (shift 35(0), red'n 29(4)) on '&'
state 58 state 59
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr.'+' expr expr: expr.'+' expr
expr: expr.'-' expr expr: expr.'-' expr
@@ -1181,16 +1199,63 @@ state 58
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 29 (src line 168) . reduce 29 (src line 170)
state 59
expr: '(' expr ')'. (14)
. reduce 14 (src line 108)
state 60 state 60
expr: IDENT '(' exprs.')'
exprs: exprs.',' expr
',' shift 65
')' shift 64
. error
state 61
expr: expr.'?' expr ':' expr
expr: expr.'+' expr
expr: expr.'-' expr
expr: expr.'*' expr
expr: expr.'/' expr
expr: expr.'%' expr
expr: expr.EQEQ expr
expr: expr.NEQ expr
expr: expr.'>' expr
expr: expr.GE expr
expr: expr.'<' expr
expr: expr.LE expr
expr: expr.'|' expr
expr: expr.OROR expr
expr: expr.'&' expr
expr: expr.ANDAND expr
exprs: expr. (32)
EQEQ shift 27
NEQ shift 28
GE shift 30
LE shift 32
OROR shift 34
ANDAND shift 36
'?' shift 21
'>' shift 29
'<' shift 31
'+' shift 22
'-' shift 23
'*' shift 24
'/' shift 25
'%' shift 26
'|' shift 33
'&' shift 35
. reduce 32 (src line 183)
state 62
expr: '(' expr ')'. (14)
. reduce 14 (src line 110)
state 63
expr: expr '?' expr ':'.expr expr: expr '?' expr ':'.expr
IDENT shift 10 IDENT shift 10
@@ -1202,11 +1267,31 @@ state 60
'(' shift 16 '(' shift 16
. error . error
expr goto 61 expr goto 66
61: shift/reduce conflict (shift 33(0), red'n 13(2)) on '|' state 64
61: shift/reduce conflict (shift 35(0), red'n 13(2)) on '&' expr: IDENT '(' exprs ')'. (30)
state 61
. reduce 30 (src line 174)
state 65
exprs: exprs ','.expr
IDENT shift 10
NUMBER shift 11
STRING shift 15
'-' shift 12
'!' shift 13
'^' shift 14
'(' shift 16
. error
expr goto 67
66: shift/reduce conflict (shift 33(0), red'n 13(2)) on '|'
66: shift/reduce conflict (shift 35(0), red'n 13(2)) on '&'
state 66
expr: expr.'?' expr ':' expr expr: expr.'?' expr ':' expr
expr: expr '?' expr ':' expr. (13) expr: expr '?' expr ':' expr. (13)
expr: expr.'+' expr expr: expr.'+' expr
@@ -1241,18 +1326,56 @@ state 61
'%' shift 26 '%' shift 26
'|' shift 33 '|' shift 33
'&' shift 35 '&' shift 35
. reduce 13 (src line 104) . reduce 13 (src line 106)
37 terminals, 8 nonterminals state 67
36 grammar rules, 62/8000 states expr: expr.'?' expr ':' expr
expr: expr.'+' expr
expr: expr.'-' expr
expr: expr.'*' expr
expr: expr.'/' expr
expr: expr.'%' expr
expr: expr.EQEQ expr
expr: expr.NEQ expr
expr: expr.'>' expr
expr: expr.GE expr
expr: expr.'<' expr
expr: expr.LE expr
expr: expr.'|' expr
expr: expr.OROR expr
expr: expr.'&' expr
expr: expr.ANDAND expr
exprs: exprs ',' expr. (33)
EQEQ shift 27
NEQ shift 28
GE shift 30
LE shift 32
OROR shift 34
ANDAND shift 36
'?' shift 21
'>' shift 29
'<' shift 31
'+' shift 22
'-' shift 23
'*' shift 24
'/' shift 25
'%' shift 26
'|' shift 33
'&' shift 35
. reduce 33 (src line 187)
37 terminals, 9 nonterminals
40 grammar rules, 68/2000 states
66 shift/reduce, 0 reduce/reduce conflicts reported 66 shift/reduce, 0 reduce/reduce conflicts reported
57 working sets used 58 working sets used
memory: parser 35/120000 memory: parser 38/30000
46 extra closures 49 extra closures
390 shift entries, 5 exceptions 439 shift entries, 5 exceptions
34 goto entries 37 goto entries
2 entries saved by goto default 2 entries saved by goto default
Optimizer space used: output 249/120000 Optimizer space used: output 251/30000
249 table entries, 78 zero 251 table entries, 74 zero
maximum spread: 37, maximum offset: 60 maximum spread: 37, maximum offset: 65

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@@ -39,14 +40,6 @@ func (e *Error) Error() string {
// Func is function interface to reflect functions internaly. // Func is function interface to reflect functions internaly.
type Func func(args ...reflect.Value) (reflect.Value, error) type Func func(args ...reflect.Value) (reflect.Value, error)
func (f Func) String() string {
return fmt.Sprintf("[Func: %p]", f)
}
func ToFunc(f Func) reflect.Value {
return reflect.ValueOf(f)
}
// Run executes statements in the specified environment. // Run executes statements in the specified environment.
func Run(stmts []ast.Stmt, env *Env) (reflect.Value, error) { func Run(stmts []ast.Stmt, env *Env) (reflect.Value, error) {
rv := NilValue rv := NilValue
@@ -386,6 +379,80 @@ func invokeExpr(expr ast.Expr, env *Env) (reflect.Value, error) {
default: default:
return NilValue, errors.New("Unknown operator") 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: case *ast.TernaryOpExpr:
rv, err := invokeExpr(e.Expr, env) rv, err := invokeExpr(e.Expr, env)
if err != nil { if err != nil {

54
vendor/github.com/mattn/kinako/vm/vm_test.go generated vendored Normal file
View File

@@ -0,0 +1,54 @@
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)
}
}
}