9 Commits

Author SHA1 Message Date
Leonel Quinteros
7d86bb66fe Unify fmt.Sprintf behaviour on Po and Locale 2017-11-02 10:21:20 -03:00
Leonel Quinteros
50cdb4e058 Add Go 1.9 to travis builds 2017-10-26 16:27:26 -03:00
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
21 changed files with 999 additions and 462 deletions

View File

@@ -4,6 +4,7 @@ go:
- 1.6
- 1.7
- 1.8
- 1.9
- tip
before_install:

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)
[![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)
[![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)
@@ -9,8 +9,6 @@
[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
@@ -118,39 +116,6 @@ A library directory structure can look like:
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
## 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.
```go
import "github.com/leonelquinteros/gotext"
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
// 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
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.
```go
import "github.com/leonelquinteros/gotext"
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Configure package
@@ -189,7 +160,7 @@ func main() {
name := "John"
// 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.
```go
import "github.com/leonelquinteros/gotext"
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Create Locale with library path and language code
@@ -211,13 +185,13 @@ func main() {
l.AddDomain("default")
// Translate text from default domain
println(l.Get("Translate this"))
fmt.Println(l.Get("Translate this"))
// Load different domain
l.AddDomain("translations")
// 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.
```go
import "github.com/leonelquinteros/gotext"
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Set PO content
@@ -251,10 +228,10 @@ msgstr "This one sets the var: %s"
`
// Create Po object
po := new(Po)
po := new(gotext.Po)
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)
```go
import "github.com/leonelquinteros/gotext"
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Set PO content
@@ -292,10 +272,10 @@ msgstr[1] "This one is the plural: %s"
`
// Create Po object
po := new(Po)
po := new(gotext.Po)
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"
}
```

View File

@@ -3,22 +3,27 @@ Package gotext implements GNU gettext utilities.
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() {
// Configure package
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
// 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
println(gotext.GetD("domain2", "Another text on a different domain"))
fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
}
*/
package gotext
import "fmt"
// Global environment variables
var (
// Default domain to look at when no domain is specified. Used by package level functions.
@@ -149,3 +154,12 @@ func GetNDC(dom, str, plural string, n int, ctx string, vars ...interface{}) str
// Return translation
return storage.GetNDC(dom, str, plural, n, ctx, vars...)
}
// printf applies text formatting only when needed to parse variables.
func printf(str string, vars ...interface{}) string {
if len(vars) > 0 {
return fmt.Sprintf(str, vars...)
}
return str
}

View File

@@ -3,6 +3,7 @@ package gotext
import (
"os"
"path"
"sync"
"testing"
)
@@ -32,10 +33,12 @@ func TestGettersSetters(t *testing.T) {
func TestPackageFunctions(t *testing.T) {
// Set PO content
str := `
# msgid ""
# msgstr ""
msgid ""
msgstr "Project-Id-Version: %s\n"
"Report-Msgid-Bugs-To: %s\n"
# Initial comment
# Headers below
# More Headers below
"Language: en\n"
"Content-Type: text/plain; charset=UTF-8\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[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"
msgid "One with var: %s"
msgid_plural "Several with vars: %s"
@@ -79,6 +74,11 @@ msgstr "Some random translation in a context"
msgid "More"
msgstr "More translation"
msgid "Untranslated"
msgid_plural "Several untranslated"
msgstr[0] ""
msgstr[1] ""
`
// 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) {
// Set PO content
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())
}
// Init sync channels
c1 := make(chan bool)
c2 := make(chan bool)
var wg sync.WaitGroup
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")
done <- true
}(c1)
GetN("One with var: %s", "Several with vars: %s", 2, "test")
}
go func(done chan bool) {
Get("My text")
done <- true
}(c2)
Get("My text")
wg.Wait()
}

View File

@@ -1,7 +1,6 @@
package gotext
import (
"fmt"
"os"
"path"
"sync"
@@ -14,7 +13,10 @@ multiple languages at the same time by working with this object.
Example:
import "github.com/leonelquinteros/gotext"
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
func main() {
// Create Locale with library path and language code
@@ -24,13 +26,13 @@ Example:
l.AddDomain("default")
// 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')
l.AddDomain("extras")
// Translate text from domain
println(l.GetD("extras", "Translate this"))
fmt.Println(l.GetD("extras", "Translate this"))
}
*/
@@ -135,7 +137,7 @@ func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) stri
}
// Return the same we received by default
return fmt.Sprintf(plural, vars...)
return printf(plural, vars...)
}
// GetC uses a domain "default" to return the corresponding translation of the given string in the given context.
@@ -172,5 +174,5 @@ func (l *Locale) GetNDC(dom, str, plural string, n int, ctx string, vars ...inte
}
// Return the same we received by default
return fmt.Sprintf(plural, vars...)
return printf(plural, vars...)
}

View File

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

150
po.go
View File

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

View File

@@ -9,6 +9,9 @@ import (
func TestPo(t *testing.T) {
// Set PO content
str := `
msgid ""
msgstr ""
# Initial comment
# Headers below
"Language: en\n"
@@ -48,14 +51,6 @@ msgstr[0] "This one is the singular: %s"
msgstr[1] "This one is the plural: %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"
msgid "One with var: %s"
msgid_plural "Several with vars: %s"
@@ -69,8 +64,17 @@ msgctxt "Ctx"
msgid "Some random 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"
msgstr "More translation"
`
// Write PO content to file
@@ -143,17 +147,6 @@ msgstr "More translation"
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
v = "Test"
tr = po.GetC("One with var: %s", "Ctx", v)
@@ -177,6 +170,27 @@ msgstr "More translation"
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
tr = po.Get("More")
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) {
// Set PO content
str := `
msgid ""
msgstr ""
# Initial comment
# Headers below
"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) {
// Single form
str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=1; plural=0;"
# Some comment
@@ -262,6 +330,8 @@ msgstr[3] "Plural form 3"
func TestPluralForms2(t *testing.T) {
// 2 forms
str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;"
# Some comment
@@ -301,6 +371,8 @@ msgstr[3] "Plural form 3"
func TestPluralForms3(t *testing.T) {
// 3 forms
str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;"
# Some comment
@@ -348,6 +420,8 @@ msgstr[3] "Plural form 3"
func TestPluralFormsSpecial(t *testing.T) {
// 3 forms special
str := `
msgid ""
msgstr ""
"Plural-Forms: nplurals=3;"
"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
}
// CallExpr provide calling expression.
type CallExpr struct {
ExprImpl
Func interface{}
Name string
SubExprs []Expr
}
// ParenExpr provide parent block expression.
type ParenExpr struct {
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"
)
//line parser.go.y:15
//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
@@ -82,106 +83,107 @@ const yyEofCode = 1
const yyErrCode = 2
const yyInitialStackSize = 16
//line parser.go.y:194
//line parser.go.y:213
//line yacctab:1
var yyExca = [...]int{
-1, 1,
1, -1,
-2, 0,
-1, 49,
-1, 50,
7, 0,
8, 0,
-2, 20,
-1, 50,
-1, 51,
7, 0,
8, 0,
-2, 21,
}
const yyNprod = 36
const yyNprod = 40
const yyPrivate = 57344
var yyTokenNames []string
var yyStates []string
const yyLast = 249
const yyLast = 251
var yyAct = [...]int{
9, 6, 7, 33, 35, 4, 22, 23, 3, 18,
24, 25, 26, 37, 38, 39, 2, 40, 33, 35,
17, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 58, 27, 28,
30, 32, 34, 36, 8, 1, 21, 60, 0, 29,
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, 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,
0, 0, 22, 23, 0, 0, 24, 25, 26, 0,
0, 0, 0, 59, 33, 35, 27, 28, 30, 32,
34, 36, 0, 20, 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, 34,
36, 0, 0, 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,
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, 216, -35, -35, -1000, -1000, -1000, -1000, 89,
-1000, -1000, 216, 216, 216, -1000, 216, -1000, 196, -1000,
216, 216, 216, 216, 216, 216, 216, 216, 216, 216,
216, 216, 216, 216, 216, 216, 216, -31, -31, -31,
60, -1000, 118, 31, 203, 203, -31, -31, -31, 189,
189, -16, -16, -16, -16, 118, 147, 118, 169, -1000,
216, 118,
-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, 45, 8, 44, 0, 16, 5, 55,
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,
5, 5, 6, 6, 7, 7,
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,
0, 1, 1, 2, 1, 1,
4, 0, 1, 3, 0, 1, 1, 2, 1, 1,
}
var yyChk = [...]int{
-1000, -1, -5, -2, -6, -7, 36, 37, -3, -4,
4, 5, 23, 30, 31, 6, 32, -5, -6, -7,
-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, -4, -4, -4,
-4, -3, -4, -4, -4, -4, -4, -4, -4, -4,
-4, -4, -4, -4, -4, -4, -4, -4, -4, 33,
16, -4,
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{
30, -2, 1, 30, 31, 32, 34, 35, 3, 6,
7, 8, 0, 0, 0, 12, 0, 2, 31, 33,
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, 9, 10, 11,
0, 4, 5, 0, 15, 16, 17, 18, 19, -2,
-2, 22, 23, 24, 25, 26, 27, 28, 29, 14,
0, 13,
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{
@@ -547,19 +549,19 @@ yydefault:
case 1:
yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:43
//line parser.go.y:45
{
yyVAL.compstmt = nil
}
case 2:
yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:47
//line parser.go.y:49
{
yyVAL.compstmt = yyDollar[1].stmts
}
case 3:
yyDollar = yyS[yypt-2 : yypt+1]
//line parser.go.y:53
//line parser.go.y:55
{
yyVAL.stmts = []ast.Stmt{yyDollar[2].stmt}
if l, ok := yylex.(*Lexer); ok {
@@ -568,7 +570,7 @@ yydefault:
}
case 4:
yyDollar = yyS[yypt-3 : yypt+1]
//line parser.go.y:60
//line parser.go.y:62
{
if yyDollar[3].stmt != nil {
yyVAL.stmts = append(yyDollar[1].stmts, yyDollar[3].stmt)
@@ -579,172 +581,196 @@ yydefault:
}
case 5:
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}
}
case 6:
yyDollar = yyS[yypt-1 : yypt+1]
//line parser.go.y:75
//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:81
//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:85
//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:89
//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:93
//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:97
//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:101
//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:105
//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:109
//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:113
//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:117
//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:121
//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:125
//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:129
//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:133
//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:137
//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:141
//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:145
//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:149
//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:153
//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:157
//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:161
//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:165
//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:169
//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:179
//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:182
//line parser.go.y:201
{
}
case 34:
case 38:
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]
//line parser.go.y:190
//line parser.go.y:209
{
}
}

View File

@@ -11,12 +11,14 @@ import (
%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
@@ -169,6 +171,23 @@ 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

View File

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

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"math"
"os"
"reflect"
"strconv"
"strings"
@@ -39,14 +40,6 @@ func (e *Error) Error() string {
// Func is function interface to reflect functions internaly.
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.
func Run(stmts []ast.Stmt, env *Env) (reflect.Value, error) {
rv := NilValue
@@ -386,6 +379,80 @@ func invokeExpr(expr ast.Expr, env *Env) (reflect.Value, error) {
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 {

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)
}
}
}